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