haiku/src/apps/soundrecorder/VUView.cpp

249 lines
5.3 KiB
C++

/*
* Copyright 2005, Jérôme Duval. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Inspired by SoundCapture from Be newsletter (Media Kit Basics:
* Consumers and Producers)
*/
#include <stdio.h>
#include <string.h>
#include <MediaDefs.h>
#include <Screen.h>
#include <StackOrHeapArray.h>
#include <Window.h>
#include "DrawingTidbits.h"
#include "VUView.h"
const rgb_color back_color = {12, 36, 12};
const rgb_color low_color = {40, 120, 40};
const rgb_color high_color = {240, 255, 240};
VUView::VUView(BRect rect, uint32 resizeFlags)
: BView(rect, "vumeter", resizeFlags, B_WILL_DRAW),
fThreadId(-1),
fBitmap(NULL),
fQuitting(false)
{
rect.OffsetTo(B_ORIGIN);
fLevelCount = int(rect.Height()) / 2;
fChannels = 2;
fCurrentLevels = new int32[fChannels];
for (int channel = 0; channel < fChannels; channel++)
fCurrentLevels[channel] = 0;
fBitmap = new BBitmap(rect, BScreen().ColorSpace(), true);
memset(fBitmap->Bits(), 0, fBitmap->BitsLength());
fBitmapView = new BView(rect, "bitmapView", B_FOLLOW_LEFT|B_FOLLOW_TOP,
B_WILL_DRAW);
fBitmap->AddChild(fBitmapView);
}
VUView::~VUView()
{
delete fBitmap;
}
void
VUView::AttachedToWindow()
{
SetViewColor(B_TRANSPARENT_COLOR);
_Run();
}
void
VUView::DetachedFromWindow()
{
_Quit();
}
void
VUView::Draw(BRect updateRect)
{
DrawBitmap(fBitmap);
Sync();
}
void
VUView::_Run()
{
fThreadId = spawn_thread(_RenderLaunch, "VU view", B_NORMAL_PRIORITY,
this);
if (fThreadId < 0)
return;
resume_thread(fThreadId);
}
void
VUView::_Quit()
{
fQuitting = true;
snooze(10000);
kill_thread(fThreadId);
}
int32
VUView::_RenderLaunch(void *data)
{
VUView *vu = (VUView*) data;
vu->_RenderLoop();
return B_OK;
}
#define SHIFT_UNTIL(value,shift,min) \
value = (value - shift > min) ? (value - shift) : min
void
VUView::_RenderLoop()
{
BStackOrHeapArray<rgb_color[2], 64> levels(fLevelCount);
if (!levels.IsValid())
return;
for (int32 i = 0; i < fLevelCount; i++) {
levels[i][0] = levels[i][1] = back_color;
}
while (!fQuitting) {
/* computing */
for (int32 channel = 0; channel < 2; channel++) {
int32 level = fCurrentLevels[channel];
for (int32 i = 0; i < level; i++) {
if (levels[i][channel].red >= 90) {
SHIFT_UNTIL(levels[i][channel].red, 15, low_color.red);
SHIFT_UNTIL(levels[i][channel].blue, 15, low_color.blue);
} else {
SHIFT_UNTIL(levels[i][channel].red, 7, low_color.red);
SHIFT_UNTIL(levels[i][channel].blue, 7, low_color.blue);
SHIFT_UNTIL(levels[i][channel].green, 14, low_color.green);
}
}
levels[level][channel] = high_color;
for (int32 i = level + 1; i < fLevelCount; i++) {
if (levels[i][channel].red >= 85) {
SHIFT_UNTIL(levels[i][channel].red, 15, back_color.red);
SHIFT_UNTIL(levels[i][channel].blue, 15, back_color.blue);
} else {
SHIFT_UNTIL(levels[i][channel].red, 7, back_color.red);
SHIFT_UNTIL(levels[i][channel].blue, 7, back_color.blue);
SHIFT_UNTIL(levels[i][channel].green, 14,
back_color.green);
}
}
}
/* rendering */
fBitmap->Lock();
fBitmapView->BeginLineArray(fLevelCount * 2);
BPoint start1, end1, start2, end2;
start1.x = 3;
start2.x = 22;
end1.x = 16;
end2.x = 35;
start1.y = end1.y = start2.y = end2.y = 2;
for (int32 i = fLevelCount - 1; i >= 0; i--) {
fBitmapView->AddLine(start1, end1, levels[i][0]);
fBitmapView->AddLine(start2, end2, levels[i][1]);
start1.y = end1.y = start2.y = end2.y = end2.y + 2;
}
fBitmapView->EndLineArray();
fBitmap->Unlock();
/* ask drawing */
if (Window()->LockWithTimeout(5000) == B_OK) {
Invalidate();
Window()->Unlock();
snooze(50000);
}
}
}
template<typename T>
T
VUView::_ComputeNextLevel(const void *data, size_t size, uint32 format,
int32 channel)
{
const T* samp = (const T*)data;
// get the min and max values in the nibbling interval
// and set max to be the greater of the absolute value
// of these.
T min = 0, max = 0;
for (uint32 i = channel; i < size/sizeof(T); i += fChannels) {
if (min > samp[i])
min = samp[i];
else if (max < samp[i])
max = samp[i];
}
if (-max > (min + 1))
max = -min;
return max;
}
void
VUView::ComputeLevels(const void* data, size_t size, uint32 format)
{
for (int32 channel = 0; channel < fChannels; channel++) {
switch (format) {
case media_raw_audio_format::B_AUDIO_FLOAT:
{
float max = _ComputeNextLevel<float>(data, size, format,
channel);
fCurrentLevels[channel] = (uint8)(max * 127);
break;
}
case media_raw_audio_format::B_AUDIO_INT:
{
int32 max = _ComputeNextLevel<int32>(data, size, format,
channel);
fCurrentLevels[channel] = max / (2 << (32-7));
break;
}
case media_raw_audio_format::B_AUDIO_SHORT:
{
int16 max = _ComputeNextLevel<int16>(data, size, format,
channel);
fCurrentLevels[channel] = max / (2 << (16-7));
break;
}
case media_raw_audio_format::B_AUDIO_UCHAR:
{
uchar max = _ComputeNextLevel<uchar>(data, size, format,
channel);
fCurrentLevels[channel] = max / 2 - 127;
break;
}
case media_raw_audio_format::B_AUDIO_CHAR:
{
char max = _ComputeNextLevel<char>(data, size, format,
channel);
fCurrentLevels[channel] = max / 2;
break;
}
}
if (fCurrentLevels[channel] < 0)
fCurrentLevels[channel] = 0;
}
}