dosbox-x/src/gui/midi.cpp
Robert de Rooy 575eac06bd Spelling fixes for src dir
Note: large parts of libs, in particular libs/decoders were skipped
2022-09-03 14:02:46 +02:00

807 lines
21 KiB
C++

/*
* Copyright (C) 2002-2021 The DOSBox Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <string>
#include <algorithm>
#include "SDL.h"
#include "dosbox.h"
#include "midi.h"
#include "control.h"
#include "cross.h"
#include "support.h"
#include "setup.h"
#include "mapper.h"
#include "pic.h"
#include "hardware.h"
#include "logging.h"
#include "timer.h"
#define RAWBUF 1024
uint8_t MIDI_evt_len[256] = {
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0x00
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0x10
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0x20
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0x30
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0x40
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0x50
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0x60
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0x70
3,3,3,3, 3,3,3,3, 3,3,3,3, 3,3,3,3, // 0x80
3,3,3,3, 3,3,3,3, 3,3,3,3, 3,3,3,3, // 0x90
3,3,3,3, 3,3,3,3, 3,3,3,3, 3,3,3,3, // 0xa0
3,3,3,3, 3,3,3,3, 3,3,3,3, 3,3,3,3, // 0xb0
2,2,2,2, 2,2,2,2, 2,2,2,2, 2,2,2,2, // 0xc0
2,2,2,2, 2,2,2,2, 2,2,2,2, 2,2,2,2, // 0xd0
3,3,3,3, 3,3,3,3, 3,3,3,3, 3,3,3,3, // 0xe0
0,2,3,2, 0,0,1,0, 1,0,1,1, 1,0,1,0 // 0xf0
};
MidiHandler * handler_list = 0;
MidiHandler::MidiHandler(){
next = handler_list;
handler_list = this;
}
MidiHandler Midi_none;
DB_Midi midi;
std::string sffile="Not available";
static struct {
bool init;
bool ignore;
// NOTE: 16-bit ($ffff = not used, $00-ff = data value)
uint16_t code_80[0x80]; // note off (w/ velocity)
uint16_t code_90[0x80]; // note on (w/ velocity)
uint16_t code_a0[0x80]; // aftertouch (polyphonic key pressure)
uint16_t code_b0[0x80]; // Continuous controller (GM 1.0 + GS)
uint16_t code_c0[1]; // patch change
uint16_t code_d0[1]; // channel pressure (after-touch)
uint16_t code_e0[2]; // pitch bend
//uint16_t code_f0-ff // system messages
uint16_t code_rpn_coarse[3]; // registered parameter numbers (GM 1.0)
uint16_t code_rpn_fine[3]; // registered parameter numbers (GM 1.0)
} midi_state[16];
/* Include different midi drivers, lowest ones get checked first for default */
#if C_MT32
#include "midi_mt32.h"
#endif
#if C_FLUIDSYNTH || defined(WIN32) && !defined(HX_DOS)
#include "midi_synth.h"
#endif
#if !defined(WIN32)
# include "midi_timidity.h"
#endif
#if defined(MACOSX)
#include "midi_coremidi.h"
#include "midi_coreaudio.h"
#elif defined (WIN32)
#include "midi_win32.h"
#else
#include "midi_oss.h"
#endif
#if defined (HAVE_ALSA)
#include "midi_alsa.h"
#endif
//#define WIN32_MIDI_STATE_DEBUG
void MIDI_State_Reset()
{
memset( &midi_state, 0xff, sizeof(midi_state) );
midi_state[0].init = true;
midi_state[0].ignore = false;
}
void MIDI_State_SaveMessage()
{
uint8_t channel, command, arg1, arg2;
if( midi_state[0].init == false ) {
MIDI_State_Reset();
}
if( midi_state[0].ignore == true ) return;
// skip MUNT
//if( strcmp( midi.handler->GetName(), "mt32" ) == 0 ) {
// return;
//}
channel = midi.cmd_buf[0] & 0xf;
command = midi.cmd_buf[0] >> 4;
arg1 = midi.cmd_buf[1];
arg2 = midi.cmd_buf[2];
switch( command ) {
case 0x8: // Note off
// - arg1 = note, arg2 = velocity off
midi_state[channel].code_80[arg1] = arg2;
memset( &midi_state[channel].code_90[arg1], 0xff, sizeof(midi_state[channel].code_90[arg1]) );
memset( &midi_state[channel].code_a0[arg1], 0xff, sizeof(midi_state[channel].code_a0[arg1]) );
memset( midi_state[channel].code_d0, 0xff, sizeof(midi_state[channel].code_d0) );
break;
case 0x9: // Note on
if( arg2 > 0 ) {
// - arg1 = note, arg2 = velocity on
midi_state[channel].code_90[arg1] = arg2;
memset( &midi_state[channel].code_80[arg1], 0xff, sizeof(midi_state[channel].code_80[arg1]) );
}
else {
// velocity = 0 (note off)
midi_state[channel].code_80[arg1] = arg2;
memset( &midi_state[channel].code_90[arg1], 0xff, sizeof(midi_state[channel].code_90[arg1]) );
memset( &midi_state[channel].code_a0[arg1], 0xff, sizeof(midi_state[channel].code_a0[arg1]) );
memset( midi_state[channel].code_d0, 0xff, sizeof(midi_state[channel].code_d0) );
}
break;
case 0xA: // Aftertouch (polyphonic pressure)
midi_state[channel].code_a0[arg1] = arg2;
break;
case 0xB: // Controller #s
midi_state[channel].code_b0[arg1] = arg2;
switch( arg1 ) {
// General Midi 1.0
case 0x01: break; // Modulation
case 0x07: break; // Volume
case 0x0A: break; // Pan
case 0x0B: break; // Expression
case 0x40: break; // Sustain pedal
case 0x64: break; // Registered Parameter Number (RPN) LSB
case 0x65: break; // Registered Parameter Number (RPN) MSB
case 0x79: // All controllers off
/*
(likely GM1+GM2)
Set Expression (#11) to 127
Set Modulation (#1) to 0
Set Pedals (#64, #65, #66, #67) to 0
Set Registered and Non-registered parameter number LSB and MSB (#98-#101) to null value (127)
Set pitch bender to center (64/0)
Reset channel pressure to 0
Reset polyphonic pressure for all notes to 0.
*/
memset( midi_state[channel].code_a0, 0xff, sizeof(midi_state[channel].code_a0) );
memset( midi_state[channel].code_c0, 0xff, sizeof(midi_state[channel].code_c0) );
memset( midi_state[channel].code_d0, 0xff, sizeof(midi_state[channel].code_d0) );
memset( midi_state[channel].code_e0, 0xff, sizeof(midi_state[channel].code_e0) );
memset( midi_state[channel].code_b0+0x01, 0xff, sizeof(midi_state[channel].code_b0[0x01]) );
memset( midi_state[channel].code_b0+0x0b, 0xff, sizeof(midi_state[channel].code_b0[0x0b]) );
memset( midi_state[channel].code_b0+0x40, 0xff, sizeof(midi_state[channel].code_b0[0x40]) );
memset( midi_state[channel].code_rpn_coarse, 0xff, sizeof(midi_state[channel].code_rpn_coarse) );
memset( midi_state[channel].code_rpn_fine, 0xff, sizeof(midi_state[channel].code_rpn_fine) );
/*
(likely GM1+GM2)
Do NOT reset Bank Select (#0/#32)
Do NOT reset Volume (#7)
Do NOT reset Pan (#10)
Do NOT reset Program Change
Do NOT reset Effect Controllers (#91-#95) (5B-5F)
Do NOT reset Sound Controllers (#70-#79) (46-4F)
Do NOT reset other channel mode messages (#120-#127) (78-7F)
Do NOT reset registered or non-registered parameters.
*/
//Intentional fall-through
case 0x7b: // All notes off
memset( midi_state[channel].code_80, 0xff, sizeof(midi_state[channel].code_80) );
memset( midi_state[channel].code_90, 0xff, sizeof(midi_state[channel].code_90) );
break;
//******************************************
//******************************************
//******************************************
// Roland GS
case 0x00: break; // Bank select MSB
case 0x05: break; // Portamento time
case 0x20: break; // Bank select LSB
case 0x41: break; // Portamento
case 0x42: break; // Sostenuto
case 0x43: break; // Soft pedal
case 0x54: break; // Portamento control
case 0x5B: break; // Effect 1 (Reverb) send level
case 0x5D: break; // Effect 3 (Chorus) send level
case 0x5E: break; // Effect 4 (Variation) send level
// TODO: Add more as needed
case 0x62: break; // NRPN LSB
case 0x63: break; // NRPN MSB
case 0x78: // All sounds off
memset( midi_state[channel].code_80, 0xff, sizeof(midi_state[channel].code_80) );
memset( midi_state[channel].code_90, 0xff, sizeof(midi_state[channel].code_90) );
break;
/*
// Roland MT-32
case 0x64:
parts[part]->setRPNLSB(velocity);
break;
case 0x65:
parts[part]->setRPNMSB(velocity);
break;
case 0x7B: // All notes off
//printDebug("All notes off");
parts[part]->allNotesOff();
break;
case 0x7C:
case 0x7D:
case 0x7E:
case 0x7F:
// CONFIRMED:Mok: A real LAPC-I responds to these controllers as follows:
parts[part]->setHoldPedal(false);
parts[part]->allNotesOff();
break;
*/
//******************************************
//******************************************
//******************************************
/*
(LSB,MSB) RPN
GM 1.0
00,00 = Pitch bend range (fine + coarse)
01,00 = Channel Fine tuning
02,00 = Channel Coarse tuning
7F,7F = None
*/
case 0x06: // Data entry (coarse)
{
int rpn;
rpn = midi_state[channel].code_b0[0x64];
rpn |= (midi_state[channel].code_b0[0x65]) << 8;
// GM 1.0 = 0-2
if( rpn >= 3 ) break;
//if( rpn == 0x7f7f ) break;
midi_state[channel].code_rpn_coarse[rpn] = arg2;
}
break;
case 0x26: // Data entry (fine)
{
int rpn;
rpn = midi_state[channel].code_b0[0x64];
rpn |= (midi_state[channel].code_b0[0x65]) << 8;
// GM 1.0 = 0-2
if( rpn >= 3 ) break;
//if( rpn == 0x7f7f ) break;
midi_state[channel].code_rpn_fine[rpn] = arg2;
}
break;
default: // unsupported
break;
}
break;
case 0xC: // Patch change
midi_state[channel].code_c0[0] = arg1;
break;
case 0xD: // Channel pressure (aftertouch)
midi_state[channel].code_d0[0] = arg1;
break;
case 0xE: // Pitch bend
midi_state[channel].code_e0[0] = arg1;
midi_state[channel].code_e0[1] = arg2;
break;
case 0xF: // System messages
// TODO: General Midi 1.0 says 'Master Volume' SysEx
break;
// unhandled case
default:
break;
}
}
void MIDI_RawOutByte(uint8_t);
void MIDI_State_LoadMessage()
{
if( midi_state[0].init == false ) {
MIDI_State_Reset();
}
midi_state[0].ignore = true;
// flush data buffer - throw invalid midi message
MIDI_RawOutByte(0xf7);
// shutdown sound
for( int lcv=0; lcv<16; lcv++ ) {
MIDI_RawOutByte( 0xb0+lcv );
MIDI_RawOutByte( 0x78 );
MIDI_RawOutByte( 0x00 );
MIDI_RawOutByte( 0x79 );
MIDI_RawOutByte( 0x00 );
MIDI_RawOutByte( 0x7b );
MIDI_RawOutByte( 0x00 );
}
for( int lcv=0; lcv<16; lcv++ ) {
// control states (bx - 00-5F)
MIDI_RawOutByte( 0xb0+lcv );
for( int lcv2=0; lcv2<0x80; lcv2++ ) {
if( lcv2 >= 0x60 ) break;
if( midi_state[lcv].code_b0[lcv2] == 0xffff ) continue;
// RPN data - coarse / fine
if( lcv2 == 0x06 ) continue;
if( lcv2 == 0x26 ) continue;
//MIDI_RawOutByte( 0xb0+lcv );
MIDI_RawOutByte( lcv2 );
MIDI_RawOutByte( midi_state[lcv].code_b0[lcv2] & 0xff );
}
// control states - RPN data (GM 1.0)
for( int lcv2=0; lcv2<3; lcv2++ ) {
if( midi_state[lcv].code_rpn_coarse[lcv2] == 0xffff &&
midi_state[lcv].code_rpn_fine[lcv2] == 0xffff ) continue;
//MIDI_RawOutByte( 0xb0+lcv );
MIDI_RawOutByte( 0x64 );
MIDI_RawOutByte( lcv2 );
MIDI_RawOutByte( 0x65 );
MIDI_RawOutByte( 0x00 );
//MIDI_RawOutByte( 0xb0+lcv );
if( midi_state[lcv].code_rpn_coarse[lcv2] != 0xffff ) {
MIDI_RawOutByte( 0x06 );
MIDI_RawOutByte( midi_state[lcv].code_rpn_coarse[lcv2] & 0xff );
}
if( midi_state[lcv].code_rpn_fine[lcv2] != 0xffff ) {
MIDI_RawOutByte( 0x26 );
MIDI_RawOutByte( midi_state[lcv].code_rpn_fine[lcv2] & 0xff );
}
//MIDI_RawOutByte( 0xb0+lcv );
MIDI_RawOutByte( 0x64 );
MIDI_RawOutByte( 0x7f );
MIDI_RawOutByte( 0x65 );
MIDI_RawOutByte( 0x7f );
}
// program change
if( midi_state[lcv].code_c0[0] != 0xffff ) {
MIDI_RawOutByte( 0xc0+lcv );
MIDI_RawOutByte( midi_state[lcv].code_c0[0]&0xff );
}
// pitch wheel change
if( midi_state[lcv].code_e0[0] != 0xffff ) {
MIDI_RawOutByte( 0xe0+lcv );
MIDI_RawOutByte( midi_state[lcv].code_e0[0]&0xff );
MIDI_RawOutByte( midi_state[lcv].code_e0[1]&0xff );
}
//******************************************
//******************************************
//******************************************
// note on
MIDI_RawOutByte( 0x90+lcv );
for( int lcv2=0; lcv2<0x80; lcv2++ ) {
if( midi_state[lcv].code_90[lcv2] == 0xffff ) continue;
//MIDI_RawOutByte( 0x90+lcv );
MIDI_RawOutByte( lcv2 );
MIDI_RawOutByte( midi_state[lcv].code_90[lcv2]&0xff );
}
// polyphonic aftertouch
MIDI_RawOutByte( 0xa0+lcv );
for( int lcv2=0; lcv2<0x80; lcv2++ ) {
if( midi_state[lcv].code_a0[lcv2] == 0xffff ) continue;
//MIDI_RawOutByte( 0xa0+lcv );
MIDI_RawOutByte( lcv2 );
MIDI_RawOutByte( midi_state[lcv].code_a0[lcv2]&0xff );
}
// channel aftertouch
if( midi_state[lcv].code_d0[0] != 0xffff ) {
MIDI_RawOutByte( 0xd0+lcv );
MIDI_RawOutByte( midi_state[lcv].code_d0[0]&0xff );
}
}
midi_state[0].ignore = false;
}
void MIDI_RawOutByte(uint8_t data) {
if (midi.sysex.start) {
uint32_t passed_ticks = GetTicks() - midi.sysex.start;
if (passed_ticks < midi.sysex.delay) SDL_Delay((Uint32)(midi.sysex.delay - passed_ticks));
}
/* Test for a realtime MIDI message */
if (data>=0xf8) {
midi.rt_buf[0]=data;
midi.handler->PlayMsg(midi.rt_buf);
return;
}
/* Test for a active sysex transfer */
if (midi.status==0xf0) {
if (!(data&0x80)) {
if (midi.sysex.used<(SYSEX_SIZE-1)) midi.sysex.buf[midi.sysex.used++] = data;
return;
} else {
midi.sysex.buf[midi.sysex.used++] = 0xf7;
if ((midi.sysex.start) && (midi.sysex.used >= 4) && (midi.sysex.used <= 9) && (midi.sysex.buf[1] == 0x41) && (midi.sysex.buf[3] == 0x16)) {
LOG(LOG_ALL,LOG_ERROR)("MIDI:Skipping invalid MT-32 SysEx midi message (too short to contain a checksum)");
} else {
// LOG(LOG_ALL,LOG_NORMAL)("Play sysex; address:%02X %02X %02X, length:%4d, delay:%3d", midi.sysex.buf[5], midi.sysex.buf[6], midi.sysex.buf[7], midi.sysex.used, midi.sysex.delay);
midi.handler->PlaySysex(midi.sysex.buf, midi.sysex.used);
if (midi.sysex.start) {
if (midi.sysex.buf[5] == 0x7F) {
midi.sysex.delay = 290; // All Parameters reset
} else if (midi.sysex.buf[5] == 0x10 && midi.sysex.buf[6] == 0x00 && midi.sysex.buf[7] == 0x04) {
midi.sysex.delay = 145; // Viking Child
} else if (midi.sysex.buf[5] == 0x10 && midi.sysex.buf[6] == 0x00 && midi.sysex.buf[7] == 0x01) {
midi.sysex.delay = 30; // Dark Sun 1
} else midi.sysex.delay = (Bitu)(((float)(midi.sysex.used) * 1.25f) * 1000.0f / 3125.0f) + 2;
midi.sysex.start = GetTicks();
}
}
LOG(LOG_ALL,LOG_NORMAL)("Sysex message size %d",(int)midi.sysex.used);
if (CaptureState & CAPTURE_MIDI) {
CAPTURE_AddMidi( true, midi.sysex.used-1, &midi.sysex.buf[1]);
}
}
}
if (data&0x80) {
midi.status=data;
midi.cmd_pos=0;
midi.cmd_len=MIDI_evt_len[data];
if (midi.status==0xf0) {
midi.sysex.buf[0]=0xf0;
midi.sysex.used=1;
}
}
if (midi.cmd_len) {
midi.cmd_buf[midi.cmd_pos++]=data;
if (midi.cmd_pos >= midi.cmd_len) {
if (CaptureState & CAPTURE_MIDI) {
CAPTURE_AddMidi(false, midi.cmd_len, midi.cmd_buf);
}
midi.handler->PlayMsg(midi.cmd_buf);
midi.cmd_pos=1; //Use Running status
MIDI_State_SaveMessage();
}
}
}
bool MIDI_Available(void) {
return midi.available;
}
class MIDI:public Module_base{
public:
MIDI(Section* configuration):Module_base(configuration){
Section_prop * section = static_cast<Section_prop *>(configuration);
const char * dev=section->Get_string("mididevice");
std::string fullconf = section->Get_string("midiconfig");
#if C_FLUIDSYNTH || defined(WIN32) && !defined(HX_DOS)
synthsamplerate = section->Get_int("samplerate");
if (synthsamplerate == 0) synthsamplerate = 44100;
#endif
if (control->opt_silent) dev = "none";
/* If device = "default" go for first handler that works */
MidiHandler * handler;
bool opened = false;
// MAPPER_AddHandler(MIDI_SaveRawEvent,MK_f8,MMOD1|MMOD2,"caprawmidi","Cap MIDI");
midi.sysex.delay = 0;
midi.sysex.start = 0;
if (fullconf.find("delaysysex") != std::string::npos) {
midi.sysex.start = GetTicks();
fullconf.erase(fullconf.find("delaysysex"));
LOG(LOG_MISC,LOG_DEBUG)("MIDI:Using delayed SysEx processing");
}
trim(fullconf);
const char * conf = fullconf.c_str();
midi.status=0x00;
midi.cmd_pos=0;
midi.cmd_len=0;
if (strcasecmp(dev,"default")) {
for (handler = handler_list; handler; handler = handler->next) {
if (!strcasecmp(dev,handler->GetName())) {
opened = handler->Open(conf);
break;
}
}
if (handler == NULL)
LOG_MSG("MIDI:Cannot find device:%s. Finding default handler.",dev);
else if (!opened)
LOG_MSG("MIDI:Cannot open device:%s with config:%s. Finding default handler.",dev,conf);
}
if (!opened) {
for (handler = handler_list; handler; handler = handler->next) {
opened = handler->Open(conf);
if (opened) break;
}
}
if (!opened) {
// This shouldn't be possible
LOG_MSG("MIDI:Could not open a handler");
return;
}
midi.available=true;
midi.handler=handler;
LOG_MSG("MIDI:Opened device:%s",handler->GetName());
// force reset to prevent crashes (when not properly shutdown)
// ex. Roland VSC = unexpected hard system crash
midi_state[0].init = false;
MIDI_State_LoadMessage();
}
~MIDI(){
if( midi.status < 0xf0 ) {
// throw invalid midi message - start new cmd
MIDI_RawOutByte(0x80);
}
else if( midi.status == 0xf0 ) {
// SysEx - throw invalid midi message
MIDI_RawOutByte(0xf7);
}
if(midi.available) midi.handler->Close();
midi.available = false;
midi.handler = 0;
}
};
void MIDI_ListHandler(Program *caller, const char *name)
{
if (!*name) return;
bool found=false;
for (auto *handler = handler_list; handler; handler = handler->next)
if (!strcasecmp(handler->GetName(), name)) {
handler->ListAll(caller);
found=true;
break;
}
if (!found) caller->WriteOut("MIDI handler not available - %s\n", name);
}
static MIDI* test = NULL;
void MIDI_Destroy(Section* /*sec*/){
if (test != NULL) {
delete test;
test = NULL;
}
}
void MIDI_Init() {
LOG(LOG_MISC,LOG_DEBUG)("Initializing MIDI emulation");
test = new MIDI(control->GetSection("midi"));
AddExitFunction(AddExitFunctionFuncPair(MIDI_Destroy),true);
}
void MIDI_GUI_OnSectionPropChange(Section *x) {
(void)x;//UNUSED
if (test != NULL) {
delete test;
test = NULL;
}
test = new MIDI(control->GetSection("midi"));
}
//save state support
namespace {
class SerializeMidi : public SerializeGlobalPOD {
public:
SerializeMidi() : SerializeGlobalPOD("Midi")
{}
private:
virtual void getBytes(std::ostream& stream)
{
if( !test ) return;
//******************************************
//******************************************
//******************************************
SerializeGlobalPOD::getBytes(stream);
// - pure data
WRITE_POD( &midi.status, midi.status );
WRITE_POD( &midi.cmd_len, midi.cmd_len );
WRITE_POD( &midi.cmd_pos, midi.cmd_pos );
WRITE_POD( &midi.cmd_buf, midi.cmd_buf );
WRITE_POD( &midi.rt_buf, midi.rt_buf );
WRITE_POD( &midi.sysex, midi.sysex );
//*******************************************
//*******************************************
//*******************************************
// Supports MT-32 MUNT only!!
if(0) {
}
// external MIDI
else if( midi.available ) {
const char pod_name[32] = "External";
WRITE_POD( &pod_name, pod_name );
//*******************************************
//*******************************************
//*******************************************
// - pure data
WRITE_POD( &midi_state, midi_state );
}
}
virtual void setBytes(std::istream& stream)
{
if( !test ) return;
//******************************************
//******************************************
//******************************************
SerializeGlobalPOD::setBytes(stream);
// - pure data
READ_POD( &midi.status, midi.status );
READ_POD( &midi.cmd_len, midi.cmd_len );
READ_POD( &midi.cmd_pos, midi.cmd_pos );
READ_POD( &midi.cmd_buf, midi.cmd_buf );
READ_POD( &midi.rt_buf, midi.rt_buf );
READ_POD( &midi.sysex, midi.sysex );
//*******************************************
//*******************************************
//*******************************************
// Supports MT-32 MUNT only!!
if(0) {
}
// external MIDI
else if( midi.available ) {
char pod_name[32] = {0};
// error checking
READ_POD( &pod_name, pod_name );
if( strcmp( pod_name, "External" ) ) {
stream.clear( std::istream::failbit | std::istream::badbit );
return;
}
//************************************************
//************************************************
//************************************************
// - pure data
READ_POD( &midi_state, midi_state );
//************************************************
//************************************************
//************************************************
MIDI_State_LoadMessage();
}
}
} dummy;
}