diff options
Diffstat (limited to 'engines/sci/sfx/seq/oss-adlib.cpp')
-rw-r--r-- | engines/sci/sfx/seq/oss-adlib.cpp | 374 |
1 files changed, 374 insertions, 0 deletions
diff --git a/engines/sci/sfx/seq/oss-adlib.cpp b/engines/sci/sfx/seq/oss-adlib.cpp new file mode 100644 index 0000000000..0406556f56 --- /dev/null +++ b/engines/sci/sfx/seq/oss-adlib.cpp @@ -0,0 +1,374 @@ +/*************************************************************************** + oss-adlib.c Copyright (C) 2001 Solomon Peachy, 03,04 Christoph Reichenbach + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + +***************************************************************************/ + +#include <stdio.h> +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif +#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 */ + +#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 guint8 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(guint8 command, guint8 note, guint8 velocity) +{ + guint8 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(guint8 command, guint8 param) +{ + guint8 channel; + guint8 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 */ +}; + +#endif /* HAVE_SYS_SOUNDCARD_H */ |