From 5b3262cfe2534eae5b70a57d858ab1fd8d2ce9d4 Mon Sep 17 00:00:00 2001 From: folkert van heusden Date: Mon, 16 Aug 2021 11:35:20 +0200 Subject: [PATCH 1/2] The queue is accessed from a thread as well as main so requires locking. --- RtMidi.cpp | 18 ++++++++++++++++-- RtMidi.h | 2 ++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/RtMidi.cpp b/RtMidi.cpp index dd3f5792..b36171ff 100644 --- a/RtMidi.cpp +++ b/RtMidi.cpp @@ -661,8 +661,9 @@ double MidiInApi :: getMessage( std::vector *message ) return timeStamp; } +// structure should be locked before using this method unsigned int MidiInApi::MidiQueue::size( unsigned int *__back, - unsigned int *__front ) + unsigned int *__front) { // Access back/front members exactly once and make stack copies for // size calculation @@ -676,6 +677,7 @@ unsigned int MidiInApi::MidiQueue::size( unsigned int *__back, // to member variables are needed. if ( __back ) *__back = _back; if ( __front ) *__front = _front; + return _size; } @@ -685,6 +687,8 @@ bool MidiInApi::MidiQueue::push( const MidiInApi::MidiMessage& msg ) // Local stack copies of front/back unsigned int _back, _front, _size; + lock.lock(); + // Get back/front indexes exactly once and calculate current size _size = size( &_back, &_front ); @@ -692,9 +696,12 @@ bool MidiInApi::MidiQueue::push( const MidiInApi::MidiMessage& msg ) { ring[_back] = msg; back = (back+1)%ringSize; + lock.unlock(); return true; } + lock.unlock(); + return false; } @@ -703,11 +710,15 @@ bool MidiInApi::MidiQueue::pop( std::vector *msg, double* timeSta // Local stack copies of front/back unsigned int _back, _front, _size; + lock.lock(); + // Get back/front indexes exactly once and calculate current size _size = size( &_back, &_front ); - if ( _size == 0 ) + if ( _size == 0 ) { + lock.unlock(); return false; + } // Copy queued message to the vector pointer argument and then "pop" it. msg->assign( ring[_front].bytes.begin(), ring[_front].bytes.end() ); @@ -715,6 +726,9 @@ bool MidiInApi::MidiQueue::pop( std::vector *msg, double* timeSta // Update front front = (front+1)%ringSize; + + lock.unlock(); + return true; } diff --git a/RtMidi.h b/RtMidi.h index 4a38c8aa..eb015593 100644 --- a/RtMidi.h +++ b/RtMidi.h @@ -62,6 +62,7 @@ #include #include +#include #include #include @@ -565,6 +566,7 @@ class RTMIDI_DLL_PUBLIC MidiInApi : public MidiApi unsigned int back; unsigned int ringSize; MidiMessage *ring; + std::mutex lock; // Default constructor. MidiQueue() From eced4e25120932eeaab4f30f86401e5602e415cd Mon Sep 17 00:00:00 2001 From: folkert van heusden Date: Mon, 16 Aug 2021 11:38:58 +0200 Subject: [PATCH 2/2] Throw an error back to the calling python script when the (virtual/physical-) MIDI device got unplugged. --- RtMidi.cpp | 23 +++++++++++++++++++++-- RtMidi.h | 10 ++++++++-- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/RtMidi.cpp b/RtMidi.cpp index b36171ff..221e2c85 100644 --- a/RtMidi.cpp +++ b/RtMidi.cpp @@ -654,10 +654,15 @@ double MidiInApi :: getMessage( std::vector *message ) return 0.0; } + RtMidiError::Type err = RtMidiError::NO_ERROR; + std::string err_msg; double timeStamp; - if ( !inputData_.queue.pop( message, &timeStamp ) ) + if ( !inputData_.queue.pop( message, &timeStamp, &err, &err_msg ) ) return 0.0; + if ( err != RtMidiError::NO_ERROR ) + error( err, err_msg ); + return timeStamp; } @@ -705,7 +710,7 @@ bool MidiInApi::MidiQueue::push( const MidiInApi::MidiMessage& msg ) return false; } -bool MidiInApi::MidiQueue::pop( std::vector *msg, double* timeStamp ) +bool MidiInApi::MidiQueue::pop( std::vector *msg, double* timeStamp, RtMidiError::Type *err, std::string *err_msg ) { // Local stack copies of front/back unsigned int _back, _front, _size; @@ -723,6 +728,8 @@ bool MidiInApi::MidiQueue::pop( std::vector *msg, double* timeSta // Copy queued message to the vector pointer argument and then "pop" it. msg->assign( ring[_front].bytes.begin(), ring[_front].bytes.end() ); *timeStamp = ring[_front].timeStamp; + *err = ring[_front].err; + *err_msg = ring[_front].err_msg; // Update front front = (front+1)%ringSize; @@ -1672,6 +1679,17 @@ static void *alsaMidiHandler( void *ptr ) << (int) ev->data.connect.dest.port << std::endl; #endif + if ( data->usingCallback ) { + std::cerr << "\nPort connection has closed!\n\n"; + data->this_->error( RtMidiError::SYSTEM_ERROR, "Port connection has closed!" ); + } + else { + MidiInApi::MidiMessage err_message; + err_message.err = RtMidiError::SYSTEM_ERROR; + err_message.err_msg = "Port connection has closed"; + if ( !data->queue.push( err_message ) ) + std::cerr << "\nMidiInCore: message queue limit reached!!\n\n"; + } break; case SND_SEQ_EVENT_QFRAME: // MIDI time code @@ -1854,6 +1872,7 @@ void MidiInAlsa :: initialize( const std::string& clientName ) data->trigger_fds[1] = -1; apiData_ = (void *) data; inputData_.apiData = (void *) data; + inputData_.this_ = this; if ( pipe(data->trigger_fds) == -1 ) { errorString_ = "MidiInAlsa::initialize: error creating pipe objects."; diff --git a/RtMidi.h b/RtMidi.h index eb015593..952e7834 100644 --- a/RtMidi.h +++ b/RtMidi.h @@ -82,6 +82,7 @@ class RTMIDI_DLL_PUBLIC RtMidiError : public std::exception public: //! Defined RtMidiError types. enum Type { + NO_ERROR = 0, WARNING, /*!< A non-critical error. */ DEBUG_WARNING, /*!< A non-critical error which might be useful for debugging. */ UNSPECIFIED, /*!< The default, unspecified error type. */ @@ -556,9 +557,13 @@ class RTMIDI_DLL_PUBLIC MidiInApi : public MidiApi //! Time in seconds elapsed since the previous message double timeStamp; + // Can also contain an error (when bytes.size() == 0) + RtMidiError::Type err; + std::string err_msg; + // Default constructor. MidiMessage() - : bytes(0), timeStamp(0.0) {} + : bytes(0), timeStamp(0.0), err(RtMidiError::NO_ERROR) {} }; struct MidiQueue { @@ -572,7 +577,7 @@ class RTMIDI_DLL_PUBLIC MidiInApi : public MidiApi MidiQueue() : front(0), back(0), ringSize(0), ring(0) {} bool push( const MidiMessage& ); - bool pop( std::vector*, double* ); + bool pop( std::vector*, double*, RtMidiError::Type*, std::string* ); unsigned int size( unsigned int *back=0, unsigned int *front=0 ); }; @@ -589,6 +594,7 @@ class RTMIDI_DLL_PUBLIC MidiInApi : public MidiApi RtMidiIn::RtMidiCallback userCallback; void *userData; bool continueSysex; + MidiInApi *this_; // Default constructor. RtMidiInData()