From 63a4a8846cd5689c63a33b185a6cc6f67503a80d Mon Sep 17 00:00:00 2001 From: Dale Whinham Date: Thu, 3 Mar 2022 16:40:44 +0000 Subject: [PATCH] Ignore note-ons received during SoundFont switch Fixes #247. --- CHANGELOG.md | 1 + include/midiparser.h | 4 ++-- include/mt32pi.h | 1 + src/midiparser.cpp | 11 +++++++---- src/mt32pi.cpp | 26 ++++++++++++++++++++++++-- 5 files changed, 35 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7fc9cbd..00276fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - A bug in the config file reader (unterminated string) could cause the last entry in the file to be read as a corrupted value if the file ended without a newline. - Some FTP commands could work without being logged in. - Some DAC accessories which make use of a hardware "mute" pin (e.g. Adafruit I²S Audio Bonnet) could be held in a muted state due to a conflict with the Blokas Pisound driver's probing routine (issue #233). The driver now resets these GPIO pins to the initial power-on state, which should fix this issue. Thanks to @htamas2 for the report! +- Sudden loud noise caused by switching SoundFonts whilst receiving MIDI data (issue #247). Any note-on messages received whilst busy switching SoundFonts are now discarded. Thanks to @c0d3h4x0r for the report! ## [0.11.0] - 2021-12-12 diff --git a/include/midiparser.h b/include/midiparser.h index e957533..46f86c8 100644 --- a/include/midiparser.h +++ b/include/midiparser.h @@ -30,7 +30,7 @@ class CMIDIParser public: CMIDIParser(); - void ParseMIDIBytes(const u8* pData, size_t nSize); + void ParseMIDIBytes(const u8* pData, size_t nSize, bool bIgnoreNoteOns = false); protected: virtual void OnShortMessage(u32 nMessage) = 0; @@ -51,7 +51,7 @@ class CMIDIParser static constexpr size_t SysExBufferSize = 1000; void ParseStatusByte(u8 nByte); - bool CheckCompleteShortMessage(); + bool CheckCompleteShortMessage(bool bIgnoreNoteOns = false); u32 PrepareShortMessage() const; void ResetState(bool bClearStatusByte); diff --git a/include/mt32pi.h b/include/mt32pi.h index 56fd721..ad0f262 100644 --- a/include/mt32pi.h +++ b/include/mt32pi.h @@ -117,6 +117,7 @@ class CMT32Pi : CMultiCoreSupport, CPower, CMIDIParser, CAppleMIDIHandler void UpdateUSB(bool bStartup = false); void UpdateNetwork(); void UpdateMIDI(); + void PurgeMIDIBuffers(); size_t ReceiveSerialMIDI(u8* pOutData, size_t nSize); bool ParseCustomSysEx(const u8* pData, size_t nSize); diff --git a/src/midiparser.cpp b/src/midiparser.cpp index 2d4462a..dda4dad 100644 --- a/src/midiparser.cpp +++ b/src/midiparser.cpp @@ -33,7 +33,7 @@ CMIDIParser::CMIDIParser() { } -void CMIDIParser::ParseMIDIBytes(const u8* pData, size_t nSize) +void CMIDIParser::ParseMIDIBytes(const u8* pData, size_t nSize, bool bIgnoreNoteOns) { // Process MIDI messages // See: https://www.midi.org/specifications/item/table-1-summary-of-midi-message @@ -71,7 +71,7 @@ void CMIDIParser::ParseMIDIBytes(const u8* pData, size_t nSize) } m_MessageBuffer[m_nMessageLength++] = nByte; - CheckCompleteShortMessage(); + CheckCompleteShortMessage(bIgnoreNoteOns); break; // Expecting a SysEx data byte or EOX @@ -167,7 +167,7 @@ void CMIDIParser::ParseStatusByte(u8 nByte) } } -bool CMIDIParser::CheckCompleteShortMessage() +bool CMIDIParser::CheckCompleteShortMessage(bool bIgnoreNoteOns) { const u8 nStatus = m_MessageBuffer[0]; @@ -176,7 +176,10 @@ bool CMIDIParser::CheckCompleteShortMessage() if (m_nMessageLength == 3 || (m_nMessageLength == 2 && ((nStatus >= 0xC0 && nStatus <= 0xDF) || nStatus == 0xF1 || nStatus == 0xF3))) { - OnShortMessage(PrepareShortMessage()); + const bool bIsNoteOn = (nStatus & 0xF0) == 0x90; + + if (!(bIsNoteOn && bIgnoreNoteOns)) + OnShortMessage(PrepareShortMessage()); // Clear running status if System Common ResetState(nStatus >= 0xF1 && nStatus <= 0xF7); diff --git a/src/mt32pi.cpp b/src/mt32pi.cpp index e9af315..994f794 100644 --- a/src/mt32pi.cpp +++ b/src/mt32pi.cpp @@ -906,6 +906,22 @@ void CMT32Pi::UpdateMIDI() s_pThis->m_nActiveSenseTime = s_pThis->m_pTimer->GetTicks(); } +void CMT32Pi::PurgeMIDIBuffers() +{ + size_t nBytes; + u8 Buffer[MIDIRxBufferSize]; + + // Process MIDI messages from all devices/ring buffers, but ignore note-ons + while (m_bSerialMIDIEnabled && (nBytes = ReceiveSerialMIDI(Buffer, sizeof(Buffer))) > 0) + ParseMIDIBytes(Buffer, nBytes, true); + + while (m_pUSBSerialDevice && (nBytes = m_pUSBSerialDevice->Read(Buffer, sizeof(Buffer))) > 0) + ParseMIDIBytes(Buffer, nBytes, true); + + while ((nBytes = m_MIDIRxBuffer.Dequeue(Buffer, sizeof(Buffer))) > 0) + ParseMIDIBytes(Buffer, nBytes, true); +} + size_t CMT32Pi::ReceiveSerialMIDI(u8* pOutData, size_t nSize) { // Read serial MIDI data @@ -1124,8 +1140,14 @@ void CMT32Pi::SwitchSoundFont(size_t nIndex) return; m_pLogger->Write(MT32PiName, LogNotice, "Switching to SoundFont %d", nIndex); - if (m_pSoundFontSynth->SwitchSoundFont(nIndex) && m_pCurrentSynth == m_pSoundFontSynth) - m_pSoundFontSynth->ReportStatus(); + if (m_pSoundFontSynth->SwitchSoundFont(nIndex)) + { + // Handle any MIDI data that has been queued up while busy + PurgeMIDIBuffers(); + + if (m_pCurrentSynth == m_pSoundFontSynth) + m_pSoundFontSynth->ReportStatus(); + } } void CMT32Pi::DeferSwitchSoundFont(size_t nIndex)