Fixes for SYSEX channel messages / Added master volume method (also for SYSEX) (#249)

* Fix for using the right MIDI channel for SYSEX.
Fix for SYSEX MIDI dump output.

* Small fixes for recognizing MIDI channel in SYSEX.
Disabled printing of MIDI data in incoming serial data.
Added some mor debug output.

* Reenabled showing incomfing MIDI data when MIDI-DUmp is enabled.
Fix for using MIDI channel for SYSEX.

* Several fixes for SYSEX handling.

* Code for sending a voice dump via MIDI started.

* Fix for sending SYSEX voice dump to all interfaces.

* Sending voice data via SYSEX when voice is changed.
Adding master volume and changing master volume when SYSEX master volume is triggered.

* Forgot to initialize nMasterVolume - just added it.

* Merge.

* Added a SpinLock around MIDI message processing.

* Added notesOff() when changing algorithm parameter (can be extended later for other parameters).

Co-authored-by: Holger Wirtz <wirtz@parasitstudio.de>
pull/228/head
Holger 2 years ago committed by GitHub
parent 2f044b6fcb
commit c00ed09398
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 106
      src/mididevice.cpp
  2. 9
      src/mididevice.h
  3. 144
      src/minidexed.cpp
  4. 5
      src/minidexed.h
  5. 16
      src/serialmididevice.cpp

@ -114,24 +114,37 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
switch(pMessage[0])
{
case MIDI_SYSTEM_EXCLUSIVE_BEGIN:
printf("SysEx data length: [%d]\n",uint16_t(nLength));
printf("SysEx data:\n");
printf("MIDI%u: SysEx data length: [%d]:",nCable, uint16_t(nLength));
for (uint16_t i = 0; i < nLength; i++)
{
if((i % 8) == 0)
printf("%04d:",i);
if((i % 16) == 0)
printf("\n%04d:",i);
printf(" 0x%02x",pMessage[i]);
if((i % 8) == 0)
printf("\n");
}
printf("\n");
break;
default:
printf("Unhandled MIDI event type %0x02x\n",pMessage[0]);
printf("MIDI%u: Unhandled MIDI event type %0x02x\n",nCable,pMessage[0]);
}
break;
}
}
// Only for debugging:
/*
if(pMessage[0]==MIDI_SYSTEM_EXCLUSIVE_BEGIN)
{
printf("MIDI%u: SysEx data length: [%d]:",nCable, uint16_t(nLength));
for (uint16_t i = 0; i < nLength; i++)
{
if((i % 16) == 0)
printf("\n%04d:",i);
printf(" 0x%02x",pMessage[i]);
}
printf("\n");
}
*/
// Handle MIDI Thru
if (m_DeviceName.compare (m_pConfig->GetMIDIThruIn ()) == 0)
{
@ -150,6 +163,8 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
return;
}
m_MIDISpinLock.Acquire ();
u8 ucStatus = pMessage[0];
u8 ucChannel = ucStatus & 0x0F;
u8 ucType = ucStatus >> 4;
@ -157,17 +172,24 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
// GLOBAL MIDI SYSEX
if (pMessage[0] == MIDI_SYSTEM_EXCLUSIVE_BEGIN && pMessage[3] == 0x04 && pMessage[4] == 0x01 && pMessage[nLength-1] == MIDI_SYSTEM_EXCLUSIVE_END) // MASTER VOLUME
{
float32_t nMasterVolume=(pMessage[5] & (pMessage[6]<<7))/(1<<14);
float32_t nMasterVolume=((pMessage[5] & 0x7c) & ((pMessage[6] & 0x7c) <<7))/(1<<14);
LOGNOTE("Master volume: %f",nMasterVolume);
// TODO: Handle global master volume
m_pSynthesizer->setMasterVolume(nMasterVolume);
}
else
{
for (unsigned nTG = 0; nTG < CConfig::ToneGenerators; nTG++)
{
// MIDI SYSEX per MIDI channel
if (ucStatus == MIDI_SYSTEM_EXCLUSIVE_BEGIN && m_ChannelMap[nTG] == pMessage[2] & 0x07)
HandleSystemExclusive(pMessage, nLength, nTG);
if (ucStatus == MIDI_SYSTEM_EXCLUSIVE_BEGIN)
{
// MIDI SYSEX per MIDI channel
uint8_t ucSysExChannel = (pMessage[2] & 0x07);
if (m_ChannelMap[nTG] == ucSysExChannel || m_ChannelMap[nTG] == OmniMode)
{
LOGNOTE("MIDI-SYSEX: channel: %u, len: %u, TG: %u",m_ChannelMap[nTG],nLength,nTG);
HandleSystemExclusive(pMessage, nLength, nCable, nTG);
}
}
else
{
if ( m_ChannelMap[nTG] == ucChannel
@ -293,6 +315,7 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
}
}
}
m_MIDISpinLock.Release ();
}
void CMIDIDevice::AddDevice (const char *pDeviceName)
@ -306,7 +329,7 @@ void CMIDIDevice::AddDevice (const char *pDeviceName)
s_DeviceMap.insert (std::pair<std::string, CMIDIDevice *> (pDeviceName, this));
}
void CMIDIDevice::HandleSystemExclusive(const uint8_t* pMessage, const size_t nLength, const uint8_t nTG)
void CMIDIDevice::HandleSystemExclusive(const uint8_t* pMessage, const size_t nLength, const unsigned nCable, const uint8_t nTG)
{
int16_t sysex_return;
@ -319,33 +342,33 @@ void CMIDIDevice::HandleSystemExclusive(const uint8_t* pMessage, const size_t nL
LOGERR("SysEx end status byte not detected.");
break;
case -2:
LOGERR("E: SysEx vendor not Yamaha.");
LOGERR("SysEx vendor not Yamaha.");
break;
case -3:
LOGERR("E: Unknown SysEx parameter change.");
LOGERR("Unknown SysEx parameter change.");
break;
case -4:
LOGERR(" Unknown SysEx voice or function.");
LOGERR("Unknown SysEx voice or function.");
break;
case -5:
LOGERR("E: Not a SysEx voice bulk upload.");
LOGERR("Not a SysEx voice bulk upload.");
break;
case -6:
LOGERR("E: Wrong length for SysEx voice bulk upload (not 155).");
LOGERR("Wrong length for SysEx voice bulk upload (not 155).");
break;
case -7:
LOGERR("E: Checksum error for one voice.");
LOGERR("Checksum error for one voice.");
break;
case -8:
LOGERR("E: Not a SysEx bank bulk upload.");
LOGERR("Not a SysEx bank bulk upload.");
break;
case -9:
LOGERR("E: Wrong length for SysEx bank bulk upload (not 4096).");
LOGERR("Wrong length for SysEx bank bulk upload (not 4096).");
case -10:
LOGERR("E: Checksum error for bank.");
LOGERR("Checksum error for bank.");
break;
case -11:
LOGERR("E: Unknown SysEx message.");
LOGERR("Unknown SysEx message.");
break;
case 64:
LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]);
@ -407,7 +430,6 @@ void CMIDIDevice::HandleSystemExclusive(const uint8_t* pMessage, const size_t nL
// load sysex-data into voice memory
LOGDBG("One Voice bulk upload");
m_pSynthesizer->loadVoiceParameters(pMessage,nTG);
break;
case 200:
LOGDBG("Bank bulk upload.");
@ -415,9 +437,41 @@ void CMIDIDevice::HandleSystemExclusive(const uint8_t* pMessage, const size_t nL
LOGNOTE("Currently code for storing a bulk bank upload is missing!");
break;
default:
LOGDBG("SysEx voice parameter change: %d value: %d",pMessage[4] + ((pMessage[3] & 0x03) * 128), pMessage[5]);
m_pSynthesizer->setVoiceDataElement(pMessage[4] + ((pMessage[3] & 0x03) * 128), pMessage[5],nTG);
if(sysex_return >= 300 && sysex_return < 500)
{
LOGDBG("SysEx voice parameter change: Parameter %d value: %d",pMessage[4] + ((pMessage[3] & 0x03) * 128), pMessage[5]);
m_pSynthesizer->setVoiceDataElement(pMessage[4] + ((pMessage[3] & 0x03) * 128), pMessage[5],nTG);
switch(pMessage[4] + ((pMessage[3] & 0x03) * 128))
{
case 134:
m_pSynthesizer->notesOff(0,nTG);
break;
}
}
else if(sysex_return >= 500 && sysex_return < 600)
{
LOGDBG("SysEx send voice %u request",sysex_return-500);
SendSystemExclusiveVoice(sysex_return-500, nCable, nTG);
}
break;
}
}
void CMIDIDevice::SendSystemExclusiveVoice(uint8_t nVoice, const unsigned nCable, uint8_t nTG)
{
uint8_t voicedump[163];
LOGDBG("Sending SysEx voice %u ",nVoice);
// Get voice sysex dump from TG
m_pSynthesizer->getSysExVoiceDump(voicedump, nTG);
TDeviceMap::const_iterator Iterator;
// send voice dump to all MIDI interfaces
for(Iterator = s_DeviceMap.begin(); Iterator != s_DeviceMap.end(); ++Iterator)
{
Iterator->second->Send (voicedump, sizeof(voicedump)*sizeof(uint8_t), nCable);
LOGNOTE("Send SYSEX voice dump to \"%s\"\n",Iterator->first);
}
}

