xref: /haiku/src/apps/soundrecorder/ScopeView.cpp (revision 122d4ef7e9f6576042189ce21ff9d8654b5a7fb6)
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 
ScopeView(BRect rect,uint32 resizeFlags)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 
~ScopeView()43 ScopeView::~ScopeView()
44 {
45 	delete_sem(fRenderSem);
46 }
47 
48 
49 void
AttachedToWindow()50 ScopeView::AttachedToWindow()
51 {
52 	SetViewColor(B_TRANSPARENT_COLOR);
53 	InitBitmap();
54 	Run();
55 }
56 
57 
58 void
DetachedFromWindow()59 ScopeView::DetachedFromWindow()
60 {
61 	Quit();
62 }
63 
64 
65 void
Draw(BRect updateRect)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
Run()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
Quit()99 ScopeView::Quit()
100 {
101 	delete_sem(fRenderSem);
102 	snooze(10000);
103 	kill_thread(fThreadId);
104 }
105 
106 
107 
108 int32
RenderLaunch(void * data)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
ComputeRendering()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
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 
174 void
RenderLoop()175 ScopeView::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 
218 void
SetMainTime(bigtime_t timestamp)219 ScopeView::SetMainTime(bigtime_t timestamp)
220 {
221 	fMainTime = timestamp;
222 	Invalidate();
223 	TRACE("invalidate done\n");
224 }
225 
226 
227 void
SetTotalTime(bigtime_t timestamp,bool reset)228 ScopeView::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 
241 void
SetLeftTime(bigtime_t timestamp)242 ScopeView::SetLeftTime(bigtime_t timestamp)
243 {
244 	fLeftTime = timestamp;
245 	RenderBitmap();
246 	Invalidate();
247 	TRACE("invalidate done\n");
248 }
249 
250 void
SetRightTime(bigtime_t timestamp)251 ScopeView::SetRightTime(bigtime_t timestamp)
252 {
253 	fRightTime = timestamp;
254 	RenderBitmap();
255 	Invalidate();
256 	TRACE("invalidate done\n");
257 }
258 
259 
260 void
RenderTrack(BMediaTrack * track,const media_format & format)261 ScopeView::RenderTrack(BMediaTrack *track, const media_format &format)
262 {
263 	fMediaTrack = track;
264 	fPlayFormat = format;
265 	release_sem(fRenderSem);
266 }
267 
268 
269 void
CancelRendering()270 ScopeView::CancelRendering()
271 {
272 	fIsRendering = false;
273 }
274 
275 
276 void
FrameResized(float width,float height)277 ScopeView::FrameResized(float width, float height)
278 {
279 	InitBitmap();
280 	RenderBitmap();
281 	Invalidate();
282 	TRACE("invalidate done\n");
283 }
284 
285 
286 void
MouseDown(BPoint position)287 ScopeView::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 
329 void
InitBitmap()330 ScopeView::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 
351 void
RenderBitmap()352 ScopeView::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