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 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 52 MidiEventMeter::~MidiEventMeter() 53 { 54 if (fCounter != NULL) 55 fCounter->Release(); 56 } 57 58 59 void 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 75 MidiEventMeter::Bounds() const 76 { 77 return METER_BOUNDS; 78 } 79 80 81 void 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 130 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