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