1/*
2 * Copyright 2005, J��r��me Duval. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Inspired by SoundCapture from Be newsletter (Media Kit Basics:
6 * 	Consumers and Producers)
7 */
8
9#include <stdio.h>
10#include <string.h>
11#include <IconUtils.h>
12#include <MimeType.h>
13#include <Screen.h>
14#include <Window.h>
15#include "DrawingTidbits.h"
16#include "ScopeView.h"
17
18#define SAMPLES_COUNT 20000
19
20//#define TRACE 1
21#ifdef TRACE
22#define TRACE(x...) printf(x)
23#else
24#define TRACE(x...)
25#endif
26
27ScopeView::ScopeView(BRect rect, uint32 resizeFlags)
28	: BView(rect, "scope", resizeFlags, B_WILL_DRAW | B_FRAME_EVENTS),
29	fThreadId(-1),
30	fBitmap(NULL),
31	fBitmapView(NULL),
32	fIsRendering(false),
33	fMediaTrack(NULL),
34	fMainTime(0),
35	fRightTime(1000000),
36	fLeftTime(0),
37	fTotalTime(1000000)
38{
39	fHeight = Bounds().Height();
40}
41
42
43ScopeView::~ScopeView()
44{
45	delete_sem(fRenderSem);
46}
47
48
49void
50ScopeView::AttachedToWindow()
51{
52	SetViewColor(B_TRANSPARENT_COLOR);
53	InitBitmap();
54	Run();
55}
56
57
58void
59ScopeView::DetachedFromWindow()
60{
61	Quit();
62}
63
64
65void
66ScopeView::Draw(BRect updateRect)
67{
68	BRect bounds = Bounds();
69	SetHighColor(0,0,0);
70
71	if (!fIsRendering)
72		DrawBitmapAsync(fBitmap, BPoint(0, 0));
73	else
74		FillRect(bounds);
75
76	float x = 0;
77	if (fTotalTime != 0)
78		x += (fMainTime - fLeftTime) * bounds.right
79			/ (fRightTime - fLeftTime);
80	SetHighColor(60,255,40);
81	StrokeLine(BPoint(x, bounds.top), BPoint(x, bounds.bottom));
82
83	Sync();
84}
85
86
87void
88ScopeView::Run()
89{
90	fRenderSem = create_sem(0, "scope rendering");
91	fThreadId = spawn_thread(&RenderLaunch, "Scope view", B_NORMAL_PRIORITY,
92		this);
93	if (fThreadId < 0)
94		return;
95	resume_thread(fThreadId);
96}
97
98void
99ScopeView::Quit()
100{
101	delete_sem(fRenderSem);
102	snooze(10000);
103	kill_thread(fThreadId);
104}
105
106
107
108int32
109ScopeView::RenderLaunch(void *data)
110{
111	ScopeView *scope = (ScopeView*) data;
112	scope->RenderLoop();
113	return B_OK;
114}
115
116
117template<typename T, typename U>
118void
119ScopeView::ComputeRendering()
120{
121	int64 framesCount = fMediaTrack->CountFrames() / SAMPLES_COUNT;
122	if (framesCount <= 0)
123		return;
124	T samples[fPlayFormat.u.raw_audio.buffer_size
125		/ (fPlayFormat.u.raw_audio.format
126		& media_raw_audio_format::B_AUDIO_SIZE_MASK)];
127	int64 frames = 0;
128	U sum = 0;
129	int64 sumCount = 0;
130	float middle = fHeight / 2;
131	int32 previewMax = 0;
132	//fMediaTrack->SeekToFrame(&frames);
133
134	TRACE("begin computing\n");
135
136	int32 previewIndex = 0;
137
138	while (fIsRendering && fMediaTrack->ReadFrames(samples, &frames) == B_OK) {
139		//TRACE("reading block\n");
140		int64 framesIndex = 0;
141
142		while (framesIndex < frames) {
143			for (; framesIndex < frames && sumCount < framesCount;
144				framesIndex++, sumCount++) {
145				sum += samples[2 * framesIndex];
146				sum += samples[2 * framesIndex + 1];
147			}
148
149			if (previewIndex >= SAMPLES_COUNT)
150				break;
151
152			if (sumCount >= framesCount) {
153				// TRACE("computing block %ld, sumCount %ld\n", previewIndex,
154				// sumCount);
155				fPreview[previewIndex] = (int32)(sum
156					/ fPlayFormat.u.raw_audio.channel_count / framesCount);
157				if (previewMax < fPreview[previewIndex])
158					previewMax = fPreview[previewIndex];
159				sumCount = 0;
160				sum = 0;
161				previewIndex++;
162			}
163		}
164	}
165
166	if (previewMax <= 0)
167		return;
168	for (int i = 0; i < SAMPLES_COUNT; i++)
169		fPreview[i] = (int32)(fPreview[i] * 1.0 / previewMax
170			* middle + middle);
171}
172
173
174void
175ScopeView::RenderLoop()
176{
177	while (acquire_sem(fRenderSem) == B_OK) {
178		fIsRendering = true;
179
180		switch (fPlayFormat.u.raw_audio.format) {
181			case media_raw_audio_format::B_AUDIO_FLOAT:
182				ComputeRendering<float, float>();
183				break;
184			case media_raw_audio_format::B_AUDIO_INT:
185				ComputeRendering<int32, int64>();
186				break;
187			case media_raw_audio_format::B_AUDIO_SHORT:
188				ComputeRendering<int16, int64>();
189				break;
190			case media_raw_audio_format::B_AUDIO_UCHAR:
191				ComputeRendering<uchar, uint32>();
192				break;
193			case media_raw_audio_format::B_AUDIO_CHAR:
194				ComputeRendering<char, int32>();
195				break;
196		}
197
198		TRACE("finished computing, rendering\n");
199
200		/* rendering */
201		RenderBitmap();
202
203		TRACE("rendering done\n");
204
205		/* ask drawing */
206
207		fIsRendering = false;
208
209		if (Window()->LockWithTimeout(5000) == B_OK) {
210			Invalidate();
211			TRACE("invalidate done\n");
212			Window()->Unlock();
213		}
214	}
215}
216
217
218void
219ScopeView::SetMainTime(bigtime_t timestamp)
220{
221	fMainTime = timestamp;
222	Invalidate();
223	TRACE("invalidate done\n");
224}
225
226
227void
228ScopeView::SetTotalTime(bigtime_t timestamp, bool reset)
229{
230	fTotalTime = timestamp;
231	if (reset) {
232		fMainTime = 0;
233		fLeftTime = 0;
234		fRightTime = fTotalTime;
235	}
236	Invalidate();
237	TRACE("invalidate done\n");
238}
239
240
241void
242ScopeView::SetLeftTime(bigtime_t timestamp)
243{
244	fLeftTime = timestamp;
245	RenderBitmap();
246	Invalidate();
247	TRACE("invalidate done\n");
248}
249
250void
251ScopeView::SetRightTime(bigtime_t timestamp)
252{
253	fRightTime = timestamp;
254	RenderBitmap();
255	Invalidate();
256	TRACE("invalidate done\n");
257}
258
259
260void
261ScopeView::RenderTrack(BMediaTrack *track, const media_format &format)
262{
263	fMediaTrack = track;
264	fPlayFormat = format;
265	release_sem(fRenderSem);
266}
267
268
269void
270ScopeView::CancelRendering()
271{
272	fIsRendering = false;
273}
274
275
276void
277ScopeView::FrameResized(float width, float height)
278{
279	InitBitmap();
280	RenderBitmap();
281	Invalidate();
282	TRACE("invalidate done\n");
283}
284
285
286void
287ScopeView::MouseDown(BPoint position)
288{
289	if (!fMediaTrack)
290		return;
291
292	uint32 buttons;
293	BPoint point;
294	GetMouse(&point, &buttons);
295
296	if (buttons & B_PRIMARY_MOUSE_BUTTON) {
297		// fill the drag message
298		BMessage drag(B_SIMPLE_DATA);
299		drag.AddInt32("be:actions", B_COPY_TARGET);
300		drag.AddString("be:clip_name", "Audio Clip");
301		drag.AddString("be:types", B_FILE_MIME_TYPE);
302
303		uint8* data;
304		size_t size;
305
306		BMimeType wavType("audio/x-wav");
307		if (wavType.InitCheck() < B_OK
308			|| wavType.GetIcon(&data, &size) < B_OK) {
309			wavType.SetTo("audio");
310			if (wavType.InitCheck() < B_OK
311				|| wavType.GetIcon(&data, &size) < B_OK) {
312				return;
313			}
314		}
315
316		BBitmap* bitmap = new BBitmap(
317			BRect(0, 0, 31, 31), 0, B_RGBA32);
318		if (BIconUtils::GetVectorIcon(data, size, bitmap) < B_OK) {
319			delete[] data;
320			delete bitmap;
321			return;
322		}
323		delete[] data;
324		DragMessage(&drag, bitmap, B_OP_ALPHA, BPoint(0,0));
325	}
326}
327
328
329void
330ScopeView::InitBitmap()
331{
332	if (fBitmap != NULL && fBitmapView != NULL) {
333		fBitmap->RemoveChild(fBitmapView);
334		delete fBitmapView;
335	}
336	if (fBitmap)
337		delete fBitmap;
338
339	BRect rect = Bounds();
340
341	fBitmap = new BBitmap(rect, BScreen().ColorSpace(), true);
342	memset(fBitmap->Bits(), 0, fBitmap->BitsLength());
343
344	rect.OffsetToSelf(B_ORIGIN);
345	fBitmapView = new BView(rect.OffsetToSelf(B_ORIGIN), "bitmapView",
346		B_FOLLOW_LEFT|B_FOLLOW_TOP, B_WILL_DRAW);
347	fBitmap->AddChild(fBitmapView);
348}
349
350
351void
352ScopeView::RenderBitmap()
353{
354	if (!fMediaTrack)
355		return;
356
357	/* rendering */
358	fBitmap->Lock();
359	memset(fBitmap->Bits(), 0, fBitmap->BitsLength());
360	float width = fBitmapView->Bounds().Width() + 1;
361
362	fBitmapView->SetDrawingMode(B_OP_ADD);
363	fBitmapView->SetHighColor(15,60,15);
364	int32 leftIndex =
365		(fTotalTime != 0) ? fLeftTime * 20000 / fTotalTime : 0;
366	int32 rightIndex =
367		(fTotalTime != 0) ? fRightTime * 20000 / fTotalTime : 20000;
368
369	for (int32 i = leftIndex; i<rightIndex; i++) {
370		BPoint point((i - leftIndex) * width / (rightIndex - leftIndex),
371			fPreview[i]);
372		//TRACE("point x %f y %f\n", point.x, point.y);
373		fBitmapView->StrokeLine(point, point);
374	}
375
376	fBitmap->Unlock();
377}
378
379