xref: /haiku/src/apps/soundrecorder/ScopeView.cpp (revision 4f00613311d0bd6b70fa82ce19931c41f071ea4e)
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: Consumers and Producers)
6  */
7 
8 #include <stdio.h>
9 #include <string.h>
10 #include <Screen.h>
11 #include <Window.h>
12 #include "DrawingTidbits.h"
13 #include "ScopeView.h"
14 
15 //#define TRACE 1
16 #ifdef TRACE
17 #define TRACE(x) printf(x)
18 #else
19 #define TRACE(x)
20 #endif
21 
22 ScopeView::ScopeView(BRect rect, uint32 resizeFlags)
23 	: BView(rect, "scope", resizeFlags, B_WILL_DRAW | B_FRAME_EVENTS),
24 	fThreadId(-1),
25 	fBitmap(NULL),
26 	fIsRendering(false),
27 	fMediaTrack(NULL),
28 	fQuitting(false),
29 	fMainTime(0),
30 	fRightTime(1000000),
31 	fLeftTime(0),
32 	fTotalTime(1000000)
33 {
34 	fBitmap = new BBitmap(rect, BScreen().ColorSpace(), true);
35 	memset(fBitmap->Bits(), 0, fBitmap->BitsLength());
36 
37 	rect.OffsetToSelf(B_ORIGIN);
38 	rect.right -= 2;
39 	fBitmapView = new BView(rect.OffsetToSelf(B_ORIGIN), "bitmapView", B_FOLLOW_LEFT|B_FOLLOW_TOP, B_WILL_DRAW);
40 	fBitmap->AddChild(fBitmapView);
41 
42 	fRenderSem = create_sem(0, "scope rendering");
43 	fHeight = Bounds().Height();
44 }
45 
46 
47 ScopeView::~ScopeView()
48 {
49 	delete_sem(fRenderSem);
50 }
51 
52 
53 void
54 ScopeView::AttachedToWindow()
55 {
56 	SetViewColor(B_TRANSPARENT_COLOR);
57 	Run();
58 }
59 
60 
61 void
62 ScopeView::DetachedFromWindow()
63 {
64 	Quit();
65 }
66 
67 
68 void
69 ScopeView::Draw(BRect updateRect)
70 {
71 	BRect bounds = Bounds();
72 	if (!fIsRendering)
73 		DrawBitmapAsync(fBitmap, BPoint(2,0));
74 
75 	float x = 2;
76 	if (fTotalTime !=0)
77 		x = 2 + (fMainTime - fLeftTime) * (bounds.right - 2) / (fRightTime - fLeftTime);
78 	SetHighColor(60,255,40);
79 	StrokeLine(BPoint(x, bounds.top), BPoint(x, bounds.bottom));
80 
81 	Sync();
82 }
83 
84 
85 void
86 ScopeView::Run()
87 {
88 	fThreadId = spawn_thread(&RenderLaunch, "Scope view", B_NORMAL_PRIORITY, this);
89 	if (fThreadId < 0)
90 		return;
91 	resume_thread(fThreadId);
92 }
93 
94 void
95 ScopeView::Quit()
96 {
97 	delete_sem(fRenderSem);
98 	fQuitting = true;
99 	snooze(10000);
100 	kill_thread(fThreadId);
101 }
102 
103 
104 
105 int32
106 ScopeView::RenderLaunch(void *data)
107 {
108 	ScopeView *scope = (ScopeView*) data;
109 	scope->RenderLoop();
110 	return B_OK;
111 }
112 
113 
114 void
115 ScopeView::RenderLoop()
116 {
117 
118 	while (!fQuitting) {
119 
120 		if (acquire_sem(fRenderSem)!=B_OK)
121 			continue;
122 
123 		fIsRendering = true;
124 
125 		int32 frame_size = (fPlayFormat.u.raw_audio.format & 0xf) * fPlayFormat.u.raw_audio.channel_count;
126 		int64 totalFrames = fMediaTrack->CountFrames();
127 		int16 samples[fPlayFormat.u.raw_audio.buffer_size / (fPlayFormat.u.raw_audio.format & 0xf)];
128 		int64 frames = 0;
129 		int64 sum = 0;
130 		int64 framesIndex = 0;
131 		int32 sumCount = 0;
132 		fMediaTrack->SeekToFrame(&frames);
133 
134 		TRACE("begin computing\n");
135 
136 		int32 previewIndex = 0;
137 
138 		while (fMediaTrack->ReadFrames(samples, &frames) == B_OK) {
139 			//TRACE("reading block\n");
140 			framesIndex = 0;
141 
142 			while (framesIndex < frames) {
143 
144 				for (; framesIndex < frames && sumCount < totalFrames/20000; framesIndex++, sumCount++) {
145 					sum += samples[2*framesIndex];
146 					sum += samples[2*framesIndex+1];
147 				}
148 
149 				if (previewIndex >= 20000) {
150 					break;
151 				}
152 
153 				if (sumCount >= totalFrames/20000) {
154 					//TRACE("computing block %ld, sumCount %ld\n", previewIndex, sumCount);
155 					fPreview[previewIndex++] = (int32)(sum / 2 /(totalFrames/20000) / 32767.0 * fHeight / 2 + fHeight / 2);
156 					sumCount = 0;
157 					sum = 0;
158 				}
159 			}
160 
161 
162 		}
163 
164 		TRACE("finished computing\n");
165 
166 		/* rendering */
167 		RenderBitmap();
168 
169 		/* ask drawing */
170 
171 		fIsRendering = false;
172 
173 		if (Window()->LockWithTimeout(5000) == B_OK) {
174 			Invalidate();
175 			Window()->Unlock();
176 		}
177 	}
178 }
179 
180 
181 void
182 ScopeView::SetMainTime(bigtime_t timestamp)
183 {
184 	fMainTime = timestamp;
185 	Invalidate();
186 }
187 
188 
189 void
190 ScopeView::SetTotalTime(bigtime_t timestamp)
191 {
192 	fTotalTime = timestamp;
193 	Invalidate();
194 }
195 
196 void
197 ScopeView::SetLeftTime(bigtime_t timestamp)
198 {
199 	fLeftTime = timestamp;
200 	RenderBitmap();
201 	Invalidate();
202 }
203 
204 void
205 ScopeView::SetRightTime(bigtime_t timestamp)
206 {
207 	fRightTime = timestamp;
208 	RenderBitmap();
209 	Invalidate();
210 }
211 
212 
213 void
214 ScopeView::RenderTrack(BMediaTrack *track, media_format format)
215 {
216 	fMediaTrack = track;
217 	fPlayFormat = format;
218 	release_sem(fRenderSem);
219 }
220 
221 
222 void
223 ScopeView::FrameResized(float width, float height)
224 {
225 	InitBitmap();
226 	RenderBitmap();
227 	Invalidate();
228 }
229 
230 
231 void
232 ScopeView::InitBitmap()
233 {
234 	if (fBitmapView) {
235 		fBitmap->RemoveChild(fBitmapView);
236 		delete fBitmapView;
237 	}
238 	if (fBitmap)
239 		delete fBitmap;
240 
241 	BRect rect = Bounds();
242 
243 	fBitmap = new BBitmap(rect, BScreen().ColorSpace(), true);
244 	memset(fBitmap->Bits(), 0, fBitmap->BitsLength());
245 
246 	rect.OffsetToSelf(B_ORIGIN);
247 	rect.right -= 2;
248 	fBitmapView = new BView(rect.OffsetToSelf(B_ORIGIN), "bitmapView", B_FOLLOW_LEFT|B_FOLLOW_TOP, B_WILL_DRAW);
249 	fBitmap->AddChild(fBitmapView);
250 }
251 
252 
253 void
254 ScopeView::RenderBitmap()
255 {
256 	if (!fMediaTrack)
257 		return;
258 
259 	/* rendering */
260 	fBitmap->Lock();
261 	memset(fBitmap->Bits(), 0, fBitmap->BitsLength());
262 	float width = fBitmapView->Bounds().Width();
263 
264 	fBitmapView->SetDrawingMode(B_OP_ADD);
265 	fBitmapView->SetHighColor(15,60,15);
266 	int32 leftIndex = (fTotalTime != 0) ? fLeftTime * 20000 / fTotalTime : 0;
267 	int32 rightIndex = (fTotalTime != 0) ? fRightTime * 20000 / fTotalTime : 20000;
268 
269 	for (int32 i = leftIndex; i<rightIndex; i++) {
270 		BPoint point((i - leftIndex) * width / (rightIndex - leftIndex), fPreview[i]);
271 		//TRACE("point x %f y %f\n", point.x, point.y);
272 		fBitmapView->StrokeLine(point, point);
273 	}
274 
275 	fBitmap->Unlock();
276 }
277 
278