diff options
Diffstat (limited to 'sound/mididrv.cpp')
-rw-r--r-- | sound/mididrv.cpp | 200 |
1 files changed, 200 insertions, 0 deletions
diff --git a/sound/mididrv.cpp b/sound/mididrv.cpp index 0dc14f9558..4dfda237e6 100644 --- a/sound/mididrv.cpp +++ b/sound/mididrv.cpp @@ -1444,6 +1444,205 @@ void MidiDriver_MIDIEMU::midi_fm_endnote(int voice) { } +#if defined(UNIX) && defined(USE_ALSA) + +#include <alsa/asoundlib.h> + +/* + * ALSA sequencer driver + * Mostly cut'n'pasted from Virtual Tiny Keyboard (vkeybd) by Takashi Iwai + * (you really rox, you know?) + */ + +#if SND_LIB_MINOR >= 6 +#define snd_seq_flush_output(x) snd_seq_drain_output(x) +#define snd_seq_set_client_group(x,name) /*nop*/ +#define my_snd_seq_open(seqp) snd_seq_open(seqp, "hw", SND_SEQ_OPEN_OUTPUT, 0) +#else +/* SND_SEQ_OPEN_OUT causes oops on early version of ALSA */ +#define my_snd_seq_open(seqp) snd_seq_open(seqp, SND_SEQ_OPEN) +#endif + +/* + * parse address string + */ + +#define ADDR_DELIM ".:" + +static int parse_addr(char *arg, int *client, int *port) +{ + char *p; + + if (isdigit(*arg)) { + if ((p = strpbrk(arg, ADDR_DELIM)) == NULL) + return -1; + *client = atoi(arg); + *port = atoi(p + 1); + } else { + if (*arg == 's' || *arg == 'S') { + *client = SND_SEQ_ADDRESS_SUBSCRIBERS; + *port = 0; + } else + return -1; + } + return 0; +} + +class MidiDriver_ALSA : public MidiDriver { +public: + MidiDriver_ALSA(); + int open(int mode); + void close(); + void send(uint32 b); + void pause(bool pause); + void set_stream_callback(void *param, StreamCallback *sc); + +private: + void send_event(int do_flush); + snd_seq_event_t ev; + StreamCallback *_stream_proc; + void *_stream_param; + int _mode; + snd_seq_t *seq_handle; + int seq_client, seq_port; + int my_client, my_port; +}; + +MidiDriver_ALSA::MidiDriver_ALSA() { + printf("Driver: ALSA\n"); +} + +void MidiDriver_ALSA::send_event(int do_flush) +{ + snd_seq_ev_set_direct(&ev); + snd_seq_ev_set_source(&ev, my_port); + snd_seq_ev_set_dest(&ev, seq_client, seq_port); + + snd_seq_event_output(seq_handle, &ev); + if (do_flush) + snd_seq_flush_output(seq_handle); +} + +int MidiDriver_ALSA::open(int mode) { + char * var; + unsigned int caps; + + if (_mode != 0) + return MERR_ALREADY_OPEN; + _mode=mode; + if (mode!=MO_SIMPLE) return MERR_STREAMING_NOT_AVAILABLE; + + if (!(var = getenv("SCUMMVM_PORT"))) { + error("You have to define the environnement variable SCUMMVM_PORT"); + return -1; + } + + if (parse_addr(var, &seq_client, &seq_port) < 0) { + error("Invalid port %s", var); + return -1; + } + + if (my_snd_seq_open(&seq_handle)) { + error("Can't open sequencer"); + return -1; + } + + my_client = snd_seq_client_id(seq_handle); + snd_seq_set_client_name(seq_handle, "SCUMMVM"); + snd_seq_set_client_group(seq_handle, "input"); + + caps = SND_SEQ_PORT_CAP_READ; + if (seq_client == SND_SEQ_ADDRESS_SUBSCRIBERS) + caps = ~SND_SEQ_PORT_CAP_SUBS_READ; + my_port = snd_seq_create_simple_port(seq_handle, "SCUMMVM", caps, SND_SEQ_PORT_TYPE_MIDI_GENERIC | SND_SEQ_PORT_TYPE_APPLICATION); + if (my_port < 0) { + snd_seq_close(seq_handle); + error("Can't create port"); + return -1; + } + + if (seq_client != SND_SEQ_ADDRESS_SUBSCRIBERS) { + /* subscribe to MIDI port */ + if (snd_seq_connect_to(seq_handle, my_port, seq_client, seq_port) < 0) { + snd_seq_close(seq_handle); + error("Can't subscribe to MIDI port (%d:%d)", seq_client, seq_port); + return -1; + } + } + + printf("ALSA client initialised [%d:%d]\n", my_client, my_port); + + return 0; +} + +void MidiDriver_ALSA::close() { + _mode = 0; + snd_seq_close(seq_handle); +} + + +void MidiDriver_ALSA::send(uint32 b) +{ + unsigned int midiCmd[4]; + ev.type = SND_SEQ_EVENT_OSS; + ev.data.raw8.d[0] = b; + + midiCmd[3] = (b & 0xFF000000) >> 24; + midiCmd[2] = (b & 0x00FF0000) >> 16; + midiCmd[1] = (b & 0x0000FF00) >> 8; + midiCmd[0] = (b & 0x000000FF); + + unsigned char chanID = midiCmd[0] & 0x0F; + switch (midiCmd[0] & 0xF0) { + case 0x80: + snd_seq_ev_set_noteoff(&ev, chanID, midiCmd[1], midiCmd[2]); + send_event(1); + break; + case 0x90: + snd_seq_ev_set_noteon(&ev, chanID, midiCmd[1], midiCmd[2]); + send_event(1); + break; + + case 0xB0: // Effect + snd_seq_ev_set_controller(&ev, chanID, midiCmd[1], midiCmd[2]); + send_event(1); + break; + case 0xC0: + snd_seq_ev_set_pgmchange(&ev, chanID, midiCmd[1]); + send_event(0); + break; + + case 0xE0:{ // Pitch bend + long theBend = + ((((long)midiCmd[1] + (long)(midiCmd[2] << 8))) - 0x4000) / 4; + snd_seq_ev_set_pitchbend(&ev, chanID, theBend); + send_event(1); + } + break; + + default: + error("Unknown Command: %08x\n", (int)b); + break; + } +} + +void MidiDriver_ALSA::pause(bool pause) { + if (_mode == MO_STREAMING) { + } +} + +void MidiDriver_ALSA::set_stream_callback(void *param, StreamCallback *sc) { + _stream_param = param; + _stream_proc = sc; +} + +MidiDriver *MidiDriver_ALSA_create() { + return new MidiDriver_ALSA(); +} + +#endif + + #if 0 /* Old code for timidity support, maybe somebody can rewrite this for the @@ -1503,3 +1702,4 @@ void MidiDriver::midiInitTimidity() #endif /* 0 */ + |