⛏️ index : haiku.git

/* MidiEventMeter.cpp
 * ------------------
 * Implements the MidiEventMeter class.
 *
 * Copyright 2013, Haiku, Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Revisions by Pete Goodeve
 *
 * Copyright 1999, Be Incorporated.   All Rights Reserved.
 * This file may be used under the terms of the Be Sample Code License.
 */

#include "MidiEventMeter.h"
 
#include <stdio.h>
#include <MidiRoster.h>
#include <MidiProducer.h>
#include <MidiConsumer.h>
#include <View.h>

#include "CountEventConsumer.h"

static const BRect METER_BOUNDS(0, 0, 7, 31);

// If we get this number of events per pulse or greater, we will
// max out the event meter.
// Value was determined empirically based on my banging on a MIDI
// keyboard controller with a pulse of 200ms.
static const int32 METER_SCALE = 10;


MidiEventMeter::MidiEventMeter(int32 producerID)
	:
	fCounter(NULL),
	fMeterLevel(0)
{
	BMidiRoster* roster = BMidiRoster::MidiRoster();
	if (roster != NULL) {
		BMidiProducer* producer = roster->FindProducer(producerID);
		if (producer != NULL) {
			BString name;
			name << producer->Name() << " Event Meter";
			fCounter = new CountEventConsumer(name.String());
			producer->Connect(fCounter);
			producer->Release();
		}
	}
}


MidiEventMeter::~MidiEventMeter()
{
	if (fCounter != NULL)
		fCounter->Release();
}


void
MidiEventMeter::Pulse(BView* view)
{
	int32 newLevel = fMeterLevel;
	if (fCounter != NULL) {
		newLevel = CalcMeterLevel(fCounter->CountEvents());
		fCounter->Reset();
	}
	if (newLevel != fMeterLevel) {
		fMeterLevel = newLevel;
		view->Invalidate(BRect(METER_BOUNDS).InsetBySelf(1, 1));
	}
}


BRect
MidiEventMeter::Bounds() const
{
	return METER_BOUNDS;
}


void
MidiEventMeter::Draw(BView* view)
{
	const rgb_color METER_BLACK = { 0, 0, 0, 255 };
	const rgb_color METER_GREY = { 180, 180, 180, 255 };
	const rgb_color METER_GREEN = { 0, 255, 0, 255 };

	view->PushState();

	// draw the frame
	BPoint lt = METER_BOUNDS.LeftTop();
	BPoint rb = METER_BOUNDS.RightBottom();
	view->BeginLineArray(4);
	view->AddLine(BPoint(lt.x, lt.y), BPoint(rb.x - 1, lt.y), METER_BLACK);
	view->AddLine(BPoint(rb.x, lt.y), BPoint(rb.x, rb.y - 1), METER_BLACK);
	view->AddLine(BPoint(rb.x, rb.y), BPoint(lt.x + 1, rb.y), METER_BLACK);
	view->AddLine(BPoint(lt.x, rb.y), BPoint(lt.x, lt.y + 1), METER_BLACK);
	view->EndLineArray();
	
	// draw the cells
	BRect cell = METER_BOUNDS;
	cell.InsetBy(1, 1);
	cell.bottom = cell.top + (cell.Height() + 1) / 5;
	cell.bottom--;

	const float kTintArray[] =
		{B_DARKEN_4_TINT,
		 B_DARKEN_3_TINT,
		 B_DARKEN_2_TINT,
		 B_DARKEN_1_TINT,
		 B_NO_TINT};
	
	for (int32 i = 4; i >= 0; i--) {
		rgb_color color;
		if (fMeterLevel > i) {
			color = tint_color(METER_GREEN, kTintArray[i]);
		} else {
			color = METER_GREY;
		}
		view->SetHighColor(color);
		view->FillRect(cell);
		cell.OffsetBy(0, cell.Height() + 1);
	}
		
	view->PopState();
}


int32
MidiEventMeter::CalcMeterLevel(int32 eventCount) const
{
	// we use an approximately logarithmic scale for determing the actual
	// drawn meter level, so that low-density event streams show up well.
	if (eventCount == 0)
		return 0;
	else if (eventCount < (int32)(0.5 * METER_SCALE))
		return 1;
	else if (eventCount < (int32)(0.75 * METER_SCALE))
		return 2;
	else if (eventCount < (int32)(0.9 * METER_SCALE))
		return 3;
	else if (eventCount < METER_SCALE)
		return 4;
	return 5;
}