1 /*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2020 - Raw Material Software Limited
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 The code included in this file is provided under the terms of the ISC license
11 http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12 To use, copy, modify, and/or distribute this software for any purpose with or
13 without fee is hereby granted provided that the above copyright notice and
14 this permission notice appear in all copies.
15
16 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
18 DISCLAIMED.
19
20 ==============================================================================
21 */
22
23 namespace juce
24 {
25
MidiKeyboardState()26 MidiKeyboardState::MidiKeyboardState()
27 {
28 zerostruct (noteStates);
29 }
30
~MidiKeyboardState()31 MidiKeyboardState::~MidiKeyboardState()
32 {
33 }
34
35 //==============================================================================
reset()36 void MidiKeyboardState::reset()
37 {
38 const ScopedLock sl (lock);
39 zerostruct (noteStates);
40 eventsToAdd.clear();
41 }
42
isNoteOn(const int midiChannel,const int n) const43 bool MidiKeyboardState::isNoteOn (const int midiChannel, const int n) const noexcept
44 {
45 jassert (midiChannel > 0 && midiChannel <= 16);
46
47 return isPositiveAndBelow (n, 128)
48 && (noteStates[n] & (1 << (midiChannel - 1))) != 0;
49 }
50
isNoteOnForChannels(const int midiChannelMask,const int n) const51 bool MidiKeyboardState::isNoteOnForChannels (const int midiChannelMask, const int n) const noexcept
52 {
53 return isPositiveAndBelow (n, 128)
54 && (noteStates[n] & midiChannelMask) != 0;
55 }
56
noteOn(const int midiChannel,const int midiNoteNumber,const float velocity)57 void MidiKeyboardState::noteOn (const int midiChannel, const int midiNoteNumber, const float velocity)
58 {
59 jassert (midiChannel > 0 && midiChannel <= 16);
60 jassert (isPositiveAndBelow (midiNoteNumber, 128));
61
62 const ScopedLock sl (lock);
63
64 if (isPositiveAndBelow (midiNoteNumber, 128))
65 {
66 const int timeNow = (int) Time::getMillisecondCounter();
67 eventsToAdd.addEvent (MidiMessage::noteOn (midiChannel, midiNoteNumber, velocity), timeNow);
68 eventsToAdd.clear (0, timeNow - 500);
69
70 noteOnInternal (midiChannel, midiNoteNumber, velocity);
71 }
72 }
73
noteOnInternal(const int midiChannel,const int midiNoteNumber,const float velocity)74 void MidiKeyboardState::noteOnInternal (const int midiChannel, const int midiNoteNumber, const float velocity)
75 {
76 if (isPositiveAndBelow (midiNoteNumber, 128))
77 {
78 noteStates[midiNoteNumber] = static_cast<uint16> (noteStates[midiNoteNumber] | (1 << (midiChannel - 1)));
79 listeners.call ([&] (Listener& l) { l.handleNoteOn (this, midiChannel, midiNoteNumber, velocity); });
80 }
81 }
82
noteOff(const int midiChannel,const int midiNoteNumber,const float velocity)83 void MidiKeyboardState::noteOff (const int midiChannel, const int midiNoteNumber, const float velocity)
84 {
85 const ScopedLock sl (lock);
86
87 if (isNoteOn (midiChannel, midiNoteNumber))
88 {
89 const int timeNow = (int) Time::getMillisecondCounter();
90 eventsToAdd.addEvent (MidiMessage::noteOff (midiChannel, midiNoteNumber), timeNow);
91 eventsToAdd.clear (0, timeNow - 500);
92
93 noteOffInternal (midiChannel, midiNoteNumber, velocity);
94 }
95 }
96
noteOffInternal(const int midiChannel,const int midiNoteNumber,const float velocity)97 void MidiKeyboardState::noteOffInternal (const int midiChannel, const int midiNoteNumber, const float velocity)
98 {
99 if (isNoteOn (midiChannel, midiNoteNumber))
100 {
101 noteStates[midiNoteNumber] = static_cast<uint16> (noteStates[midiNoteNumber] & ~(1 << (midiChannel - 1)));
102 listeners.call ([&] (Listener& l) { l.handleNoteOff (this, midiChannel, midiNoteNumber, velocity); });
103 }
104 }
105
allNotesOff(const int midiChannel)106 void MidiKeyboardState::allNotesOff (const int midiChannel)
107 {
108 const ScopedLock sl (lock);
109
110 if (midiChannel <= 0)
111 {
112 for (int i = 1; i <= 16; ++i)
113 allNotesOff (i);
114 }
115 else
116 {
117 for (int i = 0; i < 128; ++i)
118 noteOff (midiChannel, i, 0.0f);
119 }
120 }
121
processNextMidiEvent(const MidiMessage & message)122 void MidiKeyboardState::processNextMidiEvent (const MidiMessage& message)
123 {
124 if (message.isNoteOn())
125 {
126 noteOnInternal (message.getChannel(), message.getNoteNumber(), message.getFloatVelocity());
127 }
128 else if (message.isNoteOff())
129 {
130 noteOffInternal (message.getChannel(), message.getNoteNumber(), message.getFloatVelocity());
131 }
132 else if (message.isAllNotesOff())
133 {
134 for (int i = 0; i < 128; ++i)
135 noteOffInternal (message.getChannel(), i, 0.0f);
136 }
137 }
138
processNextMidiBuffer(MidiBuffer & buffer,const int startSample,const int numSamples,const bool injectIndirectEvents)139 void MidiKeyboardState::processNextMidiBuffer (MidiBuffer& buffer,
140 const int startSample,
141 const int numSamples,
142 const bool injectIndirectEvents)
143 {
144 const ScopedLock sl (lock);
145
146 for (const auto metadata : buffer)
147 processNextMidiEvent (metadata.getMessage());
148
149 if (injectIndirectEvents)
150 {
151 const int firstEventToAdd = eventsToAdd.getFirstEventTime();
152 const double scaleFactor = numSamples / (double) (eventsToAdd.getLastEventTime() + 1 - firstEventToAdd);
153
154 for (const auto metadata : eventsToAdd)
155 {
156 const auto pos = jlimit (0, numSamples - 1, roundToInt ((metadata.samplePosition - firstEventToAdd) * scaleFactor));
157 buffer.addEvent (metadata.getMessage(), startSample + pos);
158 }
159 }
160
161 eventsToAdd.clear();
162 }
163
164 //==============================================================================
addListener(Listener * listener)165 void MidiKeyboardState::addListener (Listener* listener)
166 {
167 const ScopedLock sl (lock);
168 listeners.add (listener);
169 }
170
removeListener(Listener * listener)171 void MidiKeyboardState::removeListener (Listener* listener)
172 {
173 const ScopedLock sl (lock);
174 listeners.remove (listener);
175 }
176
177 } // namespace juce
178