diff options
-rw-r--r-- | backends/midi/alsa.cpp | 246 | ||||
-rw-r--r-- | backends/module.mk | 3 | ||||
-rw-r--r-- | sound/mididrv.cpp | 222 |
3 files changed, 248 insertions, 223 deletions
diff --git a/backends/midi/alsa.cpp b/backends/midi/alsa.cpp new file mode 100644 index 0000000000..538b0cd74b --- /dev/null +++ b/backends/midi/alsa.cpp @@ -0,0 +1,246 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001 Ludvig Strigeus + * Copyright (C) 2001/2002 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$ + */ + +#if defined(UNIX) && defined(USE_ALSA) + +#include "sound/mpu401.h" + +#include "stdafx.h" +#include "common/engine.h" // for warning/error/debug +#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 ".:" + +class MidiDriver_ALSA:public MidiDriver_MPU401 { +public: + MidiDriver_ALSA(); + int open(int mode); + void close(); + void send(uint32 b); + void pause(bool p); + void set_stream_callback(void *param, StreamCallback *sc); + // void setPitchBendRange (byte channel, uint range); + +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; + static int parse_addr(char *arg, int *client, int *port); +}; + +MidiDriver_ALSA::MidiDriver_ALSA():_mode(0), seq_handle(0), seq_client(0), seq_port(0), my_client(0), +my_port(0) +{ +} + +int MidiDriver_ALSA::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; +} + +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; + + if (mode != MO_SIMPLE) + return MERR_STREAMING_NOT_AVAILABLE; + + _mode = mode; + + if (!(var = getenv("SCUMMVM_PORT"))) { + // default alsa port if none specified + if (parse_addr("65:0", &seq_client, &seq_port) < 0) { + error("Invalid port %s", var); + return -1; + } + } + else { + 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; + if (seq_handle) + snd_seq_close(seq_handle); +} + + +void MidiDriver_ALSA::send(uint32 b) +{ + unsigned int midiCmd[4]; + ev.type = SND_SEQ_EVENT_OSS; + + midiCmd[3] = (b & 0xFF000000) >> 24; + midiCmd[2] = (b & 0x00FF0000) >> 16; + midiCmd[1] = (b & 0x0000FF00) >> 8; + midiCmd[0] = (b & 0x000000FF); + ev.data.raw32.d[0] = midiCmd[0]; + ev.data.raw32.d[1] = midiCmd[1]; + ev.data.raw32.d[2] = midiCmd[2]; + + 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: + /* is it this simple ? Wow... */ + 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:{ + // long theBend = ((((long)midiCmd[1] + (long)(midiCmd[2] << 7))) - 0x2000) / 4; + // snd_seq_ev_set_pitchbend(&ev, chanID, theBend); + long theBend = ((long)midiCmd[1] + (long)(midiCmd[2] << 7)) - 0x2000; + snd_seq_ev_set_pitchbend(&ev, chanID, theBend); + send_event(1); + } + break; + + default: + error("Unknown Command: %08x\n", (int)b); + /* I don't know if this works but, well... */ + send_event(1); + break; + } +} + +void MidiDriver_ALSA::pause(bool p) +{ + if (_mode == MO_STREAMING) { + /* Err... and what? */ + } +} + +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 diff --git a/backends/module.mk b/backends/module.mk index 4e36c8cd2a..bacff39d7f 100644 --- a/backends/module.mk +++ b/backends/module.mk @@ -7,7 +7,8 @@ MODULE_OBJS = \ backends/midi/adlib.o \ backends/midi/coreaudio.o \ backends/midi/null.o \ - backends/midi/quicktime.o + backends/midi/quicktime.o \ + backends/midi/alsa.o # Include common rules include common.rules diff --git a/sound/mididrv.cpp b/sound/mididrv.cpp index c13cb8e0f5..9e1ca82db8 100644 --- a/sound/mididrv.cpp +++ b/sound/mididrv.cpp @@ -655,225 +655,3 @@ MidiDriver *MidiDriver_SEQ_create() #endif - -#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 ".:" - -class MidiDriver_ALSA:public MidiDriver_MPU401 { -public: - MidiDriver_ALSA(); - int open(int mode); - void close(); - void send(uint32 b); - void pause(bool p); - void set_stream_callback(void *param, StreamCallback *sc); - // void setPitchBendRange (byte channel, uint range); - -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; - static int parse_addr(char *arg, int *client, int *port); -}; - -MidiDriver_ALSA::MidiDriver_ALSA():_mode(0), seq_handle(0), seq_client(0), seq_port(0), my_client(0), -my_port(0) -{ -} - -int MidiDriver_ALSA::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; -} - -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; - - if (mode != MO_SIMPLE) - return MERR_STREAMING_NOT_AVAILABLE; - - _mode = mode; - - if (!(var = getenv("SCUMMVM_PORT"))) { - // default alsa port if none specified - if (parse_addr("65:0", &seq_client, &seq_port) < 0) { - error("Invalid port %s", var); - return -1; - } - } - else { - 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; - if (seq_handle) - snd_seq_close(seq_handle); -} - - -void MidiDriver_ALSA::send(uint32 b) -{ - unsigned int midiCmd[4]; - ev.type = SND_SEQ_EVENT_OSS; - - midiCmd[3] = (b & 0xFF000000) >> 24; - midiCmd[2] = (b & 0x00FF0000) >> 16; - midiCmd[1] = (b & 0x0000FF00) >> 8; - midiCmd[0] = (b & 0x000000FF); - ev.data.raw32.d[0] = midiCmd[0]; - ev.data.raw32.d[1] = midiCmd[1]; - ev.data.raw32.d[2] = midiCmd[2]; - - 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: - /* is it this simple ? Wow... */ - 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:{ - // long theBend = ((((long)midiCmd[1] + (long)(midiCmd[2] << 7))) - 0x2000) / 4; - // snd_seq_ev_set_pitchbend(&ev, chanID, theBend); - long theBend = ((long)midiCmd[1] + (long)(midiCmd[2] << 7)) - 0x2000; - snd_seq_ev_set_pitchbend(&ev, chanID, theBend); - send_event(1); - } - break; - - default: - error("Unknown Command: %08x\n", (int)b); - /* I don't know if this works but, well... */ - send_event(1); - break; - } -} - -void MidiDriver_ALSA::pause(bool p) -{ - if (_mode == MO_STREAMING) { - /* Err... and what? */ - } -} - -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 |