⛏️ index : haiku.git

/*
 * 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;
	}
}