1 //=========================================================
2 // MusE
3 // Linux Music Editor
4 // $Id: mididev.cpp,v 1.10.2.6 2009/11/05 03:14:35 terminator356 Exp $
5 //
6 // (C) Copyright 1999-2004 Werner Schweer (ws@seh.de)
7 // (C) Copyright 2011, 2016 Tim E. Real (terminator356 on users dot sourceforge dot net)
8 //
9 // This program is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU General Public License
11 // as published by the Free Software Foundation; version 2 of
12 // the License, or (at your option) any later version.
13 //
14 // This program is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 // GNU General Public License for more details.
18 //
19 // You should have received a copy of the GNU General Public License
20 // along with this program; if not, write to the Free Software
21 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 //
23 //=========================================================
24
25 #include <config.h>
26
27 #include <QMessageBox>
28 #include <stdio.h>
29 #include <unistd.h>
30 #include <errno.h>
31
32 #include "midictrl.h"
33 #include "song.h"
34 #include "midi_consts.h"
35 #include "midiport.h"
36 #include "mididev.h"
37 #include "config.h"
38 #include "gconfig.h"
39 #include "globals.h"
40 #include "audio.h"
41 #include "audiodev.h"
42 #include "midiseq.h"
43 #include "midiitransform.h"
44 #include "mitplugin.h"
45 #include "part.h"
46 #include "drummap.h"
47 #include "helper.h"
48 #include "ticksynth.h"
49
50 // Forwards from header:
51 #include "xml.h"
52
53 // Undefine if and when multiple output routes are added to midi tracks.
54 #define _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_
55
56 // For debugging output: Uncomment the fprintf section.
57 //#define DEBUG_MIDI_DEVICE(dev, format, args...) //fprintf(dev, format, ##args);
58
59 namespace MusEGlobal {
60 MusECore::MidiDeviceList midiDevices;
61 }
62
63 namespace MusECore {
64
65 #ifdef MIDI_DRIVER_MIDI_SERIAL
66 extern void initMidiSerial();
67 #endif
68 extern bool initMidiAlsa();
69 extern bool initMidiJack();
70
71 extern void processMidiInputTransformPlugins(MEvent&);
72
73 // Static.
74 const int MidiDevice::extClockHistoryCapacity = 1024;
75
76
77 //---------------------------------------------------------
78 // initMidiDevices
79 //---------------------------------------------------------
80
initMidiDevices()81 void initMidiDevices()
82 {
83 #ifdef MIDI_DRIVER_MIDI_SERIAL
84 initMidiSerial();
85 #endif
86 #ifdef ALSA_SUPPORT
87 if(MusEGlobal::config.enableAlsaMidiDriver || // User setting
88 MusEGlobal::useAlsaWithJack || // Command line override
89 MusEGlobal::audioDevice->deviceType() != AudioDevice::JACK_AUDIO) // Jack not running
90 {
91 if(initMidiAlsa())
92 {
93 QMessageBox::critical(NULL, "MusE fatal error.", "MusE failed to initialize the\n"
94 "Alsa midi subsystem, check\n"
95 "your configuration.");
96 exit(-1);
97 }
98 }
99 #endif
100
101 if(initMidiJack())
102 {
103 QMessageBox::critical(NULL, "MusE fatal error.", "MusE failed to initialize the\n"
104 "Jack midi subsystem, check\n"
105 "your configuration.");
106 exit(-1);
107 }
108 }
109
110 //---------------------------------------------------------
111 // init
112 //---------------------------------------------------------
113
init()114 void MidiDevice::init()
115 {
116 _extClockHistoryFifo = new LockFreeBuffer<ExtMidiClock>(extClockHistoryCapacity);
117
118 // TODO: Scale these according to the current audio segment size.
119 _playbackEventBuffers = new LockFreeMPSCRingBuffer<MidiPlayEvent>(1024);
120 _userEventBuffers = new LockFreeMPSCRingBuffer<MidiPlayEvent>(1024);
121
122 _sysExOutDelayedEvents = new std::vector<MidiPlayEvent>;
123 // Initially reserve a fair number of items to hold potentially a lot
124 // of messages when the sysex processor is busy (in the Sending state).
125 _sysExOutDelayedEvents->reserve(1024);
126 _stopFlag.store(false);
127
128 _state = QString("Closed");
129 _readEnable = false;
130 _writeEnable = false;
131 _rwFlags = 3;
132 _openFlags = 3;
133 _port = -1;
134 }
135
136 //---------------------------------------------------------
137 // MidiDevice
138 //---------------------------------------------------------
139
MidiDevice()140 MidiDevice::MidiDevice()
141 {
142 for(unsigned int i = 0; i < MusECore::MUSE_MIDI_CHANNELS + 1; ++i)
143 _tmpRecordCount[i] = 0;
144
145 _sysexFIFOProcessed = false;
146
147 init();
148 }
149
MidiDevice(const QString & n)150 MidiDevice::MidiDevice(const QString& n)
151 : _name(n)
152 {
153 for(unsigned int i = 0; i < MusECore::MUSE_MIDI_CHANNELS + 1; ++i)
154 _tmpRecordCount[i] = 0;
155
156 _sysexFIFOProcessed = false;
157
158 init();
159 }
160
~MidiDevice()161 MidiDevice::~MidiDevice()
162 {
163 if(_sysExOutDelayedEvents)
164 delete _sysExOutDelayedEvents;
165 if(_extClockHistoryFifo)
166 delete _extClockHistoryFifo;
167 if(_userEventBuffers)
168 delete _userEventBuffers;
169 if(_playbackEventBuffers)
170 delete _playbackEventBuffers;
171 }
172
deviceTypeString() const173 QString MidiDevice::deviceTypeString() const
174 {
175 switch(deviceType())
176 {
177 case ALSA_MIDI:
178 return "ALSA";
179 case JACK_MIDI:
180 return "JACK";
181 case SYNTH_MIDI:
182 {
183 const SynthI* s = dynamic_cast<const SynthI*>(this);
184 if(s && s->synth())
185 return MusECore::synthType2String(s->synth()->synthType());
186 else
187 return "SYNTH";
188 }
189 }
190 return "UNKNOWN";
191 }
192
setPort(int p)193 void MidiDevice::setPort(int p)
194 {
195 _port = p;
196 if(_port != -1)
197 MusEGlobal::midiPorts[_port].clearInitSent();
198 }
199
200 //---------------------------------------------------------
201 // filterEvent
202 // return true if event filtered
203 //---------------------------------------------------------
204
filterEvent(const MEvent & event,int type,bool thru)205 bool filterEvent(const MEvent& event, int type, bool thru)
206 {
207 switch(event.type()) {
208 case ME_NOTEON:
209 case ME_NOTEOFF:
210 if (type & MIDI_FILTER_NOTEON)
211 return true;
212 break;
213 case ME_POLYAFTER:
214 if (type & MIDI_FILTER_POLYP)
215 return true;
216 break;
217 case ME_CONTROLLER:
218 if (type & MIDI_FILTER_CTRL)
219 return true;
220 if (!thru && (MusEGlobal::midiFilterCtrl1 == event.dataA()
221 || MusEGlobal::midiFilterCtrl2 == event.dataA()
222 || MusEGlobal::midiFilterCtrl3 == event.dataA()
223 || MusEGlobal::midiFilterCtrl4 == event.dataA())) {
224 return true;
225 }
226 break;
227 case ME_PROGRAM:
228 if (type & MIDI_FILTER_PROGRAM)
229 return true;
230 break;
231 case ME_AFTERTOUCH:
232 if (type & MIDI_FILTER_AT)
233 return true;
234 break;
235 case ME_PITCHBEND:
236 if (type & MIDI_FILTER_PITCH)
237 return true;
238 break;
239 case ME_SYSEX:
240 if (type & MIDI_FILTER_SYSEX)
241 return true;
242 break;
243 default:
244 break;
245 }
246 return false;
247 }
248
249 //---------------------------------------------------------
250 // afterProcess
251 // clear all recorded events after a process cycle
252 //---------------------------------------------------------
253
afterProcess()254 void MidiDevice::afterProcess()
255 {
256 for(unsigned int i = 0; i < MusECore::MUSE_MIDI_CHANNELS + 1; ++i)
257 {
258 while (_tmpRecordCount[i]--)
259 _recordFifo[i].remove();
260 }
261 }
262
263 //---------------------------------------------------------
264 // beforeProcess
265 // "freeze" fifo for this process cycle
266 //---------------------------------------------------------
267
beforeProcess()268 void MidiDevice::beforeProcess()
269 {
270 for(unsigned int i = 0; i < MusECore::MUSE_MIDI_CHANNELS + 1; ++i)
271 _tmpRecordCount[i] = _recordFifo[i].getSize();
272
273 // Reset this.
274 _sysexFIFOProcessed = false;
275 }
276
277 //---------------------------------------------------------
278 // midiClockInput
279 // Midi clock (24 ticks / quarter note)
280 //---------------------------------------------------------
281
midiClockInput(unsigned int frame)282 void MidiDevice::midiClockInput(unsigned int frame)
283 {
284 // Put a midi clock record event into the clock history fifo. Ignore port and channel.
285 // Timestamp with the current frame.
286 const ExtMidiClock ext_clk = MusEGlobal::midiSyncContainer.midiClockInput(midiPort(), frame);
287 if(ext_clk.isValid() && extClockHistory())
288 extClockHistory()->put(ext_clk);
289 }
290
291 //---------------------------------------------------------
292 // recordEvent
293 //---------------------------------------------------------
294
recordEvent(MidiRecordEvent & event)295 void MidiDevice::recordEvent(MidiRecordEvent& event)
296 {
297 if(MusEGlobal::audio->isPlaying())
298 event.setLoopNum(MusEGlobal::audio->loopCount());
299
300 if (MusEGlobal::midiInputTrace) {
301 fprintf(stderr, "MidiInput: ");
302 dumpMPEvent(&event);
303 }
304
305 int typ = event.type();
306
307 if(_port != -1)
308 {
309 int idin = MusEGlobal::midiPorts[_port].syncInfo().idIn();
310
311 //---------------------------------------------------
312 // filter some SYSEX events
313 //---------------------------------------------------
314
315 if (typ == ME_SYSEX) {
316 const unsigned char* p = event.data();
317 int n = event.len();
318 if (n >= 4) {
319 if ((p[0] == 0x7f)
320 && ((p[1] == 0x7f) || (idin == 0x7f) || (p[1] == idin))) {
321 if (p[2] == 0x06) {
322 MusEGlobal::midiSyncContainer.mmcInput(_port, p, n);
323 return;
324 }
325 if (p[2] == 0x01) {
326 MusEGlobal::midiSyncContainer.mtcInputFull(_port, p, n);
327 return;
328 }
329 }
330 else if (p[0] == 0x7e) {
331 MusEGlobal::midiSyncContainer.nonRealtimeSystemSysex(_port, p, n);
332 return;
333 }
334 }
335 }
336 else
337 // Trigger general activity indicator detector. Sysex has no channel, don't trigger.
338 MusEGlobal::midiPorts[_port].syncInfo().trigActDetect(event.channel());
339 }
340
341 //
342 // process midi event input filtering and
343 // transformation
344 //
345
346 processMidiInputTransformPlugins(event);
347
348 if (filterEvent(event, MusEGlobal::midiRecordType, false))
349 return;
350
351 if (!applyMidiInputTransformation(event)) {
352 if (MusEGlobal::midiInputTrace)
353 fprintf(stderr, " midi input transformation: event filtered\n");
354 return;
355 }
356
357 //
358 // transfer noteOn and Off events to gui for step recording and keyboard
359 // remote control (changed by flo93: added noteOff-events)
360 //
361 if (typ == ME_NOTEON) {
362 int pv = ((event.dataA() & 0xff)<<8) + (event.dataB() & 0xff);
363 MusEGlobal::song->putEvent(pv);
364 }
365 else if (typ == ME_NOTEOFF) {
366 int pv = ((event.dataA() & 0xff)<<8) + (0x00); //send an event with velo=0
367 MusEGlobal::song->putEvent(pv);
368 }
369 else if (MusEGlobal::rcEnableCC && typ == ME_CONTROLLER) {
370 char cc = static_cast<char>(event.dataA() & 0xff);
371 printf("*** Input CC: %d\n", cc);
372 MusEGlobal::song->putEventCC(cc);
373 }
374
375 // Do not bother recording if it is NOT actually being used by a port.
376 // Because from this point on, process handles things, by selected port.
377 if(_port == -1)
378 return;
379
380 // Split the events up into channel fifos. Special 'channel' number 17 for sysex events.
381 unsigned int ch = (typ == ME_SYSEX)? MusECore::MUSE_MIDI_CHANNELS : event.channel();
382 if(_recordFifo[ch].put(event))
383 fprintf(stderr, "MidiDevice::recordEvent: fifo channel %d overflow\n", ch);
384 }
385
386 //---------------------------------------------------------
387 // find
388 //---------------------------------------------------------
389
find(const QString & s,int typeHint)390 MidiDevice* MidiDeviceList::find(const QString& s, int typeHint)
391 {
392 for (iMidiDevice i = begin(); i != end(); ++i)
393 if( (typeHint == -1 || typeHint == (*i)->deviceType()) && ((*i)->name() == s) )
394 return *i;
395 return 0;
396 }
397
398 //---------------------------------------------------------
399 // add
400 //---------------------------------------------------------
401
add(MidiDevice * dev)402 void MidiDeviceList::add(MidiDevice* dev)
403 {
404 bool gotUniqueName=false;
405 int increment = 0;
406 const QString origname = dev->name();
407 QString newName = origname;
408 while (!gotUniqueName) {
409 gotUniqueName = true;
410 // check if the name's been taken
411 for (iMidiDevice i = begin(); i != end(); ++i) {
412 const QString s = (*i)->name();
413 if (s == newName)
414 {
415 newName = origname + QString("_%1").arg(++increment);
416 gotUniqueName = false;
417 }
418 }
419 }
420 if(origname != newName)
421 dev->setName(newName);
422 push_back(dev);
423 }
424
425 //---------------------------------------------------------
426 // remove
427 //---------------------------------------------------------
428
remove(MidiDevice * dev)429 void MidiDeviceList::remove(MidiDevice* dev)
430 {
431 for (iMidiDevice i = begin(); i != end(); ++i) {
432 if (*i == dev) {
433 erase(i);
434 break;
435 }
436 }
437 }
438
439 //---------------------------------------------------------
440 // resetCurParamNums
441 // Reset output channel's current parameter numbers to -1.
442 // All channels if chan = -1.
443 //---------------------------------------------------------
444
resetCurOutParamNums(int chan)445 void MidiDevice::resetCurOutParamNums(int chan)
446 {
447 if(chan == -1)
448 {
449 for(int i = 0; i < MusECore::MUSE_MIDI_CHANNELS; ++i)
450 _curOutParamNums[i].resetParamNums();
451 return;
452 }
453 _curOutParamNums[chan].resetParamNums();
454 }
455
456 //---------------------------------------------------------
457 // putEvent
458 // return true if event cannot be delivered
459 //---------------------------------------------------------
460
putEvent(const MidiPlayEvent & ev,LatencyType latencyType,EventBufferType bufferType)461 bool MidiDevice::putEvent(const MidiPlayEvent& ev, LatencyType latencyType, EventBufferType bufferType)
462 {
463 // TODO: Decide whether we want the driver cached values always updated like this,
464 // even if not writeable or if error.
465 // if(!_writeEnable)
466 // return true;
467
468 // Automatically shift the time forward if specified.
469 MidiPlayEvent fin_ev = ev;
470 switch(latencyType)
471 {
472 case NotLate:
473 break;
474
475 case Late:
476 fin_ev.setTime(fin_ev.time() + pbForwardShiftFrames());
477 break;
478 }
479
480 //DEBUG_MIDI_DEVICE(stderr, "MidiDevice::putUserEvent devType:%d time:%d type:%d ch:%d A:%d B:%d\n",
481 // deviceType(), fin_ev.time(), fin_ev.type(), fin_ev.channel(), fin_ev.dataA(), fin_ev.dataB());
482 if (MusEGlobal::midiOutputTrace)
483 {
484 fprintf(stderr, "MidiDevice::putEvent: %s: <%s>: ", deviceTypeString().toLatin1().constData(), name().toLatin1().constData());
485 dumpMPEvent(&fin_ev);
486 }
487
488 bool rv = true;
489 switch(bufferType)
490 {
491 case PlaybackBuffer:
492 rv = !_playbackEventBuffers->put(fin_ev);
493 break;
494
495 case UserBuffer:
496 rv = !_userEventBuffers->put(fin_ev);
497 break;
498 }
499
500 if(rv)
501 fprintf(stderr, "MidiDevice::putEvent: Error: Device buffer overflow. bufferType:%d\n", bufferType);
502
503 return rv;
504 }
505
506 //---------------------------------------------------------
507 // processStuckNotes
508 // To be called by audio thread only.
509 //---------------------------------------------------------
510
processStuckNotes()511 void MidiDevice::processStuckNotes()
512 {
513 // Must be playing for valid nextTickPos, right? But wasn't checked in Audio::processMidi().
514 // MusEGlobal::audio->isPlaying() might not be true during seek right now.
515 //if(MusEGlobal::audio->isPlaying())
516 {
517 const bool extsync = MusEGlobal::extSyncFlag;
518 const unsigned syncFrame = MusEGlobal::audio->curSyncFrame();
519 const unsigned curTickPos = MusEGlobal::audio->tickPos();
520 const unsigned nextTick = MusEGlobal::audio->nextTick();
521 // What is the current transport frame?
522 const unsigned int pos_fr = MusEGlobal::audio->pos().frame();
523 // What is the (theoretical) next transport frame?
524 const unsigned int next_pos_fr = pos_fr + MusEGlobal::audio->curCycleFrames();
525 ciMPEvent k;
526
527 //---------------------------------------------------
528 // Play any stuck notes which were put directly to the device
529 //---------------------------------------------------
530
531 for (k = _stuckNotes.begin(); k != _stuckNotes.end(); ++k) {
532 MidiPlayEvent ev(*k);
533 unsigned int off_tick = ev.time();
534 // If external sync is not on, we can take advantage of frame accuracy but
535 // first we must allow the next tick position to be included in the search
536 // even if it is equal to the current tick position.
537 if (extsync ? (off_tick >= nextTick) : (off_tick > nextTick))
538 break;
539 unsigned int off_frame = 0;
540 if(extsync)
541 {
542 if(off_tick < curTickPos)
543 off_tick = curTickPos;
544 off_frame = MusEGlobal::audio->extClockHistoryTick2Frame(off_tick - curTickPos) + MusEGlobal::segmentSize;
545 }
546 else
547 {
548 // What is the exact transport frame that the event should be played at?
549 const unsigned int fr = MusEGlobal::tempomap.tick2frame(off_tick);
550 // Is the event frame outside of the current transport frame range?
551 if(fr >= next_pos_fr)
552 break;
553 off_frame = (fr < pos_fr) ? 0 : fr - pos_fr;
554 off_frame += syncFrame;
555 }
556 ev.setTime(off_frame);
557
558 _userEventBuffers->put(ev);
559 }
560 _stuckNotes.erase(_stuckNotes.begin(), k);
561
562 //------------------------------------------------------------
563 // To save time, playing of any track-related playback stuck notes (NOT 'live' notes)
564 // which were not put directly to the device, is done in Audio::processMidi().
565 //------------------------------------------------------------
566 }
567 }
568
569 //---------------------------------------------------------
570 // handleStop
571 // To be called by audio thread only.
572 //---------------------------------------------------------
573
handleStop()574 void MidiDevice::handleStop()
575 {
576 // If the device is not in use by a port, don't bother it.
577 if(_port == -1)
578 return;
579
580 MidiPort* mp = &MusEGlobal::midiPorts[_port];
581
582 //---------------------------------------------------
583 // send midi stop
584 //---------------------------------------------------
585
586 // Don't send if external sync is on. The master, and our sync routing system will take care of that.
587 if(!MusEGlobal::extSyncFlag)
588 {
589 // Shall we check open flags? DELETETHIS 4?
590 //if(!(dev->rwFlags() & 0x1) || !(dev->openFlags() & 1))
591 //if(!(dev->openFlags() & 1))
592 // return;
593
594 MidiSyncInfo& si = mp->syncInfo();
595 if(si.MMCOut())
596 mp->sendMMCStop();
597
598 if(si.MRTOut())
599 {
600 mp->sendStop();
601 //DELETETHIS 5?
602 // Added check of option send continue not start. Hmm, is this required? Seems to make other devices unhappy.
603 // (Could try now that this is in MidiDevice.)
604 //if(!si.sendContNotStart())
605 // mp->sendSongpos(MusEGlobal::audio->tickPos() * 4 / MusEGlobal::config.division);
606 }
607 }
608
609 //---------------------------------------------------
610 // Clear all notes and flush out any stuck notes
611 // which were put directly to the device
612 //---------------------------------------------------
613
614 setStopFlag(true);
615 for(iMPEvent i = _stuckNotes.begin(); i != _stuckNotes.end(); ++i)
616 {
617 MidiPlayEvent ev(*i);
618 ev.setTime(0); // Immediate processing. TODO Use curFrame?
619 //ev.setTime(MusEGlobal::audio->midiQueueTimeStamp(ev.time()));
620 putEvent(ev, MidiDevice::NotLate);
621 }
622 _stuckNotes.clear();
623
624 //------------------------------------------------------------
625 // Flush out any track-related playback stuck notes (NOT 'live' notes)
626 // which were not put directly to the device
627 //------------------------------------------------------------
628
629 for(ciMidiTrack imt = MusEGlobal::song->midis()->begin(); imt != MusEGlobal::song->midis()->end(); ++imt)
630 {
631 MPEventList& mel = (*imt)->stuckNotes;
632 for(iMPEvent i = mel.begin(), i_next = i; i != mel.end(); i = i_next)
633 {
634 ++i_next;
635
636 if((*i).port() != _port)
637 continue;
638 MidiPlayEvent ev(*i);
639 ev.setTime(0); // Immediate processing. TODO Use curFrame?
640 //ev.setTime(MusEGlobal::audio->midiQueueTimeStamp(ev.time()));
641 putEvent(ev, MidiDevice::NotLate);
642
643 mel.erase(i);
644 }
645 }
646
647 //---------------------------------------------------
648 // reset sustain
649 //---------------------------------------------------
650
651 for(int ch = 0; ch < MusECore::MUSE_MIDI_CHANNELS; ++ch)
652 {
653 if(mp->hwCtrlState(ch, CTRL_SUSTAIN) == 127)
654 {
655 MidiPlayEvent ev(0, _port, ch, ME_CONTROLLER, CTRL_SUSTAIN, 0); // Immediate processing. TODO Use curFrame?
656 //ev.setTime(MusEGlobal::audio->midiQueueTimeStamp(ev.time()));
657 putEvent(ev, MidiDevice::NotLate);
658 }
659 }
660 }
661
662 //---------------------------------------------------------
663 // handleSeek
664 // To be called by audio thread only.
665 //---------------------------------------------------------
666
handleSeek()667 void MidiDevice::handleSeek()
668 {
669 //---------------------------------------------------
670 // If playing, clear all notes and flush out any
671 // stuck notes which were put directly to the device
672 //---------------------------------------------------
673
674 if(MusEGlobal::audio->isPlaying())
675 {
676 // TODO: Don't clear, let it play whatever was scheduled ?
677 //setStopFlag(true);
678 for(iMPEvent i = _stuckNotes.begin(); i != _stuckNotes.end(); ++i)
679 {
680 MidiPlayEvent ev(*i);
681 ev.setTime(0); // Immediate processing. TODO Use curFrame?
682 //ev.setTime(MusEGlobal::audio->midiQueueTimeStamp(ev.time()));
683 putEvent(ev, MidiDevice::NotLate);
684 }
685 _stuckNotes.clear();
686 }
687 }
688
689 //================================================
690 // BEGIN Latency correction/compensation routines.
691 //================================================
692
prepareLatencyScan()693 void MidiDevice::prepareLatencyScan() {
694 // Reset some latency info to prepare for (re)computation.
695 _captureLatencyInfo.initialize();
696 _playbackLatencyInfo.initialize();
697 }
698
isLatencyInputTerminalMidi(bool capture)699 bool MidiDevice::isLatencyInputTerminalMidi(bool capture)
700 {
701 TrackLatencyInfo* tli = capture ? &_captureLatencyInfo : &_playbackLatencyInfo;
702
703 // Have we been here before during this scan?
704 // Just return the cached value.
705 if(tli->_isLatencyInputTerminalProcessed)
706 return tli->_isLatencyInputTerminal;
707
708 const int port = midiPort();
709
710 // Playback devices are considered a termination point.
711 if(!capture || port < 0 || port >= MusECore::MIDI_PORTS)
712 {
713 tli->_isLatencyInputTerminal = true;
714 tli->_isLatencyInputTerminalProcessed = true;
715 return true;
716 }
717
718 MidiPort* mp = &MusEGlobal::midiPorts[port];
719 const RouteList* rl = mp->outRoutes();
720 for (ciRoute ir = rl->begin(); ir != rl->end(); ++ir) {
721 switch(ir->type)
722 {
723 case Route::TRACK_ROUTE:
724 if(!ir->track)
725 continue;
726 if(ir->track->isMidiTrack())
727 {
728 Track* track = ir->track;
729 if(track->off()) // ||
730 //(atrack->canRecordMonitor() && (MusEGlobal::config.monitoringAffectsLatency || !atrack->isRecMonitored())))
731 //&& atrack->canRecord() && !atrack->recordFlag()))
732 continue;
733
734 tli->_isLatencyInputTerminal = false;
735 tli->_isLatencyInputTerminalProcessed = true;
736 return false;
737 }
738 break;
739
740 default:
741 break;
742 }
743 }
744
745 tli->_isLatencyInputTerminal = true;
746 tli->_isLatencyInputTerminalProcessed = true;
747 return true;
748 }
749
isLatencyOutputTerminalMidi(bool capture)750 bool MidiDevice::isLatencyOutputTerminalMidi(bool capture)
751 {
752 TrackLatencyInfo* tli = capture ? &_captureLatencyInfo : &_playbackLatencyInfo;
753
754 // Have we been here before during this scan?
755 // Just return the cached value.
756 if(tli->_isLatencyOutputTerminalProcessed)
757 return tli->_isLatencyOutputTerminal;
758
759 const int port = midiPort();
760
761 // Playback devices are considered a termination point.
762 if(!capture || port < 0 || port >= MusECore::MIDI_PORTS)
763 {
764 tli->_isLatencyOutputTerminal = true;
765 tli->_isLatencyOutputTerminalProcessed = true;
766 return true;
767 }
768
769 MidiPort* mp = &MusEGlobal::midiPorts[port];
770 const RouteList* rl = mp->outRoutes();
771 for (ciRoute ir = rl->begin(); ir != rl->end(); ++ir) {
772 switch(ir->type)
773 {
774 case Route::TRACK_ROUTE:
775 if(!ir->track)
776 continue;
777 if(ir->track->isMidiTrack())
778 {
779 Track* track = ir->track;
780 if(track->off()) // ||
781 //(atrack->canRecordMonitor() && (MusEGlobal::config.monitoringAffectsLatency || !atrack->isRecMonitored())))
782 //&& atrack->canRecord() && !atrack->recordFlag()))
783 continue;
784
785 tli->_isLatencyOutputTerminal = false;
786 tli->_isLatencyOutputTerminalProcessed = true;
787 return false;
788 }
789 break;
790
791 default:
792 break;
793 }
794 }
795
796 tli->_isLatencyOutputTerminal = true;
797 tli->_isLatencyOutputTerminalProcessed = true;
798 return true;
799 }
800
801 //---------------------------------------------------------
802 // getWorstSelfLatencyMidi
803 //---------------------------------------------------------
804
getWorstSelfLatencyMidi(bool capture)805 float MidiDevice::getWorstSelfLatencyMidi(bool capture)
806 {
807 TrackLatencyInfo* tli = capture ? &_captureLatencyInfo : &_playbackLatencyInfo;
808
809 // Have we been here before during this scan?
810 // Just return the cached value.
811 if(tli->_worstSelfLatencyMidiProcessed)
812 return tli->_worstSelfLatencyMidi;
813
814 // REMOVE Tim. latency. Changed. TESTING. Reinstate.
815 // for(int i = 0; i < MusECore::MUSE_MIDI_CHANNELS; ++i)
816 {
817 //if(!used_chans[i])
818 // continue;
819 // const float lat = selfLatencyMidi(i, capture);
820 const float lat = selfLatencyMidi(0, capture);
821 //const float lat = selfLatencyMidi(i, 0 /*playback*/);
822 if(lat > tli->_worstSelfLatencyMidi)
823 tli->_worstSelfLatencyMidi = lat;
824 }
825
826 // The absolute latency of signals leaving this track is the sum of
827 // any connected route latencies and this track's latency.
828 tli->_worstSelfLatencyMidiProcessed = true;
829 return tli->_worstSelfLatencyMidi;
830 }
831
canDominateOutputLatencyMidi(bool capture) const832 inline bool MidiDevice::canDominateOutputLatencyMidi(bool capture) const
833 {
834 if(capture)
835 return true;
836 return false;
837 }
838
canDominateInputLatencyMidi(bool) const839 inline bool MidiDevice::canDominateInputLatencyMidi(bool /*capture*/) const
840 {
841 return false;
842 }
843
canDominateEndPointLatencyMidi(bool capture) const844 inline bool MidiDevice::canDominateEndPointLatencyMidi(bool capture) const
845 {
846 if(capture)
847 return false;
848 return true;
849 }
850
canPassThruLatencyMidi(bool) const851 inline bool MidiDevice::canPassThruLatencyMidi(bool /*capture*/) const
852 {
853 return true;
854 }
855
856 //---------------------------------------------------------
857 // getDominanceInfoMidi
858 //---------------------------------------------------------
859
getDominanceInfoMidi(bool capture,bool input)860 TrackLatencyInfo& MidiDevice::getDominanceInfoMidi(bool capture, bool input)
861 {
862 TrackLatencyInfo* tli = capture ? &_captureLatencyInfo : &_playbackLatencyInfo;
863
864 // Have we been here before during this scan?
865 // Just return the cached value.
866 if((input && tli->_canDominateInputProcessed) ||
867 (!input && tli->_canDominateProcessed))
868 return *tli;
869
870 // Get the default domination for this track type.
871 bool can_dominate_lat = input ? canDominateInputLatencyMidi(capture) : canDominateOutputLatencyMidi(capture);
872 bool can_correct_lat = canCorrectOutputLatencyMidi();
873
874 const bool passthru = canPassThruLatencyMidi(capture);
875
876 bool item_found = false;
877
878 const int port = midiPort();
879 const int open_flags = openFlags();
880
881 // Gather latency info from all connected input branches,
882 // but ONLY if the track is not off.
883 // Currently there are no routes FROM tracks (audio or midi) TO midi capture devices,
884 // only TO midi playback devices.
885 // CAUTION: The ABSENCE of the '!capture' caused an infinite loop crash, where
886 // MidiDevice::getDominanceInfoMidi called Track::getDominanceInfo which called
887 // MidiDevice::getDominanceInfoMidi again, and repeat inf...
888 // When that happens, all the "Have we been here before...?" checks say 'no'
889 // because each call has not finished yet, where at the end we say 'yes'.
890 // So I'm not sure how we could support the above future plan, if any.
891 if(!capture && (open_flags & (/*capture ? 2 :*/ 1)) && (passthru || input) &&
892 port >= 0 && port < MusECore::MIDI_PORTS)
893 {
894 // bool used_chans[MusECore::MUSE_MIDI_CHANNELS];
895 // for(int i = 0; i < MusECore::MUSE_MIDI_CHANNELS; ++i)
896 // used_chans[i] = false;
897 // bool all_chans = false;
898
899 #ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_
900 const MidiTrackList& tl = *MusEGlobal::song->midis();
901 const MidiTrackList::size_type tl_sz = tl.size();
902 for(MidiTrackList::size_type it = 0; it < tl_sz; ++it)
903 {
904 MidiTrack* track = static_cast<MidiTrack*>(tl[it]);
905 if(track->outPort() != port)
906 continue;
907
908 //if((open_flags & (/*capture ? 2 :*/ 1)) && !track->off() && (passthru || input))
909 if(!track->off())
910 {
911 const TrackLatencyInfo& li = track->getDominanceInfo(false);
912
913 // Whether the branch can dominate or correct latency or if we
914 // want to allow unterminated input branches to
915 // participate in worst branch latency calculations.
916 const bool participate =
917 (li._canCorrectOutputLatency ||
918 li._canDominateOutputLatency ||
919 MusEGlobal::config.correctUnterminatedInBranchLatency);
920
921 if(participate)
922 {
923 // Is it the first found item?
924 if(item_found)
925 {
926 // If any one of the branches can dominate the latency,
927 // that overrides any which cannot.
928 if(li._canDominateOutputLatency)
929 can_dominate_lat = true;
930 if(li._canCorrectOutputLatency)
931 can_correct_lat = true;
932 }
933 else
934 {
935 item_found = true;
936 // Override the defaults with this first item's values.
937 can_dominate_lat = li._canDominateOutputLatency;
938 can_correct_lat = li._canCorrectOutputLatency;
939 }
940 }
941 }
942 }
943
944 #else
945
946 MidiPort* mp = &MusEGlobal::midiPorts[port];
947 RouteList* rl = mp->inRoutes();
948 for (iRoute ir = rl->begin(); ir != rl->end(); ++ir)
949 {
950 switch(ir->type)
951 {
952 case Route::TRACK_ROUTE:
953 if(!ir->track)
954 continue;
955
956 if(ir->track->isMidiTrack())
957 {
958 if(ir->channel < -1 || ir->channel >= MusECore::MUSE_MIDI_CHANNELS)
959 continue;
960
961 Track* track = ir->track;
962 // if(ir->channel < 0)
963 // all_chans = true;
964 // else
965 // used_chans[ir->channel] = true;
966
967 //if((open_flags & (/*capture ? 2 :*/ 1)) && !track->off() && (passthru || input))
968 if(!track->off())
969 {
970 const TrackLatencyInfo& li = track->getDominanceInfo(false);
971
972 // Whether the branch can dominate or correct latency or if we
973 // want to allow unterminated input branches to
974 // participate in worst branch latency calculations.
975 const bool participate =
976 (li._canCorrectOutputLatency ||
977 li._canDominateOutputLatency ||
978 MusEGlobal::config.correctUnterminatedInBranchLatency);
979
980 if(participate)
981 {
982 // Is it the first found item?
983 if(item_found)
984 {
985 // If any one of the branches can dominate the latency,
986 // that overrides any which cannot.
987 if(li._canDominateOutputLatency)
988 can_dominate_lat = true;
989 if(li._canCorrectOutputLatency)
990 can_correct_lat = true;
991 }
992 else
993 {
994 item_found = true;
995 // Override the defaults with this first item's values.
996 can_dominate_lat = li._canDominateOutputLatency;
997 can_correct_lat = li._canCorrectOutputLatency;
998 }
999 }
1000 }
1001 }
1002 break;
1003
1004 default:
1005 break;
1006 }
1007 }
1008
1009 #endif
1010
1011 // Special for the built-in metronome.
1012 //if(!capture)
1013 //{
1014 MusECore::MetronomeSettings* metro_settings =
1015 MusEGlobal::metroUseSongSettings ? &MusEGlobal::metroSongSettings : &MusEGlobal::metroGlobalSettings;
1016
1017 if(metro_settings->midiClickFlag && metro_settings->clickPort == port)
1018 {
1019 //if((open_flags & (/*capture ? 2 :*/ 1)) && !MusECore::metronome->off() && (passthru || input))
1020 if(!MusECore::metronome->off())
1021 {
1022 const TrackLatencyInfo& li = MusECore::metronome->getDominanceInfoMidi(capture, false);
1023
1024 // Whether the branch can dominate or correct latency or if we
1025 // want to allow unterminated input branches to
1026 // participate in worst branch latency calculations.
1027 const bool participate =
1028 (li._canCorrectOutputLatency ||
1029 li._canDominateOutputLatency ||
1030 MusEGlobal::config.correctUnterminatedInBranchLatency);
1031
1032 if(participate)
1033 {
1034 // Is it the first found item?
1035 if(item_found)
1036 {
1037 // If any one of the branches can dominate the latency,
1038 // that overrides any which cannot.
1039 if(li._canDominateOutputLatency)
1040 can_dominate_lat = true;
1041 if(li._canCorrectOutputLatency)
1042 can_correct_lat = true;
1043 }
1044 else
1045 {
1046 item_found = true;
1047 // Override the defaults with this first item's values.
1048 //route_worst_out_corr = li._outputAvailableCorrection;
1049 can_dominate_lat = li._canDominateOutputLatency;
1050 can_correct_lat = li._canCorrectOutputLatency;
1051 }
1052 }
1053 }
1054 }
1055 //}
1056 }
1057
1058 // Set the correction of all connected input branches,
1059 // but ONLY if the track is not off.
1060 if((open_flags & (capture ? 2 : 1)))
1061 {
1062 if(input)
1063 {
1064 tli->_canDominateInputLatency = can_dominate_lat;
1065 }
1066 else
1067 {
1068 tli->_canDominateOutputLatency = can_dominate_lat;
1069 // If any of the branches can dominate, then this node cannot correct.
1070 tli->_canCorrectOutputLatency = can_correct_lat && !can_dominate_lat;
1071 }
1072 }
1073
1074 if(input)
1075 tli->_canDominateInputProcessed = true;
1076 else
1077 tli->_canDominateProcessed = true;
1078
1079 return *tli;
1080 }
1081
1082 //---------------------------------------------------------
1083 // getDominanceLatencyInfoMidi
1084 //---------------------------------------------------------
1085
getDominanceLatencyInfoMidi(bool capture,bool input)1086 TrackLatencyInfo& MidiDevice::getDominanceLatencyInfoMidi(bool capture, bool input)
1087 {
1088 TrackLatencyInfo* tli = capture ? &_captureLatencyInfo : &_playbackLatencyInfo;
1089
1090 // Have we been here before during this scan?
1091 // Just return the cached value.
1092 if((input && tli->_dominanceInputProcessed) ||
1093 (!input && tli->_dominanceProcessed))
1094 return *tli;
1095
1096 float route_worst_latency = 0.0f;
1097
1098 const bool passthru = canPassThruLatencyMidi(capture);
1099
1100 bool item_found = false;
1101
1102 const int open_flags = openFlags();
1103
1104 float worst_self_latency = 0.0f;
1105 if(!input && (open_flags & (capture ? 2 : 1)))
1106 worst_self_latency = getWorstSelfLatencyMidi(capture);
1107
1108 const int port = midiPort();
1109
1110 // Gather latency info from all connected input branches,
1111 // but ONLY if the track is not off.
1112 // Currently there are no routes FROM tracks (audio or midi) TO midi capture devices,
1113 // only TO midi playback devices.
1114 // CAUTION: See the warning in getDominanceInfoMidi about infinite recursion.
1115 if(!capture && (open_flags & (/*capture ? 2 :*/ 1)) && (passthru || input) &&
1116 port >= 0 && port < MusECore::MIDI_PORTS)
1117 {
1118 // bool used_chans[MusECore::MUSE_MIDI_CHANNELS];
1119 // for(int i = 0; i < MusECore::MUSE_MIDI_CHANNELS; ++i)
1120 // used_chans[i] = false;
1121 // bool all_chans = false;
1122
1123 #ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_
1124 const MidiTrackList& tl = *MusEGlobal::song->midis();
1125 const MidiTrackList::size_type tl_sz = tl.size();
1126 for(MidiTrackList::size_type it = 0; it < tl_sz; ++it)
1127 {
1128 MidiTrack* track = static_cast<MidiTrack*>(tl[it]);
1129 if(track->outPort() != port)
1130 continue;
1131
1132 //if((open_flags & (/*capture ? 2 :*/ 1)) && !track->off() && (passthru || input))
1133 if(!track->off())
1134 {
1135 const TrackLatencyInfo& li = track->getDominanceLatencyInfo(false);
1136
1137 // Whether the branch can dominate or correct latency or if we
1138 // want to allow unterminated input branches to
1139 // participate in worst branch latency calculations.
1140 const bool participate =
1141 (li._canCorrectOutputLatency ||
1142 li._canDominateOutputLatency ||
1143 MusEGlobal::config.correctUnterminatedInBranchLatency);
1144
1145 if(participate)
1146 {
1147 // Is it the first found item?
1148 if(item_found)
1149 {
1150 // If any one of the branches can dominate the latency,
1151 // that overrides any which cannot.
1152 if(li._canDominateOutputLatency)
1153 {
1154 // Override the current worst value if the latency is greater,
1155 // but ONLY if the branch can dominate.
1156 //if(li._outputLatency > route_worst_latency)
1157 // route_worst_latency = li._outputLatency;
1158 }
1159 // Override the current worst value if the latency is greater,
1160 // but ONLY if the branch can dominate.
1161 if(li._outputLatency > route_worst_latency)
1162 route_worst_latency = li._outputLatency;
1163 }
1164 else
1165 {
1166 item_found = true;
1167 // Override the default worst value, but ONLY if the branch can dominate.
1168 //if(li._canDominateOutputLatency)
1169 route_worst_latency = li._outputLatency;
1170 }
1171 }
1172 }
1173 }
1174
1175 #else
1176
1177 MidiPort* mp = &MusEGlobal::midiPorts[port];
1178 RouteList* rl = mp->inRoutes();
1179 for (iRoute ir = rl->begin(); ir != rl->end(); ++ir)
1180 {
1181 switch(ir->type)
1182 {
1183 case Route::TRACK_ROUTE:
1184 if(!ir->track)
1185 continue;
1186
1187 if(ir->track->isMidiTrack())
1188 {
1189 if(ir->channel < -1 || ir->channel >= MusECore::MUSE_MIDI_CHANNELS)
1190 continue;
1191
1192 Track* track = ir->track;
1193 // if(ir->channel < 0)
1194 // all_chans = true;
1195 // else
1196 // used_chans[ir->channel] = true;
1197
1198 //if((open_flags & (/*capture ? 2 :*/ 1)) && !track->off() && (passthru || input))
1199 if(!track->off())
1200 {
1201 const TrackLatencyInfo& li = track->getDominanceLatencyInfo(false);
1202
1203 // Whether the branch can dominate or correct latency or if we
1204 // want to allow unterminated input branches to
1205 // participate in worst branch latency calculations.
1206 const bool participate =
1207 (li._canCorrectOutputLatency ||
1208 li._canDominateOutputLatency ||
1209 MusEGlobal::config.correctUnterminatedInBranchLatency);
1210
1211 if(participate)
1212 {
1213 // Is it the first found item?
1214 if(item_found)
1215 {
1216 // If any one of the branches can dominate the latency,
1217 // that overrides any which cannot.
1218 if(li._canDominateOutputLatency)
1219 {
1220 // Override the current worst value if the latency is greater,
1221 // but ONLY if the branch can dominate.
1222 //if(li._outputLatency > route_worst_latency)
1223 // route_worst_latency = li._outputLatency;
1224 }
1225 // Override the current worst value if the latency is greater,
1226 // but ONLY if the branch can dominate.
1227 if(li._outputLatency > route_worst_latency)
1228 route_worst_latency = li._outputLatency;
1229 }
1230 else
1231 {
1232 item_found = true;
1233 // Override the default worst value, but ONLY if the branch can dominate.
1234 //if(li._canDominateOutputLatency)
1235 route_worst_latency = li._outputLatency;
1236 }
1237 }
1238 }
1239 }
1240 break;
1241
1242 default:
1243 break;
1244 }
1245 }
1246
1247 #endif
1248
1249 // Special for the built-in metronome.
1250 //if(!capture)
1251 //{
1252 MusECore::MetronomeSettings* metro_settings =
1253 MusEGlobal::metroUseSongSettings ? &MusEGlobal::metroSongSettings : &MusEGlobal::metroGlobalSettings;
1254
1255 //if(sendMetronome())
1256 if(metro_settings->midiClickFlag && metro_settings->clickPort == port)
1257 {
1258 //if((open_flags & (/*capture ? 2 :*/ 1)) && !MusECore::metronome->off() && (passthru || input))
1259 if(!MusECore::metronome->off())
1260 {
1261 const TrackLatencyInfo& li = MusECore::metronome->getDominanceLatencyInfoMidi(capture, false);
1262
1263 // Whether the branch can dominate or correct latency or if we
1264 // want to allow unterminated input branches to
1265 // participate in worst branch latency calculations.
1266 const bool participate =
1267 (li._canCorrectOutputLatency ||
1268 li._canDominateOutputLatency ||
1269 MusEGlobal::config.correctUnterminatedInBranchLatency);
1270
1271 if(participate)
1272 {
1273 // Is it the first found item?
1274 if(item_found)
1275 {
1276 // If any one of the branches can dominate the latency,
1277 // that overrides any which cannot.
1278 if(li._canDominateOutputLatency)
1279 {
1280 // Override the current worst value if the latency is greater,
1281 // but ONLY if the branch can dominate.
1282 //if(li._outputLatency > route_worst_latency)
1283 // route_worst_latency = li._outputLatency;
1284 }
1285 // Override the current worst value if the latency is greater,
1286 // but ONLY if the branch can dominate.
1287 if(li._outputLatency > route_worst_latency)
1288 route_worst_latency = li._outputLatency;
1289 }
1290 else
1291 {
1292 item_found = true;
1293 // Override the default worst value, but ONLY if the branch can dominate.
1294 //if(li._canDominateOutputLatency)
1295 route_worst_latency = li._outputLatency;
1296 }
1297 }
1298 }
1299 }
1300 //}
1301 }
1302
1303 // Set the correction of all connected input branches,
1304 // but ONLY if the track is not off.
1305 if((open_flags & (capture ? 2 : 1)))
1306 {
1307 if(input)
1308 {
1309 tli->_inputLatency = route_worst_latency;
1310 }
1311 else
1312 {
1313 if(passthru)
1314 {
1315 tli->_outputLatency = worst_self_latency + route_worst_latency;
1316 tli->_inputLatency = route_worst_latency;
1317 }
1318 else
1319 {
1320 tli->_outputLatency = worst_self_latency + tli->_sourceCorrectionValue;
1321 }
1322 }
1323 }
1324
1325 if(input)
1326 tli->_dominanceInputProcessed = true;
1327 else
1328 tli->_dominanceProcessed = true;
1329
1330 return *tli;
1331 }
1332
1333 //---------------------------------------------------------
1334 // setCorrectionLatencyInfoMidi
1335 //---------------------------------------------------------
1336
setCorrectionLatencyInfoMidi(bool capture,bool input,float finalWorstLatency,float callerBranchLatency)1337 TrackLatencyInfo& MidiDevice::setCorrectionLatencyInfoMidi(bool capture, bool input, float finalWorstLatency, float callerBranchLatency)
1338 {
1339 TrackLatencyInfo* tli = capture ? &_captureLatencyInfo : &_playbackLatencyInfo;
1340
1341 const bool passthru = canPassThruLatencyMidi(capture);
1342
1343 const int open_flags = openFlags();
1344
1345 float worst_self_latency = 0.0f;
1346 if(!input && (open_flags & 1 /*write*/))
1347 worst_self_latency = getWorstSelfLatencyMidi(capture);
1348
1349 // The _trackLatency should already be calculated in the dominance scan.
1350 const float branch_lat = callerBranchLatency + worst_self_latency;
1351
1352 const int port = midiPort();
1353 // Currently there are no routes FROM tracks (audio or midi) TO midi capture devices,
1354 // only TO midi playback devices.
1355 // CAUTION: See the warning in getDominanceInfoMidi about infinite recursion.
1356 if(!capture && (open_flags & 1 /*write*/) && (passthru || input) &&
1357 port >= 0 && port < MusECore::MIDI_PORTS)
1358 {
1359 // Set the correction of all connected input branches.
1360 // The _trackLatency should already be calculated in the dominance scan.
1361 #ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_
1362 const MidiTrackList& tl = *MusEGlobal::song->midis();
1363 const MidiTrackList::size_type tl_sz = tl.size();
1364 for(MidiTrackList::size_type it = 0; it < tl_sz; ++it)
1365 {
1366 MidiTrack* track = static_cast<MidiTrack*>(tl[it]);
1367 if(track->outPort() != port)
1368 continue;
1369 //if((open_flags & 1 /*write*/) && !track->off() && (passthru || input))
1370 if(!track->off())
1371 track->setCorrectionLatencyInfo(false, finalWorstLatency, branch_lat);
1372 }
1373
1374 #else
1375
1376 MidiPort* mp = &MusEGlobal::midiPorts[port];
1377 RouteList* mrl = mp->inRoutes();
1378 for (iRoute ir = mrl->begin(); ir != mrl->end(); ++ir)
1379 {
1380 switch(ir->type)
1381 {
1382 case Route::TRACK_ROUTE:
1383 if(!ir->track)
1384 continue;
1385
1386 if(ir->track->isMidiTrack())
1387 {
1388 if(ir->channel < -1 || ir->channel >= MusECore::MUSE_MIDI_CHANNELS)
1389 continue;
1390 Track* track = ir->track;
1391 //if((open_flags & 1 /*write*/) && !track->off() && (passthru || input))
1392 if(!track->off())
1393 track->setCorrectionLatencyInfo(false, finalWorstLatency, branch_lat);
1394 }
1395 break;
1396
1397 default:
1398 break;
1399 }
1400 }
1401
1402 #endif
1403
1404 // Special for the built-in metronome.
1405 //if(!capture)
1406 //{
1407 MusECore::MetronomeSettings* metro_settings =
1408 MusEGlobal::metroUseSongSettings ? &MusEGlobal::metroSongSettings : &MusEGlobal::metroGlobalSettings;
1409
1410 //if(sendMetronome())
1411 if(metro_settings->midiClickFlag && metro_settings->clickPort == port)
1412 {
1413 //if((open_flags & 1 /*write*/) && !MusECore::metronome->off() && (passthru || input))
1414 if(!MusECore::metronome->off())
1415 MusECore::metronome->setCorrectionLatencyInfoMidi(capture, false, finalWorstLatency, branch_lat);
1416 }
1417 //}
1418 }
1419
1420 // Set the correction of all connected input branches,
1421 // but ONLY if the track is not off.
1422 if(open_flags & 1 /*write*/ && !capture/*Tim*/)
1423 {
1424 if(input)
1425 {
1426 }
1427 else
1428 {
1429 if(canCorrectOutputLatencyMidi() && tli->_canCorrectOutputLatency)
1430 {
1431 float corr = 0.0f;
1432 if(MusEGlobal::config.commonProjectLatency)
1433 corr -= finalWorstLatency;
1434
1435 corr -= branch_lat;
1436 // The _sourceCorrectionValue is initialized to zero.
1437 // Whichever calling branch needs the most correction gets it.
1438 if(corr < tli->_sourceCorrectionValue)
1439 tli->_sourceCorrectionValue = corr;
1440 }
1441 }
1442 }
1443
1444 return *tli;
1445 }
1446
1447 //---------------------------------------------------------
1448 // getLatencyInfoMidi
1449 //---------------------------------------------------------
1450
getLatencyInfoMidi(bool capture,bool input)1451 TrackLatencyInfo& MidiDevice::getLatencyInfoMidi(bool capture, bool input)
1452 {
1453 TrackLatencyInfo* tli = capture ? &_captureLatencyInfo : &_playbackLatencyInfo;
1454
1455 // Have we been here before during this scan?
1456 // Just return the cached value.
1457 if((input && tli->_inputProcessed) ||
1458 (!input && tli->_processed))
1459 return *tli;
1460
1461 MusECore::MetronomeSettings* metro_settings =
1462 MusEGlobal::metroUseSongSettings ? &MusEGlobal::metroSongSettings : &MusEGlobal::metroGlobalSettings;
1463
1464 float route_worst_latency = tli->_inputLatency;
1465
1466 const bool passthru = canPassThruLatencyMidi(capture);
1467
1468 const int port = midiPort();
1469 const int open_flags = openFlags();
1470
1471 if(passthru || input)
1472 {
1473 // Currently there are no routes FROM tracks (audio or midi) TO midi capture devices,
1474 // only TO midi playback devices.
1475 // CAUTION: See the warning in getDominanceInfoMidi about infinite recursion.
1476 if(!capture && port >= 0 && port < MusECore::MIDI_PORTS)
1477 {
1478 #ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_
1479 const MidiTrackList& tl = *MusEGlobal::song->midis();
1480 const MidiTrackList::size_type tl_sz = tl.size();
1481 for(MidiTrackList::size_type it = 0; it < tl_sz; ++it)
1482 {
1483 MidiTrack* track = static_cast<MidiTrack*>(tl[it]);
1484 if(track->outPort() != port)
1485 continue;
1486
1487 // TODO: FIXME: Where to store? We have no route to store it in.
1488 // Default to zero.
1489 //ir->audioLatencyOut = 0.0f;
1490
1491 if((open_flags & (/*capture ? 2 :*/ 1)) && !track->off())
1492 {
1493 TrackLatencyInfo& li = track->getLatencyInfo(false);
1494
1495 const bool participate =
1496 (li._canCorrectOutputLatency ||
1497 li._canDominateOutputLatency ||
1498 MusEGlobal::config.correctUnterminatedInBranchLatency);
1499
1500 if(participate)
1501 {
1502 // TODO: FIXME: Where to store? We have no route to store it in.
1503 // Prepare the latency value to be passed to the compensator's writer,
1504 // by adjusting each route latency value. ie. the route with the worst-case
1505 // latency will get ZERO delay, while routes having smaller latency will get
1506 // MORE delay, to match all the signal timings together.
1507 // The route's audioLatencyOut should have already been calculated and
1508 // conveniently stored in the route.
1509 // ir->audioLatencyOut = route_worst_latency - li._outputLatency;
1510 // // Should not happen, but just in case.
1511 // if((long int)ir->audioLatencyOut < 0)
1512 // ir->audioLatencyOut = 0.0f;
1513
1514 // Special for Midi Tracks: We don't have Midi Track to Midi Port routes yet
1515 // because we don't have multiple Midi Track outputs yet, only a single output port.
1516 // So we must store this information here just for Midi Tracks.
1517 li._latencyOutMidiTrack = route_worst_latency - li._outputLatency;
1518 // Should not happen, but just in case.
1519 if((long int)li._latencyOutMidiTrack < 0)
1520 li._latencyOutMidiTrack = 0.0f;
1521 }
1522 }
1523 }
1524
1525 #else
1526
1527 MidiPort* mp = &MusEGlobal::midiPorts[port];
1528 RouteList* rl = mp->inRoutes();
1529 for (iRoute ir = rl->begin(); ir != rl->end(); ++ir)
1530 {
1531 switch(ir->type)
1532 {
1533 case Route::TRACK_ROUTE:
1534 if(!ir->track)
1535 continue;
1536
1537 if(ir->track->isMidiTrack())
1538 {
1539 if(ir->channel < -1 || ir->channel >= MusECore::MUSE_MIDI_CHANNELS)
1540 continue;
1541
1542 Track* track = ir->track;
1543
1544 // Default to zero.
1545 ir->audioLatencyOut = 0.0f;
1546
1547 if((open_flags & (/*capture ? 2 :*/ 1)) && !track->off())
1548 {
1549 const TrackLatencyInfo& li = track->getLatencyInfo(false);
1550 const bool participate =
1551 (li._canCorrectOutputLatency ||
1552 li._canDominateOutputLatency ||
1553 MusEGlobal::config.correctUnterminatedInBranchLatency);
1554
1555 if(participate)
1556 {
1557 // Prepare the latency value to be passed to the compensator's writer,
1558 // by adjusting each route latency value. ie. the route with the worst-case
1559 // latency will get ZERO delay, while routes having smaller latency will get
1560 // MORE delay, to match all the signal timings together.
1561 // The route's audioLatencyOut should have already been calculated and
1562 // conveniently stored in the route.
1563 ir->audioLatencyOut = route_worst_latency - li._outputLatency;
1564 // Should not happen, but just in case.
1565 if((long int)ir->audioLatencyOut < 0)
1566 ir->audioLatencyOut = 0.0f;
1567 }
1568 }
1569 }
1570 break;
1571
1572 default:
1573 break;
1574 }
1575 }
1576
1577 #endif
1578
1579 // Special for the built-in metronome.
1580 //if(!capture)
1581 //{
1582 // TODO: FIXME: Where to store? We have no route to store it in.
1583 // Default to zero.
1584 //ir->audioLatencyOut = 0.0f;
1585
1586 if((open_flags & (/*capture ? 2 :*/ 1)) && !MusECore::metronome->off() && // sendMetronome() &&
1587 metro_settings->midiClickFlag && metro_settings->clickPort == port)
1588 {
1589 TrackLatencyInfo& li = MusECore::metronome->getLatencyInfoMidi(capture, false);
1590 const bool participate =
1591 (li._canCorrectOutputLatency ||
1592 li._canDominateOutputLatency ||
1593 MusEGlobal::config.correctUnterminatedInBranchLatency);
1594
1595 if(participate)
1596 {
1597 // TODO: FIXME: Where to store? We have no route to store it in.
1598 // Prepare the latency value to be passed to the compensator's writer,
1599 // by adjusting each route latency value. ie. the route with the worst-case
1600 // latency will get ZERO delay, while routes having smaller latency will get
1601 // MORE delay, to match all the signal timings together.
1602 // The route's audioLatencyOut should have already been calculated and
1603 // conveniently stored in the route.
1604
1605 // ir->audioLatencyOut = route_worst_latency - ir->audioLatencyOut;
1606 // // Should not happen, but just in case.
1607 // if((long int)ir->audioLatencyOut < 0)
1608 // ir->audioLatencyOut = 0.0f;
1609
1610 // Special for Midi Tracks: We don't have Midi Track to Midi Port routes yet
1611 // because we don't have multiple Midi Track outputs yet, only a single output port.
1612 // So we must store this information here just for Midi Tracks.
1613 // li._latencyOutMidiTrack = route_worst_latency - li._outputLatency;
1614 // // Should not happen, but just in case.
1615 // if((long int)li._latencyOutMidiTrack < 0)
1616 // li._latencyOutMidiTrack = 0.0f;
1617
1618 // Special for Midi Tracks: We don't have Midi Track to Midi Port routes yet
1619 // because we don't have multiple Midi Track outputs yet, only a single output port.
1620 // So we must store this information here just for Midi Tracks.
1621 li._latencyOutMetronome = route_worst_latency - li._latencyOutMetronome;
1622 // Should not happen, but just in case.
1623 if((long int)li._latencyOutMetronome < 0)
1624 li._latencyOutMetronome = 0.0f;
1625 }
1626 }
1627 //}
1628 }
1629 }
1630
1631 if(input)
1632 tli->_inputProcessed = true;
1633 else
1634 tli->_processed = true;
1635
1636 return *tli;
1637 }
1638
1639 //---------------------------------------------------------
1640 // latencyCompWriteOffset
1641 //---------------------------------------------------------
1642
latencyCompWriteOffsetMidi(bool capture) const1643 inline unsigned long MidiDevice::latencyCompWriteOffsetMidi(bool capture) const
1644 {
1645 return capture ? _captureLatencyInfo._compensatorWriteOffset : _playbackLatencyInfo._compensatorWriteOffset;
1646 }
1647
setLatencyCompWriteOffsetMidi(float worstCase,bool capture)1648 void MidiDevice::setLatencyCompWriteOffsetMidi(float worstCase, bool capture)
1649 {
1650 TrackLatencyInfo* tli = capture ? &_captureLatencyInfo : &_playbackLatencyInfo;
1651
1652 // If independent branches are NOT to affect project latency,
1653 // then there should be no need for any extra delay in the branch.
1654 if(!MusEGlobal::config.commonProjectLatency)
1655 {
1656 tli->_compensatorWriteOffset = 0;
1657 //fprintf(stderr, "MidiDevice::setLatencyCompWriteOffset() name:%s capture:%d worstCase:%f _outputLatency:%f _compensatorWriteOffset:%lu\n",
1658 // name().toLatin1().constData(), capture, worstCase, tli->_outputLatency, tli->_compensatorWriteOffset);
1659 return;
1660 }
1661
1662 if(tli->_canDominateOutputLatency)
1663 {
1664 const long unsigned int wc = worstCase;
1665 const long unsigned int ol = tli->_outputLatency;
1666 if(ol > wc)
1667 tli->_compensatorWriteOffset = 0;
1668 else
1669 tli->_compensatorWriteOffset = wc - ol;
1670 }
1671 else
1672 {
1673 // if(tli->_outputLatency < 0)
1674 tli->_compensatorWriteOffset = 0;
1675 // else
1676 // tli->_compensatorWriteOffset = tli->_outputLatency;
1677 }
1678
1679 //fprintf(stderr,
1680 // "MidiDevice::setLatencyCompWriteOffset() name:%s capture:%d worstCase:%f"
1681 // " _outputLatency:%f _canDominateOutputLatency:%d _compensatorWriteOffset:%lu\n",
1682 // name().toLatin1().constData(), capture, worstCase, tli->_outputLatency,
1683 // tli->_canDominateOutputLatency, tli->_compensatorWriteOffset);
1684 }
1685
1686 //================================================
1687 // END Latency correction/compensation routines.
1688 //================================================
1689
1690
1691 } // namespace MusECore
1692
1693