diff options
Diffstat (limited to 'sound/gmidi.cpp')
-rw-r--r-- | sound/gmidi.cpp | 414 |
1 files changed, 414 insertions, 0 deletions
diff --git a/sound/gmidi.cpp b/sound/gmidi.cpp new file mode 100644 index 0000000000..f76a00d708 --- /dev/null +++ b/sound/gmidi.cpp @@ -0,0 +1,414 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001 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> + */ + + +#include "stdafx.h" + +#if !defined USE_ADLIB + +#include "scumm.h" +#include "sound.h" + +#ifdef USE_TIMIDITY +#include <sys/time.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netdb.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +/* Copy-pasted from Timidity */ +#define SEQ_MIDIPUTC 5 + +#endif /* USE_TIMIDITY */ + + +#define SPECIAL_CHANNEL 9 + +#if defined(WIN32) + +void MidiSoundDriver::midiInit() { + if (midiOutOpen((HMIDIOUT*)&_mo, MIDI_MAPPER, NULL, NULL, 0) != MMSYSERR_NOERROR) + error("midiOutOpen failed"); +} + +#define MIDI_OUT(a,b) midiOutShortMsg((HMIDIOUT)(a), (b)) + +#elif defined(USE_TIMIDITY) + +static int connect_to_timidity(int port) +{ + struct hostent *serverhost; + struct sockaddr_in sadd; + int s; + + serverhost = gethostbyname("localhost"); + if (serverhost == NULL) + error("Could not resolve host"); + 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 socket"); + if (connect(s, (struct sockaddr *) &sadd, sizeof(struct sockaddr_in)) < 0) + error("Could not connect to server"); + + return s; +} + +void SoundEngine::midiInit() { + 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; +} + +#define DEVICE_NUM 0 + +static inline void MIDI_OUT(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); +} + +#else +#define MIDI_OUT(a,b) +void SoundEngine::midiInit() { } +#endif + +void MidiSoundDriver::midiPitchBend(byte chan, int16 pitchbend) { + uint16 tmp; + + if (_midi_pitchbend_last[chan] != pitchbend) { + _midi_pitchbend_last[chan] = pitchbend; + tmp = (pitchbend<<2) + 0x2000; + MIDI_OUT(_mo, ((tmp>>7)&0x7F)<<16 | (tmp&0x7F)<<8 | 0xE0 | chan); + } +} + +void MidiSoundDriver::midiVolume(byte chan, byte volume) { + if (_midi_volume_last[chan] != volume) { + _midi_volume_last[chan] = volume; + MIDI_OUT(_mo, 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_OUT(_mo, pedal<<16 | 64<<8 | 0xB0 | chan); + } +} + +void MidiSoundDriver::midiModWheel(byte chan, byte modwheel) { + if (_midi_modwheel_last[chan] != modwheel) { + _midi_modwheel_last[chan] = modwheel; + MIDI_OUT(_mo, modwheel<<16 | 1<<8 | 0xB0 | chan); + } +} + +void MidiSoundDriver::midiEffectLevel(byte chan, byte level) { + if (_midi_effectlevel_last[chan] != level) { + _midi_effectlevel_last[chan] = level; + MIDI_OUT(_mo, level<<16 | 91<<8 | 0xB0 | chan); + } +} + +void MidiSoundDriver::midiChorus(byte chan, byte chorus) { + if (_midi_chorus_last[chan] != chorus) { + _midi_chorus_last[chan] = chorus; + MIDI_OUT(_mo, chorus<<16 | 93<<8 | 0xB0 | chan); + } +} + +void MidiSoundDriver::midiControl0(byte chan, byte value) { + MIDI_OUT(_mo, value<<16 | 0<<8 | 0xB0 | chan); +} + +static const byte mt32_to_gmidi[128] = { + 0, 1, 2, 4, 4, 5, 5, 3, 16, 17, 18, 18, 19, + 19, 20, 21, 6, 6, 6, 7, 7, 7, 8, 8, 62, 63, + 62, 63, 38, 39, 38, 39, 88, 89, 52, 113, 97, 96, 91, + 85, 14, 101, 68, 95, 86, 103, 88, 80, 48, 49, 51, 45, + 40, 40, 42, 42, 43, 46, 46, 24, 25, 26, 27, 104, 32, + 33, 34, 39, 36, 37, 38, 35, 79, 73, 72, 72, 74, 75, + 64, 65, 66, 67, 71, 71, 68, 69, 70, 22, 56, 59, 57, + 63, 60, 60, 58, 61, 61, 11, 11, 12, 88, 9, 14, 13, + 12, 107, 111, 77, 78, 78, 76, 121, 47, 117, 127, 115, 118, + 116, 118, 94, 115, 9, 55, 124, 123, 125, 126, 127 +}; + + +void MidiSoundDriver::midiProgram(byte chan, byte program) { + if (_mt32emulate) + program=mt32_to_gmidi[program]; + MIDI_OUT(_mo, program<<8 | 0xC0 | chan); +} + +void MidiSoundDriver::midiPan(byte chan, int8 pan) { + if (_midi_pan_last[chan] != pan) { + _midi_pan_last[chan] = pan; + MIDI_OUT(_mo, ((pan-64)&0x7F)<<16 | 10<<8 | 0xB0 | chan); + } +} + +void MidiSoundDriver::midiNoteOn(byte chan, byte note, byte velocity) { + MIDI_OUT(_mo, velocity<<16 | note<<8 | 0x90 | chan); +} + +void MidiSoundDriver::midiNoteOff(byte chan, byte note) { + MIDI_OUT(_mo, note<<8 | 0x80 | chan); +} + +void MidiSoundDriver::midiSilence(byte chan) { + MIDI_OUT(_mo, (64<<8)|0xB0|chan); + MIDI_OUT(_mo, (123<<8)|0xB0|chan); +} + + +void MidiSoundDriver::part_key_on(Part *part, byte note, byte velocity) { + MidiChannelGM *mc = part->_mc->gm(); + + if (mc) { + mc->_actives[note>>4] |= (1<<(note&0xF)); + midiNoteOn(mc->_chan, note, velocity); + } else if (part->_percussion) { + midiVolume(SPECIAL_CHANNEL, part->_vol_eff); + midiProgram(SPECIAL_CHANNEL, part->_bank); + midiNoteOn(SPECIAL_CHANNEL, note, velocity); + } +} + +void MidiSoundDriver::part_key_off(Part *part, byte note) { + MidiChannelGM *mc = part->_mc->gm(); + + if (mc) { + mc->_actives[note>>4] &= ~(1<<(note&0xF)); + midiNoteOff(mc->_chan, note); + } else if (part->_percussion) { + midiNoteOff(SPECIAL_CHANNEL, note); + } +} + +void MidiSoundDriver::init(SoundEngine *eng) { + int i; + MidiChannelGM *mc; + + _se = eng; + + for(i=0,mc=_midi_channels; i!=ARRAYSIZE(_midi_channels);i++,mc++) + mc->_chan = i; + + midiInit(); +} + +void MidiSoundDriver::update_pris() { + Part *part,*hipart; + int i; + byte hipri,lopri; + MidiChannelGM *mc,*lomc; + + while(true) { + hipri = 0; + hipart = NULL; + for(i=32,part=_se->parts_ptr(); i; i--,part++) { + if (part->_player && !part->_percussion && part->_on && !part->_mc && part->_pri_eff>=hipri) { + hipri = part->_pri_eff; + hipart = part; + } + } + + if (!hipart) + return; + + lopri = 255; + lomc = NULL; + for(i=ARRAYSIZE(_midi_channels),mc=_midi_channels;;mc++) { + if (!mc->_part) { + lomc = mc; + break; + } + if (mc->_part->_pri_eff<=lopri) { + lopri = mc->_part->_pri_eff; + lomc = mc; + } + + if (!--i) { + if (lopri >= hipri) + return; + lomc->_part->off(); + break; + } + } + + hipart->_mc = lomc; + lomc->_part = hipart; + hipart->changed(pcAll); + } +} + +int MidiSoundDriver::part_update_active(Part *part, uint16 *active) { + int i,j; + uint16 *act,mask,bits; + int count = 0; + + bits = 1<<part->_chan; + + act = part->_mc->gm()->_actives; + + for(i=8; i; i--) { + mask = *act++; + if (mask) { + for(j=16; j; j--,mask>>=1,active++) { + if (mask&1 && !(*active&bits)) { + *active|=bits; + count++; + } + } + } else { + active += 16; + } + } + return count; +} + +void MidiSoundDriver::part_changed(Part *part, byte what) { + MidiChannelGM *mc; + + /* Mark for re-schedule if program changed when in pre-state */ + if (what&pcProgram && part->_percussion) { + part->_percussion = false; + update_pris(); + } + + if (!(mc = part->_mc->gm())) + return; + + if (what & pcMod) + midiPitchBend(mc->_chan, clamp(part->_pitchbend + part->_detune_eff + (part->_transpose_eff<<7), -2048, 2047)); + + if (what & pcVolume) + midiVolume(mc->_chan, part->_vol_eff); + + if (what & pcPedal) + midiPedal(mc->_chan, part->_pedal); + + if (what & pcModwheel) + midiModWheel(mc->_chan, part->_modwheel); + + if (what & pcPan) + midiPan(mc->_chan, part->_pan_eff); + + if (what & pcEffectLevel) + midiEffectLevel(mc->_chan, part->_effect_level); + + if (what & pcProgram) { + if (part->_bank) { + midiControl0(mc->_chan, part->_bank); + midiProgram(mc->_chan, part->_program); + midiControl0(mc->_chan, 0); + } else { + midiProgram(mc->_chan, part->_program); + } + } + + if (what & pcChorus) + midiChorus(mc->_chan, part->_effect_level); +} + + +void MidiSoundDriver::part_off(Part *part) { + MidiChannelGM *mc = part->_mc->gm(); + if (mc) { + part->_mc = NULL; + mc->_part = NULL; + memset(mc->_actives, 0, sizeof(mc->_actives)); + midiSilence(mc->_chan); + } +} + +#endif
\ No newline at end of file |