xref: /haiku/src/apps/soundrecorder/ScopeView.cpp (revision 9563f44bfd75799f67201067483694e82b5779b9)
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 
27 ScopeView::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 
43 ScopeView::~ScopeView()
44 {
45 	delete_sem(fRenderSem);
46 }
47 
48 
49 void
50 ScopeView::AttachedToWindow()
51 {
52 	SetViewColor(B_TRANSPARENT_COLOR);
53 	InitBitmap();
54 	Run();
55 }
56 
57 
58 void
59 ScopeView::DetachedFromWindow()
60 {
61 	Quit();
62 }
63 
64 
65 void
66 ScopeView::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 
87 void
88 ScopeView::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 
98 void
99 ScopeView::Quit()
100 {
101 	delete_sem(fRenderSem);
102 	snooze(10000);
103 	kill_thread(fThreadId);
104 }
105 
106 
107 
108 int32
109 ScopeView::RenderLaunch(void *data)
110 {
111 	ScopeView *scope = (ScopeView*) data;
112 	scope->RenderLoop();
113 	return B_OK;
114 }
115 
116 
117 template<typename T, typename U>
118 void
119 ScopeView::ComputeRendering()
120 {
121 	int64 framesCount = fMediaTrack->CountFrames() / SAMPLES_COUNT;
122 	if (framesCount <= 0)
123 		return;
124 	T samples[fPlayFormat.u.raw_audio.buffer_size / (fPlayFormat.u.raw_audio.format
125 		& media_raw_audio_format::B_AUDIO_SIZE_MASK)];
126 	int64 frames = 0;
127 	U sum = 0;
128 	int64 sumCount = 0;
129 	float middle = fHeight / 2;
130 	int32 previewMax = 0;
131 	//fMediaTrack->SeekToFrame(&frames);
132 
133 	TRACE("begin computing\n");
134 
135 	int32 previewIndex = 0;
136 
137 	while (fIsRendering && fMediaTrack->ReadFrames(samples, &frames) == B_OK) {
138 		//TRACE("reading block\n");
139 		int64 framesIndex = 0;
140 
141 		while (framesIndex < frames) {
142 			for (; framesIndex < frames && sumCount < framesCount;
143 				framesIndex++, sumCount++) {
144 				sum += samples[2 * framesIndex];
145 				sum += samples[2 * framesIndex + 1];
146 			}
147 
148 			if (previewIndex >= SAMPLES_COUNT)
149 				break;
150 
151 			if (sumCount >= framesCount) {
152 				//TRACE("computing block %ld, sumCount %ld\n", previewIndex, sumCount);
153 				fPreview[previewIndex] = (int32)(sum
154 					/ fPlayFormat.u.raw_audio.channel_count / framesCount);
155 				if (previewMax < fPreview[previewIndex])
156 					previewMax = fPreview[previewIndex];
157 				sumCount = 0;
158 				sum = 0;
159 				previewIndex++;
160 			}
161 		}
162 	}
163 
164 	if (previewMax <= 0)
165 		return;
166 	for (int i = 0; i < SAMPLES_COUNT; i++)
167 		fPreview[i] = (int32)(fPreview[i] * 1.0 / previewMax * middle + middle);
168 }
169 
170 
171 void
172 ScopeView::RenderLoop()
173 {
174 	while (acquire_sem(fRenderSem) == B_OK) {
175 		fIsRendering = true;
176 
177 		switch (fPlayFormat.u.raw_audio.format) {
178 			case media_raw_audio_format::B_AUDIO_FLOAT:
179 				ComputeRendering<float, float>();
180 				break;
181 			case media_raw_audio_format::B_AUDIO_INT:
182 				ComputeRendering<int32, int64>();
183 				break;
184 			case media_raw_audio_format::B_AUDIO_SHORT:
185 				ComputeRendering<int16, int64>();
186 				break;
187 			case media_raw_audio_format::B_AUDIO_UCHAR:
188 				ComputeRendering<uchar, uint32>();
189 				break;
190 			case media_raw_audio_format::B_AUDIO_CHAR:
191 				ComputeRendering<char, int32>();
192 				break;
193 		}
194 
195 		TRACE("finished computing, rendering\n");
196 
197 		/* rendering */
198 		RenderBitmap();
199 
200 		TRACE("rendering done\n");
201 
202 		/* ask drawing */
203 
204 		fIsRendering = false;
205 
206 		if (Window()->LockWithTimeout(5000) == B_OK) {
207 			Invalidate();
208 			TRACE("invalidate done\n");
209 			Window()->Unlock();
210 		}
211 	}
212 }
213 
214 
215 void
216 ScopeView::SetMainTime(bigtime_t timestamp)
217 {
218 	fMainTime = timestamp;
219 	Invalidate();
220 	TRACE("invalidate done\n");
221 }
222 
223 
224 void
225 ScopeView::SetTotalTime(bigtime_t timestamp, bool reset)
226 {
227 	fTotalTime = timestamp;
228 	if (reset) {
229 		fMainTime = 0;
230 		fLeftTime = 0;
231 		fRightTime = fTotalTime;
232 	}
233 	Invalidate();
234 	TRACE("invalidate done\n");
235 }
236 
237 
238 void
239 ScopeView::SetLeftTime(bigtime_t timestamp)
240 {
241 	fLeftTime = timestamp;
242 	RenderBitmap();
243 	Invalidate();
244 	TRACE("invalidate done\n");
245 }
246 
247 void
248 ScopeView::SetRightTime(bigtime_t timestamp)
249 {
250 	fRightTime = timestamp;
251 	RenderBitmap();
252 	Invalidate();
253 	TRACE("invalidate done\n");
254 }
255 
256 
257 void
258 ScopeView::RenderTrack(BMediaTrack *track, media_format format)
259 {
260 	fMediaTrack = track;
261 	fPlayFormat = format;
262 	release_sem(fRenderSem);
263 }
264 
265 
266 void
267 ScopeView::CancelRendering()
268 {
269 	fIsRendering = false;
270 }
271 
272 
273 void
274 ScopeView::FrameResized(float width, float height)
275 {
276 	InitBitmap();
277 	RenderBitmap();
278 	Invalidate();
279 	TRACE("invalidate done\n");
280 }
281 
282 
283 void
284 ScopeView::MouseDown(BPoint position)
285 {
286 	if (!fMediaTrack)
287 		return;
288 
289 	uint32 buttons;
290 	BPoint point;
291 	GetMouse(&point, &buttons);
292 
293 	if (buttons & B_PRIMARY_MOUSE_BUTTON) {
294 		// fill the drag message
295 		BMessage drag(B_SIMPLE_DATA);
296 		drag.AddInt32("be:actions", B_COPY_TARGET);
297 		drag.AddString("be:clip_name", "Audio Clip");
298 		drag.AddString("be:types", B_FILE_MIME_TYPE);
299 
300 		uint8* data;
301 		size_t size;
302 
303 		BMimeType wavType("audio/x-wav");
304 		if (wavType.InitCheck() < B_OK
305 			|| wavType.GetIcon(&data, &size) < B_OK) {
306 			wavType.SetTo("audio");
307 			if (wavType.InitCheck() < B_OK
308 				|| wavType.GetIcon(&data, &size) < B_OK) {
309 				return;
310 			}
311 		}
312 
313 		BBitmap* bitmap = new BBitmap(
314 			BRect(0, 0, 31, 31), 0, B_RGBA32);
315 		if (BIconUtils::GetVectorIcon(data, size, bitmap) < B_OK) {
316 			delete[] data;
317 			delete bitmap;
318 			return;
319 		}
320 		delete[] data;
321 		DragMessage(&drag, bitmap, B_OP_ALPHA, BPoint(0,0));
322 	}
323 }
324 
325 
326 void
327 ScopeView::InitBitmap()
328 {
329 	if (fBitmapView) {
330 		fBitmap->RemoveChild(fBitmapView);
331 		delete fBitmapView;
332 	}
333 	if (fBitmap)
334 		delete fBitmap;
335 
336 	BRect rect = Bounds();
337 
338 	fBitmap = new BBitmap(rect, BScreen().ColorSpace(), true);
339 	memset(fBitmap->Bits(), 0, fBitmap->BitsLength());
340 
341 	rect.OffsetToSelf(B_ORIGIN);
342 	fBitmapView = new BView(rect.OffsetToSelf(B_ORIGIN), "bitmapView",
343 		B_FOLLOW_LEFT|B_FOLLOW_TOP, B_WILL_DRAW);
344 	fBitmap->AddChild(fBitmapView);
345 }
346 
347 
348 void
349 ScopeView::RenderBitmap()
350 {
351 	if (!fMediaTrack)
352 		return;
353 
354 	/* rendering */
355 	fBitmap->Lock();
356 	memset(fBitmap->Bits(), 0, fBitmap->BitsLength());
357 	float width = fBitmapView->Bounds().Width() + 1;
358 
359 	fBitmapView->SetDrawingMode(B_OP_ADD);
360 	fBitmapView->SetHighColor(15,60,15);
361 	int32 leftIndex =
362 		(fTotalTime != 0) ? fLeftTime * 20000 / fTotalTime : 0;
363 	int32 rightIndex =
364 		(fTotalTime != 0) ? fRightTime * 20000 / fTotalTime : 20000;
365 
366 	for (int32 i = leftIndex; i<rightIndex; i++) {
367 		BPoint point((i - leftIndex) * width / (rightIndex - leftIndex),
368 			fPreview[i]);
369 		//TRACE("point x %f y %f\n", point.x, point.y);
370 		fBitmapView->StrokeLine(point, point);
371 	}
372 
373 	fBitmap->Unlock();
374 }
375 
376