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
20const rgb_color back_color = {12, 36, 12};
21const rgb_color low_color = {40, 120, 40};
22const rgb_color high_color = {240, 255, 240};
23
24VUView::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
47VUView::~VUView()
48{
49	delete fBitmap;
50}
51
52
53void
54VUView::AttachedToWindow()
55{
56	SetViewColor(B_TRANSPARENT_COLOR);
57	_Run();
58}
59
60
61void
62VUView::DetachedFromWindow()
63{
64	_Quit();
65}
66
67
68void
69VUView::Draw(BRect updateRect)
70{
71	DrawBitmap(fBitmap);
72
73	Sync();
74}
75
76
77void
78VUView::_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
87void
88VUView::_Quit()
89{
90	fQuitting = true;
91	snooze(10000);
92	kill_thread(fThreadId);
93}
94
95
96
97int32
98VUView::_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
109void
110VUView::_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
179template<typename T>
180T
181VUView::_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
204void
205VUView::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