@ -27,6 +27,7 @@
#include <string>
#include <unordered_map>
#include <circle/types.h>
#include <circle/spinlock.h>
class CMiniDexed;
@ -49,14 +50,12 @@ public:
u8 GetChannel (unsigned nTG) const;
virtual void Send (const u8 *pMessage, size_t nLength, unsigned nCable = 0) {}
virtual void SendSystemExclusiveVoice(uint8_t nVoice, const unsigned nCable, uint8_t nTG);
protected:
void MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsigned nCable = 0);
void AddDevice (const char *pDeviceName);
void HandleSystemExclusive(const uint8_t* pMessage, const size_t nLength, const uint8_t nTG);
void HandleSystemExclusive(const uint8_t* pMessage, const size_t nLength, const unsigned nCable, const uint8_t nTG);
private:
CMiniDexed *m_pSynthesizer;
CConfig *m_pConfig;
@ -67,6 +66,8 @@ private:
typedef std::unordered_map<std::string, CMIDIDevice *> TDeviceMap;
static TDeviceMap s_DeviceMap;
CSpinLock m_MIDISpinLock;
};
#endif

@ -125,6 +125,8 @@ CMiniDexed::CMiniDexed (CConfig *pConfig, CInterruptSystem *pInterrupt,
}
#endif
setMasterVolume(1.0);
// BEGIN setup tg_mixer
tg_mixer = new AudioStereoMixer<CConfig::ToneGenerators>(pConfig->GetChunkSize()/2);
// END setup tgmixer
@ -371,6 +373,7 @@ void CMiniDexed::ProgramChange (unsigned nProgram, unsigned nTG)
assert (m_pTG[nTG]);
m_pTG[nTG]->loadVoiceParameters (Buffer);
m_SerialMIDI.SendSystemExclusiveVoice(nProgram,0,nTG);
m_UI.ParameterChanged ();
}
@ -875,56 +878,70 @@ void CMiniDexed::ProcessSound (void)
}
// BEGIN TG mixing
for (uint8_t i = 0; i < CConfig::ToneGenerators; i++)
{
tg_mixer->doAddMix(i,m_OutputLevel[i]);
reverb_send_mixer->doAddMix(i,m_OutputLevel[i]);
}
// END TG mixing
// BEGIN create SampleBuffer for holding audio data
float32_t SampleBuffer[2][nFrames];
// END create SampleBuffer for holding audio data
// get the mix of all TGs
tg_mixer->getMix(SampleBuffer[indexL], SampleBuffer[indexR]);
float32_t tmp_float[nFrames*2];
int16_t tmp_int[nFrames*2];
// BEGIN adding reverb
if (m_nParameter[ParameterReverbEnable])
if(nMasterVolume > 0.0)
{
float32_t ReverbBuffer[2][nFrames];
float32_t ReverbSendBuffer[2][nFrames];
arm_fill_f32(0.0f, ReverbBuffer[indexL], nFrames);
arm_fill_f32(0.0f, ReverbBuffer[indexR], nFrames);
arm_fill_f32(0.0f, ReverbSendBuffer[indexR], nFrames);
arm_fill_f32(0.0f, ReverbSendBuffer[indexL], nFrames);
m_ReverbSpinLock.Acquire ();
reverb_send_mixer->getMix(ReverbSendBuffer[indexL], ReverbSendBuffer[indexR]);
reverb->doReverb(ReverbSendBuffer[indexL],ReverbSendBuffer[indexR],ReverbBuffer[indexL], ReverbBuffer[indexR],nFrames);
for (uint8_t i = 0; i < CConfig::ToneGenerators; i++)
{
tg_mixer->doAddMix(i,m_OutputLevel[i]);
reverb_send_mixer->doAddMix(i,m_OutputLevel[i]);
}
// END TG mixing
// BEGIN create SampleBuffer for holding audio data
float32_t SampleBuffer[2][nFrames];
// END create SampleBuffer for holding audio data
// scale down and add left reverb buffer by reverb level
arm_scale_f32(ReverbBuffer[indexL], reverb->get_level(), ReverbBuffer[indexL], nFrames);
arm_add_f32(SampleBuffer[indexL], ReverbBuffer[indexL], SampleBuffer[indexL], nFrames);
// scale down and add right reverb buffer by reverb level
arm_scale_f32(ReverbBuffer[indexR], reverb->get_level(), ReverbBuffer[indexR], nFrames);
arm_add_f32(SampleBuffer[indexR], ReverbBuffer[indexR], SampleBuffer[indexR], nFrames);
// get the mix of all TGs
tg_mixer->getMix(SampleBuffer[indexL], SampleBuffer[indexR]);
m_ReverbSpinLock.Release ();
}
// END adding reverb
// BEGIN adding reverb
if (m_nParameter[ParameterReverbEnable])
{
float32_t ReverbBuffer[2][nFrames];
float32_t ReverbSendBuffer[2][nFrames];
// Convert dual float array (left, right) to single int16 array (left/right)
float32_t tmp_float[nFrames*2];
int16_t tmp_int[nFrames*2];
for(uint16_t i=0; i<nFrames;i++)
{
tmp_float[i*2]=SampleBuffer[indexL][i];
tmp_float[(i*2)+1]=SampleBuffer[indexR][i];
arm_fill_f32(0.0f, ReverbBuffer[indexL], nFrames);
arm_fill_f32(0.0f, ReverbBuffer[indexR], nFrames);
arm_fill_f32(0.0f, ReverbSendBuffer[indexR], nFrames);
arm_fill_f32(0.0f, ReverbSendBuffer[indexL], nFrames);
m_ReverbSpinLock.Acquire ();
reverb_send_mixer->getMix(ReverbSendBuffer[indexL], ReverbSendBuffer[indexR]);
reverb->doReverb(ReverbSendBuffer[indexL],ReverbSendBuffer[indexR],ReverbBuffer[indexL], ReverbBuffer[indexR],nFrames);
// scale down and add left reverb buffer by reverb level
arm_scale_f32(ReverbBuffer[indexL], reverb->get_level(), ReverbBuffer[indexL], nFrames);
arm_add_f32(SampleBuffer[indexL], ReverbBuffer[indexL], SampleBuffer[indexL], nFrames);
// scale down and add right reverb buffer by reverb level
arm_scale_f32(ReverbBuffer[indexR], reverb->get_level(), ReverbBuffer[indexR], nFrames);
arm_add_f32(SampleBuffer[indexR], ReverbBuffer[indexR], SampleBuffer[indexR], nFrames);
m_ReverbSpinLock.Release ();
}
// END adding reverb
// Convert dual float array (left, right) to single int16 array (left/right)
for(uint16_t i=0; i<nFrames;i++)
{
if(nMasterVolume >0.0 && nMasterVolume <1.0)
{
tmp_float[i*2]=SampleBuffer[indexL][i] * nMasterVolume;
tmp_float[(i*2)+1]=SampleBuffer[indexR][i] * nMasterVolume;
}
else if(nMasterVolume == 1.0)
{
tmp_float[i*2]=SampleBuffer[indexL][i];
tmp_float[(i*2)+1]=SampleBuffer[indexR][i];
}
}
arm_float_to_q15(tmp_float,tmp_int,nFrames*2);
}
arm_float_to_q15(tmp_float,tmp_int,nFrames*2);
else
arm_fill_q15(0, tmp_int, nFrames * 2);
if (m_pSoundDevice->Write (tmp_int, sizeof(tmp_int)) != (int) sizeof(tmp_int))
{
@ -1168,5 +1185,44 @@ void CMiniDexed::setVoiceDataElement(uint8_t data, uint8_t number, uint8_t nTG)
int16_t CMiniDexed::checkSystemExclusive(const uint8_t* pMessage,const uint16_t nLength, uint8_t nTG)
{
assert (nTG < CConfig::ToneGenerators);
assert (m_pTG[nTG]);
return(m_pTG[nTG]->checkSystemExclusive(pMessage, nLength));
}
void CMiniDexed::getSysExVoiceDump(uint8_t* dest, uint8_t nTG)
{
uint8_t checksum = 0;
uint8_t data[155];
assert (nTG < CConfig::ToneGenerators);
assert (m_pTG[nTG]);
m_pTG[nTG]->getVoiceData(data);
dest[0] = 0xF0; // SysEx start
dest[1] = 0x43; // ID=Yamaha
dest[2] = GetTGParameter(TGParameterMIDIChannel, nTG); // Sub-status and MIDI channel
dest[3] = 0x00; // Format number (0=1 voice)
dest[4] = 0x01; // Byte count MSB
dest[5] = 0x1B; // Byte count LSB
for (uint8_t n = 0; n < 155; n++)
{
checksum -= data[n];
dest[6 + n] = data[n];
}
dest[161] = checksum & 0x7f; // Checksum
dest[162] = 0xF7; // SysEx end
}
void CMiniDexed::setMasterVolume (float32_t vol)
{
if(vol < 0.0)
vol = 0.0;
else if(vol > 1.0)
vol = 1.0;
nMasterVolume=vol;
}

@ -100,6 +100,7 @@ public:
void setAftertouchTarget(uint8_t target, uint8_t nTG);
void loadVoiceParameters(const uint8_t* data, uint8_t nTG);
void setVoiceDataElement(uint8_t data, uint8_t number, uint8_t nTG);
void getSysExVoiceDump(uint8_t* dest, uint8_t nTG);
int16_t checkSystemExclusive(const uint8_t* pMessage, const uint16_t nLength, uint8_t nTG);
@ -151,6 +152,8 @@ public:
bool SavePerformance (void);
bool DoSavePerformance (void);
void setMasterVolume (float32_t vol);
private:
int16_t ApplyNoteLimits (int16_t pitch, unsigned nTG); // returns < 0 to ignore note
uint8_t m_uchOPMask[CConfig::ToneGenerators];
@ -195,6 +198,8 @@ private:
unsigned m_nReverbSend[CConfig::ToneGenerators];
float32_t nMasterVolume;
CUserInterface m_UI;
CSysExFileLoader m_SysExFileLoader;
CPerformanceConfig m_PerformanceConfig;

@ -20,10 +20,14 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
#include <circle/logger.h>
#include <cstring>
#include "serialmididevice.h"
#include <assert.h>
LOGMODULE("serialmididevice");
CSerialMIDIDevice::CSerialMIDIDevice (CMiniDexed *pSynthesizer, CInterruptSystem *pInterrupt,
CConfig *pConfig)
: CMIDIDevice (pSynthesizer, pConfig),
@ -58,23 +62,20 @@ void CSerialMIDIDevice::Process (void)
if (nResult <= 0)
{
if(nResult!=0)
printf("Serial-Read: %d\n",nResult);
LOGERR("Serial.Read() error: %d\n",nResult);
return;
}
if (m_pConfig->GetMIDIDumpEnabled ())
{
printf("Incoming MIDI data:\n");
printf("Incoming MIDI data:");
for (uint16_t i = 0; i < nResult; i++)
{
if((i % 8) == 0)
printf("%04d:",i);
printf("\n%04d:",i);
printf(" 0x%02x",Buffer[i]);
if((i > 1 ) && (i % 8) == 0)
printf("\n");
}
if((nResult % 8) != 0)
printf("\n");
printf("\n");
}
// Process MIDI messages
@ -157,6 +158,7 @@ void CSerialMIDIDevice::Process (void)
}
}
}
void CSerialMIDIDevice::Send (const u8 *pMessage, size_t nLength, unsigned nCable)
{
m_SendBuffer.Write (pMessage, nLength);

Loading…
Cancel
Save