1// MidiEventMeter.cpp
2// ------------------
3// Implements the MidiEventMeter class.
4//
5// Copyright 1999, Be Incorporated.   All Rights Reserved.
6// This file may be used under the terms of the Be Sample Code License.
7
8#include <stdio.h>
9#include <MidiRoster.h>
10#include <MidiProducer.h>
11#include <MidiConsumer.h>
12#include <View.h>
13#include "CountEventConsumer.h"
14#include "MidiEventMeter.h"
15
16static const BRect METER_BOUNDS(0,0,7,31);
17
18// If we get this number of events per pulse or greater, we will
19// max out the event meter.
20// Value was determined empirically based on my banging on a MIDI
21// keyboard controller with a pulse of 200ms.
22static const int32 METER_SCALE = 10;
23
24MidiEventMeter::MidiEventMeter(int32 producerID)
25	: m_counter(NULL), m_meterLevel(0)
26{
27	BMidiRoster* roster = BMidiRoster::MidiRoster();
28	if (roster) {
29		BMidiProducer* producer = roster->FindProducer(producerID);
30		if (producer) {
31			BString name;
32			name << producer->Name() << " Event Meter";
33			m_counter = new CountEventConsumer(name.String());
34			producer->Connect(m_counter);
35			producer->Release();
36		}
37	}
38}
39
40MidiEventMeter::~MidiEventMeter()
41{
42	if (m_counter) m_counter->Release();
43}
44
45void MidiEventMeter::Pulse(BView* view)
46{
47	int32 newLevel = m_meterLevel;
48	if (m_counter) {
49		newLevel = CalcMeterLevel(m_counter->CountEvents());
50		m_counter->Reset();
51	}
52	if (newLevel != m_meterLevel) {
53		m_meterLevel = newLevel;
54		view->Invalidate(BRect(METER_BOUNDS).InsetBySelf(1,1));
55	}
56}
57
58BRect MidiEventMeter::Bounds() const
59{
60	return METER_BOUNDS;
61}
62
63void MidiEventMeter::Draw(BView* view)
64{
65	const rgb_color METER_BLACK = { 0, 0, 0, 255 };
66	const rgb_color METER_GREY = { 180, 180, 180, 255 };
67	const rgb_color METER_GREEN = { 0, 255, 0, 255 };
68
69	view->PushState();
70
71	// draw the frame
72	BPoint lt = METER_BOUNDS.LeftTop();
73	BPoint rb = METER_BOUNDS.RightBottom();
74	view->BeginLineArray(4);
75	view->AddLine(BPoint(lt.x, lt.y), BPoint(rb.x - 1, lt.y), METER_BLACK);
76	view->AddLine(BPoint(rb.x, lt.y), BPoint(rb.x, rb.y - 1), METER_BLACK);
77	view->AddLine(BPoint(rb.x, rb.y), BPoint(lt.x + 1, rb.y), METER_BLACK);
78	view->AddLine(BPoint(lt.x, rb.y), BPoint(lt.x, lt.y + 1), METER_BLACK);
79	view->EndLineArray();
80
81	// draw the cells
82	BRect cell = METER_BOUNDS;
83	cell.InsetBy(1,1);
84	cell.bottom = cell.top + (cell.Height() + 1) / 5;
85	cell.bottom--;
86
87	const float kTintArray[] = { B_DARKEN_4_TINT, B_DARKEN_3_TINT, B_DARKEN_2_TINT, B_DARKEN_1_TINT, B_NO_TINT };
88
89	for (int32 i=4; i>=0; i--)
90	{
91		rgb_color color;
92		if (m_meterLevel > i) {
93			color = tint_color(METER_GREEN, kTintArray[i]);
94		} else {
95			color = METER_GREY;
96		}
97		view->SetHighColor(color);
98		view->FillRect(cell);
99		cell.OffsetBy(0, cell.Height() + 1);
100	}
101
102	view->PopState();
103}
104
105int32 MidiEventMeter::CalcMeterLevel(int32 eventCount) const
106{
107	// we use an approximately logarithmic scale for determing the actual
108	// drawn meter level, so that low-density event streams show up well.
109	if (eventCount == 0) {
110		return 0;
111	} else if (eventCount < (int32)(.5*METER_SCALE)) {
112		return 1;
113	} else if (eventCount < (int32)(.75*METER_SCALE)) {
114		return 2;
115	} else if (eventCount < (int32)(.9*METER_SCALE)) {
116		return 3;
117	} else if (eventCount < METER_SCALE) {
118		return 4;
119	} else {
120		return 5;
121	}
122}
123