diff options
Diffstat (limited to 'backends')
| -rw-r--r-- | backends/midi/alsa.cpp | 246 | ||||
| -rw-r--r-- | backends/module.mk | 3 | 
2 files changed, 248 insertions, 1 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 | 
