xref: /haiku/src/apps/patchbay/MidiEventMeter.cpp (revision c14bca2958fb7b0c34d5464ccfdd87038f909a0c)
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 
24 static 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.
30 static const int32 METER_SCALE = 10;
31 
32 
MidiEventMeter(int32 producerID)33 MidiEventMeter::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 
~MidiEventMeter()52 MidiEventMeter::~MidiEventMeter()
53 {
54 	if (fCounter != NULL)
55 		fCounter->Release();
56 }
57 
58 
59 void
Pulse(BView * view)60 MidiEventMeter::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 
74 BRect
Bounds() const75 MidiEventMeter::Bounds() const
76 {
77 	return METER_BOUNDS;
78 }
79 
80 
81 void
Draw(BView * view)82 MidiEventMeter::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 
129 int32
CalcMeterLevel(int32 eventCount) const130 MidiEventMeter::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