scummvm/sound/gmidi.cpp
2002-02-02 19:07:32 +00:00

415 lines
9.6 KiB
C++

/* 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 MidiSoundDriver::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 MidiSoundDriver::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