Just Intonation  Version 1.3.1 (19)
Explore key-independent dynamically adapting tuning in just intonation
midimicrotonalhelper.cpp
Go to the documentation of this file.
1 /*****************************************************************************
2  * Copyright 2016-2017 Karolin Stange, Christoph Wick, and Haye Hinrichsen
3  *
4  * This file is part of JustIntonation.
5  *
6  * JustIntonation is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by the
8  * Free Software Foundation, either version 3 of the License, or (at your
9  * option) any later version.
10  *
11  * JustIntonation is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14  * for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with JustIntonation. If not, see http://www.gnu.org/licenses/.
18  *****************************************************************************/
19 
20 //=============================================================================
21 // Microtonal Midi Helper Class
22 //=============================================================================
23 
24 #include "midimicrotonalhelper.h"
25 #include "midimicrotonal.h"
26 
27 
28 //-----------------------------------------------------------------------------
29 // Constructor
30 //-----------------------------------------------------------------------------
35 
37  : pMidiMicrotonal(midimicrotone)
38  , mMappedChannelIsUsed(16,false)
39  , mKeyOfMappedChannel(16,-1)
40  , mSequentialNumber(0)
41  , mSequentialNumberOfMappedChannel(16,0)
42  , mCurrentPitch(128,0)
43  , mInstrument(0)
44 {
45  setModuleName("MidiMicrotone");
46 }
47 
48 
49 //-----------------------------------------------------------------------------
50 // Set the instrument (program) of a given input channel
51 //-----------------------------------------------------------------------------
56 
58 {
59  LOGMESSAGE << "Set Midi output channel to" << instrument;
60  mInstrument = instrument & 0x7f;
61 }
62 
63 
64 //-----------------------------------------------------------------------------
65 // Turn a note on
66 //-----------------------------------------------------------------------------
74 
75 void MidiMicrotonalHelper::turnNoteOn (int channel, int key, int volume, double delta)
76 {
77  if (volume==0)
78  {
79  turnNoteOff (channel,key,delta);
80  return;
81  }
82 
83  LOGSTATUS << "Turn note on: Channel =" << channel << "/ key =" << key
84  << "/ volume =" << volume;
85 
86  // Look for oldest channel not in use, called noteChannel
87  int mappedChannel = -1;
88  int lowestSequentialNumber = 0x7fffffff;
89  for (int c=0; c<16; ++c) if (c != cDrumChannel and not mMappedChannelIsUsed[c])
90  {
91  if (mSequentialNumberOfMappedChannel[c] < lowestSequentialNumber)
92  {
93  lowestSequentialNumber = mSequentialNumberOfMappedChannel[c];
94  mappedChannel = c;
95  }
96  }
97 
98  // If all channels are used search the one with the lowest sequential number
99  if (mappedChannel < 0)
100  {
101  lowestSequentialNumber = 0x7fffffff;
102  for (int c=0; c<16; ++c) if (c != cDrumChannel)
103  {
104  if (mSequentialNumberOfMappedChannel[c] < lowestSequentialNumber)
105  {
106  lowestSequentialNumber = mSequentialNumberOfMappedChannel[c];
107  mappedChannel = c;
108  }
109  }
110  if (mappedChannel < 0) LOGERROR << "Search inconsistent";
111 
112  LOGSTATUS << "Release key on mapped channel" << mappedChannel;
113  playNote(false,mappedChannel,mKeyOfMappedChannel[mappedChannel],0,0);
114  }
115 
116  // Now we have a free channel, the noteChannel
117  mMappedChannelIsUsed[mappedChannel] = true;
118  mKeyOfMappedChannel[mappedChannel] = key;
120  playNote (true,mappedChannel,key,volume,delta);
121 }
122 
123 
124 
125 //-----------------------------------------------------------------------------
126 // Polyphonic Key Pressure (Aftertouch).
127 //-----------------------------------------------------------------------------
135 
136 void MidiMicrotonalHelper::afterTouch (int channel, int key, int volume, double delta)
137 {
138  LOGSTATUS << "Polyphonic Key Pressure, channel =" << channel << "/ key =" << key;
139 
140  for (int c=0; c<16; ++c) if (c != cDrumChannel and
141  mMappedChannelIsUsed[c] and
142  mKeyOfMappedChannel[c] == key)
143  {
144  quint8 command = 0xA0 | c;
145  emit pMidiMicrotonal->sendMidiEvent(QMidiMessage(command, key & 0x7f, volume & 0x7f, delta));
146  }
147 }
148 
149 
150 
151 //-----------------------------------------------------------------------------
152 // Polyphonic Key Pressure (Aftertouch).
153 //-----------------------------------------------------------------------------
161 
162 void MidiMicrotonalHelper::controlChange (int channel, int control, int value, double delta)
163 {
164  LOGSTATUS << "Control change, channel =" << channel << "/ control =" << control;
165 
166  for (int c=0; c<16; ++c) if (c != cDrumChannel)
167  {
168  quint8 command = 0xB0 | c;
169  emit pMidiMicrotonal->sendMidiEvent(QMidiMessage(command, control & 0x7f, value & 0x7f, delta));
170  }
171 }
172 
173 
174 
175 //-----------------------------------------------------------------------------
176 // Bank select
177 //-----------------------------------------------------------------------------
183 
184 void MidiMicrotonalHelper::bankSelect(int bank, double delta)
185 {
186  LOGSTATUS << "bank select, bank =" << bank;
187 
188  for (int c=0; c<16; ++c) if (c != cDrumChannel)
189  {
190  quint8 command = 0xB0 | c;
191  emit pMidiMicrotonal->sendMidiEvent(QMidiMessage(command, 0, 0, delta));
192  command = 0xB0 | c;
193  emit pMidiMicrotonal->sendMidiEvent(QMidiMessage(command, 0x20, bank, delta));
194  command = 0xC0 | c;
195  emit pMidiMicrotonal->sendMidiEvent(QMidiMessage(command, mInstrument & 0x7f, delta));
196  }
197 
198 }
199 
200 
201 //-----------------------------------------------------------------------------
202 // Turn a note off
203 //-----------------------------------------------------------------------------
210 
211 void MidiMicrotonalHelper::turnNoteOff (int channel, int key, double delta)
212 {
213  LOGSTATUS << "Turn note off: channel =" << channel << "/ key =" << key;
214 
215  for (int c=0; c<16; ++c) if (c != cDrumChannel and
216  mMappedChannelIsUsed[c] and
217  mKeyOfMappedChannel[c] == key)
218  {
219  playNote(false,c,key,0,delta);
220  mMappedChannelIsUsed[c] = false;
221  }
222 }
223 
224 
225 //-----------------------------------------------------------------------------
226 // Turn a note off
227 //-----------------------------------------------------------------------------
231 
233 {
234  // Send 'all notes off' on all Midi channels
235  for (quint8 command = 0xB0U; command < 0xC0U; command++)
236  emit pMidiMicrotonal->sendMidiEvent (QMidiMessage(command, 123, 0, 0.0));
238 }
239 
240 
241 //-----------------------------------------------------------------------------
242 // Reset all controllers
243 //-----------------------------------------------------------------------------
247 
249 {
250  // Send 'reset all controllers' on all Midi channels
251  for (quint8 command = 0xB0U; command < 0xC0U; command++)
252  emit pMidiMicrotonal->sendMidiEvent (QMidiMessage(command, 121, 0, 0.0));
253 }
254 
255 
256 //-----------------------------------------------------------------------------
257 // Tune a note
258 //-----------------------------------------------------------------------------
268 
269 void MidiMicrotonalHelper::tune (int key, double pitch)
270 {
271  //LOGSTATUS << "**************************" << key << pitch;
272  mCurrentPitch[key]=pitch;
273  for (int c=0; c<16; c++)
274  if (mMappedChannelIsUsed[c] and mKeyOfMappedChannel[c] == key)
275  {
276  setPitchBend(c,pitch);
277  }
278 }
279 
280 
281 //-----------------------------------------------------------------------------
282 // Play a note
283 //-----------------------------------------------------------------------------
285 
286 void MidiMicrotonalHelper::playNote (bool on, int mappedChannel, int key, int vol, double delta)
287 {
288  int command = (on ? 0x90 : 0x80) + (mappedChannel & 0x0f);
289  emit pMidiMicrotonal->sendMidiEvent(QMidiMessage(command, key & 0x7f, vol & 0x7f, delta));
290  if (on) setPitchBend(mappedChannel,mCurrentPitch[key]);
291 }
292 
293 
294 
295 //-----------------------------------------------------------------------------
296 // Set pitch bend
297 //-----------------------------------------------------------------------------
299 
300 void MidiMicrotonalHelper::setPitchBend (int channel, double pitch)
301 {
302  LOGSTATUS << "Pitch bend: channel =" << channel << "/ pitch =" << pitch;
303  if (qAbs(pitch) >= 200)
304  {
305  LOGWARNING << "Cannot tune" << pitch << "cents";
306  return;
307  }
308  int command = 0xe0 + (channel & 0x0f);
309  int pitchbend = qRound(8192 * (1+pitch/200));
310  qint8 arg1 = pitchbend & 0x7f;
311  qint8 arg2 = (pitchbend >> 7) & 0x7f;
312  emit pMidiMicrotonal->sendMidiEvent(QMidiMessage(command,arg1,arg2,0.0));
313 }
314 
315 
316 //-----------------------------------------------------------------------------
317 // Send initial Midi commands
318 //-----------------------------------------------------------------------------
325 
327 {
328  if (not pMidiMicrotonal->isWorking()) return;
330 
331  localControl(false); // Switch off local control
332  bankSelect(2,0);
333  for (int c=0; c<16; ++c) if (c != cDrumChannel)
335  QMidiMessage(0xc0 | c, mInstrument & 0x7f, 0.0));
336 }
337 
338 
339 //-----------------------------------------------------------------------------
340 // Enable local control
341 //-----------------------------------------------------------------------------
351 
353 {
354  LOGMESSAGE << "Local control of external Midi =" << on;
355  for (quint8 command = 0xB0U; command < 0xC0U; command++)
356  {
358  QMidiMessage(command, 122, on ? 0x7f : 0, 0.0));
359  }
360 }
void resetAllControllers()
Reset all controllers.
void setModuleName(const QString &name)
Specify the name of the class-specific module.
Definition: log.cpp:82
#define LOGMESSAGE
Definition: log.h:43
QVector< bool > mMappedChannelIsUsed
Indicates usage of channel.
MidiMicrotonalHelper(MidiMicrotonal *midimicrotone)
Constructor, resetting the member variables.
void setPitchBend(int channel, double pitch)
void allNotesOff()
Turn all notes off.
void bankSelect(int bank, double delta)
MidiMicrotonalHelper::bankSelect.
void controlChange(int channel, int control, int value, double delta)
Polyphonic Key Pressure (Aftertouch).
void sendMidiEvent(QMidiMessage event)
Output signal: This is the converted outgoing stream of Midi events.
MidiMicrotonal * pMidiMicrotonal
Pointer back.
void setInstrument(int instrument)
Set the instrument (program)
void playNote(bool on, int mappedChannel, int key, int vol, double delta)
#define LOGERROR
Definition: log.h:45
int mSequentialNumber
Increasing tag.
Class for converting an ordinary Midi stream into a microtonal one.
void turnNoteOn(int channel, int key, int volume, double delta)
Turn note on.
void localControl(bool on)
Enable local control.
#define LOGSTATUS
Definition: log.h:42
void sendInitialMidiCommands(void)
Send initial Midi commands.
void tune(int key, double pitch)
MidiMicrotonalHelper::tune.
void afterTouch(int channel, int key, int volume, double delta)
Polyphonic Key Pressure (Aftertouch).
static const QMidiMessage cLoopMarker
Closed-loop marking event.
const int cDrumChannel
Take out drum channel.
int mInstrument
The selected instrument.
QVector< double > mCurrentPitch
Pitch.
void turnNoteOff(int channel, int key, double delta)
Turn note off.
QVector< int > mSequentialNumberOfMappedChannel
Tag storage.
QVector< int > mKeyOfMappedChannel
Associated key.
#define LOGWARNING
Definition: log.h:44