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