/* * Copyright (C) 2002-2019 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 #include #include #include #include #include "SDL.h" #include "dosbox.h" #include "midi.h" #include "cross.h" #include "support.h" #include "setup.h" #include "mapper.h" #include "pic.h" #include "hardware.h" #include "timer.h" #define RAWBUF 1024 Bit8u 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; /* Include different midi drivers, lowest ones get checked first for default. Each header provides an independent midi interface. */ #if defined(MACOSX) #if defined(C_SUPPORTS_COREMIDI) #include "midi_coremidi.h" #endif #if defined(C_SUPPORTS_COREAUDIO) #include "midi_coreaudio.h" #endif #elif defined (WIN32) #include "midi_win32.h" #else #include "midi_oss.h" #endif #if defined (HAVE_ALSA) #include "midi_alsa.h" #endif DB_Midi midi; void MIDI_RawOutByte(Bit8u data) { if (midi.sysex.start) { Bit32u passed_ticks = GetTicks() - midi.sysex.start; if (passed_ticks < midi.sysex.delay) SDL_Delay(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 tranfer */ 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", static_cast(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 } } } bool MIDI_Available(void) { return midi.available; } class MIDI:public Module_base{ public: MIDI(Section* configuration):Module_base(configuration){ Section_prop * section=static_cast(configuration); const char * dev=section->Get_string("mididevice"); std::string fullconf=section->Get_string("midiconfig"); /* If device = "default" go for first handler that works */ MidiHandler * handler; // 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_MSG("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")) goto getdefault; handler=handler_list; while (handler) { if (!strcasecmp(dev,handler->GetName())) { if (!handler->Open(conf)) { LOG_MSG("MIDI: Can't open device:%s with config:%s.",dev,conf); goto getdefault; } midi.handler=handler; midi.available=true; LOG_MSG("MIDI: Opened device:%s",handler->GetName()); return; } handler=handler->next; } LOG_MSG("MIDI: Can't find device:%s, finding default handler.",dev); getdefault: handler=handler_list; while (handler) { if (handler->Open(conf)) { midi.available=true; midi.handler=handler; LOG_MSG("MIDI: Opened device:%s",handler->GetName()); return; } handler=handler->next; } /* This shouldn't be possible */ } ~MIDI(){ if(midi.available) midi.handler->Close(); midi.available = false; midi.handler = 0; } }; static MIDI* test; void MIDI_Destroy(Section* /*sec*/){ delete test; } void MIDI_Init(Section * sec) { test = new MIDI(sec); sec->AddDestroyFunction(&MIDI_Destroy,true); }