Just Intonation  Version 1.3.1 (19)
Explore key-independent dynamically adapting tuning in just intonation
midimicrotonal.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 Converter
22 //=============================================================================
23 
24 #include "midimicrotonal.h"
25 
26 #include <QTimer>
27 
28 //-----------------------------------------------------------------------------
29 // Constructor
30 //-----------------------------------------------------------------------------
34 
36  : mMidiMicrotonalHelper(this)
37  , mRunning(false)
38  , mActive(false)
39  , mSuspended(false)
40  , mLastChannelModeMessage(0)
41  , mTimer()
42 {
43  setModuleName ("MidiMicrotone");
46  connect(&mTimer,&QTimer::timeout,
48  // Update channels (intruments) after 5 seconds idle time
49  mTimer.setInterval(5000);
50 }
51 
52 //-----------------------------------------------------------------------------
53 // Closed loop marker
54 //-----------------------------------------------------------------------------
64 const QMidiMessage MidiMicrotonal::cLoopMarker(0xf2,0x1a,0x37,0.0);
65 
66 
67 //-----------------------------------------------------------------------------
68 // Init
69 //-----------------------------------------------------------------------------
74 
76 { return true; }
77 
78 
79 //-----------------------------------------------------------------------------
80 // Exit
81 //-----------------------------------------------------------------------------
86 
88 { return true; }
89 
90 
91 //-----------------------------------------------------------------------------
92 // Start
93 //-----------------------------------------------------------------------------
98 
100 {
101  LOGMESSAGE << "Microtonal converter started";
102  mRunning = true;
103  mTimer.start();
104  return true;
105 }
106 
107 
108 //-----------------------------------------------------------------------------
109 // Stop
110 //-----------------------------------------------------------------------------
115 
117 {
118  if (mRunning)
119  {
121  allNotesOff();
122  mRunning = false;
123  LOGMESSAGE << "Microtonal converter stopped";
124  }
125  else LOGWARNING << "Microtonal converter is already off";
126  return true;
127 }
128 
129 
130 //-----------------------------------------------------------------------------
131 // Suspend
132 //-----------------------------------------------------------------------------
138 
140 {
141  mSuspended = true;
142 }
143 
144 
145 //-----------------------------------------------------------------------------
146 // Resume
147 //-----------------------------------------------------------------------------
151 
153 {
154  if (mSuspended)
155  {
156  mSuspended = false;
157  mTimer.start();
158  }
159 }
160 
161 
162 
163 
164 //-----------------------------------------------------------------------------
165 // Input slot: Receive Midi event
166 //-----------------------------------------------------------------------------
171 void MidiMicrotonal::activate (bool active)
172 {
173  if (active == mActive) return;
174  if (active)
175  {
176  mActive = true;
177  resume();
179  }
180  else
181  {
182  mActive = false;
184  }
185 }
186 
187 
188 //-----------------------------------------------------------------------------
189 // Input slot: Receive Midi event
190 //-----------------------------------------------------------------------------
191 
192 void MidiMicrotonal::receiveMidiEvent (const QMidiMessage event)
193 {
194  // If converter is not working return
195  if (not isWorking())
196  {
197  LOGSTATUS << "Microtonal converter not working." << event;
198  return;
199  }
200 
201  // If a closed loop is detected suspend the converter and emit a signal
202  if (event.message() == cLoopMarker.message())
203  {
204  LOGWARNING << "Detected closed Midi loop";
205  suspend();
206  //emit onClosedMidiLoopDetected();
207  return;
208  }
209 
210  LOGSTATUS << "Receive Midi event" << event;
211  int channel = event.byte0() & 0x0f;
212  quint8 code = event.byte0() & 0xf0;
213 
214  // if channel mode message
215  QMutexLocker locker(&mMutex);
216 
217  if (code == 0xB0)
218  {
219  // Ignore two subsequent messages of the same type
220  if (mLastChannelModeMessage == event.byte1()) return;
221  mLastChannelModeMessage = event.byte1();
222  switch (event.byte1())
223  {
224  //case 120: // All sound off immediately
225 
226  case 121: // Reset all controllers
227  case 123: // All notes off including release
229  break;
230  case 0:
231  //case 7:
232  case 0x40:
233  case 0x41:
234  case 0x42:
235  case 0x43:
236  case 0x44:
237  mMidiMicrotonalHelper.controlChange(channel,event.byte1(),event.byte2(),event.timestamp());
238  break;
239  default:
240  break;
241  }
242  }
243  else // if no channel mode message
244  {
246  // Lock access since different threads could call this slot
247  // at the same time.
248  switch (code)
249  {
250  case 0x80:
251  mMidiMicrotonalHelper.turnNoteOff(channel,event.byte1(),event.timestamp());
252  break;
253  case 0x90:
254  mMidiMicrotonalHelper.turnNoteOn(channel,event.byte1(),event.byte2(),event.timestamp());
255  break;
256  case 0xA0:
257  //mConverter.afterTouch(channel,event.arg1,event.arg2,event.delta);
258  break;
259  case 0xB0: // this was done above
260  break;
261  case 0xC0:
262  //mConverter.setInstrument(channel,event.arg1);
263  break;
264  case 0xE0:
265  // Ignore pitch bends
266  break;
267  default:
268  LOGMESSAGE << "Unrecognized Midi code" << event.byte0();
269  break;
270  }
271  }
272  // After each Midi event the timer is restarted. On timeout the channels
273  // of the target device are set to the selected instrument
274  mTimer.start();
275 }
276 
277 
278 //-----------------------------------------------------------------------------
279 // Input slot: Receive tuning corrections
280 //-----------------------------------------------------------------------------
281 
282 void MidiMicrotonal::receiveTuningCorrections(QMap<int, double> corrections)
283 {
284  if (not mRunning)
285  {
286  LOGSTATUS << "Microtonal converter is switched off";
287  return;
288  }
289  LOGSTATUS << "tune" << corrections;
290  QMapIterator<int, double> entry(corrections);
291 
292  // Lock access since different threads could call this slot
293  // at the same time.
294  QMutexLocker locker(&mMutex);
295  while (entry.hasNext())
296  {
297  entry.next();
298  mMidiMicrotonalHelper.tune(entry.key(),entry.value());
299  }
300 }
301 
302 
303 //-----------------------------------------------------------------------------
304 // Input slot: Turn all notes off
305 //-----------------------------------------------------------------------------
306 
308 {
309  QMutexLocker locker(&mMutex);
310  if (not mRunning)
311  {
312  LOGSTATUS << "Microtonal converter is switched off";
313  return;
314  }
315  LOGSTATUS << "Turn all notes off";
316  // Lock access
318 }
319 
320 //-----------------------------------------------------------------------------
321 // Input slot: set the instrument channel
322 //-----------------------------------------------------------------------------
323 
325 {
326  QMutexLocker locker(&mMutex);
329 }
330 
331 
332 //-----------------------------------------------------------------------------
333 // Input slot: Re-initialize Midi output strem
334 //-----------------------------------------------------------------------------
335 
337 {
338  // Start sending intial string of Midi commands after a waiting time
339  if (not isWorking())
340  {
341  LOGMESSAGE << "Re-initialize after I/O device change";
342  resume();
343  }
344  QTimer::singleShot(200,&mMidiMicrotonalHelper,
346 }
bool stop()
Stop the microtonal converter.
void receiveTuningCorrections(QMap< int, double > corrections)
Input slot: Receive tuning corrections from the tuner (<key,cent>)
void setModuleName(const QString &name)
Specify the name of the class-specific module.
Definition: log.cpp:82
MidiMicrotonal()
Constructor, resetting member variables.
int getVerbosity()
Get the actual verbosity level.
Definition: log.cpp:118
#define LOGMESSAGE
Definition: log.h:43
bool exit()
Shutdown, no functionality.
void allNotesOff()
Turn all notes off.
void controlChange(int channel, int control, int value, double delta)
Polyphonic Key Pressure (Aftertouch).
bool start()
Start the microtonal converter.
void allNotesOff()
Slot: Turns all notes off.
void setVerbosity(int verbosity)
Set the verbosity level of the class-specific messages.
Definition: log.cpp:107
void setInstrument(int instrument)
Set the instrument (program)
void activate(bool active)
Activate or deactivate the module.
void setMidiOutputChannel(int channel)
Slot: Set the instrument channel of the external Midi device.
void turnNoteOn(int channel, int key, int volume, double delta)
Turn note on.
void localControl(bool on)
Enable local control.
void resume()
Resume from suspend mode.
#define LOGSTATUS
Definition: log.h:42
bool init()
Initialization, no functionality.
void sendInitialMidiCommands(void)
Send initial Midi commands.
void tune(int key, double pitch)
MidiMicrotonalHelper::tune.
static const QMidiMessage cLoopMarker
Closed-loop marking event.
void suspend()
Suspend the microtonal converter.
void reInitializeConvertedMidiStream()
Slot: Re-initialize the output stream.
void receiveMidiEvent(const QMidiMessage event)
Input slot: Here the module receives the stream of incoming Midi events.
void turnNoteOff(int channel, int key, double delta)
Turn note off.
MidiMicrotonalHelper mMidiMicrotonalHelper
#define LOGWARNING
Definition: log.h:44