diff options
author | Ludvig Strigeus | 2002-04-13 18:34:11 +0000 |
---|---|---|
committer | Ludvig Strigeus | 2002-04-13 18:34:11 +0000 |
commit | 9f191ea9c5882f59cd23adbcbaa4a1c704909573 (patch) | |
tree | f2ddc0e916248a56c82418b8346569bafd6e915a /sound | |
parent | 38628f3543cf16d23968f541e4a35c95c2e17f13 (diff) | |
download | scummvm-rg350-9f191ea9c5882f59cd23adbcbaa4a1c704909573.tar.gz scummvm-rg350-9f191ea9c5882f59cd23adbcbaa4a1c704909573.tar.bz2 scummvm-rg350-9f191ea9c5882f59cd23adbcbaa4a1c704909573.zip |
new midi driver API,
no more USE_ADLIB,
a couple of sdl graphics driver fixes.
svn-id: r3925
Diffstat (limited to 'sound')
-rw-r--r-- | sound/adlib.cpp | 14 | ||||
-rw-r--r-- | sound/gmidi.cpp | 412 | ||||
-rw-r--r-- | sound/gmidi.h | 106 | ||||
-rw-r--r-- | sound/imuse.cpp | 26 | ||||
-rw-r--r-- | sound/mididrv.cpp | 689 |
5 files changed, 821 insertions, 426 deletions
diff --git a/sound/adlib.cpp b/sound/adlib.cpp index de550d3361..9700d99255 100644 --- a/sound/adlib.cpp +++ b/sound/adlib.cpp @@ -23,7 +23,6 @@ #include "scumm.h" #include "fmopl.h" -#if defined USE_ADLIB static byte lookup_table[64][32]; const byte volume_table[] = { @@ -643,7 +642,7 @@ void AdlibSoundDriver::mc_init_stuff(MidiChannelAdl *mc, Struct10 * s10, s11->s10->unk3 = 0; break; default: - s10->start_value = part->_drv->adlib_read_param(mc->_channel, s11->param); + s10->start_value = ((AdlibSoundDriver*)part->_drv)->adlib_read_param(mc->_channel, s11->param); } struct10_init(s10, ie); @@ -661,18 +660,18 @@ void AdlibSoundDriver::mc_inc_stuff(MidiChannelAdl *mc, Struct10 * s10, switch (s11->param) { case 0: mc->_vol_2 = s10->start_value + s11->modify_val; - part->_drv->adlib_set_param(mc->_channel, 0, + ((AdlibSoundDriver*)part->_drv)->adlib_set_param(mc->_channel, 0, volume_table[lookup_table[mc->_vol_2] [part->_vol_eff >> 2]]); break; case 13: mc->_vol_1 = s10->start_value + s11->modify_val; if (mc->_twochan) { - part->_drv->adlib_set_param(mc->_channel, 13, + ((AdlibSoundDriver*)part->_drv)->adlib_set_param(mc->_channel, 13, volume_table[lookup_table[mc->_vol_1] [part->_vol_eff >> 2]]); } else { - part->_drv->adlib_set_param(mc->_channel, 13, mc->_vol_1); + ((AdlibSoundDriver*)part->_drv)->adlib_set_param(mc->_channel, 13, mc->_vol_1); } break; case 30: @@ -682,14 +681,14 @@ void AdlibSoundDriver::mc_inc_stuff(MidiChannelAdl *mc, Struct10 * s10, s11->s10->unk3 = (char)s11->modify_val; break; default: - part->_drv->adlib_set_param(mc->_channel, s11->param, + ((AdlibSoundDriver*)part->_drv)->adlib_set_param(mc->_channel, s11->param, s10->start_value + s11->modify_val); break; } } if (code & 2 && s11->flag0x10) - part->_drv->adlib_key_onoff(mc->_channel); + ((AdlibSoundDriver*)part->_drv)->adlib_key_onoff(mc->_channel); } void AdlibSoundDriver::part_changed(Part *part, byte what) @@ -988,4 +987,3 @@ int AdlibSoundDriver::part_update_active(Part *part, uint16 *active) return count; } -#endif diff --git a/sound/gmidi.cpp b/sound/gmidi.cpp index 9fea3c0472..ecd4e560c1 100644 --- a/sound/gmidi.cpp +++ b/sound/gmidi.cpp @@ -19,393 +19,20 @@ * $Header$ */ -/* - * Timidity support by Lionel Ulmer <lionel.ulmer@free.fr> - * QuickTime support by Florent Boudet <flobo@ifrance.com> - * Raw output support by Michael Pearce - * MorphOS support by Ruediger Hanke - */ - -#ifdef __MORPHOS__ -#include <devices/timer.h> -#endif - - #include "stdafx.h" #include "scumm.h" #include "gmidi.h" -void MidiSoundDriver::midiSetDriver(int devicetype) -{ - _midi_driver.DeviceType = devicetype; - _midi_driver.midiInit(); -} - -void MidiDriver::midiInit() -{ - if (MidiInitialized != true) { - switch (DeviceType) { - case MIDI_NULL: - midiInitNull(); - break; - case MIDI_WINDOWS: - midiInitWindows(); - break; - case MIDI_TIMIDITY: - midiInitTimidity(); - break; - case MIDI_SEQ: - midiInitSeq(); - break; - case MIDI_QTMUSIC: - midiInitQuicktime(); - break; - case MIDI_AMIDI: - break; - default: - DeviceType = 0; - midiInitNull(); - break; - } - MidiInitialized = true; - } else { - error("Midi driver already initialized"); - } -} - -void MidiDriver::MidiOut(int b) -{ - if (MidiInitialized != true) - midiInit(); - - if (MidiInitialized == true) { - switch (DeviceType) { - case MIDI_NULL: - break; - case MIDI_WINDOWS: - MidiOutWindows(_mo, b); - break; - case MIDI_TIMIDITY: - case MIDI_SEQ: - MidiOutSeq(_mo, b); - break; - case MIDI_QTMUSIC: - MidiOutQuicktime(_mo, b); - break; - case MIDI_AMIDI: - MidiOutMorphOS(_mo, b); - break; - default: - error("Invalid midi device type "); - break; - } - } else { - warning("Trying to write midi data without the driver being initialized"); - } -} - -/*********** Windows */ -void MidiDriver::midiInitWindows() +void MidiSoundDriver::midiSetDriver(MidiDriver *driver) { -#ifdef WIN32 - if (midiOutOpen((HMIDIOUT *) & _mo, MIDI_MAPPER, NULL, NULL, 0) != - MMSYSERR_NOERROR) - error("midiOutOpen failed"); -#endif -} + _md = driver; -void MidiDriver::MidiOutWindows(void *a, int b) -{ -#ifdef WIN32 - midiOutShortMsg((HMIDIOUT) a, b); -#endif -} - -/*********** Raw midi support */ -void MidiDriver::midiInitSeq() -{ - int device = open_sequencer_device(); - _mo = (void *)device; + int result = _md->open(MidiDriver::MO_SIMPLE); + if (result) + error("MidiSoundDriver::error = %s", MidiDriver::get_error_name(result)); } -int MidiDriver::open_sequencer_device() -{ - int device = 0; -#if !defined(__APPLE__CW) // No getenv support on Apple Carbon - char *device_name = getenv("SCUMMVM_MIDI"); - if (device_name != NULL) { - device = (open((device_name), O_RDWR, 0)); - } else { - warning - ("You need to set-up the SCUMMVM_MIDI environment variable properly (see readme.txt) "); - } - if ((device_name == NULL) || (device < 0)) { - if (device_name == NULL) - warning("Opening /dev/null (no music will be heard) "); - else - warning - ("Cannot open rawmidi device %s - using /dev/null (no music will be heard) ", - device_name); - device = (open(("/dev/null"), O_RDWR, 0)); - if (device < 0) - error("Cannot open /dev/null to dump midi output"); - } -#endif - return device; -} - -/*********** Timidity */ -int MidiDriver::connect_to_timidity(int port) -{ - int s = 0; -#if !defined(__APPLE__CW) && !defined(__MORPHOS__) // No socket support on Apple Carbon or Morphos - struct hostent *serverhost; - struct sockaddr_in sadd; - - serverhost = gethostbyname("localhost"); - if (serverhost == NULL) - error("Could not resolve Timidity host ('localhost')"); - - sadd.sin_family = serverhost->h_addrtype; - sadd.sin_port = htons(port); - memcpy(&(sadd.sin_addr), serverhost->h_addr_list[0], serverhost->h_length); - - s = socket(AF_INET, SOCK_STREAM, 0); - if (s < 0) - error("Could not open Timidity socket"); - - if (connect(s, (struct sockaddr *)&sadd, sizeof(struct sockaddr_in)) < 0) - error("Could not connect to Timidity server"); -#endif - return s; -} - -void MidiDriver::midiInitTimidity() -{ - int s, s2; - int len; - int dummy, newport; - char buf[256]; - - s = connect_to_timidity(7777); - len = read(s, buf, 256); // buf[len] = '\0'; printf("%s", buf); - sprintf(buf, "SETBUF %f %f\n", 0.1, 0.15); - write(s, buf, strlen(buf)); - len = read(s, buf, 256); // buf[len] = '\0'; printf("%s", buf); - - sprintf(buf, "OPEN lsb\n"); - write(s, buf, strlen(buf)); - len = read(s, buf, 256); // buf[len] = '\0'; printf("%s", buf); - - sscanf(buf, "%d %d", &dummy, &newport); - printf(" => port = %d\n", newport); - - s2 = connect_to_timidity(newport); - _mo = (void *)s2; -} - -void MidiDriver::MidiOutSeq(void *a, int b) -{ - int s = (int)a; - unsigned char buf[256]; - int position = 0; - - switch (b & 0xF0) { - case 0x80: - case 0x90: - case 0xA0: - case 0xB0: - case 0xE0: - buf[position++] = SEQ_MIDIPUTC; - buf[position++] = b; - buf[position++] = DEVICE_NUM; - buf[position++] = 0; - buf[position++] = SEQ_MIDIPUTC; - buf[position++] = (b >> 8) & 0x7F; - buf[position++] = DEVICE_NUM; - buf[position++] = 0; - buf[position++] = SEQ_MIDIPUTC; - buf[position++] = (b >> 16) & 0x7F; - buf[position++] = DEVICE_NUM; - buf[position++] = 0; - break; - case 0xC0: - case 0xD0: - buf[position++] = SEQ_MIDIPUTC; - buf[position++] = b; - buf[position++] = DEVICE_NUM; - buf[position++] = 0; - buf[position++] = SEQ_MIDIPUTC; - buf[position++] = (b >> 8) & 0x7F; - buf[position++] = DEVICE_NUM; - buf[position++] = 0; - break; - default: - fprintf(stderr, "Unknown : %08x\n", b); - break; - } - write(s, buf, position); -} - -/* Quicktime music support */ -void MidiDriver::midiInitQuicktime() -{ -#ifdef __APPLE__CW - ComponentResult qtErr = noErr; - qtNoteAllocator = NULL; - - for (int i = 0; i < 15; i++) - qtNoteChannel[i] = NULL; - - qtNoteAllocator = OpenDefaultComponent(kNoteAllocatorComponentType, 0); - if (qtNoteAllocator == NULL) - goto bail; - - simpleNoteRequest.info.flags = 0; - *(short *)(&simpleNoteRequest.info.polyphony) = EndianS16_NtoB(15); // simultaneous tones - *(Fixed *) (&simpleNoteRequest.info.typicalPolyphony) = - EndianU32_NtoB(0x00010000); - - qtErr = NAStuffToneDescription(qtNoteAllocator, 1, &simpleNoteRequest.tone); - if (qtErr != noErr) - goto bail; - - for (int i = 0; i < 15; i++) { - qtErr = - NANewNoteChannel(qtNoteAllocator, &simpleNoteRequest, - &(qtNoteChannel[i])); - if ((qtErr != noErr) || (qtNoteChannel == NULL)) - goto bail; - } - return; - -bail: - fprintf(stderr, "Init QT failed %x %x %d\n", qtNoteAllocator, qtNoteChannel, - qtErr); - for (int i = 0; i < 15; i++) { - if (qtNoteChannel[i] != NULL) - NADisposeNoteChannel(qtNoteAllocator, qtNoteChannel[i]); - } - - if (qtNoteAllocator != NULL) - CloseComponent(qtNoteAllocator); -#endif -} - -void MidiDriver::MidiOutQuicktime(void *a, int b) -{ -#ifdef __APPLE__CW - MusicMIDIPacket midPacket; - unsigned char *midiCmd = midPacket.data; - midPacket.length = 3; - midiCmd[3] = (b & 0xFF000000) >> 24; - midiCmd[2] = (b & 0x00FF0000) >> 16; - midiCmd[1] = (b & 0x0000FF00) >> 8; - midiCmd[0] = b; - - unsigned char chanID = midiCmd[0] & 0x0F; - switch (midiCmd[0] & 0xF0) { - case 0x80: // Note off - NAPlayNote(qtNoteAllocator, qtNoteChannel[chanID], midiCmd[1], 0); - break; - - case 0x90: // Note on - NAPlayNote(qtNoteAllocator, qtNoteChannel[chanID], midiCmd[1], - midiCmd[2]); - break; - - case 0xB0: // Effect - switch (midiCmd[1]) { - case 0x01: // Modulation - NASetController(qtNoteAllocator, qtNoteChannel[chanID], - kControllerModulationWheel, midiCmd[2] << 8); - break; - - case 0x07: // Volume - NASetController(qtNoteAllocator, qtNoteChannel[chanID], - kControllerVolume, midiCmd[2] * 300); - break; - - case 0x0A: // Pan - NASetController(qtNoteAllocator, qtNoteChannel[chanID], kControllerPan, - (midiCmd[2] << 1) + 0xFF); - break; - - case 0x40: // Sustain on/off - NASetController(qtNoteAllocator, qtNoteChannel[chanID], - kControllerSustain, midiCmd[2]); - break; - - case 0x5b: // ext effect depth - NASetController(qtNoteAllocator, qtNoteChannel[chanID], - kControllerReverb, midiCmd[2] << 8); - break; - - case 0x5d: // chorus depth - NASetController(qtNoteAllocator, qtNoteChannel[chanID], - kControllerChorus, midiCmd[2] << 8); - break; - - case 0x7b: // mode message all notes off - for (int i = 0; i < 128; i++) - NAPlayNote(qtNoteAllocator, qtNoteChannel[chanID], i, 0); - break; - - default: - fprintf(stderr, "Unknown MIDI effect: %08x\n", b); - break; - } - break; - - case 0xC0: // Program change - NASetInstrumentNumber(qtNoteAllocator, qtNoteChannel[chanID], midiCmd[1]); - break; - - case 0xE0:{ // Pitch bend - long theBend = - ((((long)midiCmd[1] + (long)(midiCmd[2] << 8))) - 0x4000) / 4; - NASetController(qtNoteAllocator, qtNoteChannel[chanID], - kControllerPitchBend, theBend); - } - break; - - default: - fprintf(stderr, "Unknown Command: %08x\n", b); - NASendMIDI(qtNoteAllocator, qtNoteChannel[chanID], &midPacket); - break; - } -#endif -} - -/*********** MorphOS */ -void MidiDriver::MidiOutMorphOS(void *a, int b) -{ -#ifdef __MORPHOS__ - if (ScummMidiRequest) { - ULONG midi_data = b; // you never know about an int's size ;-) - ScummMidiRequest->amr_Std.io_Command = CMD_WRITE; - ScummMidiRequest->amr_Std.io_Data = &midi_data; - ScummMidiRequest->amr_Std.io_Length = 4; - DoIO((struct IORequest *)ScummMidiRequest); - } -#endif -} - - - - - - - -void MidiDriver::midiInitNull() -{ - warning - ("Music not enabled - MIDI support selected with no MIDI driver available. Try Adlib"); -} - - - -/************************* Common midi code **********************/ +/****************** Common midi code (SCUMM specific) *****************/ void MidiSoundDriver::midiPitchBend(byte chan, int16 pitchbend) { uint16 tmp; @@ -413,8 +40,7 @@ void MidiSoundDriver::midiPitchBend(byte chan, int16 pitchbend) if (_midi_pitchbend_last[chan] != pitchbend) { _midi_pitchbend_last[chan] = pitchbend; tmp = (pitchbend << 2) + 0x2000; - _midi_driver. - MidiOut(((tmp >> 7) & 0x7F) << 16 | (tmp & 0x7F) << 8 | 0xE0 | chan); + _md->send(((tmp >> 7) & 0x7F) << 16 | (tmp & 0x7F) << 8 | 0xE0 | chan); } } @@ -422,14 +48,14 @@ void MidiSoundDriver::midiVolume(byte chan, byte volume) { if (_midi_volume_last[chan] != volume) { _midi_volume_last[chan] = volume; - _midi_driver.MidiOut(volume << 16 | 7 << 8 | 0xB0 | chan); + _md->send(volume << 16 | 7 << 8 | 0xB0 | chan); } } void MidiSoundDriver::midiPedal(byte chan, bool pedal) { if (_midi_pedal_last[chan] != pedal) { _midi_pedal_last[chan] = pedal; - _midi_driver.MidiOut(pedal << 16 | 64 << 8 | 0xB0 | chan); + _md->send(pedal << 16 | 64 << 8 | 0xB0 | chan); } } @@ -437,7 +63,7 @@ void MidiSoundDriver::midiModWheel(byte chan, byte modwheel) { if (_midi_modwheel_last[chan] != modwheel) { _midi_modwheel_last[chan] = modwheel; - _midi_driver.MidiOut(modwheel << 16 | 1 << 8 | 0xB0 | chan); + _md->send(modwheel << 16 | 1 << 8 | 0xB0 | chan); } } @@ -445,7 +71,7 @@ void MidiSoundDriver::midiEffectLevel(byte chan, byte level) { if (_midi_effectlevel_last[chan] != level) { _midi_effectlevel_last[chan] = level; - _midi_driver.MidiOut(level << 16 | 91 << 8 | 0xB0 | chan); + _md->send(level << 16 | 91 << 8 | 0xB0 | chan); } } @@ -453,13 +79,13 @@ void MidiSoundDriver::midiChorus(byte chan, byte chorus) { if (_midi_chorus_last[chan] != chorus) { _midi_chorus_last[chan] = chorus; - _midi_driver.MidiOut(chorus << 16 | 93 << 8 | 0xB0 | chan); + _md->send(chorus << 16 | 93 << 8 | 0xB0 | chan); } } void MidiSoundDriver::midiControl0(byte chan, byte value) { - _midi_driver.MidiOut(value << 16 | 0 << 8 | 0xB0 | chan); + _md->send(value << 16 | 0 << 8 | 0xB0 | chan); } void MidiSoundDriver::midiProgram(byte chan, byte program) @@ -468,7 +94,7 @@ void MidiSoundDriver::midiProgram(byte chan, byte program) if (_se->_mt32emulate) program = mt32_to_gmidi[program]; - _midi_driver.MidiOut(program << 8 | 0xC0 | chan); + _md->send(program << 8 | 0xC0 | chan); } } @@ -476,24 +102,24 @@ void MidiSoundDriver::midiPan(byte chan, int8 pan) { if (_midi_pan_last[chan] != pan) { _midi_pan_last[chan] = pan; - _midi_driver.MidiOut(((pan - 64) & 0x7F) << 16 | 10 << 8 | 0xB0 | chan); + _md->send(((pan - 64) & 0x7F) << 16 | 10 << 8 | 0xB0 | chan); } } void MidiSoundDriver::midiNoteOn(byte chan, byte note, byte velocity) { - _midi_driver.MidiOut(velocity << 16 | note << 8 | 0x90 | chan); + _md->send(velocity << 16 | note << 8 | 0x90 | chan); } void MidiSoundDriver::midiNoteOff(byte chan, byte note) { - _midi_driver.MidiOut(note << 8 | 0x80 | chan); + _md->send(note << 8 | 0x80 | chan); } void MidiSoundDriver::midiSilence(byte chan) { - _midi_driver.MidiOut((64 << 8) | 0xB0 | chan); - _midi_driver.MidiOut((123 << 8) | 0xB0 | chan); + _md->send((64 << 8) | 0xB0 | chan); + _md->send((123 << 8) | 0xB0 | chan); } diff --git a/sound/gmidi.h b/sound/gmidi.h index 9a63151a58..7e9d7e8db5 100644 --- a/sound/gmidi.h +++ b/sound/gmidi.h @@ -6,16 +6,108 @@ #define SPECIAL_CHANNEL 9 #define DEVICE_NUM 0 -/* Sound output type - MIDI */ +struct MidiEvent { + uint32 delta; + uint32 event; +}; + +/* Lowlevel Abstract Midi Driver Class */ +class MidiDriver { + +public: + /* called whenever the midi driver is in streaming mode, + * and more midi commands need to be generated + * return 0 to tell the mididriver that the end of stream was reached + */ + typedef int StreamCallback(void *param, MidiEvent *ev, int num); + + + /* open modes, pass one of those to open() */ + enum { + MO_SIMPLE = 1, + MO_STREAMING = 2, + }; + + /* Special events that can be inserted in a MidiEvent. + * event = (ME_xxx<<24) | <24-bit data associated with event> + */ + enum { + ME_NONE = 0, + ME_TEMPO = 1, + }; + + /* error codes returned by open. + * can be converted to a string with get_error_name() + */ + enum { + MERR_CANNOT_CONNECT = 1, + MERR_STREAMING_NOT_AVAILABLE = 2, + MERR_DEVICE_NOT_AVAILABLE = 3, + MERR_ALREADY_OPEN = 4, + }; + + enum { + PROP_TIMEDIV = 1, + }; + + + /* destroy the midi object */ + virtual void destroy() = 0; + + /* open the midi driver. + * returns 0 if successful. + * otherwise an error code. */ + virtual int open(int mode) = 0; + + /* close the midi driver */ + virtual void close() = 0; + + /* output a packed midi command to the midi stream + * valid only if mode is MO_SIMPLE + */ + virtual void send(uint32 b) = 0; + + /* set callback when more streams need to be generated. + * valid only when mode==MO_STREAMING + */ + virtual void set_stream_callback(void *param, StreamCallback *sc) = 0; + + /* Pause or resume streaming MIDI */ + virtual void pause(bool pause) = 0; + + + /* Get or set a property */ + virtual uint32 property(int prop, uint32 param) = 0; + + /* retrieve a string representation of an error code */ + static const char *get_error_name(int error_code); +}; + + + +/* driver types */ enum { - MIDI_NULL = 0, - MIDI_WINDOWS = 1, - MIDI_TIMIDITY = 2, - MIDI_SEQ = 3, - MIDI_QTMUSIC = 4, - MIDI_AMIDI = 5 + MD_AUTO = 0, + MD_NULL = 1, + MD_WINDOWS = 2, + MD_TIMIDITY = 3, + MD_SEQ = 4, + MD_QTMUSIC = 5, + MD_AMIDI = 6, }; + +/* Factory functions => no need to include the specific classes + * in this header => faster compile */ +MidiDriver *MidiDriver_NULL_create(); +MidiDriver *MidiDriver_WIN_create(); +MidiDriver *MidiDriver_TIMIDITY_create(); +MidiDriver *MidiDriver_SEQ_create(); +MidiDriver *MidiDriver_QT_create(); +MidiDriver *MidiDriver_AMIDI_create(); + + + /* Roland to General Midi patch table. Still needs much work. */ static const byte mt32_to_gmidi[128] = { 0, 1, 2, 4, 4, 5, 5, 3, 16, 17, 18, 18, 19, diff --git a/sound/imuse.cpp b/sound/imuse.cpp index 92e3333aae..b91b8a845c 100644 --- a/sound/imuse.cpp +++ b/sound/imuse.cpp @@ -27,18 +27,6 @@ int num_mix; #define TICKS_PER_BEAT 480 -#ifdef USE_ADLIB -#ifdef _WIN32_WCE -#define TEMPO_BASE 0x1F0000 * 2 // Sampled down to 11 kHz -#else -#define TEMPO_BASE 0x1924E0 -#endif -#define HARDWARE_TYPE 1 -#else -#define TEMPO_BASE 0x400000 -#define HARDWARE_TYPE 5 -#endif - #define SYSEX_ID 0x7D #define SPECIAL_CHANNEL 9 @@ -984,7 +972,9 @@ int SoundEngine::initialize(Scumm *scumm, SoundDriver * driver) scumm->_soundEngine = this; _s = scumm; - _driver = (SOUND_DRIVER_TYPE *) driver; + _driver = driver; + + _hardware_type = driver->get_hardware_type(); _master_volume = 127; if (_music_volume < 1) @@ -1160,7 +1150,7 @@ void Player::set_tempo(uint32 b) uint32 i, j; if (_se->_s->_gameTempo < 1000) - i = TEMPO_BASE; + i = _se->_driver->get_base_tempo(); else i = _se->_s->_gameTempo; @@ -1357,7 +1347,7 @@ void Player::parse_sysex(byte *p, uint len) switch (code = *p++) { case 16: /* set instrument in part */ a = *p++ & 0x0F; - if (HARDWARE_TYPE != *p++) + if (_se->_hardware_type != *p++) break; decode_sysex_bytes(p, buf, len - 3); part = get_part(a); @@ -1367,7 +1357,7 @@ void Player::parse_sysex(byte *p, uint len) case 17: /* set global instrument */ p++; - if (HARDWARE_TYPE != *p++) + if (_se->_hardware_type != *p++) break; a = *p++; decode_sysex_bytes(p, buf, len - 4); @@ -1376,7 +1366,7 @@ void Player::parse_sysex(byte *p, uint len) case 33: /* param adjust */ a = *p++ & 0x0F; - if (HARDWARE_TYPE != *p++) + if (_se->_hardware_type != *p++) break; decode_sysex_bytes(p, buf, len - 3); part = get_part(a); @@ -2400,7 +2390,7 @@ void Part::key_off(byte note) void Part::init(SoundDriver * driver) { - _drv = (SOUND_DRIVER_TYPE *) driver; + _drv = driver; _player = NULL; _next = NULL; _prev = NULL; diff --git a/sound/mididrv.cpp b/sound/mididrv.cpp new file mode 100644 index 0000000000..7a881e7cb1 --- /dev/null +++ b/sound/mididrv.cpp @@ -0,0 +1,689 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001 Ludvig Strigeus + * Copyright (C) 2001/2002 The ScummVM project + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +/* + * Timidity support by Lionel Ulmer <lionel.ulmer@free.fr> + * QuickTime support by Florent Boudet <flobo@ifrance.com> + * Raw output support by Michael Pearce + * MorphOS support by Ruediger Hanke + */ + +#ifdef __MORPHOS__ +#include <devices/timer.h> +#endif + + +#include "stdafx.h" +#include "scumm.h" +#include "gmidi.h" + + +#ifdef WIN32 + +/* Windows MIDI driver */ +class MidiDriver_WIN : public MidiDriver { +public: + void destroy(); + int open(int mode); + void close(); + void send(uint32 b); + void pause(bool pause); + void set_stream_callback(void *param, StreamCallback *sc); + +private: + struct MyMidiHdr { + MIDIHDR hdr; + }; + + enum { + NUM_PREPARED_HEADERS=2, + MIDI_EVENT_SIZE = 64, + BUFFER_SIZE = MIDI_EVENT_SIZE * 12, + }; + + StreamCallback *_stream_proc; + void *_stream_param; + int _mode; + + HMIDIOUT _mo; + HMIDISTRM _ms; + + MyMidiHdr *_prepared_headers; + + uint16 _time_div; + + void unprepare(); + void prepare(); + void check_error(MMRESULT result); + void fill_all(); + uint32 property(int prop, uint32 param); + + static void CALLBACK midi_callback(HMIDIOUT hmo, UINT wMsg, + DWORD dwInstance, DWORD dwParam1, DWORD dwParam2); +}; + +void MidiDriver_WIN::set_stream_callback(void *param, StreamCallback *sc) { + _stream_param = param; + _stream_proc = sc; +} + +void MidiDriver_WIN::destroy() { + close(); + delete this; +} + +void CALLBACK MidiDriver_WIN::midi_callback(HMIDIOUT hmo, UINT wMsg, + DWORD dwInstance, DWORD dwParam1, DWORD dwParam2) { + + switch(wMsg) { + case MM_MOM_DONE:{ + MidiDriver_WIN *md = ((MidiDriver_WIN*)dwInstance); + if (md->_mode) + md->fill_all(); + break; + } + } +} + +int MidiDriver_WIN::open(int mode) { + if (_mode != 0) + return MERR_ALREADY_OPEN; + + _mode = mode; + + if (mode == MO_SIMPLE) { + MMRESULT res = midiOutOpen((HMIDIOUT *) &_mo, MIDI_MAPPER, NULL, NULL, 0); + if (res != MMSYSERR_NOERROR) + check_error(res); + } else { + /* streaming mode */ + MIDIPROPTIMEDIV mptd; + UINT _midi_device_id = 0; + + check_error(midiStreamOpen(&_ms, &_midi_device_id, 1, + (uint32)midi_callback, (uint32)this, CALLBACK_FUNCTION)); + + prepare(); + + mptd.cbStruct = sizeof(mptd); + mptd.dwTimeDiv = _time_div; + + check_error(midiStreamProperty(_ms, (byte*)&mptd, + MIDIPROP_SET | MIDIPROP_TIMEDIV)); + + fill_all(); + } + + return 0; +} + +void MidiDriver_WIN::fill_all() { + if (_stream_proc == NULL) { + error("MidiDriver_WIN::fill_all() called, but _stream_proc==NULL"); + } + + uint i; + MyMidiHdr *mmh = _prepared_headers; + MidiEvent my_evs[64]; + + for(i=0; i!=NUM_PREPARED_HEADERS; i++,mmh++) { + if (!(mmh->hdr.dwFlags & MHDR_INQUEUE)) { + int num = _stream_proc(_stream_param, my_evs, 64); + int i; + + /* end of stream? */ + if (num == 0) + break; + + MIDIEVENT *ev = (MIDIEVENT*)mmh->hdr.lpData; + MidiEvent *my_ev = my_evs; + + for(i=0; i!=num; i++,my_ev++) { + ev->dwStreamID = 0; + ev->dwDeltaTime = my_ev->delta; + + switch(my_ev->event>>24) { + case 0: + ev->dwEvent = my_ev->event; + break; + case ME_TEMPO: + /* change tempo event */ + ev->dwEvent = (MEVT_TEMPO<<24) | (my_ev->event&0xFFFFFF); + break; + default: + error("Invalid event type passed"); + } + + /* increase stream pointer by 12 bytes + * (need to be 12 bytes, and sizeof(MIDIEVENT) is 16) + */ + ev = (MIDIEVENT*)((byte*)ev + 12); + } + + mmh->hdr.dwBytesRecorded = num * 12; + check_error(midiStreamOut(_ms, &mmh->hdr, sizeof(mmh->hdr))); + } + } +} + +void MidiDriver_WIN::prepare() { + int i; + MyMidiHdr *mmh; + + _prepared_headers = (MyMidiHdr*)calloc(sizeof(MyMidiHdr), 2); + + for(i=0,mmh=_prepared_headers; i!=NUM_PREPARED_HEADERS; i++,mmh++) { + mmh->hdr.dwBufferLength = BUFFER_SIZE; + mmh->hdr.lpData = (LPSTR)calloc(BUFFER_SIZE,1); + + check_error(midiOutPrepareHeader( + (HMIDIOUT)_ms, &mmh->hdr, sizeof(mmh->hdr))); + } +} + +void MidiDriver_WIN::unprepare() { + uint i; + MyMidiHdr *mmh = _prepared_headers; + + for(i=0; i!=NUM_PREPARED_HEADERS; i++,mmh++) { + check_error(midiOutUnprepareHeader( + (HMIDIOUT)_ms, &mmh->hdr, sizeof(mmh->hdr))); + free(mmh->hdr.lpData); + mmh->hdr.lpData = NULL; + } + + free(_prepared_headers); +} + +void MidiDriver_WIN::close() { + int mode_was = _mode; + _mode = 0; + + switch(mode_was) { + case MO_SIMPLE: + check_error(midiOutClose(_mo)); + break; + case MO_STREAMING:; + check_error(midiStreamStop(_ms)); + check_error(midiOutReset((HMIDIOUT)_ms)); + unprepare(); + check_error(midiStreamClose(_ms)); + break; + } +} + +void MidiDriver_WIN::send(uint32 b) { + if (_mode != MO_SIMPLE) + error("MidiDriver_WIN:send called but driver is not in simple mode"); + check_error(midiOutShortMsg(_mo, b)); +} + +void MidiDriver_WIN::pause(bool pause) { + if (_mode == MO_STREAMING) { + if (pause) + check_error(midiStreamPause(_ms)); + else + check_error(midiStreamRestart(_ms)); + } +} + +void MidiDriver_WIN::check_error(MMRESULT result) { + char buf[200]; + if (result != MMSYSERR_NOERROR) { + midiOutGetErrorText(result, buf, 200); + error("MM System Error '%s'", buf); + } +} + +uint32 MidiDriver_WIN::property(int prop, uint32 param) { + switch(prop) { + + /* 16-bit time division according to standard midi specification */ + case PROP_TIMEDIV: + _time_div = (uint16)param; + return 1; + } + + return 0; +} + +MidiDriver *MidiDriver_WIN_create() { + return new MidiDriver_WIN(); +} + +#endif // WIN32 + + +/* NULL driver */ +class MidiDriver_NULL : public MidiDriver { +public: + void destroy(); + int open(int mode); + void close(); + void send(uint32 b); + void pause(bool pause); + void set_stream_callback(void *param, StreamCallback *sc); + uint32 property(int prop, uint32 param); +private: +}; + +int MidiDriver_NULL::open(int mode) { + warning("Music not enabled - MIDI support selected with no MIDI driver available. Try Adlib"); + return 0; +} +void MidiDriver_NULL::close() {} +void MidiDriver_NULL::destroy() {} +void MidiDriver_NULL::send(uint32 b) {} +void MidiDriver_NULL::pause(bool pause) {} +void MidiDriver_NULL::set_stream_callback(void *param, StreamCallback *sc) {} +uint32 MidiDriver_NULL::property(int prop, uint32 param) { return 0; } + +MidiDriver *MidiDriver_NULL_create() { + return new MidiDriver_NULL(); +} + + +/* retrieve a string representation of an error code */ +const char *MidiDriver::get_error_name(int error_code) { + static const char * const midi_errors[] = { + "No error", + "Cannot connect", + "Streaming not available", + "Device not available", + "Driver already open" + }; + + if ((uint)error_code >= ARRAYSIZE(midi_errors)) + return "Unknown Error"; + return midi_errors[error_code]; +} + + + + + + + + +#if 0 + + + + + +void MidiDriver::midiInit() +{ + if (MidiInitialized != true) { + switch (DeviceType) { + case MIDI_NULL: + midiInitNull(); + break; + case MIDI_WINDOWS: + midiInitWindows(); + break; + case MIDI_TIMIDITY: + midiInitTimidity(); + break; + case MIDI_SEQ: + midiInitSeq(); + break; + case MIDI_QTMUSIC: + midiInitQuicktime(); + break; + case MIDI_AMIDI: + break; + default: + DeviceType = 0; + midiInitNull(); + break; + } + MidiInitialized = true; + } else { + error("Midi driver already initialized"); + } +} + +void MidiDriver::MidiOut(int b) +{ + if (MidiInitialized != true) + midiInit(); + + if (MidiInitialized == true) { + switch (DeviceType) { + case MIDI_NULL: + break; + case MIDI_WINDOWS: + MidiOutWindows(_mo, b); + break; + case MIDI_TIMIDITY: + case MIDI_SEQ: + MidiOutSeq(_mo, b); + break; + case MIDI_QTMUSIC: + MidiOutQuicktime(_mo, b); + break; + case MIDI_AMIDI: + MidiOutMorphOS(_mo, b); + break; + default: + error("Invalid midi device type "); + break; + } + } else { + warning("Trying to write midi data without the driver being initialized"); + } +} + +/*********** Windows */ +void MidiDriver::midiInitWindows() +{ +#ifdef WIN32 + if (midiOutOpen((HMIDIOUT *) & _mo, MIDI_MAPPER, NULL, NULL, 0) != + MMSYSERR_NOERROR) + error("midiOutOpen failed"); +#endif +} + +void MidiDriver::MidiOutWindows(void *a, int b) +{ +#ifdef WIN32 + midiOutShortMsg((HMIDIOUT) a, b); +#endif +} + +/*********** Raw midi support */ +void MidiDriver::midiInitSeq() +{ + int device = open_sequencer_device(); + _mo = (void *)device; +} + +int MidiDriver::open_sequencer_device() +{ + int device = 0; +#if !defined(__APPLE__CW) // No getenv support on Apple Carbon + char *device_name = getenv("SCUMMVM_MIDI"); + if (device_name != NULL) { + device = (open((device_name), O_RDWR, 0)); + } else { + warning + ("You need to set-up the SCUMMVM_MIDI environment variable properly (see readme.txt) "); + } + if ((device_name == NULL) || (device < 0)) { + if (device_name == NULL) + warning("Opening /dev/null (no music will be heard) "); + else + warning + ("Cannot open rawmidi device %s - using /dev/null (no music will be heard) ", + device_name); + device = (open(("/dev/null"), O_RDWR, 0)); + if (device < 0) + error("Cannot open /dev/null to dump midi output"); + } +#endif + return device; +} + +/*********** Timidity */ +int MidiDriver::connect_to_timidity(int port) +{ + int s = 0; +#if !defined(__APPLE__CW) && !defined(__MORPHOS__) // No socket support on Apple Carbon or Morphos + struct hostent *serverhost; + struct sockaddr_in sadd; + + serverhost = gethostbyname("localhost"); + if (serverhost == NULL) + error("Could not resolve Timidity host ('localhost')"); + + sadd.sin_family = serverhost->h_addrtype; + sadd.sin_port = htons(port); + memcpy(&(sadd.sin_addr), serverhost->h_addr_list[0], serverhost->h_length); + + s = socket(AF_INET, SOCK_STREAM, 0); + if (s < 0) + error("Could not open Timidity socket"); + + if (connect(s, (struct sockaddr *)&sadd, sizeof(struct sockaddr_in)) < 0) + error("Could not connect to Timidity server"); +#endif + return s; +} + +void MidiDriver::midiInitTimidity() +{ + int s, s2; + int len; + int dummy, newport; + char buf[256]; + + s = connect_to_timidity(7777); + len = read(s, buf, 256); // buf[len] = '\0'; printf("%s", buf); + sprintf(buf, "SETBUF %f %f\n", 0.1, 0.15); + write(s, buf, strlen(buf)); + len = read(s, buf, 256); // buf[len] = '\0'; printf("%s", buf); + + sprintf(buf, "OPEN lsb\n"); + write(s, buf, strlen(buf)); + len = read(s, buf, 256); // buf[len] = '\0'; printf("%s", buf); + + sscanf(buf, "%d %d", &dummy, &newport); + printf(" => port = %d\n", newport); + + s2 = connect_to_timidity(newport); + _mo = (void *)s2; +} + +void MidiDriver::MidiOutSeq(void *a, int b) +{ + int s = (int)a; + unsigned char buf[256]; + int position = 0; + + switch (b & 0xF0) { + case 0x80: + case 0x90: + case 0xA0: + case 0xB0: + case 0xE0: + buf[position++] = SEQ_MIDIPUTC; + buf[position++] = b; + buf[position++] = DEVICE_NUM; + buf[position++] = 0; + buf[position++] = SEQ_MIDIPUTC; + buf[position++] = (b >> 8) & 0x7F; + buf[position++] = DEVICE_NUM; + buf[position++] = 0; + buf[position++] = SEQ_MIDIPUTC; + buf[position++] = (b >> 16) & 0x7F; + buf[position++] = DEVICE_NUM; + buf[position++] = 0; + break; + case 0xC0: + case 0xD0: + buf[position++] = SEQ_MIDIPUTC; + buf[position++] = b; + buf[position++] = DEVICE_NUM; + buf[position++] = 0; + buf[position++] = SEQ_MIDIPUTC; + buf[position++] = (b >> 8) & 0x7F; + buf[position++] = DEVICE_NUM; + buf[position++] = 0; + break; + default: + fprintf(stderr, "Unknown : %08x\n", b); + break; + } + write(s, buf, position); +} + +/* Quicktime music support */ +void MidiDriver::midiInitQuicktime() +{ +#ifdef __APPLE__CW + ComponentResult qtErr = noErr; + qtNoteAllocator = NULL; + + for (int i = 0; i < 15; i++) + qtNoteChannel[i] = NULL; + + qtNoteAllocator = OpenDefaultComponent(kNoteAllocatorComponentType, 0); + if (qtNoteAllocator == NULL) + goto bail; + + simpleNoteRequest.info.flags = 0; + *(short *)(&simpleNoteRequest.info.polyphony) = EndianS16_NtoB(15); // simultaneous tones + *(Fixed *) (&simpleNoteRequest.info.typicalPolyphony) = + EndianU32_NtoB(0x00010000); + + qtErr = NAStuffToneDescription(qtNoteAllocator, 1, &simpleNoteRequest.tone); + if (qtErr != noErr) + goto bail; + + for (int i = 0; i < 15; i++) { + qtErr = + NANewNoteChannel(qtNoteAllocator, &simpleNoteRequest, + &(qtNoteChannel[i])); + if ((qtErr != noErr) || (qtNoteChannel == NULL)) + goto bail; + } + return; + +bail: + fprintf(stderr, "Init QT failed %x %x %d\n", qtNoteAllocator, qtNoteChannel, + qtErr); + for (int i = 0; i < 15; i++) { + if (qtNoteChannel[i] != NULL) + NADisposeNoteChannel(qtNoteAllocator, qtNoteChannel[i]); + } + + if (qtNoteAllocator != NULL) + CloseComponent(qtNoteAllocator); +#endif +} + +void MidiDriver::MidiOutQuicktime(void *a, int b) +{ +#ifdef __APPLE__CW + MusicMIDIPacket midPacket; + unsigned char *midiCmd = midPacket.data; + midPacket.length = 3; + midiCmd[3] = (b & 0xFF000000) >> 24; + midiCmd[2] = (b & 0x00FF0000) >> 16; + midiCmd[1] = (b & 0x0000FF00) >> 8; + midiCmd[0] = b; + + unsigned char chanID = midiCmd[0] & 0x0F; + switch (midiCmd[0] & 0xF0) { + case 0x80: // Note off + NAPlayNote(qtNoteAllocator, qtNoteChannel[chanID], midiCmd[1], 0); + break; + + case 0x90: // Note on + NAPlayNote(qtNoteAllocator, qtNoteChannel[chanID], midiCmd[1], + midiCmd[2]); + break; + + case 0xB0: // Effect + switch (midiCmd[1]) { + case 0x01: // Modulation + NASetController(qtNoteAllocator, qtNoteChannel[chanID], + kControllerModulationWheel, midiCmd[2] << 8); + break; + + case 0x07: // Volume + NASetController(qtNoteAllocator, qtNoteChannel[chanID], + kControllerVolume, midiCmd[2] * 300); + break; + + case 0x0A: // Pan + NASetController(qtNoteAllocator, qtNoteChannel[chanID], kControllerPan, + (midiCmd[2] << 1) + 0xFF); + break; + + case 0x40: // Sustain on/off + NASetController(qtNoteAllocator, qtNoteChannel[chanID], + kControllerSustain, midiCmd[2]); + break; + + case 0x5b: // ext effect depth + NASetController(qtNoteAllocator, qtNoteChannel[chanID], + kControllerReverb, midiCmd[2] << 8); + break; + + case 0x5d: // chorus depth + NASetController(qtNoteAllocator, qtNoteChannel[chanID], + kControllerChorus, midiCmd[2] << 8); + break; + + case 0x7b: // mode message all notes off + for (int i = 0; i < 128; i++) + NAPlayNote(qtNoteAllocator, qtNoteChannel[chanID], i, 0); + break; + + default: + fprintf(stderr, "Unknown MIDI effect: %08x\n", b); + break; + } + break; + + case 0xC0: // Program change + NASetInstrumentNumber(qtNoteAllocator, qtNoteChannel[chanID], midiCmd[1]); + break; + + case 0xE0:{ // Pitch bend + long theBend = + ((((long)midiCmd[1] + (long)(midiCmd[2] << 8))) - 0x4000) / 4; + NASetController(qtNoteAllocator, qtNoteChannel[chanID], + kControllerPitchBend, theBend); + } + break; + + default: + fprintf(stderr, "Unknown Command: %08x\n", b); + NASendMIDI(qtNoteAllocator, qtNoteChannel[chanID], &midPacket); + break; + } +#endif +} + +/*********** MorphOS */ +void MidiDriver::MidiOutMorphOS(void *a, int b) +{ +#ifdef __MORPHOS__ + if (ScummMidiRequest) { + ULONG midi_data = b; // you never know about an int's size ;-) + ScummMidiRequest->amr_Std.io_Command = CMD_WRITE; + ScummMidiRequest->amr_Std.io_Data = &midi_data; + ScummMidiRequest->amr_Std.io_Length = 4; + DoIO((struct IORequest *)ScummMidiRequest); + } +#endif +} + + + +void MidiDriver::midiInitNull() +{ + warning + ("Music not enabled - MIDI support selected with no MIDI driver available. Try Adlib"); +} +#endif
\ No newline at end of file |