annotate data/midi/MIDIInput.cpp @ 1881:b504df98c3be

Ensure completion on output model is started at zero, so if it's checked before the input model has become ready and the transform has begun, it is not accidentally reported as complete (affected re-aligning models in Sonic Lineup when replacing the session)
author Chris Cannam
date Fri, 26 Jun 2020 11:45:39 +0100
parents 157c62ff0056
children
rev   line source
Chris@562 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@562 2
Chris@562 3 /*
Chris@562 4 Sonic Visualiser
Chris@562 5 An audio file viewer and annotation editor.
Chris@562 6 Centre for Digital Music, Queen Mary, University of London.
Chris@562 7 This file copyright 2006-2009 Chris Cannam and QMUL.
Chris@562 8
Chris@562 9 This program is free software; you can redistribute it and/or
Chris@562 10 modify it under the terms of the GNU General Public License as
Chris@562 11 published by the Free Software Foundation; either version 2 of the
Chris@562 12 License, or (at your option) any later version. See the file
Chris@562 13 COPYING included with this distribution for more information.
Chris@562 14 */
Chris@562 15
Chris@562 16 #include "MIDIInput.h"
Chris@562 17
Chris@562 18 #include "rtmidi/RtMidi.h"
Chris@562 19
Chris@1219 20 #include "system/System.h"
Chris@608 21
Chris@567 22 MIDIInput::MIDIInput(QString name, FrameTimer *timer) :
Chris@1582 23 m_rtmidi(nullptr),
Chris@567 24 m_frameTimer(timer),
Chris@562 25 m_buffer(1023)
Chris@562 26 {
Chris@562 27 try {
Chris@1397 28 std::vector<RtMidi::Api> apis;
Chris@1397 29 RtMidi::getCompiledApi(apis);
Chris@1400 30 RtMidi::Api preferredApi = RtMidi::UNSPECIFIED;
Chris@1400 31 for (auto a: apis) {
Chris@1400 32 if (a == RtMidi::UNSPECIFIED || a == RtMidi::RTMIDI_DUMMY) {
Chris@1400 33 continue;
Chris@1400 34 }
Chris@1400 35 preferredApi = a;
Chris@1400 36 break;
Chris@1400 37 }
Chris@1400 38 if (preferredApi == RtMidi::UNSPECIFIED) {
Chris@1400 39 SVCERR << "ERROR: MIDIInput: No RtMidi APIs compiled in" << endl;
Chris@1397 40 } else {
Chris@1400 41
Chris@1400 42 m_rtmidi = new RtMidiIn(preferredApi, name.toStdString());
Chris@1400 43
Chris@1400 44 int n = m_rtmidi->getPortCount();
Chris@1400 45
Chris@1400 46 if (n == 0) {
Chris@1400 47
Chris@1400 48 SVDEBUG << "NOTE: MIDIInput: No input ports available" << endl;
Chris@1400 49 delete m_rtmidi;
Chris@1582 50 m_rtmidi = nullptr;
Chris@1400 51
Chris@1400 52 } else {
Chris@1400 53
Chris@1400 54 m_rtmidi->setCallback(staticCallback, this);
Chris@1400 55
Chris@1400 56 SVDEBUG << "MIDIInput: Available ports are:" << endl;
Chris@1400 57 for (int i = 0; i < n; ++i) {
Chris@1400 58 SVDEBUG << i << ". " << m_rtmidi->getPortName(i) << endl;
Chris@1400 59 }
Chris@1400 60 SVDEBUG << "MIDIInput: Using first port (\""
Chris@1400 61 << m_rtmidi->getPortName(0) << "\")" << endl;
Chris@1400 62
Chris@1400 63 m_rtmidi->openPort(0, tr("Input").toStdString());
Chris@1400 64 }
Chris@1397 65 }
Chris@1400 66
Chris@1465 67 } catch (const RtMidiError &e) {
Chris@1400 68 SVCERR << "ERROR: RtMidi error: " << e.getMessage() << endl;
Chris@562 69 delete m_rtmidi;
Chris@1582 70 m_rtmidi = nullptr;
Chris@562 71 }
Chris@562 72 }
Chris@562 73
Chris@562 74 MIDIInput::~MIDIInput()
Chris@562 75 {
Chris@562 76 delete m_rtmidi;
Chris@562 77 }
Chris@562 78
Chris@562 79 void
Chris@562 80 MIDIInput::staticCallback(double timestamp, std::vector<unsigned char> *message,
Chris@562 81 void *userData)
Chris@562 82 {
Chris@562 83 ((MIDIInput *)userData)->callback(timestamp, message);
Chris@562 84 }
Chris@562 85
Chris@562 86 void
Chris@562 87 MIDIInput::callback(double timestamp, std::vector<unsigned char> *message)
Chris@562 88 {
Chris@690 89 SVDEBUG << "MIDIInput::callback(" << timestamp << ")" << endl;
Chris@567 90 // In my experience so far, the timings passed to this function
Chris@567 91 // are not reliable enough to use. We request instead an audio
Chris@567 92 // frame time from whatever FrameTimer we have been given, and use
Chris@567 93 // that as the event time.
Chris@566 94 if (!message || message->empty()) return;
Chris@567 95 unsigned long t = m_frameTimer->getFrame();
Chris@569 96 MIDIByte code = (*message)[0];
Chris@567 97 MIDIEvent ev(t,
Chris@569 98 code,
Chris@566 99 message->size() > 1 ? (*message)[1] : 0,
Chris@566 100 message->size() > 2 ? (*message)[2] : 0);
Chris@566 101 postEvent(ev);
Chris@562 102 }
Chris@562 103
Chris@562 104 MIDIEvent
Chris@562 105 MIDIInput::readEvent()
Chris@562 106 {
Chris@562 107 MIDIEvent *event = m_buffer.readOne();
Chris@562 108 MIDIEvent revent = *event;
Chris@562 109 delete event;
Chris@562 110 return revent;
Chris@562 111 }
Chris@562 112
Chris@562 113 void
Chris@562 114 MIDIInput::postEvent(MIDIEvent e)
Chris@562 115 {
Chris@562 116 int count = 0, max = 5;
Chris@562 117 while (m_buffer.getWriteSpace() == 0) {
Chris@562 118 if (count == max) {
Chris@1400 119 SVCERR << "ERROR: MIDIInput::postEvent: MIDI event queue is full and not clearing -- abandoning incoming event" << endl;
Chris@562 120 return;
Chris@562 121 }
Chris@1724 122 SVCERR << "WARNING: MIDIInput::postEvent: MIDI event queue (capacity " << m_buffer.getSize() << ") is full!" << endl;
Chris@690 123 SVDEBUG << "Waiting for something to be processed" << endl;
Chris@562 124 #ifdef _WIN32
Chris@562 125 Sleep(1);
Chris@562 126 #else
Chris@562 127 sleep(1);
Chris@562 128 #endif
Chris@562 129 count++;
Chris@562 130 }
Chris@562 131
Chris@562 132 MIDIEvent *me = new MIDIEvent(e);
Chris@562 133 m_buffer.write(&me, 1);
Chris@562 134 emit eventsAvailable();
Chris@562 135 }
Chris@562 136