scummvm/engines/sci/sfx/seq/oss-adlib.cpp
Eugene Sandulenko d53c75fc8f Fix code formatting
svn-id: r38930
2009-02-27 12:59:02 +00:00

358 lines
7.8 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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.
*
* $URL$
* $Id$
*
*/
#include "common/scummsys.h"
#include "../sequencer.h"
#ifdef HAVE_SYS_SOUNDCARD_H
#include "../adlib.h"
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/soundcard.h>
#include <sfx_iterator.h> /* for some MIDI information */
namespace Sci {
#if 1
SEQ_DEFINEBUF(2048);
static int seqfd;
#else
extern unsigned char _seqbuf[2048];
extern int _seqbuflen;
extern int _seqbufptr;
extern int seqfd;
#endif
static uint8 instr[MIDI_CHANNELS];
static int dev;
static int free_voices = ADLIB_VOICES;
static long note_time[ADLIB_VOICES];
static unsigned char oper_note[ADLIB_VOICES];
static unsigned char oper_chn[ADLIB_VOICES];
#if 1
void seqbuf_dump(void) { /* OSS upcall */
if (_seqbufptr)
if (write(seqfd, _seqbuf, _seqbufptr) == -1) {
perror("ADLIB write ");
exit(-1);
}
_seqbufptr = 0;
}
#endif
/* initialise note/operator lists, etc. */
void adlib_init_lists(void) {
int i;
for (i = 0 ; i < ADLIB_VOICES ; i++) {
oper_note[i] = 255;
oper_chn[i] = 255;
note_time[i] = 0;
}
free_voices = ADLIB_VOICES;
}
int adlib_stop_note(int chn, int note, int velocity) {
int i, op = 255;
for (i = 0;i < ADLIB_VOICES && op == 255;i++) {
if (oper_chn[i] == chn)
if (oper_note[i] == note)
op = i;
}
if (op == 255) {
printf("can't stop.. chn %d %d %d\n", chn, note, velocity);
return 255; /* not playing */
}
SEQ_STOP_NOTE(dev, op, note, velocity);
SEQ_DUMPBUF();
oper_chn[op] = 255;
oper_note[op] = 255;
note_time[op] = 0;
free_voices++;
return op;
}
int adlib_kill_one_note(int chn) {
int oldest = 255, i = 255;
long time = 0;
if (free_voices >= ADLIB_VOICES) {
printf("Free list empty but no notes playing\n");
return 255;
} /* No notes playing */
for (i = 0; i < ADLIB_VOICES ; i++) {
if (oper_chn[i] != chn)
continue;
if (note_time[i] == 0)
continue;
if (time == 0) {
time = note_time[i];
oldest = i;
continue;
}
if (note_time[i] < time) {
time = note_time[i];
oldest = i;
}
}
/* printf("Killing chn %d, oper %d\n", chn, oldest); */
if (oldest == 255)
return 255; /* Was already stopped. Why? */
SEQ_STOP_NOTE(dev, oldest, oper_note[oldest], 0);
SEQ_DUMPBUF();
oper_chn[oldest] = 255;
oper_note[oldest] = 255;
note_time[oldest] = 0;
free_voices++;
return oldest;
}
static void adlib_start_note(int chn, int note, int velocity) {
int free;
struct timeval now;
if (velocity == 0) {
adlib_stop_note(chn, note, velocity);
return;
}
gettimeofday(&now, NULL);
if (free_voices <= 0)
free = adlib_kill_one_note(chn);
else
for (free = 0; free < ADLIB_VOICES ; free++)
if (oper_chn[free] == 255)
break;
/* printf("play operator %d/%d: %d %d %d\n", free, free_voices, chn, note, velocity); */
oper_chn[free] = chn;
oper_note[free] = note;
note_time[free] = now.tv_sec * 1000000 + now.tv_usec;
free_voices--;
SEQ_SET_PATCH(dev, free, instr[chn]);
SEQ_START_NOTE(dev, free, note, velocity);
SEQ_DUMPBUF();
}
static int midi_adlib_open(int data_length, byte *data_ptr, int data2_length,
byte *data2_ptr, void *seq) {
int nrdevs, i, n;
struct synth_info info;
struct sbi_instrument sbi;
if (data_length < 1344) {
printf("invalid patch.003");
return -1;
}
for (i = 0; i < 48; i++)
make_sbi((adlib_def *)(data_ptr + (28 * i)), adlib_sbi[i]);
if (data_length > 1344)
for (i = 48; i < 96; i++)
make_sbi((adlib_def *)(data_ptr + 2 + (28 * i)), adlib_sbi[i]);
memset(instr, 0, sizeof(instr));
if (!IS_VALID_FD(seqfd = open("/dev/sequencer", O_WRONLY, 0))) {
perror("/dev/sequencer");
return(-1);
}
if (ioctl(seqfd, SNDCTL_SEQ_NRSYNTHS, &nrdevs) == -1) {
perror("/dev/sequencer");
return(-1);
}
for (i = 0;i < nrdevs && dev == -1;i++) {
info.device = i;
if (ioctl(seqfd, SNDCTL_SYNTH_INFO, &info) == -1) {
perror("info: /dev/sequencer");
return(-1);
}
if (info.synth_type == SYNTH_TYPE_FM)
dev = i;
}
if (dev == -1) {
fprintf(stderr, "ADLIB: FM synthesizer not detected\n");
return(-1);
}
/* free_voices = info.nr_voices; */
adlib_init_lists();
printf("ADLIB: Loading patches into synthesizer\n");
sbi.device = dev;
sbi.key = FM_PATCH;
for (i = 0; i < 96; i++) {
for (n = 0; n < 32; n++)
memcpy(sbi.operators, &adlib_sbi[i], sizeof(sbi_instr_data));
sbi.channel = i;
SEQ_WRPATCH(&sbi, sizeof(sbi));
SEQ_DUMPBUF();
}
SEQ_START_TIMER();
SEQ_SET_TEMPO(60);
SEQ_DUMPBUF();
return 0;
}
static int midi_adlib_close(void) {
SEQ_DUMPBUF();
return close(seqfd);
}
static int midi_adlib_allstop(void) {
int i;
for (i = 0; i < ADLIB_VOICES ; i++) {
if (oper_chn[i] == 255)
continue;
adlib_stop_note(oper_chn[i], oper_note[i], 0);
}
adlib_init_lists();
return 0;
}
static int midi_adlib_reverb(int param) {
printf("reverb NYI %04x \n", param);
return 0;
}
static inline int midi_adlib_event1(uint8 command, uint8 note, uint8 velocity) {
uint8 channel, oper;
channel = command & 0x0f;
oper = command & 0xf0;
switch (oper) {
case 0x80:
adlib_stop_note(channel, note, velocity);
return 0;
case 0x90:
adlib_start_note(channel, note, velocity);
return 0;
case 0xe0: /* Pitch bend needs scaling? */
SEQ_BENDER(dev, channel, ((note << 8) & velocity));
SEQ_DUMPBUF();
break;
case 0xb0: /* CC changes. we ignore. */
/* XXXX we need to parse out 0x07 volume, at least. */
return 0;
case 0xd0: /* aftertouch */
SEQ_CHN_PRESSURE(dev, channel, note);
SEQ_DUMPBUF();
return 0;
default:
printf("ADLIB: Unknown event %02x\n", command);
return 0;
}
SEQ_DUMPBUF();
return 0;
}
static inline int midi_adlib_event2(uint8 command, uint8 param) {
uint8 channel;
uint8 oper;
channel = command & 0x0f;
oper = command & 0xf0;
switch (oper) {
case 0xc0: { /* change instrument */
int inst = param;
instr[channel] = inst; /* XXXX offset? */
// SEQ_SET_PATCH(dev, channel, inst);
// SEQ_DUMPBUF();
return 0;
}
default:
printf("ADLIB: Unknown event %02x\n", command);
}
SEQ_DUMPBUF();
return 0;
}
static int midi_adlib_event(byte command, int argc, byte *argv) {
if (argc > 1)
return midi_adlib_event1(command, argv[0], argv[1]);
else
return midi_adlib_event2(command, argv[0]);
}
static int midi_adlib_delay(int ticks) {
SEQ_DELTA_TIME(ticks);
return SFX_OK;
}
static int midi_adlib_set_option(char *name, char *value) {
return SFX_ERROR; /* No options are supported at this time */
}
/* the driver struct */
sfx_sequencer_t sfx_sequencer_oss_adlib = {
"adlib",
"0.1",
SFX_DEVICE_NONE, /* No device dependancy-- fixme, this might become ossseq */
&midi_adlib_set_option,
&midi_adlib_open,
&midi_adlib_close,
&midi_adlib_event,
&midi_adlib_delay,
NULL,
&midi_adlib_allstop,
NULL,
&midi_adlib_reverb,
003, /* patch.003 */
SFX_SEQ_PATCHFILE_NONE,
0x04, /* playflag */
0, /* do not play channel 9 */
ADLIB_VOICES, /* Max polyphony */
0 /* Does not require any write-ahead by its own */
};
} // End of namespace Sci
#endif /* HAVE_SYS_SOUNDCARD_H */