xref: /haiku/src/apps/soundrecorder/VUView.cpp (revision 820dca4df6c7bf955c46e8f6521b9408f50b2900)
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 
12 #include <MediaDefs.h>
13 #include <Screen.h>
14 #include <Window.h>
15 
16 #include "DrawingTidbits.h"
17 #include "VUView.h"
18 
19 const rgb_color back_color = {12, 36, 12};
20 const rgb_color low_color = {40, 120, 40};
21 const rgb_color high_color = {240, 255, 240};
22 
23 VUView::VUView(BRect rect, uint32 resizeFlags)
24 	: BView(rect, "vumeter", resizeFlags, B_WILL_DRAW),
25 	fThreadId(-1),
26 	fBitmap(NULL),
27 	fQuitting(false)
28 {
29 	rect.OffsetTo(B_ORIGIN);
30 	fLevelCount = int(rect.Height()) / 2;
31 	fChannels = 2;
32 	fCurrentLevels = new int32[fChannels];
33 	for (int channel = 0; channel < fChannels; channel++)
34 		fCurrentLevels[channel] = 0;
35 	fBitmap = new BBitmap(rect, BScreen().ColorSpace(), true);
36 
37 
38 	memset(fBitmap->Bits(), 0, fBitmap->BitsLength());
39 
40 	fBitmapView = new BView(rect, "bitmapView", B_FOLLOW_LEFT|B_FOLLOW_TOP,
41 		B_WILL_DRAW);
42 	fBitmap->AddChild(fBitmapView);
43 }
44 
45 
46 VUView::~VUView()
47 {
48 	delete fBitmap;
49 }
50 
51 
52 void
53 VUView::AttachedToWindow()
54 {
55 	SetViewColor(B_TRANSPARENT_COLOR);
56 	_Run();
57 }
58 
59 
60 void
61 VUView::DetachedFromWindow()
62 {
63 	_Quit();
64 }
65 
66 
67 void
68 VUView::Draw(BRect updateRect)
69 {
70 	DrawBitmap(fBitmap);
71 
72 	Sync();
73 }
74 
75 
76 void
77 VUView::_Run()
78 {
79 	fThreadId = spawn_thread(_RenderLaunch, "VU view", B_NORMAL_PRIORITY,
80 		this);
81 	if (fThreadId < 0)
82 		return;
83 	resume_thread(fThreadId);
84 }
85 
86 void
87 VUView::_Quit()
88 {
89 	fQuitting = true;
90 	snooze(10000);
91 	kill_thread(fThreadId);
92 }
93 
94 
95 
96 int32
97 VUView::_RenderLaunch(void *data)
98 {
99 	VUView *vu = (VUView*) data;
100 	vu->_RenderLoop();
101 	return B_OK;
102 }
103 
104 
105 #define SHIFT_UNTIL(value,shift,min) \
106 	value = (value - shift > min) ? (value - shift) : min
107 
108 void
109 VUView::_RenderLoop()
110 {
111 	rgb_color levels[fLevelCount][2];
112 
113 	for (int32 i = 0; i < fLevelCount; i++) {
114 		levels[i][0] = levels[i][1] = back_color;
115 	}
116 
117 	while (!fQuitting) {
118 
119 		/* computing */
120 		for (int32 channel = 0; channel < 2; channel++) {
121 			int32 level = fCurrentLevels[channel];
122 			for (int32 i = 0; i < level; i++) {
123 				if (levels[i][channel].red >= 90) {
124 					SHIFT_UNTIL(levels[i][channel].red, 15, low_color.red);
125 					SHIFT_UNTIL(levels[i][channel].blue, 15, low_color.blue);
126 				} else {
127 					SHIFT_UNTIL(levels[i][channel].red, 7, low_color.red);
128 					SHIFT_UNTIL(levels[i][channel].blue, 7, low_color.blue);
129 					SHIFT_UNTIL(levels[i][channel].green, 14, low_color.green);
130 				}
131 			}
132 
133 			levels[level][channel] = high_color;
134 
135 			for (int32 i = level + 1; i < fLevelCount; i++) {
136 				if (levels[i][channel].red >= 85) {
137 					SHIFT_UNTIL(levels[i][channel].red, 15, back_color.red);
138 					SHIFT_UNTIL(levels[i][channel].blue, 15, back_color.blue);
139 				} else {
140 					SHIFT_UNTIL(levels[i][channel].red, 7, back_color.red);
141 					SHIFT_UNTIL(levels[i][channel].blue, 7, back_color.blue);
142 					SHIFT_UNTIL(levels[i][channel].green, 14,
143 						back_color.green);
144 				}
145 			}
146 		}
147 
148 		/* rendering */
149 		fBitmap->Lock();
150 		fBitmapView->BeginLineArray(fLevelCount * 2);
151 		BPoint start1, end1, start2, end2;
152 		start1.x = 3;
153 		start2.x = 22;
154 		end1.x = 16;
155 		end2.x = 35;
156 		start1.y = end1.y = start2.y = end2.y = 2;
157 		for (int32 i = fLevelCount - 1; i >= 0; i--) {
158 			fBitmapView->AddLine(start1, end1, levels[i][0]);
159 			fBitmapView->AddLine(start2, end2, levels[i][1]);
160 			start1.y = end1.y = start2.y = end2.y = end2.y + 2;
161 		}
162 		fBitmapView->EndLineArray();
163 		fBitmap->Unlock();
164 
165 		/* ask drawing */
166 
167 		if (Window()->LockWithTimeout(5000) == B_OK) {
168 			Invalidate();
169 			Window()->Unlock();
170 			snooze(50000);
171 		}
172 	}
173 }
174 
175 
176 template<typename T>
177 T
178 VUView::_ComputeNextLevel(const void *data, size_t size, uint32 format,
179 	int32 channel)
180 {
181 	const T* samp = (const T*)data;
182 
183 	// get the min and max values in the nibbling interval
184 	// and set max to be the greater of the absolute value
185 	// of these.
186 
187 	T min = 0, max = 0;
188 	for (uint32 i = channel; i < size/sizeof(T); i += fChannels) {
189 		if (min > samp[i])
190 			min = samp[i];
191 		else if (max < samp[i])
192 			max = samp[i];
193 	}
194 	if (-max > (min + 1))
195 		max = -min;
196 
197 	return max;
198 }
199 
200 
201 void
202 VUView::ComputeLevels(const void* data, size_t size, uint32 format)
203 {
204 	for (int32 channel = 0; channel < fChannels; channel++) {
205 		switch (format) {
206 			case media_raw_audio_format::B_AUDIO_FLOAT:
207 			{
208 				float max = _ComputeNextLevel<float>(data, size, format,
209 					channel);
210 				fCurrentLevels[channel] = (uint8)(max * 127);
211 				break;
212 			}
213 			case media_raw_audio_format::B_AUDIO_INT:
214 			{
215 				int32 max = _ComputeNextLevel<int32>(data, size, format,
216 					channel);
217 				fCurrentLevels[channel] = max / (2 << (32-7));
218 				break;
219 			}
220 			case media_raw_audio_format::B_AUDIO_SHORT:
221 			{
222 				int16 max = _ComputeNextLevel<int16>(data, size, format,
223 					channel);
224 				fCurrentLevels[channel] = max / (2 << (16-7));
225 				break;
226 			}
227 			case media_raw_audio_format::B_AUDIO_UCHAR:
228 			{
229 				uchar max = _ComputeNextLevel<uchar>(data, size, format,
230 					channel);
231 				fCurrentLevels[channel] = max / 2 - 127;
232 				break;
233 			}
234 			case media_raw_audio_format::B_AUDIO_CHAR:
235 			{
236 				char max = _ComputeNextLevel<char>(data, size, format,
237 					channel);
238 				fCurrentLevels[channel] = max / 2;
239 				break;
240 			}
241 		}
242 		if (fCurrentLevels[channel] < 0)
243 			fCurrentLevels[channel] = 0;
244 	}
245 }
246