@ -20,12 +20,16 @@
// You should have received a copy of the GNU General Public License
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
//
# include <circle/logger.h>
# include "mididevice.h"
# include "mididevice.h"
# include "minidexed.h"
# include "minidexed.h"
# include "config.h"
# include "config.h"
# include <stdio.h>
# include <stdio.h>
# include <assert.h>
# include <assert.h>
LOGMODULE ( " mididevice " ) ;
# define MIDI_NOTE_OFF 0b1000
# define MIDI_NOTE_OFF 0b1000
# define MIDI_NOTE_ON 0b1001
# define MIDI_NOTE_ON 0b1001
# define MIDI_AFTERTOUCH 0b1010 // TODO
# define MIDI_AFTERTOUCH 0b1010 // TODO
@ -45,6 +49,7 @@
# define MIDI_PROGRAM_CHANGE 0b1100
# define MIDI_PROGRAM_CHANGE 0b1100
# define MIDI_PITCH_BEND 0b1110
# define MIDI_PITCH_BEND 0b1110
# define MIDI_SYSTEM_EXCLUSIVE 0xF0
# define MIDI_TIMING_CLOCK 0xF8
# define MIDI_TIMING_CLOCK 0xF8
# define MIDI_ACTIVE_SENSING 0xFE
# define MIDI_ACTIVE_SENSING 0xFE
@ -79,8 +84,6 @@ u8 CMIDIDevice::GetChannel (unsigned nTG) const
void CMIDIDevice : : MIDIMessageHandler ( const u8 * pMessage , size_t nLength , unsigned nCable )
void CMIDIDevice : : MIDIMessageHandler ( const u8 * pMessage , size_t nLength , unsigned nCable )
{
{
assert ( m_pSynthesizer ! = 0 ) ;
// The packet contents are just normal MIDI data - see
// The packet contents are just normal MIDI data - see
// https://www.midi.org/specifications/item/table-1-summary-of-midi-message
// https://www.midi.org/specifications/item/table-1-summary-of-midi-message
@ -106,6 +109,25 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
( unsigned ) pMessage [ 0 ] , ( unsigned ) pMessage [ 1 ] ,
( unsigned ) pMessage [ 0 ] , ( unsigned ) pMessage [ 1 ] ,
( unsigned ) pMessage [ 2 ] ) ;
( unsigned ) pMessage [ 2 ] ) ;
break ;
break ;
default :
switch ( pMessage [ 0 ] )
{
case MIDI_SYSTEM_EXCLUSIVE :
printf ( " SysEx data length: [%d] \n " , uint16_t ( nLength ) ) ;
printf ( " SysEx data: \n " ) ;
for ( uint16_t i = 0 ; i < nLength ; i + + )
{
if ( ( i % 8 ) = = 0 )
printf ( " %04d: " , i ) ;
printf ( " 0x%02x " , pMessage [ i ] ) ;
if ( ( i % 8 ) = = 0 )
printf ( " \n " ) ;
}
break ;
default :
printf ( " Unhandled MIDI event type %0x02x \n " , pMessage [ 0 ] ) ;
}
break ;
}
}
}
}
@ -130,126 +152,142 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
u8 ucChannel = ucStatus & 0x0F ;
u8 ucChannel = ucStatus & 0x0F ;
u8 ucType = ucStatus > > 4 ;
u8 ucType = ucStatus > > 4 ;
for ( unsigned nTG = 0 ; nTG < CConfig : : ToneGenerators ; nTG + + )
// GLOBAL MIDI SYSEX
if ( pMessage [ 0 ] = = MIDI_SYSTEM_EXCLUSIVE & & pMessage [ 3 ] = = 0x04 & & pMessage [ 4 ] = = 0x01 & & pMessage [ nLength - 1 ] = = 0xF7 ) // MASTER VOLUME
{
float32_t nMasterVolume = ( pMessage [ 5 ] & ( pMessage [ 6 ] < < 7 ) ) / ( 1 < < 14 ) ;
LOGNOTE ( " Master volume: %f " , nMasterVolume ) ;
// TODO: Handle global master volume
}
else
{
{
if ( m_ChannelMap [ nTG ] = = ucChannel
for ( unsigned nTG = 0 ; nTG < CConfig : : ToneGenerators ; nTG + + )
| | m_ChannelMap [ nTG ] = = OmniMode )
{
{
switch ( ucType )
// MIDI SYSEX per MIDI channel
if ( ucStatus = = MIDI_SYSTEM_EXCLUSIVE & & m_ChannelMap [ nTG ] = = pMessage [ 2 ] & 0x07 )
HandleSystemExclusive ( pMessage , nLength , nTG ) ;
else
{
{
case MIDI_NOTE_ON :
if ( m_ChannelMap [ nTG ] = = ucChannel
if ( nLength < 3 )
| | m_ChannelMap [ nTG ] = = OmniMode )
{
break ;
}
if ( pMessage [ 2 ] > 0 )
{
{
if ( pMessage [ 2 ] < = 127 )
switch ( ucType )
{
m_pSynthesizer - > keydown ( pMessage [ 1 ] ,
pMessage [ 2 ] , nTG ) ;
}
}
else
{
m_pSynthesizer - > keyup ( pMessage [ 1 ] , nTG ) ;
}
break ;
case MIDI_NOTE_OFF :
if ( nLength < 3 )
{
break ;
}
m_pSynthesizer - > keyup ( pMessage [ 1 ] , nTG ) ;
break ;
case MIDI_CONTROL_CHANGE :
if ( nLength < 3 )
{
break ;
}
switch ( pMessage [ 1 ] )
{
case MIDI_CC_MODULATION :
m_pSynthesizer - > setModWheel ( pMessage [ 2 ] , nTG ) ;
m_pSynthesizer - > ControllersRefresh ( nTG ) ;
break ;
case MIDI_CC_VOLUME :
m_pSynthesizer - > SetVolume ( pMessage [ 2 ] , nTG ) ;
break ;
case MIDI_CC_PAN_POSITION :
m_pSynthesizer - > SetPan ( pMessage [ 2 ] , nTG ) ;
break ;
case MIDI_CC_BANK_SELECT_LSB :
m_pSynthesizer - > BankSelectLSB ( pMessage [ 2 ] , nTG ) ;
break ;
case MIDI_CC_BANK_SUSTAIN :
m_pSynthesizer - > setSustain ( pMessage [ 2 ] > = 64 , nTG ) ;
break ;
case MIDI_CC_RESONANCE :
m_pSynthesizer - > SetResonance ( maplong ( pMessage [ 2 ] , 0 , 127 , 0 , 99 ) , nTG ) ;
break ;
case MIDI_CC_FREQUENCY_CUTOFF :
m_pSynthesizer - > SetCutoff ( maplong ( pMessage [ 2 ] , 0 , 127 , 0 , 99 ) , nTG ) ;
break ;
case MIDI_CC_REVERB_LEVEL :
m_pSynthesizer - > SetReverbSend ( maplong ( pMessage [ 2 ] , 0 , 127 , 0 , 99 ) , nTG ) ;
break ;
case MIDI_CC_DETUNE_LEVEL :
if ( pMessage [ 2 ] = = 0 )
{
// "0 to 127, with 0 being no celeste (detune) effect applied at all."
m_pSynthesizer - > SetMasterTune ( 0 , nTG ) ;
}
else
{
{
m_pSynthesizer - > SetMasterTune ( maplong ( pMessage [ 2 ] , 1 , 127 , - 99 , 99 ) , nTG ) ;
case MIDI_NOTE_ON :
if ( nLength < 3 )
{
break ;
}
if ( pMessage [ 2 ] > 0 )
{
if ( pMessage [ 2 ] < = 127 )
{
m_pSynthesizer - > keydown ( pMessage [ 1 ] ,
pMessage [ 2 ] , nTG ) ;
}
}
else
{
m_pSynthesizer - > keyup ( pMessage [ 1 ] , nTG ) ;
}
break ;
case MIDI_NOTE_OFF :
if ( nLength < 3 )
{
break ;
}
m_pSynthesizer - > keyup ( pMessage [ 1 ] , nTG ) ;
break ;
case MIDI_CONTROL_CHANGE :
if ( nLength < 3 )
{
break ;
}
switch ( pMessage [ 1 ] )
{
case MIDI_CC_MODULATION :
m_pSynthesizer - > setModWheel ( pMessage [ 2 ] , nTG ) ;
m_pSynthesizer - > ControllersRefresh ( nTG ) ;
break ;
case MIDI_CC_VOLUME :
m_pSynthesizer - > SetVolume ( pMessage [ 2 ] , nTG ) ;
break ;
case MIDI_CC_PAN_POSITION :
m_pSynthesizer - > SetPan ( pMessage [ 2 ] , nTG ) ;
break ;
case MIDI_CC_BANK_SELECT_LSB :
m_pSynthesizer - > BankSelectLSB ( pMessage [ 2 ] , nTG ) ;
break ;
case MIDI_CC_BANK_SUSTAIN :
m_pSynthesizer - > setSustain ( pMessage [ 2 ] > = 64 , nTG ) ;
break ;
case MIDI_CC_RESONANCE :
m_pSynthesizer - > SetResonance ( maplong ( pMessage [ 2 ] , 0 , 127 , 0 , 99 ) , nTG ) ;
break ;
case MIDI_CC_FREQUENCY_CUTOFF :
m_pSynthesizer - > SetCutoff ( maplong ( pMessage [ 2 ] , 0 , 127 , 0 , 99 ) , nTG ) ;
break ;
case MIDI_CC_REVERB_LEVEL :
m_pSynthesizer - > SetReverbSend ( maplong ( pMessage [ 2 ] , 0 , 127 , 0 , 99 ) , nTG ) ;
break ;
case MIDI_CC_DETUNE_LEVEL :
if ( pMessage [ 2 ] = = 0 )
{
// "0 to 127, with 0 being no celeste (detune) effect applied at all."
m_pSynthesizer - > SetMasterTune ( 0 , nTG ) ;
}
else
{
m_pSynthesizer - > SetMasterTune ( maplong ( pMessage [ 2 ] , 1 , 127 , - 99 , 99 ) , nTG ) ;
}
break ;
case MIDI_CC_ALL_SOUND_OFF :
m_pSynthesizer - > panic ( pMessage [ 2 ] , nTG ) ;
break ;
case MIDI_CC_ALL_NOTES_OFF :
m_pSynthesizer - > notesOff ( pMessage [ 2 ] , nTG ) ;
break ;
}
break ;
case MIDI_PROGRAM_CHANGE :
// do program change only if enabled in config
if ( m_pConfig - > GetMIDIRXProgramChange ( ) )
m_pSynthesizer - > ProgramChange ( pMessage [ 1 ] , nTG ) ;
break ;
case MIDI_PITCH_BEND : {
if ( nLength < 3 )
{
break ;
}
s16 nValue = pMessage [ 1 ] ;
nValue | = ( s16 ) pMessage [ 2 ] < < 7 ;
nValue - = 0x2000 ;
m_pSynthesizer - > setPitchbend ( nValue , nTG ) ;
} break ;
default :
break ;
}
}
break ;
case MIDI_CC_ALL_SOUND_OFF :
m_pSynthesizer - > panic ( pMessage [ 2 ] , nTG ) ;
break ;
case MIDI_CC_ALL_NOTES_OFF :
m_pSynthesizer - > notesOff ( pMessage [ 2 ] , nTG ) ;
break ;
}
break ;
case MIDI_PROGRAM_CHANGE :
// do program change only if enabled in config
if ( m_pConfig - > GetMIDIRXProgramChange ( ) )
m_pSynthesizer - > ProgramChange ( pMessage [ 1 ] , nTG ) ;
break ;
case MIDI_PITCH_BEND : {
if ( nLength < 3 )
{
break ;
}
}
s16 nValue = pMessage [ 1 ] ;
nValue | = ( s16 ) pMessage [ 2 ] < < 7 ;
nValue - = 0x2000 ;
m_pSynthesizer - > setPitchbend ( nValue , nTG ) ;
} break ;
default :
break ;
}
}
}
}
}
}
@ -265,3 +303,119 @@ void CMIDIDevice::AddDevice (const char *pDeviceName)
s_DeviceMap . insert ( std : : pair < std : : string , CMIDIDevice * > ( pDeviceName , this ) ) ;
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 )
{
int16_t sysex_return ;
sysex_return = m_pSynthesizer - > checkSystemExclusive ( pMessage , nLength , nTG ) ;
LOGDBG ( " SYSEX handler return value: %d " , sysex_return ) ;
switch ( sysex_return )
{
case - 1 :
LOGERR ( " SysEx end status byte not detected. " ) ;
break ;
case - 2 :
LOGERR ( " E: SysEx vendor not Yamaha. " ) ;
break ;
case - 3 :
LOGERR ( " E: Unknown SysEx parameter change. " ) ;
break ;
case - 4 :
LOGERR ( " Unknown SysEx voice or function. " ) ;
break ;
case - 5 :
LOGERR ( " E: Not a SysEx voice bulk upload. " ) ;
break ;
case - 6 :
LOGERR ( " E: Wrong length for SysEx voice bulk upload (not 155). " ) ;
break ;
case - 7 :
LOGERR ( " E: Checksum error for one voice. " ) ;
break ;
case - 8 :
LOGERR ( " E: Not a SysEx bank bulk upload. " ) ;
break ;
case - 9 :
LOGERR ( " E: Wrong length for SysEx bank bulk upload (not 4096). " ) ;
case - 10 :
LOGERR ( " E: Checksum error for bank. " ) ;
break ;
case - 11 :
LOGERR ( " E: Unknown SysEx message. " ) ;
break ;
case 64 :
LOGDBG ( " SysEx Function parameter change: %d Value %d " , pMessage [ 4 ] , pMessage [ 5 ] ) ;
m_pSynthesizer - > setMonoMode ( pMessage [ 5 ] , nTG ) ;
break ;
case 65 :
LOGDBG ( " SysEx Function parameter change: %d Value %d " , pMessage [ 4 ] , pMessage [ 5 ] ) ;
m_pSynthesizer - > setPitchbendRange ( pMessage [ 5 ] , nTG ) ;
break ;
case 66 :
LOGDBG ( " SysEx Function parameter change: %d Value %d " , pMessage [ 4 ] , pMessage [ 5 ] ) ;
m_pSynthesizer - > setPitchbendStep ( pMessage [ 5 ] , nTG ) ;
break ;
case 67 :
LOGDBG ( " SysEx Function parameter change: %d Value %d " , pMessage [ 4 ] , pMessage [ 5 ] ) ;
m_pSynthesizer - > setPortamentoMode ( pMessage [ 5 ] , nTG ) ;
break ;
case 68 :
LOGDBG ( " SysEx Function parameter change: %d Value %d " , pMessage [ 4 ] , pMessage [ 5 ] ) ;
m_pSynthesizer - > setPortamentoGlissando ( pMessage [ 5 ] , nTG ) ;
break ;
case 69 :
LOGDBG ( " SysEx Function parameter change: %d Value %d " , pMessage [ 4 ] , pMessage [ 5 ] ) ;
m_pSynthesizer - > setPortamentoTime ( pMessage [ 5 ] , nTG ) ;
break ;
case 70 :
LOGDBG ( " SysEx Function parameter change: %d Value %d " , pMessage [ 4 ] , pMessage [ 5 ] ) ;
m_pSynthesizer - > setModWheelRange ( pMessage [ 5 ] , nTG ) ;
break ;
case 71 :
LOGDBG ( " SysEx Function parameter change: %d Value %d " , pMessage [ 4 ] , pMessage [ 5 ] ) ;
m_pSynthesizer - > setModWheelTarget ( pMessage [ 5 ] , nTG ) ;
break ;
case 72 :
LOGDBG ( " SysEx Function parameter change: %d Value %d " , pMessage [ 4 ] , pMessage [ 5 ] ) ;
m_pSynthesizer - > setFootControllerRange ( pMessage [ 5 ] , nTG ) ;
break ;
case 73 :
LOGDBG ( " SysEx Function parameter change: %d Value %d " , pMessage [ 4 ] , pMessage [ 5 ] ) ;
m_pSynthesizer - > setFootControllerTarget ( pMessage [ 5 ] , nTG ) ;
break ;
case 74 :
LOGDBG ( " SysEx Function parameter change: %d Value %d " , pMessage [ 4 ] , pMessage [ 5 ] ) ;
m_pSynthesizer - > setBreathControllerRange ( pMessage [ 5 ] , nTG ) ;
break ;
case 75 :
LOGDBG ( " SysEx Function parameter change: %d Value %d " , pMessage [ 4 ] , pMessage [ 5 ] ) ;
m_pSynthesizer - > setBreathControllerTarget ( pMessage [ 5 ] , nTG ) ;
break ;
case 76 :
LOGDBG ( " SysEx Function parameter change: %d Value %d " , pMessage [ 4 ] , pMessage [ 5 ] ) ;
m_pSynthesizer - > setAftertouchRange ( pMessage [ 5 ] , nTG ) ;
break ;
case 77 :
LOGDBG ( " SysEx Function parameter change: %d Value %d " , pMessage [ 4 ] , pMessage [ 5 ] ) ;
m_pSynthesizer - > setAftertouchTarget ( pMessage [ 5 ] , nTG ) ;
break ;
case 100 :
// load sysex-data into voice memory
LOGDBG ( " One Voice bulk upload " ) ;
m_pSynthesizer - > loadVoiceParameters ( pMessage , nTG ) ;
break ;
case 200 :
LOGDBG ( " Bank bulk upload. " ) ;
//TODO: add code for storing a bank bulk upload
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 ) ;
break ;
}
}