xref: /haiku/src/apps/soundrecorder/VUView.cpp (revision 23eafdaf313f2e756170f6a543205311a4d9bc96)
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, this);
80 	if (fThreadId < 0)
81 		return;
82 	resume_thread(fThreadId);
83 }
84 
85 void
86 VUView::_Quit()
87 {
88 	fQuitting = true;
89 	snooze(10000);
90 	kill_thread(fThreadId);
91 }
92 
93 
94 
95 int32
96 VUView::_RenderLaunch(void *data)
97 {
98 	VUView *vu = (VUView*) data;
99 	vu->_RenderLoop();
100 	return B_OK;
101 }
102 
103 
104 #define SHIFT_UNTIL(value,shift,min) \
105 	value = (value - shift > min) ? (value - shift) : min
106 
107 void
108 VUView::_RenderLoop()
109 {
110 	rgb_color levels[fLevelCount][2];
111 
112 	for (int32 i = 0; i < fLevelCount; i++) {
113 		levels[i][0] = levels[i][1] = back_color;
114 	}
115 
116 	while (!fQuitting) {
117 
118 		/* computing */
119 		for (int32 channel = 0; channel < 2; channel++) {
120 			int32 level = fCurrentLevels[channel];
121 			for (int32 i = 0; i < level; i++) {
122 				if (levels[i][channel].red >= 90) {
123 					SHIFT_UNTIL(levels[i][channel].red, 15, low_color.red);
124 					SHIFT_UNTIL(levels[i][channel].blue, 15, low_color.blue);
125 				} else {
126 					SHIFT_UNTIL(levels[i][channel].red, 7, low_color.red);
127 					SHIFT_UNTIL(levels[i][channel].blue, 7, low_color.blue);
128 					SHIFT_UNTIL(levels[i][channel].green, 14, low_color.green);
129 				}
130 			}
131 
132 			levels[level][channel] = high_color;
133 
134 			for (int32 i = level + 1; i < fLevelCount; i++) {
135 				if (levels[i][channel].red >= 85) {
136 					SHIFT_UNTIL(levels[i][channel].red, 15, back_color.red);
137 					SHIFT_UNTIL(levels[i][channel].blue, 15, back_color.blue);
138 				} else {
139 					SHIFT_UNTIL(levels[i][channel].red, 7, back_color.red);
140 					SHIFT_UNTIL(levels[i][channel].blue, 7, back_color.blue);
141 					SHIFT_UNTIL(levels[i][channel].green, 14, back_color.green);
142 				}
143 			}
144 		}
145 
146 		/* rendering */
147 		fBitmap->Lock();
148 		fBitmapView->BeginLineArray(fLevelCount * 2);
149 		BPoint start1, end1, start2, end2;
150 		start1.x = 3;
151 		start2.x = 22;
152 		end1.x = 16;
153 		end2.x = 35;
154 		start1.y = end1.y = start2.y = end2.y = 2;
155 		for (int32 i = fLevelCount - 1; i >= 0; i--) {
156 			fBitmapView->AddLine(start1, end1, levels[i][0]);
157 			fBitmapView->AddLine(start2, end2, levels[i][1]);
158 			start1.y = end1.y = start2.y = end2.y = end2.y + 2;
159 		}
160 		fBitmapView->EndLineArray();
161 		fBitmap->Unlock();
162 
163 		/* ask drawing */
164 
165 		if (Window()->LockWithTimeout(5000) == B_OK) {
166 			Invalidate();
167 			Window()->Unlock();
168 			snooze(50000);
169 		}
170 	}
171 }
172 
173 
174 template<typename T>
175 T
176 VUView::_ComputeNextLevel(void *data, size_t size, uint32 format, int32 channel)
177 {
178 	T* samp = (T*)data;
179 
180 	// get the min and max values in the nibbling interval
181 	// and set max to be the greater of the absolute value
182 	// of these.
183 
184 	T min = 0, max = 0;
185 	for (uint32 i = channel; i < size/sizeof(T); i += fChannels) {
186 		if (min > samp[i])
187 			min = samp[i];
188 		else if (max < samp[i])
189 			max = samp[i];
190 	}
191 	if (-max > (min + 1))
192 		max = -min;
193 
194 	return max;
195 }
196 
197 
198 void
199 VUView::ComputeLevels(void* data, size_t size, uint32 format)
200 {
201 	for (int32 channel = 0; channel < fChannels; channel++) {
202 		switch (format) {
203 			case media_raw_audio_format::B_AUDIO_FLOAT:
204 			{
205 				float max = _ComputeNextLevel<float>(data, size, format,
206 					channel);
207 				fCurrentLevels[channel] = (uint8)(max * 127);
208 				break;
209 			}
210 			case media_raw_audio_format::B_AUDIO_INT:
211 			{
212 				int32 max = _ComputeNextLevel<int32>(data, size, format,
213 					channel);
214 				fCurrentLevels[channel] = max / (2 << (32-7));
215 				break;
216 			}
217 			case media_raw_audio_format::B_AUDIO_SHORT:
218 			{
219 				int16 max = _ComputeNextLevel<int16>(data, size, format,
220 					channel);
221 				fCurrentLevels[channel] = max / (2 << (16-7));
222 				break;
223 			}
224 			case media_raw_audio_format::B_AUDIO_UCHAR:
225 			{
226 				uchar max = _ComputeNextLevel<uchar>(data, size, format,
227 					channel);
228 				fCurrentLevels[channel] = max / 2 - 127;
229 				break;
230 			}
231 			case media_raw_audio_format::B_AUDIO_CHAR:
232 			{
233 				char max = _ComputeNextLevel<char>(data, size, format,
234 					channel);
235 				fCurrentLevels[channel] = max / 2;
236 				break;
237 			}
238 		}
239 		if (fCurrentLevels[channel] < 0)
240 			fCurrentLevels[channel] = 0;
241 	}
242 }
243