added backend midi code
This commit is contained in:
parent
8c0ae1c771
commit
cd7076a702
9 changed files with 2412 additions and 0 deletions
339
backends/midi/alsa.cpp
Normal file
339
backends/midi/alsa.cpp
Normal file
|
@ -0,0 +1,339 @@
|
||||||
|
/* Residual - A 3D game interpreter
|
||||||
|
*
|
||||||
|
* Residual is the legal property of its developers, whose names
|
||||||
|
* are too numerous to list here. Please refer to the AUTHORS
|
||||||
|
* 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/sys.h"
|
||||||
|
|
||||||
|
#if defined(UNIX) && defined(USE_ALSA)
|
||||||
|
|
||||||
|
#include "common/config-manager.h"
|
||||||
|
#include "common/util.h"
|
||||||
|
#include "sound/musicplugin.h"
|
||||||
|
#include "sound/mpu401.h"
|
||||||
|
|
||||||
|
#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_MAJOR >= 1 || 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_DUPLEX, 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();
|
||||||
|
void close();
|
||||||
|
void send(uint32 b);
|
||||||
|
void sysEx(const byte *msg, uint16 length);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void send_event(int do_flush);
|
||||||
|
bool _isOpen;
|
||||||
|
snd_seq_event_t ev;
|
||||||
|
snd_seq_t *seq_handle;
|
||||||
|
int seq_client, seq_port;
|
||||||
|
int my_client, my_port;
|
||||||
|
static int parse_addr(const char *arg, int *client, int *port);
|
||||||
|
};
|
||||||
|
|
||||||
|
MidiDriver_ALSA::MidiDriver_ALSA()
|
||||||
|
: _isOpen(false), seq_handle(0), seq_client(0), seq_port(0), my_client(0), my_port(0)
|
||||||
|
{
|
||||||
|
memset(&ev, 0, sizeof(ev));
|
||||||
|
}
|
||||||
|
|
||||||
|
int MidiDriver_ALSA::open() {
|
||||||
|
const char *var = NULL;
|
||||||
|
|
||||||
|
if (_isOpen)
|
||||||
|
return MERR_ALREADY_OPEN;
|
||||||
|
_isOpen = true;
|
||||||
|
|
||||||
|
var = getenv("SCUMMVM_PORT");
|
||||||
|
if (!var && ConfMan.hasKey("alsa_port")) {
|
||||||
|
var = ConfMan.get("alsa_port").c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (var) {
|
||||||
|
if (parse_addr(var, &seq_client, &seq_port) < 0) {
|
||||||
|
error("Invalid port %s", var);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (my_snd_seq_open(&seq_handle) < 0) {
|
||||||
|
error("Can't open sequencer");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
my_client = snd_seq_client_id(seq_handle);
|
||||||
|
if (snd_seq_set_client_name(seq_handle, "SCUMMVM") < 0) {
|
||||||
|
error("Can't set sequencer client name");
|
||||||
|
}
|
||||||
|
snd_seq_set_client_group(seq_handle, "input");
|
||||||
|
|
||||||
|
my_port = snd_seq_create_simple_port(seq_handle, "SCUMMVM port 0",
|
||||||
|
SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE |
|
||||||
|
SND_SEQ_PORT_CAP_READ, SND_SEQ_PORT_TYPE_MIDI_GENERIC);
|
||||||
|
|
||||||
|
if (my_port < 0) {
|
||||||
|
snd_seq_close(seq_handle);
|
||||||
|
error("Can't create port");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (var) {
|
||||||
|
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) {
|
||||||
|
error("Can't subscribe to MIDI port (%d:%d) see README for help", seq_client, seq_port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
int defaultPorts[] = {
|
||||||
|
65, 0,
|
||||||
|
17, 0
|
||||||
|
};
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAYSIZE(defaultPorts); i += 2) {
|
||||||
|
seq_client = defaultPorts[i];
|
||||||
|
seq_port = defaultPorts[i + 1];
|
||||||
|
if (snd_seq_connect_to(seq_handle, my_port, seq_client, seq_port) >= 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i >= ARRAYSIZE(defaultPorts))
|
||||||
|
error("Can't subscribe to MIDI port (65:0) or (17:0)");
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Connected to Alsa sequencer client [%d:%d]\n", seq_client, seq_port);
|
||||||
|
printf("ALSA client initialised [%d:0]\n", my_client);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MidiDriver_ALSA::close() {
|
||||||
|
_isOpen = false;
|
||||||
|
MidiDriver_MPU401::close();
|
||||||
|
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 0xA0:
|
||||||
|
snd_seq_ev_set_keypress(&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 0xD0:
|
||||||
|
snd_seq_ev_set_chanpress(&ev, chanID, midiCmd[1]);
|
||||||
|
send_event(1);
|
||||||
|
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:
|
||||||
|
warning("Unknown MIDI Command: %08x", (int)b);
|
||||||
|
/* I don't know if this works but, well... */
|
||||||
|
send_event(1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MidiDriver_ALSA::sysEx(const byte *msg, uint16 length) {
|
||||||
|
unsigned char buf[266];
|
||||||
|
|
||||||
|
assert(length + 2 <= ARRAYSIZE(buf));
|
||||||
|
|
||||||
|
// Add SysEx frame
|
||||||
|
buf[0] = 0xF0;
|
||||||
|
memcpy(buf + 1, msg, length);
|
||||||
|
buf[length + 1] = 0xF7;
|
||||||
|
|
||||||
|
// Send it
|
||||||
|
snd_seq_ev_set_sysex(&ev, length + 2, &buf);
|
||||||
|
send_event(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int MidiDriver_ALSA::parse_addr(const char *arg, int *client, int *port) {
|
||||||
|
const 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Plugin interface
|
||||||
|
|
||||||
|
class AlsaMusicPlugin : public MusicPluginObject {
|
||||||
|
public:
|
||||||
|
const char *getName() const {
|
||||||
|
return "ALSA";
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *getId() const {
|
||||||
|
return "alsa";
|
||||||
|
}
|
||||||
|
|
||||||
|
MusicDevices getDevices() const;
|
||||||
|
Common::Error createInstance(MidiDriver **mididriver) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define perm_ok(pinfo,bits) ((snd_seq_port_info_get_capability(pinfo) & (bits)) == (bits))
|
||||||
|
|
||||||
|
static int check_permission(snd_seq_port_info_t *pinfo)
|
||||||
|
{
|
||||||
|
if (perm_ok(pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE)) {
|
||||||
|
if (!(snd_seq_port_info_get_capability(pinfo) & SND_SEQ_PORT_CAP_NO_EXPORT))
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
MusicDevices AlsaMusicPlugin::getDevices() const {
|
||||||
|
MusicDevices devices;
|
||||||
|
|
||||||
|
snd_seq_t *seq;
|
||||||
|
if (snd_seq_open(&seq, "default", SND_SEQ_OPEN_DUPLEX, 0) < 0)
|
||||||
|
return devices; // can't open sequencer
|
||||||
|
|
||||||
|
snd_seq_client_info_t *cinfo;
|
||||||
|
snd_seq_client_info_alloca(&cinfo);
|
||||||
|
snd_seq_port_info_t *pinfo;
|
||||||
|
snd_seq_port_info_alloca(&pinfo);
|
||||||
|
snd_seq_client_info_set_client(cinfo, -1);
|
||||||
|
while (snd_seq_query_next_client(seq, cinfo) >= 0) {
|
||||||
|
bool found_valid_port = false;
|
||||||
|
|
||||||
|
/* reset query info */
|
||||||
|
snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
|
||||||
|
snd_seq_port_info_set_port(pinfo, -1);
|
||||||
|
while (!found_valid_port && snd_seq_query_next_port(seq, pinfo) >= 0) {
|
||||||
|
if (check_permission(pinfo)) {
|
||||||
|
found_valid_port = true;
|
||||||
|
// TODO: Return a different music type depending on the configuration
|
||||||
|
devices.push_back(MusicDevice(this, snd_seq_client_info_get_name(cinfo), MT_GM));
|
||||||
|
//snd_seq_client_info_get_client(cinfo) : snd_seq_port_info_get_port(pinfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
snd_seq_close(seq);
|
||||||
|
|
||||||
|
return devices;
|
||||||
|
}
|
||||||
|
|
||||||
|
Common::Error AlsaMusicPlugin::createInstance(MidiDriver **mididriver) const {
|
||||||
|
*mididriver = new MidiDriver_ALSA();
|
||||||
|
|
||||||
|
return Common::kNoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
MidiDriver *MidiDriver_ALSA_create() {
|
||||||
|
MidiDriver *mididriver;
|
||||||
|
|
||||||
|
AlsaMusicPlugin p;
|
||||||
|
p.createInstance(&mididriver);
|
||||||
|
|
||||||
|
return mididriver;
|
||||||
|
}
|
||||||
|
|
||||||
|
//#if PLUGIN_ENABLED_DYNAMIC(ALSA)
|
||||||
|
//REGISTER_PLUGIN_DYNAMIC(ALSA, PLUGIN_TYPE_MUSIC, AlsaMusicPlugin);
|
||||||
|
//#else
|
||||||
|
REGISTER_PLUGIN_STATIC(ALSA, PLUGIN_TYPE_MUSIC, AlsaMusicPlugin);
|
||||||
|
//#endif
|
||||||
|
|
||||||
|
#endif
|
212
backends/midi/camd.cpp
Normal file
212
backends/midi/camd.cpp
Normal file
|
@ -0,0 +1,212 @@
|
||||||
|
/* Residual - A 3D game interpreter
|
||||||
|
*
|
||||||
|
* Residual is the legal property of its developers, whose names
|
||||||
|
* are too numerous to list here. Please refer to the AUTHORS
|
||||||
|
* 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/sys.h"
|
||||||
|
|
||||||
|
#if defined(__amigaos4__)
|
||||||
|
|
||||||
|
#include "common/endian.h"
|
||||||
|
#include "common/util.h"
|
||||||
|
#include "sound/musicplugin.h"
|
||||||
|
#include "sound/mpu401.h"
|
||||||
|
|
||||||
|
#include <proto/camd.h>
|
||||||
|
#include <proto/exec.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CAMD sequencer driver
|
||||||
|
* Mostly cut'n'pasted from FreeSCI by Christoph Reichenbach
|
||||||
|
*/
|
||||||
|
|
||||||
|
class MidiDriver_CAMD : public MidiDriver_MPU401 {
|
||||||
|
public:
|
||||||
|
MidiDriver_CAMD();
|
||||||
|
int open();
|
||||||
|
void close();
|
||||||
|
void send(uint32 b);
|
||||||
|
void sysEx(const byte *msg, uint16 length);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool _isOpen;
|
||||||
|
struct Library *_CamdBase;
|
||||||
|
struct CamdIFace *_ICamd;
|
||||||
|
struct MidiLink *_midi_link;
|
||||||
|
|
||||||
|
char *getDevice();
|
||||||
|
void closeAll();
|
||||||
|
};
|
||||||
|
|
||||||
|
MidiDriver_CAMD::MidiDriver_CAMD()
|
||||||
|
: _isOpen(false), _CamdBase(NULL), _ICamd(NULL), _midi_link(NULL) {
|
||||||
|
}
|
||||||
|
|
||||||
|
int MidiDriver_CAMD::open() {
|
||||||
|
if (_isOpen)
|
||||||
|
return MERR_ALREADY_OPEN;
|
||||||
|
|
||||||
|
_CamdBase = IExec->OpenLibrary("camd.library", 36L);
|
||||||
|
if (!_CamdBase) {
|
||||||
|
error("Could not open 'camd.library'");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ICamd = (struct CamdIFace *) IExec->GetInterface(_CamdBase, "main", 1, NULL);
|
||||||
|
if (!_ICamd) {
|
||||||
|
closeAll();
|
||||||
|
error("Error while retrieving CAMD interface");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MidiNode *midi_node;
|
||||||
|
midi_node = _ICamd->CreateMidi(MIDI_MsgQueue, 0L, MIDI_SysExSize, 4096L, MIDI_Name, "scummvm", TAG_END);
|
||||||
|
if (!midi_node) {
|
||||||
|
closeAll();
|
||||||
|
error("Could not create CAMD MIDI node");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *devicename = getDevice();
|
||||||
|
if (!devicename) {
|
||||||
|
closeAll();
|
||||||
|
error("Could not find an output device");
|
||||||
|
return MERR_DEVICE_NOT_AVAILABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
_midi_link = _ICamd->AddMidiLink(midi_node, MLTYPE_Sender, MLINK_Location, devicename, TAG_END);
|
||||||
|
if (!_midi_link) {
|
||||||
|
closeAll();
|
||||||
|
error("Could not create CAMD MIDI link to '%s'", devicename);
|
||||||
|
return MERR_CANNOT_CONNECT;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isOpen = true;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MidiDriver_CAMD::close() {
|
||||||
|
MidiDriver_MPU401::close();
|
||||||
|
closeAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MidiDriver_CAMD::send(uint32 b) {
|
||||||
|
ULONG data = READ_LE_UINT32(&b);
|
||||||
|
_ICamd->PutMidi(_midi_link, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MidiDriver_CAMD::sysEx(const byte *msg, uint16 length) {
|
||||||
|
unsigned char buf[266];
|
||||||
|
|
||||||
|
assert(length + 2 <= ARRAYSIZE(buf));
|
||||||
|
|
||||||
|
// Add SysEx frame
|
||||||
|
buf[0] = 0xF0;
|
||||||
|
memcpy(buf + 1, msg, length);
|
||||||
|
buf[length + 1] = 0xF7;
|
||||||
|
|
||||||
|
// Send it
|
||||||
|
_ICamd->PutSysEx(_midi_link, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *MidiDriver_CAMD::getDevice() {
|
||||||
|
char *retname = NULL;
|
||||||
|
|
||||||
|
APTR key;
|
||||||
|
if (key = _ICamd->LockCAMD(CD_Linkages)) {
|
||||||
|
struct MidiCluster *cluster = _ICamd->NextCluster(NULL);
|
||||||
|
|
||||||
|
while (cluster && !retname) {
|
||||||
|
// Get the current cluster name
|
||||||
|
char *dev = cluster->mcl_Node.ln_Name;
|
||||||
|
|
||||||
|
if (strstr(dev, "out") != NULL) {
|
||||||
|
// This is an output device, return this
|
||||||
|
retname = dev;
|
||||||
|
} else {
|
||||||
|
// Search the next one
|
||||||
|
cluster = _ICamd->NextCluster(cluster);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ICamd->UnlockCAMD(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return retname;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MidiDriver_CAMD::closeAll() {
|
||||||
|
if (_ICamd)
|
||||||
|
IExec->DropInterface((struct Interface *)_ICamd);
|
||||||
|
if (_CamdBase)
|
||||||
|
IExec->CloseLibrary(_CamdBase);
|
||||||
|
|
||||||
|
_isOpen = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Plugin interface
|
||||||
|
|
||||||
|
class CamdMusicPlugin : public MusicPluginObject {
|
||||||
|
public:
|
||||||
|
const char *getName() const {
|
||||||
|
return "CAMD";
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *getId() const {
|
||||||
|
return "camd";
|
||||||
|
}
|
||||||
|
|
||||||
|
MusicDevices getDevices() const;
|
||||||
|
Common::Error createInstance(MidiDriver **mididriver) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
MusicDevices CamdMusicPlugin::getDevices() const {
|
||||||
|
MusicDevices devices;
|
||||||
|
// TODO: Return a different music type depending on the configuration
|
||||||
|
// TODO: List the available devices
|
||||||
|
devices.push_back(MusicDevice(this, "", MT_GM));
|
||||||
|
return devices;
|
||||||
|
}
|
||||||
|
|
||||||
|
Common::Error CamdMusicPlugin::createInstance(MidiDriver **mididriver) const {
|
||||||
|
*mididriver = new MidiDriver_CAMD();
|
||||||
|
|
||||||
|
return Common::kNoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
MidiDriver *MidiDriver_CAMD_create() {
|
||||||
|
MidiDriver *mididriver;
|
||||||
|
|
||||||
|
CamdMusicPlugin p;
|
||||||
|
p.createInstance(&mididriver);
|
||||||
|
|
||||||
|
return mididriver;
|
||||||
|
}
|
||||||
|
|
||||||
|
//#if PLUGIN_ENABLED_DYNAMIC(CAMD)
|
||||||
|
//REGISTER_PLUGIN_DYNAMIC(CAMD, PLUGIN_TYPE_MUSIC, CamdMusicPlugin);
|
||||||
|
//#else
|
||||||
|
REGISTER_PLUGIN_STATIC(CAMD, PLUGIN_TYPE_MUSIC, CamdMusicPlugin);
|
||||||
|
//#endif
|
||||||
|
|
||||||
|
#endif
|
253
backends/midi/coreaudio.cpp
Normal file
253
backends/midi/coreaudio.cpp
Normal file
|
@ -0,0 +1,253 @@
|
||||||
|
/* Residual - A 3D game interpreter
|
||||||
|
*
|
||||||
|
* Residual is the legal property of its developers, whose names
|
||||||
|
* are too numerous to list here. Please refer to the AUTHORS
|
||||||
|
* 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$
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef MACOSX
|
||||||
|
|
||||||
|
// HACK to disable deprecated warnings under Mac OS X 10.5.
|
||||||
|
// Apple depracted the AUGraphNewNode & AUGraphGetNodeInfo APIs
|
||||||
|
// in favor of the new AUGraphAddNode & AUGraphNodeInfo APIs.
|
||||||
|
// While it would be trivial to switch to those, this would break
|
||||||
|
// binary compatibility with all pre-10.5 systems, so we don't want
|
||||||
|
// to do that just now. Maybe when 10.6 comes... :)
|
||||||
|
#include <AvailabilityMacros.h>
|
||||||
|
#undef DEPRECATED_ATTRIBUTE
|
||||||
|
#define DEPRECATED_ATTRIBUTE
|
||||||
|
|
||||||
|
|
||||||
|
#include "common/config-manager.h"
|
||||||
|
#include "common/util.h"
|
||||||
|
#include "sound/musicplugin.h"
|
||||||
|
#include "sound/mpu401.h"
|
||||||
|
|
||||||
|
#include <CoreServices/CoreServices.h>
|
||||||
|
#include <AudioToolbox/AUGraph.h>
|
||||||
|
|
||||||
|
|
||||||
|
// Activating the following switch disables reverb support in the CoreAudio
|
||||||
|
// midi backend. Reverb will suck away a *lot* of CPU time, so on slower
|
||||||
|
// systems, you may want to turn it off completely.
|
||||||
|
// TODO: Maybe make this a config option?
|
||||||
|
//#define COREAUDIO_DISABLE_REVERB
|
||||||
|
|
||||||
|
|
||||||
|
// A macro to simplify error handling a bit.
|
||||||
|
#define RequireNoErr(error) \
|
||||||
|
do { \
|
||||||
|
err = error; \
|
||||||
|
if (err != noErr) \
|
||||||
|
goto bail; \
|
||||||
|
} while (false)
|
||||||
|
|
||||||
|
|
||||||
|
/* CoreAudio MIDI driver
|
||||||
|
* By Max Horn / Fingolfin
|
||||||
|
* Based on code by Benjamin W. Zale
|
||||||
|
*/
|
||||||
|
class MidiDriver_CORE : public MidiDriver_MPU401 {
|
||||||
|
public:
|
||||||
|
MidiDriver_CORE();
|
||||||
|
int open();
|
||||||
|
void close();
|
||||||
|
void send(uint32 b);
|
||||||
|
void sysEx(const byte *msg, uint16 length);
|
||||||
|
|
||||||
|
private:
|
||||||
|
AUGraph _auGraph;
|
||||||
|
AudioUnit _synth;
|
||||||
|
};
|
||||||
|
|
||||||
|
MidiDriver_CORE::MidiDriver_CORE()
|
||||||
|
: _auGraph(0) {
|
||||||
|
}
|
||||||
|
|
||||||
|
int MidiDriver_CORE::open() {
|
||||||
|
OSStatus err = 0;
|
||||||
|
|
||||||
|
if (_auGraph)
|
||||||
|
return MERR_ALREADY_OPEN;
|
||||||
|
|
||||||
|
// Open the Music Device.
|
||||||
|
RequireNoErr(NewAUGraph(&_auGraph));
|
||||||
|
|
||||||
|
AUNode outputNode, synthNode;
|
||||||
|
ComponentDescription desc;
|
||||||
|
|
||||||
|
// The default output device
|
||||||
|
desc.componentType = kAudioUnitType_Output;
|
||||||
|
desc.componentSubType = kAudioUnitSubType_DefaultOutput;
|
||||||
|
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
|
||||||
|
desc.componentFlags = 0;
|
||||||
|
desc.componentFlagsMask = 0;
|
||||||
|
RequireNoErr(AUGraphNewNode(_auGraph, &desc, 0, NULL, &outputNode));
|
||||||
|
|
||||||
|
// The built-in default (softsynth) music device
|
||||||
|
desc.componentType = kAudioUnitType_MusicDevice;
|
||||||
|
desc.componentSubType = kAudioUnitSubType_DLSSynth;
|
||||||
|
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
|
||||||
|
RequireNoErr(AUGraphNewNode(_auGraph, &desc, 0, NULL, &synthNode));
|
||||||
|
|
||||||
|
// Connect the softsynth to the default output
|
||||||
|
RequireNoErr(AUGraphConnectNodeInput(_auGraph, synthNode, 0, outputNode, 0));
|
||||||
|
|
||||||
|
// Open and initialize the whole graph
|
||||||
|
RequireNoErr(AUGraphOpen(_auGraph));
|
||||||
|
RequireNoErr(AUGraphInitialize(_auGraph));
|
||||||
|
|
||||||
|
// Get the music device from the graph.
|
||||||
|
RequireNoErr(AUGraphGetNodeInfo(_auGraph, synthNode, NULL, NULL, NULL, &_synth));
|
||||||
|
|
||||||
|
|
||||||
|
// Load custom soundfont, if specified
|
||||||
|
if (ConfMan.hasKey("soundfont")) {
|
||||||
|
FSRef fsref;
|
||||||
|
FSSpec fsSpec;
|
||||||
|
const char *soundfont = ConfMan.get("soundfont").c_str();
|
||||||
|
|
||||||
|
err = FSPathMakeRef ((const byte *)soundfont, &fsref, NULL);
|
||||||
|
|
||||||
|
if (err == noErr) {
|
||||||
|
err = FSGetCatalogInfo (&fsref, kFSCatInfoNone, NULL, NULL, &fsSpec, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err == noErr) {
|
||||||
|
// TODO: We should really check here whether the file contains an
|
||||||
|
// actual soundfont...
|
||||||
|
err = AudioUnitSetProperty (
|
||||||
|
_synth,
|
||||||
|
kMusicDeviceProperty_SoundBankFSSpec, kAudioUnitScope_Global,
|
||||||
|
0,
|
||||||
|
&fsSpec, sizeof(fsSpec)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err != noErr)
|
||||||
|
warning("Failed loading custom sound font '%s' (error %ld)\n", soundfont, err);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef COREAUDIO_DISABLE_REVERB
|
||||||
|
// Disable reverb mode, as that sucks up a lot of CPU power, which can
|
||||||
|
// be painful on low end machines.
|
||||||
|
// TODO: Make this customizable via a config key?
|
||||||
|
UInt32 usesReverb = 0;
|
||||||
|
AudioUnitSetProperty (_synth, kMusicDeviceProperty_UsesInternalReverb,
|
||||||
|
kAudioUnitScope_Global, 0, &usesReverb, sizeof (usesReverb));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// Finally: Start the graph!
|
||||||
|
RequireNoErr(AUGraphStart(_auGraph));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
bail:
|
||||||
|
if (_auGraph) {
|
||||||
|
AUGraphStop(_auGraph);
|
||||||
|
DisposeAUGraph(_auGraph);
|
||||||
|
_auGraph = 0;
|
||||||
|
}
|
||||||
|
return MERR_CANNOT_CONNECT;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MidiDriver_CORE::close() {
|
||||||
|
MidiDriver_MPU401::close();
|
||||||
|
|
||||||
|
if (_auGraph) {
|
||||||
|
AUGraphStop(_auGraph);
|
||||||
|
DisposeAUGraph(_auGraph);
|
||||||
|
_auGraph = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MidiDriver_CORE::send(uint32 b) {
|
||||||
|
assert(_auGraph != NULL);
|
||||||
|
|
||||||
|
byte status_byte = (b & 0x000000FF);
|
||||||
|
byte first_byte = (b & 0x0000FF00) >> 8;
|
||||||
|
byte second_byte = (b & 0x00FF0000) >> 16;
|
||||||
|
|
||||||
|
MusicDeviceMIDIEvent(_synth, status_byte, first_byte, second_byte, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MidiDriver_CORE::sysEx(const byte *msg, uint16 length) {
|
||||||
|
unsigned char buf[266];
|
||||||
|
|
||||||
|
assert(length + 2 <= ARRAYSIZE(buf));
|
||||||
|
assert(_auGraph != NULL);
|
||||||
|
|
||||||
|
// Add SysEx frame
|
||||||
|
buf[0] = 0xF0;
|
||||||
|
memcpy(buf + 1, msg, length);
|
||||||
|
buf[length + 1] = 0xF7;
|
||||||
|
|
||||||
|
// Send it
|
||||||
|
MusicDeviceSysEx(_synth, buf, length+2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Plugin interface
|
||||||
|
|
||||||
|
class CoreAudioMusicPlugin : public MusicPluginObject {
|
||||||
|
public:
|
||||||
|
const char *getName() const {
|
||||||
|
return "CoreAudio";
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *getId() const {
|
||||||
|
return "core";
|
||||||
|
}
|
||||||
|
|
||||||
|
MusicDevices getDevices() const;
|
||||||
|
Common::Error createInstance(MidiDriver **mididriver) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
MusicDevices CoreAudioMusicPlugin::getDevices() const {
|
||||||
|
MusicDevices devices;
|
||||||
|
// TODO: Return a different music type depending on the configuration
|
||||||
|
// TODO: List the available devices
|
||||||
|
devices.push_back(MusicDevice(this, "", MT_GM));
|
||||||
|
return devices;
|
||||||
|
}
|
||||||
|
|
||||||
|
Common::Error CoreAudioMusicPlugin::createInstance(MidiDriver **mididriver) const {
|
||||||
|
*mididriver = new MidiDriver_CORE();
|
||||||
|
|
||||||
|
return Common::kNoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
MidiDriver *MidiDriver_CORE_create() {
|
||||||
|
MidiDriver *mididriver;
|
||||||
|
|
||||||
|
CoreAudioMusicPlugin p;
|
||||||
|
p.createInstance(&mididriver);
|
||||||
|
|
||||||
|
return mididriver;
|
||||||
|
}
|
||||||
|
|
||||||
|
//#if PLUGIN_ENABLED_DYNAMIC(COREAUDIO)
|
||||||
|
//REGISTER_PLUGIN_DYNAMIC(COREAUDIO, PLUGIN_TYPE_MUSIC, CoreAudioMusicPlugin);
|
||||||
|
//#else
|
||||||
|
REGISTER_PLUGIN_STATIC(COREAUDIO, PLUGIN_TYPE_MUSIC, CoreAudioMusicPlugin);
|
||||||
|
//#endif
|
||||||
|
|
||||||
|
#endif // MACOSX
|
225
backends/midi/coremidi.cpp
Normal file
225
backends/midi/coremidi.cpp
Normal file
|
@ -0,0 +1,225 @@
|
||||||
|
/* Residual - A 3D game interpreter
|
||||||
|
*
|
||||||
|
* Residual is the legal property of its developers, whose names
|
||||||
|
* are too numerous to list here. Please refer to the AUTHORS
|
||||||
|
* 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$
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef MACOSX
|
||||||
|
|
||||||
|
#include "common/config-manager.h"
|
||||||
|
#include "common/util.h"
|
||||||
|
#include "sound/musicplugin.h"
|
||||||
|
#include "sound/mpu401.h"
|
||||||
|
|
||||||
|
#include <CoreMIDI/CoreMIDI.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
For information on how to unify the CoreMidi and MusicDevice code:
|
||||||
|
|
||||||
|
http://lists.apple.com/archives/Coreaudio-api/2005/Jun/msg00194.html
|
||||||
|
http://lists.apple.com/archives/coreaudio-api/2003/Mar/msg00248.html
|
||||||
|
http://lists.apple.com/archives/coreaudio-api/2003/Jul/msg00137.html
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/* CoreMIDI MIDI driver
|
||||||
|
* By Max Horn
|
||||||
|
*/
|
||||||
|
class MidiDriver_CoreMIDI : public MidiDriver_MPU401 {
|
||||||
|
public:
|
||||||
|
MidiDriver_CoreMIDI();
|
||||||
|
~MidiDriver_CoreMIDI();
|
||||||
|
int open();
|
||||||
|
void close();
|
||||||
|
void send(uint32 b);
|
||||||
|
void sysEx(const byte *msg, uint16 length);
|
||||||
|
|
||||||
|
private:
|
||||||
|
MIDIClientRef mClient;
|
||||||
|
MIDIPortRef mOutPort;
|
||||||
|
MIDIEndpointRef mDest;
|
||||||
|
};
|
||||||
|
|
||||||
|
MidiDriver_CoreMIDI::MidiDriver_CoreMIDI()
|
||||||
|
: mClient(0), mOutPort(0), mDest(0) {
|
||||||
|
|
||||||
|
OSStatus err;
|
||||||
|
err = MIDIClientCreate(CFSTR("ScummVM MIDI Driver for OS X"), NULL, NULL, &mClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
MidiDriver_CoreMIDI::~MidiDriver_CoreMIDI() {
|
||||||
|
if (mClient)
|
||||||
|
MIDIClientDispose(mClient);
|
||||||
|
mClient = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int MidiDriver_CoreMIDI::open() {
|
||||||
|
if (mDest)
|
||||||
|
return MERR_ALREADY_OPEN;
|
||||||
|
|
||||||
|
OSStatus err = noErr;
|
||||||
|
|
||||||
|
mOutPort = 0;
|
||||||
|
|
||||||
|
int dests = MIDIGetNumberOfDestinations();
|
||||||
|
if (dests > 0 && mClient) {
|
||||||
|
mDest = MIDIGetDestination(0);
|
||||||
|
err = MIDIOutputPortCreate( mClient,
|
||||||
|
CFSTR("scummvm_output_port"),
|
||||||
|
&mOutPort);
|
||||||
|
} else {
|
||||||
|
return MERR_DEVICE_NOT_AVAILABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err != noErr)
|
||||||
|
return MERR_CANNOT_CONNECT;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MidiDriver_CoreMIDI::close() {
|
||||||
|
MidiDriver_MPU401::close();
|
||||||
|
|
||||||
|
if (mOutPort && mDest) {
|
||||||
|
MIDIPortDispose(mOutPort);
|
||||||
|
mOutPort = 0;
|
||||||
|
mDest = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MidiDriver_CoreMIDI::send(uint32 b) {
|
||||||
|
assert(mOutPort != NULL);
|
||||||
|
assert(mDest != NULL);
|
||||||
|
|
||||||
|
// Extract the MIDI data
|
||||||
|
byte status_byte = (b & 0x000000FF);
|
||||||
|
byte first_byte = (b & 0x0000FF00) >> 8;
|
||||||
|
byte second_byte = (b & 0x00FF0000) >> 16;
|
||||||
|
|
||||||
|
// Generate a single MIDI packet with that data
|
||||||
|
MIDIPacketList packetList;
|
||||||
|
MIDIPacket *packet = &packetList.packet[0];
|
||||||
|
|
||||||
|
packetList.numPackets = 1;
|
||||||
|
|
||||||
|
packet->timeStamp = 0;
|
||||||
|
packet->data[0] = status_byte;
|
||||||
|
packet->data[1] = first_byte;
|
||||||
|
packet->data[2] = second_byte;
|
||||||
|
|
||||||
|
// Compute the correct length of the MIDI command. This is important,
|
||||||
|
// else things may screw up badly...
|
||||||
|
switch (status_byte & 0xF0) {
|
||||||
|
case 0x80: // Note Off
|
||||||
|
case 0x90: // Note On
|
||||||
|
case 0xA0: // Polyphonic Aftertouch
|
||||||
|
case 0xB0: // Controller Change
|
||||||
|
case 0xE0: // Pitch Bending
|
||||||
|
packet->length = 3;
|
||||||
|
break;
|
||||||
|
case 0xC0: // Programm Change
|
||||||
|
case 0xD0: // Monophonic Aftertouch
|
||||||
|
packet->length = 2;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
warning("CoreMIDI driver encountered unsupported status byte: 0x%02x", status_byte);
|
||||||
|
packet->length = 3;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally send it out to the synthesizer.
|
||||||
|
MIDISend(mOutPort, mDest, &packetList);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MidiDriver_CoreMIDI::sysEx(const byte *msg, uint16 length) {
|
||||||
|
assert(mOutPort != NULL);
|
||||||
|
assert(mDest != NULL);
|
||||||
|
|
||||||
|
byte buf[384];
|
||||||
|
MIDIPacketList *packetList = (MIDIPacketList *)buf;
|
||||||
|
MIDIPacket *packet = packetList->packet;
|
||||||
|
|
||||||
|
assert(sizeof(buf) >= sizeof(UInt32) + sizeof(MIDITimeStamp) + sizeof(UInt16) + length + 2);
|
||||||
|
|
||||||
|
packetList->numPackets = 1;
|
||||||
|
|
||||||
|
packet->timeStamp = 0;
|
||||||
|
|
||||||
|
// Add SysEx frame
|
||||||
|
packet->length = length + 2;
|
||||||
|
packet->data[0] = 0xF0;
|
||||||
|
memcpy(packet->data + 1, msg, length);
|
||||||
|
packet->data[length + 1] = 0xF7;
|
||||||
|
|
||||||
|
// Send it
|
||||||
|
MIDISend(mOutPort, mDest, packetList);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Plugin interface
|
||||||
|
|
||||||
|
class CoreMIDIMusicPlugin : public MusicPluginObject {
|
||||||
|
public:
|
||||||
|
const char *getName() const {
|
||||||
|
return "CoreMIDI";
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *getId() const {
|
||||||
|
return "coremidi";
|
||||||
|
}
|
||||||
|
|
||||||
|
MusicDevices getDevices() const;
|
||||||
|
Common::Error createInstance(MidiDriver **mididriver) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
MusicDevices CoreMIDIMusicPlugin::getDevices() const {
|
||||||
|
MusicDevices devices;
|
||||||
|
// TODO: Return a different music type depending on the configuration
|
||||||
|
// TODO: List the available devices
|
||||||
|
devices.push_back(MusicDevice(this, "", MT_GM));
|
||||||
|
return devices;
|
||||||
|
}
|
||||||
|
|
||||||
|
Common::Error CoreMIDIMusicPlugin::createInstance(MidiDriver **mididriver) const {
|
||||||
|
*mididriver = new MidiDriver_CoreMIDI();
|
||||||
|
|
||||||
|
return Common::kNoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
MidiDriver *MidiDriver_CoreMIDI_create() {
|
||||||
|
MidiDriver *mididriver;
|
||||||
|
|
||||||
|
CoreMIDIMusicPlugin p;
|
||||||
|
p.createInstance(&mididriver);
|
||||||
|
|
||||||
|
return mididriver;
|
||||||
|
}
|
||||||
|
|
||||||
|
//#if PLUGIN_ENABLED_DYNAMIC(COREMIDI)
|
||||||
|
//REGISTER_PLUGIN_DYNAMIC(COREMIDI, PLUGIN_TYPE_MUSIC, CoreMIDIMusicPlugin);
|
||||||
|
//#else
|
||||||
|
REGISTER_PLUGIN_STATIC(COREMIDI, PLUGIN_TYPE_MUSIC, CoreMIDIMusicPlugin);
|
||||||
|
//#endif
|
||||||
|
|
||||||
|
#endif // MACOSX
|
248
backends/midi/dmedia.cpp
Normal file
248
backends/midi/dmedia.cpp
Normal file
|
@ -0,0 +1,248 @@
|
||||||
|
/* Residual - A 3D game interpreter
|
||||||
|
*
|
||||||
|
* Residual is the legal property of its developers, whose names
|
||||||
|
* are too numerous to list here. Please refer to the AUTHORS
|
||||||
|
* 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: dmedia.cpp$
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IRIX dmedia support by Rainer Canavan <scumm@canavan.de>
|
||||||
|
* some code liberated from seq.cpp and coremidi.cpp
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if defined(IRIX)
|
||||||
|
|
||||||
|
#include "common/sys.h"
|
||||||
|
#include "common/util.h"
|
||||||
|
#include "common/config-manager.h"
|
||||||
|
#include "sound/musicplugin.h"
|
||||||
|
#include "sound/mpu401.h"
|
||||||
|
|
||||||
|
#include <dmedia/midi.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <bstring.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// IRIX dmedia midi driver
|
||||||
|
//
|
||||||
|
////////////////////////////////////////
|
||||||
|
|
||||||
|
#define SEQ_MIDIPUTC 5
|
||||||
|
|
||||||
|
class MidiDriver_DMEDIA : public MidiDriver_MPU401 {
|
||||||
|
public:
|
||||||
|
MidiDriver_DMEDIA();
|
||||||
|
int open();
|
||||||
|
void close();
|
||||||
|
void send(uint32 b);
|
||||||
|
void sysEx(const byte *msg, uint16 length);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool _isOpen;
|
||||||
|
int _deviceNum;
|
||||||
|
char *_midiportName;
|
||||||
|
MDport _midiPort;
|
||||||
|
int _fd;
|
||||||
|
};
|
||||||
|
|
||||||
|
MidiDriver_DMEDIA::MidiDriver_DMEDIA() {
|
||||||
|
_isOpen = false;
|
||||||
|
_deviceNum = 0;
|
||||||
|
_midiportName = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int MidiDriver_DMEDIA::open() {
|
||||||
|
int numinterfaces;
|
||||||
|
int i;
|
||||||
|
const char *var;
|
||||||
|
char *portName;
|
||||||
|
|
||||||
|
if (_isOpen)
|
||||||
|
return MERR_ALREADY_OPEN;
|
||||||
|
_isOpen = true;
|
||||||
|
|
||||||
|
numinterfaces = mdInit();
|
||||||
|
if (numinterfaces <= 0) {
|
||||||
|
fprintf(stderr, "No MIDI interfaces configured.\n");
|
||||||
|
perror("Cannot initialize libmd for sound output");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getenv("SCUMMVM_MIDIPORT")) {
|
||||||
|
_deviceNum = atoi(getenv("SCUMMVM_MIDIPORT"));
|
||||||
|
_midiportName = mdGetName(_deviceNum);
|
||||||
|
} else {
|
||||||
|
var = ConfMan.get("dmedia_port").c_str();
|
||||||
|
if (strlen(var) > 0) {
|
||||||
|
for (i = 0; i < numinterfaces; i++) {
|
||||||
|
portName = mdGetName(i);
|
||||||
|
if (strcmp(var, portName) == 0) {
|
||||||
|
_deviceNum = i;
|
||||||
|
_midiportName = portName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_midiPort = mdOpenOutPort(_midiportName);
|
||||||
|
if (!_midiPort) {
|
||||||
|
warning("Failed to open MIDI interface %s", _midiportName);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
_fd = mdGetFd(_midiPort);
|
||||||
|
if (!_fd) {
|
||||||
|
warning("Failed to aquire filehandle for MIDI port %s", _midiportName);
|
||||||
|
mdClosePort(_midiPort);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
mdSetStampMode(_midiPort, MD_NOSTAMP); /* don't use Timestamps */
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MidiDriver_DMEDIA::close() {
|
||||||
|
mdClosePort(_midiPort);
|
||||||
|
_isOpen = false;
|
||||||
|
_deviceNum = 0;
|
||||||
|
_midiportName = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MidiDriver_DMEDIA::send(uint32 b) {
|
||||||
|
MDevent event;
|
||||||
|
byte status_byte = (b & 0x000000FF);
|
||||||
|
byte first_byte = (b & 0x0000FF00) >> 8;
|
||||||
|
byte second_byte = (b & 0x00FF0000) >> 16;
|
||||||
|
|
||||||
|
|
||||||
|
event.sysexmsg = NULL;
|
||||||
|
event.msg[0] = status_byte;
|
||||||
|
event.msg[1] = first_byte;
|
||||||
|
event.msg[2] = second_byte;
|
||||||
|
|
||||||
|
switch (status_byte & 0xF0) {
|
||||||
|
case 0x80: // Note Off
|
||||||
|
case 0x90: // Note On
|
||||||
|
case 0xA0: // Polyphonic Aftertouch
|
||||||
|
case 0xB0: // Controller Change
|
||||||
|
case 0xE0: // Pitch Bending
|
||||||
|
event.msglen = 3;
|
||||||
|
break;
|
||||||
|
case 0xC0: // Programm Change
|
||||||
|
case 0xD0: // Monophonic Aftertouch
|
||||||
|
event.msglen = 2;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
warning("DMediaMIDI driver encountered unsupported status byte: 0x%02x", status_byte);
|
||||||
|
event.msglen = 3;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (mdSend(_midiPort, &event, 1) != 1) {
|
||||||
|
warning("failed sending MIDI event (dump follows...)");
|
||||||
|
warning("MIDI Event (len=%u):", event.msglen);
|
||||||
|
for (int i = 0; i < event.msglen; i++) warning("%02x ", (int)event.msg[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MidiDriver_DMEDIA::sysEx (const byte *msg, uint16 length) {
|
||||||
|
MDevent event;
|
||||||
|
char buf [1024];
|
||||||
|
|
||||||
|
assert(length + 2 <= 256);
|
||||||
|
|
||||||
|
memcpy(buf, msg, length);
|
||||||
|
buf[length] = MD_EOX;
|
||||||
|
event.sysexmsg = buf;
|
||||||
|
event.msglen = length;
|
||||||
|
event.msg[0] = MD_SYSEX;
|
||||||
|
event.msg[1] = 0;
|
||||||
|
event.msg[2] = 0;
|
||||||
|
|
||||||
|
if (mdSend(_midiPort, &event, 1) != 1) {
|
||||||
|
fprintf(stderr, "failed sending MIDI SYSEX event (dump follows...)\n");
|
||||||
|
for (int i = 0; i < event.msglen; i++) warning("%02x ", (int)event.msg[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Plugin interface
|
||||||
|
|
||||||
|
class DMediaMusicPlugin : public MusicPluginObject {
|
||||||
|
public:
|
||||||
|
const char *getName() const {
|
||||||
|
return "DMedia";
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *getId() const {
|
||||||
|
return "dmedia";
|
||||||
|
}
|
||||||
|
|
||||||
|
MusicDevices getDevices() const;
|
||||||
|
Common::Error createInstance(MidiDriver **mididriver) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
MusicDevices DMediaMusicPlugin::getDevices() const {
|
||||||
|
int numinterfaces;
|
||||||
|
int i;
|
||||||
|
char *portName;
|
||||||
|
MusicDevices devices;
|
||||||
|
|
||||||
|
// TODO: Return a different music type depending on the configuration
|
||||||
|
|
||||||
|
numinterfaces = mdInit();
|
||||||
|
if (numinterfaces <= 0) {
|
||||||
|
fprintf(stderr, "No MIDI interfaces configured.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i=0; i<numinterfaces; i++) {
|
||||||
|
portName = mdGetName(0);
|
||||||
|
fprintf(stderr, "device %i %s\n", i, portName);
|
||||||
|
devices.push_back(MusicDevice(this, portName, MT_GM));
|
||||||
|
}
|
||||||
|
|
||||||
|
return devices;
|
||||||
|
}
|
||||||
|
|
||||||
|
Common::Error DMediaMusicPlugin::createInstance(MidiDriver **mididriver) const {
|
||||||
|
*mididriver = new MidiDriver_DMEDIA();
|
||||||
|
|
||||||
|
return Common::kNoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
MidiDriver *MidiDriver_DMEDIA_create() {
|
||||||
|
MidiDriver *mididriver;
|
||||||
|
|
||||||
|
DMediaMusicPlugin p;
|
||||||
|
p.createInstance(&mididriver);
|
||||||
|
|
||||||
|
return mididriver;
|
||||||
|
}
|
||||||
|
|
||||||
|
//#if PLUGIN_ENABLED_DYNAMIC(DMEDIA)
|
||||||
|
//REGISTER_PLUGIN_DYNAMIC(DMEDIA, PLUGIN_TYPE_MUSIC, DMediaMusicPlugin);
|
||||||
|
//#else
|
||||||
|
REGISTER_PLUGIN_STATIC(DMEDIA, PLUGIN_TYPE_MUSIC, DMediaMusicPlugin);
|
||||||
|
//#endif
|
||||||
|
|
||||||
|
#endif
|
219
backends/midi/seq.cpp
Normal file
219
backends/midi/seq.cpp
Normal file
|
@ -0,0 +1,219 @@
|
||||||
|
/* Residual - A 3D game interpreter
|
||||||
|
*
|
||||||
|
* Residual is the legal property of its developers, whose names
|
||||||
|
* are too numerous to list here. Please refer to the AUTHORS
|
||||||
|
* 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$
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Raw output support by Michael Pearce
|
||||||
|
* Alsa support by Nicolas Noble <nicolas@nobis-crew.org> copied from
|
||||||
|
* both the QuickTime support and (vkeybd http://www.alsa-project.org/~iwai/alsa.html)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if defined(UNIX) && !defined(__BEOS__) && !defined(__MAEMO__) && !defined(__MINT__)
|
||||||
|
|
||||||
|
#include "common/util.h"
|
||||||
|
#include "sound/musicplugin.h"
|
||||||
|
#include "sound/mpu401.h"
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Unix dev/sequencer driver
|
||||||
|
//
|
||||||
|
////////////////////////////////////////
|
||||||
|
|
||||||
|
#define SEQ_MIDIPUTC 5
|
||||||
|
|
||||||
|
class MidiDriver_SEQ : public MidiDriver_MPU401 {
|
||||||
|
public:
|
||||||
|
MidiDriver_SEQ();
|
||||||
|
int open();
|
||||||
|
void close();
|
||||||
|
void send(uint32 b);
|
||||||
|
void sysEx(const byte *msg, uint16 length);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool _isOpen;
|
||||||
|
int device, _device_num;
|
||||||
|
};
|
||||||
|
|
||||||
|
MidiDriver_SEQ::MidiDriver_SEQ() {
|
||||||
|
_isOpen = false;
|
||||||
|
device = 0;
|
||||||
|
_device_num = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int MidiDriver_SEQ::open() {
|
||||||
|
char *device_name;
|
||||||
|
char dev_seq[] = "/dev/sequencer";
|
||||||
|
|
||||||
|
if (_isOpen)
|
||||||
|
return MERR_ALREADY_OPEN;
|
||||||
|
_isOpen = true;
|
||||||
|
device = 0;
|
||||||
|
|
||||||
|
device_name = getenv("SCUMMVM_MIDI");
|
||||||
|
|
||||||
|
if (device_name == NULL) {
|
||||||
|
warning("SCUMMVM_MIDI environment variable not set, using /dev/sequencer");
|
||||||
|
device_name = dev_seq;
|
||||||
|
}
|
||||||
|
|
||||||
|
device = (::open((device_name), O_RDWR, 0));
|
||||||
|
|
||||||
|
if ((device_name == NULL) || (device < 0)) {
|
||||||
|
if (device_name == NULL)
|
||||||
|
warning("Opening /dev/null (no music will be heard) ");
|
||||||
|
else
|
||||||
|
warning("Cannot open rawmidi device %s - using /dev/null (no music will be heard) ",
|
||||||
|
device_name);
|
||||||
|
device = (::open(("/dev/null"), O_RDWR, 0));
|
||||||
|
if (device < 0)
|
||||||
|
error("Cannot open /dev/null to dump midi output");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getenv("SCUMMVM_MIDIPORT"))
|
||||||
|
_device_num = atoi(getenv("SCUMMVM_MIDIPORT"));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MidiDriver_SEQ::close() {
|
||||||
|
MidiDriver_MPU401::close();
|
||||||
|
::close(device);
|
||||||
|
_isOpen = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MidiDriver_SEQ::send(uint32 b) {
|
||||||
|
unsigned char buf[256];
|
||||||
|
int position = 0;
|
||||||
|
|
||||||
|
switch (b & 0xF0) {
|
||||||
|
case 0x80:
|
||||||
|
case 0x90:
|
||||||
|
case 0xA0:
|
||||||
|
case 0xB0:
|
||||||
|
case 0xE0:
|
||||||
|
buf[position++] = SEQ_MIDIPUTC;
|
||||||
|
buf[position++] = (unsigned char)b;
|
||||||
|
buf[position++] = _device_num;
|
||||||
|
buf[position++] = 0;
|
||||||
|
buf[position++] = SEQ_MIDIPUTC;
|
||||||
|
buf[position++] = (unsigned char)((b >> 8) & 0x7F);
|
||||||
|
buf[position++] = _device_num;
|
||||||
|
buf[position++] = 0;
|
||||||
|
buf[position++] = SEQ_MIDIPUTC;
|
||||||
|
buf[position++] = (unsigned char)((b >> 16) & 0x7F);
|
||||||
|
buf[position++] = _device_num;
|
||||||
|
buf[position++] = 0;
|
||||||
|
break;
|
||||||
|
case 0xC0:
|
||||||
|
case 0xD0:
|
||||||
|
buf[position++] = SEQ_MIDIPUTC;
|
||||||
|
buf[position++] = (unsigned char)b;
|
||||||
|
buf[position++] = _device_num;
|
||||||
|
buf[position++] = 0;
|
||||||
|
buf[position++] = SEQ_MIDIPUTC;
|
||||||
|
buf[position++] = (unsigned char)((b >> 8) & 0x7F);
|
||||||
|
buf[position++] = _device_num;
|
||||||
|
buf[position++] = 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
warning("MidiDriver_SEQ::send: unknown : %08x", (int)b);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
(void)write(device, buf, position);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MidiDriver_SEQ::sysEx (const byte *msg, uint16 length) {
|
||||||
|
unsigned char buf [266*4];
|
||||||
|
int position = 0;
|
||||||
|
const byte *chr = msg;
|
||||||
|
|
||||||
|
assert(length + 2 <= 266);
|
||||||
|
|
||||||
|
buf[position++] = SEQ_MIDIPUTC;
|
||||||
|
buf[position++] = 0xF0;
|
||||||
|
buf[position++] = _device_num;
|
||||||
|
buf[position++] = 0;
|
||||||
|
for (; length; --length, ++chr) {
|
||||||
|
buf[position++] = SEQ_MIDIPUTC;
|
||||||
|
buf[position++] = (unsigned char) *chr & 0x7F;
|
||||||
|
buf[position++] = _device_num;
|
||||||
|
buf[position++] = 0;
|
||||||
|
}
|
||||||
|
buf[position++] = SEQ_MIDIPUTC;
|
||||||
|
buf[position++] = 0xF7;
|
||||||
|
buf[position++] = _device_num;
|
||||||
|
buf[position++] = 0;
|
||||||
|
|
||||||
|
(void)write(device, buf, position);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Plugin interface
|
||||||
|
|
||||||
|
class SeqMusicPlugin : public MusicPluginObject {
|
||||||
|
public:
|
||||||
|
const char *getName() const {
|
||||||
|
return "SEQ";
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *getId() const {
|
||||||
|
return "seq";
|
||||||
|
}
|
||||||
|
|
||||||
|
MusicDevices getDevices() const;
|
||||||
|
Common::Error createInstance(MidiDriver **mididriver) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
MusicDevices SeqMusicPlugin::getDevices() const {
|
||||||
|
MusicDevices devices;
|
||||||
|
// TODO: Return a different music type depending on the configuration
|
||||||
|
// TODO: List the available devices
|
||||||
|
devices.push_back(MusicDevice(this, "", MT_GM));
|
||||||
|
return devices;
|
||||||
|
}
|
||||||
|
|
||||||
|
Common::Error SeqMusicPlugin::createInstance(MidiDriver **mididriver) const {
|
||||||
|
*mididriver = new MidiDriver_SEQ();
|
||||||
|
|
||||||
|
return Common::kNoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
MidiDriver *MidiDriver_SEQ_create() {
|
||||||
|
MidiDriver *mididriver;
|
||||||
|
|
||||||
|
SeqMusicPlugin p;
|
||||||
|
p.createInstance(&mididriver);
|
||||||
|
|
||||||
|
return mididriver;
|
||||||
|
}
|
||||||
|
|
||||||
|
//#if PLUGIN_ENABLED_DYNAMIC(SEQ)
|
||||||
|
//REGISTER_PLUGIN_DYNAMIC(SEQ, PLUGIN_TYPE_MUSIC, SeqMusicPlugin);
|
||||||
|
//#else
|
||||||
|
REGISTER_PLUGIN_STATIC(SEQ, PLUGIN_TYPE_MUSIC, SeqMusicPlugin);
|
||||||
|
//#endif
|
||||||
|
|
||||||
|
#endif
|
159
backends/midi/stmidi.cpp
Normal file
159
backends/midi/stmidi.cpp
Normal file
|
@ -0,0 +1,159 @@
|
||||||
|
/* Residual - A 3D game interpreter
|
||||||
|
*
|
||||||
|
* Copyright (C) 2001 Ludvig Strigeus
|
||||||
|
* Copyright (C) 2001-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Raw MIDI output for the Atari ST line of computers.
|
||||||
|
* Based on the ScummVM SEQ & CoreMIDI drivers.
|
||||||
|
* Atari code by Keith Scroggins
|
||||||
|
* We, unfortunately, could not use the SEQ driver because the /dev/midi under
|
||||||
|
* FreeMiNT (and hence in libc) is considered to be a serial port for machine
|
||||||
|
* access. So, we just use OS calls then to send the data to the MIDI ports
|
||||||
|
* directly. The current implementation is sending 1 byte at a time because
|
||||||
|
* in most cases we are only sending up to 3 bytes, I believe this saves a few
|
||||||
|
* cycles. I might change so sysex messages are sent the other way later.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if defined __MINT__
|
||||||
|
|
||||||
|
#include <osbind.h>
|
||||||
|
#include "sound/mpu401.h"
|
||||||
|
#include "common/util.h"
|
||||||
|
#include "sound/musicplugin.h"
|
||||||
|
|
||||||
|
class MidiDriver_STMIDI : public MidiDriver_MPU401 {
|
||||||
|
public:
|
||||||
|
MidiDriver_STMIDI() : _isOpen (false) { }
|
||||||
|
int open();
|
||||||
|
void close();
|
||||||
|
void send(uint32 b);
|
||||||
|
void sysEx(const byte *msg, uint16 length);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool _isOpen;
|
||||||
|
};
|
||||||
|
|
||||||
|
int MidiDriver_STMIDI::open() {
|
||||||
|
if ((_isOpen) && (!Bcostat(4)))
|
||||||
|
return MERR_ALREADY_OPEN;
|
||||||
|
warning("ST Midi Port Open");
|
||||||
|
_isOpen = true;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MidiDriver_STMIDI::close() {
|
||||||
|
MidiDriver_MPU401::close();
|
||||||
|
_isOpen = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MidiDriver_STMIDI::send(uint32 b) {
|
||||||
|
|
||||||
|
byte status_byte = (b & 0x000000FF);
|
||||||
|
byte first_byte = (b & 0x0000FF00) >> 8;
|
||||||
|
byte second_byte = (b & 0x00FF0000) >> 16;
|
||||||
|
|
||||||
|
// warning("ST MIDI Packet sent");
|
||||||
|
|
||||||
|
switch (b & 0xF0) {
|
||||||
|
case 0x80: // Note Off
|
||||||
|
case 0x90: // Note On
|
||||||
|
case 0xA0: // Polyphonic Key Pressure
|
||||||
|
case 0xB0: // Controller
|
||||||
|
case 0xE0: // Pitch Bend
|
||||||
|
Bconout(3, status_byte);
|
||||||
|
Bconout(3, first_byte);
|
||||||
|
Bconout(3, second_byte);
|
||||||
|
break;
|
||||||
|
case 0xC0: // Program Change
|
||||||
|
case 0xD0: // Aftertouch
|
||||||
|
Bconout(3, status_byte);
|
||||||
|
Bconout(3, first_byte);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "Unknown : %08x\n", (int)b);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MidiDriver_STMIDI::sysEx (const byte *msg, uint16 length) {
|
||||||
|
// FIXME: LordHoto doesn't know if this will still work
|
||||||
|
// when sending 264 byte sysEx data, as needed by KYRA,
|
||||||
|
// feel free to revert it to 254 again if needed.
|
||||||
|
if (length > 264) {
|
||||||
|
warning ("Cannot send SysEx block - data too large");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const byte *chr = msg;
|
||||||
|
warning("Sending SysEx Message");
|
||||||
|
|
||||||
|
Bconout(3, '0xF0');
|
||||||
|
for (; length; --length, ++chr) {
|
||||||
|
Bconout(3,((unsigned char) *chr & 0x7F));
|
||||||
|
}
|
||||||
|
Bconout(3, '0xF7');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Plugin interface
|
||||||
|
|
||||||
|
class StMidiMusicPlugin : public MusicPluginObject {
|
||||||
|
public:
|
||||||
|
const char *getName() const {
|
||||||
|
return "STMIDI";
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *getId() const {
|
||||||
|
return "stmidi";
|
||||||
|
}
|
||||||
|
|
||||||
|
MusicDevices getDevices() const;
|
||||||
|
Common::Error createInstance(MidiDriver **mididriver)
|
||||||
|
const;
|
||||||
|
};
|
||||||
|
|
||||||
|
MusicDevices StMidiMusicPlugin::getDevices() const {
|
||||||
|
MusicDevices devices;
|
||||||
|
// TODO: Return a different music type depending on the configuration
|
||||||
|
// TODO: List the available devices
|
||||||
|
devices.push_back(MusicDevice(this, "", MT_GM));
|
||||||
|
return devices;
|
||||||
|
}
|
||||||
|
|
||||||
|
Common::Error StMidiMusicPlugin::createInstance(MidiDriver **mididriver) const {
|
||||||
|
*mididriver = new MidiDriver_STMIDI();
|
||||||
|
|
||||||
|
return Common::kNoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
MidiDriver *MidiDriver_STMIDI_create() {
|
||||||
|
MidiDriver *mididriver;
|
||||||
|
|
||||||
|
StMidiMusicPlugin p;
|
||||||
|
p.createInstance(&mididriver);
|
||||||
|
|
||||||
|
return mididriver;
|
||||||
|
}
|
||||||
|
|
||||||
|
//#if PLUGIN_ENABLED_DYNAMIC(STMIDI)
|
||||||
|
//REGISTER_PLUGIN_DYNAMIC(STMIDI, PLUGIN_TYPE_MUSIC, StMidiMusicPlugin);
|
||||||
|
//#else
|
||||||
|
REGISTER_PLUGIN_STATIC(STMIDI, PLUGIN_TYPE_MUSIC, StMidiMusicPlugin);
|
||||||
|
//#endif
|
||||||
|
|
||||||
|
#endif
|
564
backends/midi/timidity.cpp
Normal file
564
backends/midi/timidity.cpp
Normal file
|
@ -0,0 +1,564 @@
|
||||||
|
/* Residual - A 3D game interpreter
|
||||||
|
*
|
||||||
|
* Residual is the legal property of its developers, whose names
|
||||||
|
* are too numerous to list here. Please refer to the AUTHORS
|
||||||
|
* 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$
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Output to TiMidity++ MIDI server support
|
||||||
|
* by Dmitry Marakasov <amdmi3@amdmi3.ru>
|
||||||
|
* based on:
|
||||||
|
* - Raw output support (seq.cpp) by Michael Pearce
|
||||||
|
* - Pseudo /dev/sequencer of TiMidity (timidity-io.c)
|
||||||
|
* by Masanao Izumo <mo@goice.co.jp>
|
||||||
|
* - sys/soundcard.h by Hannu Savolainen (got from my FreeBSD
|
||||||
|
* distribution, for which it was modified by Luigi Rizzo)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if defined (UNIX)
|
||||||
|
|
||||||
|
#include "common/util.h"
|
||||||
|
#include "common/endian.h"
|
||||||
|
#include "sound/musicplugin.h"
|
||||||
|
#include "sound/mpu401.h"
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <netdb.h> /* for gethostbyname */
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
// WORKAROUND bug #1870304: Solaris does not provide INADDR_NONE.
|
||||||
|
#ifndef INADDR_NONE
|
||||||
|
#define INADDR_NONE 0xffffffff
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// BeOS BONE uses snooze (x/1000) in place of usleep(x)
|
||||||
|
#ifdef __BEOS__
|
||||||
|
#define usleep(v) snooze(v/1000)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#define SEQ_MIDIPUTC 5
|
||||||
|
|
||||||
|
#define TIMIDITY_LOW_DELAY
|
||||||
|
|
||||||
|
#ifdef TIMIDITY_LOW_DELAY
|
||||||
|
#define BUF_LOW_SYNC 0.1
|
||||||
|
#define BUF_HIGH_SYNC 0.15
|
||||||
|
#else
|
||||||
|
#define BUF_LOW_SYNC 0.4
|
||||||
|
#define BUF_HIGH_SYNC 0.8
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* default host & port */
|
||||||
|
#define DEFAULT_TIMIDITY_HOST "127.0.0.1"
|
||||||
|
#define DEFAULT_TIMIDITY_PORT 7777
|
||||||
|
|
||||||
|
class MidiDriver_TIMIDITY : public MidiDriver_MPU401 {
|
||||||
|
public:
|
||||||
|
MidiDriver_TIMIDITY();
|
||||||
|
|
||||||
|
int open();
|
||||||
|
void close();
|
||||||
|
void send(uint32 b);
|
||||||
|
void sysEx(const byte *msg, uint16 length);
|
||||||
|
|
||||||
|
private:
|
||||||
|
/* standart routine to extract ip address from a string */
|
||||||
|
in_addr_t host_to_addr(const char* address);
|
||||||
|
|
||||||
|
/* creates a tcp connection to TiMidity server, returns filedesc (like open()) */
|
||||||
|
int connect_to_server(const char* hostname, unsigned short tcp_port);
|
||||||
|
|
||||||
|
/* send command to the server; printf-like; returns reply string */
|
||||||
|
char *timidity_ctl_command(const char *fmt, ...) GCC_PRINTF(2, 3);
|
||||||
|
|
||||||
|
/* timidity data socket-related stuff */
|
||||||
|
void timidity_meta_seq(int p1, int p2, int p3);
|
||||||
|
int timidity_sync(int centsec);
|
||||||
|
int timidity_eot();
|
||||||
|
|
||||||
|
/* write() analogue for any midi data */
|
||||||
|
void timidity_write_data(const void *buf, size_t nbytes);
|
||||||
|
|
||||||
|
/* get single line of server reply on control connection */
|
||||||
|
int fdgets(char *buff, size_t buff_size);
|
||||||
|
|
||||||
|
/* teardown connection to server */
|
||||||
|
void teardown();
|
||||||
|
|
||||||
|
/* close (if needed) and nullify both control and data filedescs */
|
||||||
|
void close_all();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool _isOpen;
|
||||||
|
int _device_num;
|
||||||
|
|
||||||
|
int _control_fd;
|
||||||
|
int _data_fd;
|
||||||
|
|
||||||
|
/* buffer for partial data read from _control_fd - from timidity-io.c, see fdgets() */
|
||||||
|
char _controlbuffer[BUFSIZ];
|
||||||
|
int _controlbuffer_count; /* beginning of read pointer */
|
||||||
|
int _controlbuffer_size; /* end of read pointer */
|
||||||
|
};
|
||||||
|
|
||||||
|
MidiDriver_TIMIDITY::MidiDriver_TIMIDITY() {
|
||||||
|
_isOpen = false;
|
||||||
|
_device_num = 0;
|
||||||
|
|
||||||
|
/* init fd's */
|
||||||
|
_control_fd = _data_fd = -1;
|
||||||
|
|
||||||
|
/* init buffer for control connection */
|
||||||
|
_controlbuffer_count = _controlbuffer_size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int MidiDriver_TIMIDITY::open() {
|
||||||
|
char *res;
|
||||||
|
char timidity_host[MAXHOSTNAMELEN];
|
||||||
|
int timidity_port, data_port, i;
|
||||||
|
|
||||||
|
/* count ourselves open */
|
||||||
|
if (_isOpen)
|
||||||
|
return MERR_ALREADY_OPEN;
|
||||||
|
_isOpen = true;
|
||||||
|
|
||||||
|
/* get server hostname; if not specified in env, use default */
|
||||||
|
if ((res = getenv("TIMIDITY_HOST")) == NULL)
|
||||||
|
strncpy(timidity_host, DEFAULT_TIMIDITY_HOST, MAXHOSTNAMELEN);
|
||||||
|
else
|
||||||
|
strncpy(timidity_host, res, sizeof(timidity_host));
|
||||||
|
|
||||||
|
timidity_host[sizeof(timidity_host) - 1] = '\0';
|
||||||
|
|
||||||
|
/* extract control port */
|
||||||
|
if ((res = strrchr(timidity_host, ':')) != NULL) {
|
||||||
|
*res++ = '\0';
|
||||||
|
timidity_port = atoi(res);
|
||||||
|
} else {
|
||||||
|
timidity_port = DEFAULT_TIMIDITY_PORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* create control connection to the server
|
||||||
|
*/
|
||||||
|
if ((_control_fd = connect_to_server(timidity_host, timidity_port)) < 0) {
|
||||||
|
warning("TiMidity: can't open control connection (host=%s, port=%d)", timidity_host, timidity_port);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* should read greeting issued by server upon connect:
|
||||||
|
* "220 TiMidity++ v2.13.2 ready)" */
|
||||||
|
res = timidity_ctl_command(NULL);
|
||||||
|
if (atoi(res) != 220) {
|
||||||
|
warning("TiMidity: bad response from server (host=%s, port=%d): %s", timidity_host, timidity_port, res);
|
||||||
|
close_all();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* setup buf and prepare data connection
|
||||||
|
*/
|
||||||
|
/* should read: "200 OK" */
|
||||||
|
res = timidity_ctl_command("SETBUF %f %f", BUF_LOW_SYNC, BUF_HIGH_SYNC);
|
||||||
|
if (atoi(res) != 200)
|
||||||
|
warning("TiMidity: bad reply for SETBUF command: %s", res);
|
||||||
|
|
||||||
|
/* should read something like "200 63017 is ready acceptable",
|
||||||
|
* where 63017 is port for data connection */
|
||||||
|
i = 1;
|
||||||
|
if (*(char *)&i == 1)
|
||||||
|
res = timidity_ctl_command("OPEN lsb");
|
||||||
|
else
|
||||||
|
res = timidity_ctl_command("OPEN msb");
|
||||||
|
|
||||||
|
if (atoi(res) != 200) {
|
||||||
|
warning("TiMidity: bad reply for OPEN command: %s", res);
|
||||||
|
close_all();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* open data connection
|
||||||
|
*/
|
||||||
|
data_port = atoi(res + 4);
|
||||||
|
if ((_data_fd = connect_to_server(timidity_host, data_port)) < 0) {
|
||||||
|
warning("TiMidity: can't open data connection (host=%s, port=%d)", timidity_host, data_port);
|
||||||
|
close_all();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* should read message issued after connecting to data port:
|
||||||
|
* "200 Ready data connection" */
|
||||||
|
res = timidity_ctl_command(NULL);
|
||||||
|
if (atoi(res) != 200) {
|
||||||
|
warning("Can't connect timidity: %s\t(host=%s, port=%d)", res, timidity_host, data_port);
|
||||||
|
close_all();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* From seq.cpp
|
||||||
|
*/
|
||||||
|
if (getenv("SCUMMVM_MIDIPORT"))
|
||||||
|
_device_num = atoi(getenv("SCUMMVM_MIDIPORT"));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MidiDriver_TIMIDITY::close() {
|
||||||
|
teardown();
|
||||||
|
|
||||||
|
MidiDriver_MPU401::close();
|
||||||
|
_isOpen = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MidiDriver_TIMIDITY::close_all() {
|
||||||
|
if (_control_fd >= 0)
|
||||||
|
::close(_control_fd);
|
||||||
|
|
||||||
|
if (_data_fd >= 0)
|
||||||
|
::close(_data_fd);
|
||||||
|
|
||||||
|
_control_fd = _data_fd = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MidiDriver_TIMIDITY::teardown() {
|
||||||
|
char *res;
|
||||||
|
|
||||||
|
/* teardown connection to server (see timidity-io.c) if it
|
||||||
|
* is initialized */
|
||||||
|
if (_data_fd >= 0 && _control_fd >= 0) {
|
||||||
|
timidity_eot();
|
||||||
|
timidity_sync(0);
|
||||||
|
|
||||||
|
/* scroll through all "302 Data connection is (already) closed"
|
||||||
|
* messages till we reach something like "200 Bye" */
|
||||||
|
do {
|
||||||
|
res = timidity_ctl_command("QUIT");
|
||||||
|
} while (*res && atoi(res) && atoi(res) != 302);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* now close and nullify both filedescs */
|
||||||
|
close_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
in_addr_t MidiDriver_TIMIDITY::host_to_addr(const char* address) {
|
||||||
|
in_addr_t addr;
|
||||||
|
struct hostent *hp;
|
||||||
|
|
||||||
|
/* first check if IP address is given (like 127.0.0.1)*/
|
||||||
|
if ((addr = inet_addr(address)) != INADDR_NONE)
|
||||||
|
return addr;
|
||||||
|
|
||||||
|
/* if not, try to resolve a hostname */
|
||||||
|
if ((hp = gethostbyname(address)) == NULL) {
|
||||||
|
warning("TiMidity: unknown hostname: %s", address);
|
||||||
|
return INADDR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(&addr, hp->h_addr, (int)sizeof(in_addr_t) <= hp->h_length ? sizeof(in_addr_t) : hp->h_length);
|
||||||
|
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
int MidiDriver_TIMIDITY::connect_to_server(const char* hostname, unsigned short tcp_port) {
|
||||||
|
int fd;
|
||||||
|
struct sockaddr_in in;
|
||||||
|
unsigned int addr;
|
||||||
|
|
||||||
|
/* create socket */
|
||||||
|
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
|
||||||
|
warning("TiMidity: socket(): %s", strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* connect */
|
||||||
|
memset(&in, 0, sizeof(in));
|
||||||
|
in.sin_family = AF_INET;
|
||||||
|
in.sin_port = htons(tcp_port);
|
||||||
|
addr = host_to_addr(hostname);
|
||||||
|
memcpy(&in.sin_addr, &addr, 4);
|
||||||
|
|
||||||
|
if (connect(fd, (struct sockaddr *)&in, sizeof(in)) < 0) {
|
||||||
|
warning("TiMidity: connect(): %s", strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *MidiDriver_TIMIDITY::timidity_ctl_command(const char *fmt, ...) {
|
||||||
|
/* XXX: I don't like this static buffer!!! */
|
||||||
|
static char buff[BUFSIZ];
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
if (fmt != NULL) {
|
||||||
|
/* if argumends are present, write them to control connection */
|
||||||
|
va_start(ap, fmt);
|
||||||
|
int len = vsnprintf(buff, BUFSIZ-1, fmt, ap); /* leave one byte for \n */
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
/* add newline if needed */
|
||||||
|
if (len > 0 && buff[len - 1] != '\n')
|
||||||
|
buff[len++] = '\n';
|
||||||
|
|
||||||
|
/* write command to control socket */
|
||||||
|
(void)write(_control_fd, buff, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
/* read reply */
|
||||||
|
if (fdgets(buff, sizeof(buff)) <= 0) {
|
||||||
|
strcpy(buff, "Read error\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* report errors from server */
|
||||||
|
int status = atoi(buff);
|
||||||
|
if (400 <= status && status <= 499) { /* Error of data stream */
|
||||||
|
warning("TiMidity: error from server: %s", buff);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return buff;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MidiDriver_TIMIDITY::timidity_meta_seq(int p1, int p2, int p3) {
|
||||||
|
/* see _CHN_COMMON from soundcard.h; this is simplified
|
||||||
|
* to just send seq to the server without any buffers,
|
||||||
|
* delays and extra functions/macros */
|
||||||
|
u_char seqbuf[8];
|
||||||
|
|
||||||
|
seqbuf[0] = 0x92;
|
||||||
|
seqbuf[1] = 0;
|
||||||
|
seqbuf[2] = 0xff;
|
||||||
|
seqbuf[3] = 0x7f;
|
||||||
|
seqbuf[4] = p1;
|
||||||
|
seqbuf[5] = p2;
|
||||||
|
WRITE_UINT16(&seqbuf[6], p3);
|
||||||
|
|
||||||
|
timidity_write_data(seqbuf, sizeof(seqbuf));
|
||||||
|
}
|
||||||
|
|
||||||
|
int MidiDriver_TIMIDITY::timidity_sync(int centsec) {
|
||||||
|
char *res;
|
||||||
|
int status;
|
||||||
|
unsigned long sleep_usec;
|
||||||
|
|
||||||
|
timidity_meta_seq(0x02, 0x00, centsec); /* Wait playout */
|
||||||
|
|
||||||
|
/* Wait "301 Sync OK" */
|
||||||
|
do {
|
||||||
|
res = timidity_ctl_command(NULL);
|
||||||
|
status = atoi(res);
|
||||||
|
|
||||||
|
if (status != 301)
|
||||||
|
warning("TiMidity: error: SYNC: %s", res);
|
||||||
|
|
||||||
|
} while (status && status != 301);
|
||||||
|
|
||||||
|
if (status != 301)
|
||||||
|
return -1; /* error */
|
||||||
|
|
||||||
|
sleep_usec = (unsigned long)(atof(res + 4) * 1000000);
|
||||||
|
|
||||||
|
if (sleep_usec > 0)
|
||||||
|
usleep(sleep_usec);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int MidiDriver_TIMIDITY::timidity_eot(void) {
|
||||||
|
timidity_meta_seq(0x00, 0x00, 0); /* End of playing */
|
||||||
|
return timidity_sync(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MidiDriver_TIMIDITY::timidity_write_data(const void *buf, size_t nbytes) {
|
||||||
|
/* nowhere to write... */
|
||||||
|
if (_data_fd < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* write, and disable everything if write failed */
|
||||||
|
/* TODO: add reconnect? */
|
||||||
|
if (write(_data_fd, buf, nbytes) == -1) {
|
||||||
|
warning("TiMidity: DATA WRITE FAILED (%s), DISABLING MUSIC OUTPUT", strerror(errno));
|
||||||
|
close_all();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int MidiDriver_TIMIDITY::fdgets(char *buff, size_t buff_size) {
|
||||||
|
int n, len, count, size;
|
||||||
|
char *buff_endp = buff + buff_size - 1, *pbuff, *beg;
|
||||||
|
|
||||||
|
len = 0;
|
||||||
|
count = _controlbuffer_count;
|
||||||
|
size = _controlbuffer_size;
|
||||||
|
pbuff = _controlbuffer;
|
||||||
|
beg = buff;
|
||||||
|
do {
|
||||||
|
if (count == size) {
|
||||||
|
if ((n = read(_control_fd, pbuff, BUFSIZ)) <= 0) {
|
||||||
|
*buff = '\0';
|
||||||
|
if (n == 0) {
|
||||||
|
_controlbuffer_count = _controlbuffer_size = 0;
|
||||||
|
return buff - beg;
|
||||||
|
}
|
||||||
|
return -1; /* < 0 error */
|
||||||
|
}
|
||||||
|
count = _controlbuffer_count = 0;
|
||||||
|
size = _controlbuffer_size = n;
|
||||||
|
}
|
||||||
|
*buff++ = pbuff[count++];
|
||||||
|
} while (*(buff - 1) != '\n' && buff != buff_endp);
|
||||||
|
|
||||||
|
*buff = '\0';
|
||||||
|
_controlbuffer_count = count;
|
||||||
|
|
||||||
|
return buff - beg;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MidiDriver_TIMIDITY::send(uint32 b) {
|
||||||
|
unsigned char buf[256];
|
||||||
|
int position = 0;
|
||||||
|
|
||||||
|
switch (b & 0xF0) {
|
||||||
|
case 0x80:
|
||||||
|
case 0x90:
|
||||||
|
case 0xA0:
|
||||||
|
case 0xB0:
|
||||||
|
case 0xE0:
|
||||||
|
buf[position++] = SEQ_MIDIPUTC;
|
||||||
|
buf[position++] = (unsigned char)b;
|
||||||
|
buf[position++] = _device_num;
|
||||||
|
buf[position++] = 0;
|
||||||
|
buf[position++] = SEQ_MIDIPUTC;
|
||||||
|
buf[position++] = (unsigned char)((b >> 8) & 0x7F);
|
||||||
|
buf[position++] = _device_num;
|
||||||
|
buf[position++] = 0;
|
||||||
|
buf[position++] = SEQ_MIDIPUTC;
|
||||||
|
buf[position++] = (unsigned char)((b >> 16) & 0x7F);
|
||||||
|
buf[position++] = _device_num;
|
||||||
|
buf[position++] = 0;
|
||||||
|
break;
|
||||||
|
case 0xC0:
|
||||||
|
case 0xD0:
|
||||||
|
buf[position++] = SEQ_MIDIPUTC;
|
||||||
|
buf[position++] = (unsigned char)b;
|
||||||
|
buf[position++] = _device_num;
|
||||||
|
buf[position++] = 0;
|
||||||
|
buf[position++] = SEQ_MIDIPUTC;
|
||||||
|
buf[position++] = (unsigned char)((b >> 8) & 0x7F);
|
||||||
|
buf[position++] = _device_num;
|
||||||
|
buf[position++] = 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
warning("MidiDriver_TIMIDITY::send: unknown : %08x", (int)b);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
timidity_write_data(buf, position);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MidiDriver_TIMIDITY::sysEx(const byte *msg, uint16 length) {
|
||||||
|
fprintf(stderr, "Timidity::sysEx\n");
|
||||||
|
unsigned char buf[266*4];
|
||||||
|
int position = 0;
|
||||||
|
const byte *chr = msg;
|
||||||
|
|
||||||
|
assert(length + 2 <= 266);
|
||||||
|
|
||||||
|
buf[position++] = SEQ_MIDIPUTC;
|
||||||
|
buf[position++] = 0xF0;
|
||||||
|
buf[position++] = _device_num;
|
||||||
|
buf[position++] = 0;
|
||||||
|
for (; length; --length, ++chr) {
|
||||||
|
buf[position++] = SEQ_MIDIPUTC;
|
||||||
|
buf[position++] = (unsigned char) *chr & 0x7F;
|
||||||
|
buf[position++] = _device_num;
|
||||||
|
buf[position++] = 0;
|
||||||
|
}
|
||||||
|
buf[position++] = SEQ_MIDIPUTC;
|
||||||
|
buf[position++] = 0xF7;
|
||||||
|
buf[position++] = _device_num;
|
||||||
|
buf[position++] = 0;
|
||||||
|
|
||||||
|
timidity_write_data(buf, position);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Plugin interface
|
||||||
|
|
||||||
|
class TimidityMusicPlugin : public MusicPluginObject {
|
||||||
|
public:
|
||||||
|
const char *getName() const {
|
||||||
|
return "TiMidity";
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *getId() const {
|
||||||
|
return "timidity";
|
||||||
|
}
|
||||||
|
|
||||||
|
MusicDevices getDevices() const;
|
||||||
|
Common::Error createInstance(MidiDriver **mididriver) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
MusicDevices TimidityMusicPlugin::getDevices() const {
|
||||||
|
MusicDevices devices;
|
||||||
|
devices.push_back(MusicDevice(this, "", MT_GM));
|
||||||
|
return devices;
|
||||||
|
}
|
||||||
|
|
||||||
|
Common::Error TimidityMusicPlugin::createInstance(MidiDriver **mididriver) const {
|
||||||
|
*mididriver = new MidiDriver_TIMIDITY();
|
||||||
|
|
||||||
|
return Common::kNoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
MidiDriver *MidiDriver_TIMIDITY_create() {
|
||||||
|
MidiDriver *mididriver;
|
||||||
|
|
||||||
|
TimidityMusicPlugin p;
|
||||||
|
p.createInstance(&mididriver);
|
||||||
|
|
||||||
|
return mididriver;
|
||||||
|
}
|
||||||
|
|
||||||
|
//#if PLUGIN_ENABLED_DYNAMIC(TIMIDITY)
|
||||||
|
//REGISTER_PLUGIN_DYNAMIC(TIMIDITY, PLUGIN_TYPE_MUSIC, TimidityMusicPlugin);
|
||||||
|
//#else
|
||||||
|
REGISTER_PLUGIN_STATIC(TIMIDITY, PLUGIN_TYPE_MUSIC, TimidityMusicPlugin);
|
||||||
|
//#endif
|
||||||
|
|
||||||
|
#endif // defined (UNIX)
|
193
backends/midi/windows.cpp
Normal file
193
backends/midi/windows.cpp
Normal file
|
@ -0,0 +1,193 @@
|
||||||
|
/* Residual - A 3D game interpreter
|
||||||
|
*
|
||||||
|
* Residual is the legal property of its developers, whose names
|
||||||
|
* are too numerous to list here. Please refer to the AUTHORS
|
||||||
|
* 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$
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if defined(WIN32) && !defined(_WIN32_WCE)
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
// winnt.h defines ARRAYSIZE, but we want our own one...
|
||||||
|
#undef ARRAYSIZE
|
||||||
|
|
||||||
|
#include "sound/musicplugin.h"
|
||||||
|
#include "sound/mpu401.h"
|
||||||
|
|
||||||
|
#include <mmsystem.h>
|
||||||
|
|
||||||
|
////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Windows MIDI driver
|
||||||
|
//
|
||||||
|
////////////////////////////////////////
|
||||||
|
|
||||||
|
class MidiDriver_WIN : public MidiDriver_MPU401 {
|
||||||
|
private:
|
||||||
|
MIDIHDR _streamHeader;
|
||||||
|
byte _streamBuffer[266]; // SysEx blocks should be no larger than 266 bytes
|
||||||
|
HANDLE _streamEvent;
|
||||||
|
HMIDIOUT _mo;
|
||||||
|
bool _isOpen;
|
||||||
|
|
||||||
|
void check_error(MMRESULT result);
|
||||||
|
|
||||||
|
public:
|
||||||
|
MidiDriver_WIN() : _isOpen(false) { }
|
||||||
|
int open();
|
||||||
|
void close();
|
||||||
|
void send(uint32 b);
|
||||||
|
void sysEx(const byte *msg, uint16 length);
|
||||||
|
};
|
||||||
|
|
||||||
|
int MidiDriver_WIN::open() {
|
||||||
|
if (_isOpen)
|
||||||
|
return MERR_ALREADY_OPEN;
|
||||||
|
|
||||||
|
_streamEvent = CreateEvent(NULL, true, true, NULL);
|
||||||
|
MMRESULT res = midiOutOpen((HMIDIOUT *)&_mo, MIDI_MAPPER, (DWORD_PTR)_streamEvent, 0, CALLBACK_EVENT);
|
||||||
|
if (res != MMSYSERR_NOERROR) {
|
||||||
|
check_error(res);
|
||||||
|
CloseHandle(_streamEvent);
|
||||||
|
return MERR_DEVICE_NOT_AVAILABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isOpen = true;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MidiDriver_WIN::close() {
|
||||||
|
if (!_isOpen)
|
||||||
|
return;
|
||||||
|
_isOpen = false;
|
||||||
|
MidiDriver_MPU401::close();
|
||||||
|
midiOutUnprepareHeader(_mo, &_streamHeader, sizeof(_streamHeader));
|
||||||
|
check_error(midiOutClose(_mo));
|
||||||
|
CloseHandle(_streamEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MidiDriver_WIN::send(uint32 b) {
|
||||||
|
union {
|
||||||
|
DWORD dwData;
|
||||||
|
BYTE bData[4];
|
||||||
|
} u;
|
||||||
|
|
||||||
|
u.bData[3] = (byte)((b & 0xFF000000) >> 24);
|
||||||
|
u.bData[2] = (byte)((b & 0x00FF0000) >> 16);
|
||||||
|
u.bData[1] = (byte)((b & 0x0000FF00) >> 8);
|
||||||
|
u.bData[0] = (byte)(b & 0x000000FF);
|
||||||
|
|
||||||
|
check_error(midiOutShortMsg(_mo, u.dwData));
|
||||||
|
}
|
||||||
|
|
||||||
|
void MidiDriver_WIN::sysEx(const byte *msg, uint16 length) {
|
||||||
|
if (!_isOpen)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (WaitForSingleObject (_streamEvent, 2000) == WAIT_TIMEOUT) {
|
||||||
|
warning ("Could not send SysEx - MMSYSTEM is still trying to send data.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(length+2 <= 266);
|
||||||
|
|
||||||
|
midiOutUnprepareHeader(_mo, &_streamHeader, sizeof(_streamHeader));
|
||||||
|
|
||||||
|
// Add SysEx frame
|
||||||
|
_streamBuffer[0] = 0xF0;
|
||||||
|
memcpy(&_streamBuffer[1], msg, length);
|
||||||
|
_streamBuffer[length+1] = 0xF7;
|
||||||
|
|
||||||
|
_streamHeader.lpData = (char *)_streamBuffer;
|
||||||
|
_streamHeader.dwBufferLength = length + 2;
|
||||||
|
_streamHeader.dwBytesRecorded = length + 2;
|
||||||
|
_streamHeader.dwUser = 0;
|
||||||
|
_streamHeader.dwFlags = 0;
|
||||||
|
|
||||||
|
MMRESULT result = midiOutPrepareHeader(_mo, &_streamHeader, sizeof(_streamHeader));
|
||||||
|
if (result != MMSYSERR_NOERROR) {
|
||||||
|
check_error (result);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResetEvent(_streamEvent);
|
||||||
|
result = midiOutLongMsg(_mo, &_streamHeader, sizeof(_streamHeader));
|
||||||
|
if (result != MMSYSERR_NOERROR) {
|
||||||
|
check_error(result);
|
||||||
|
SetEvent(_streamEvent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MidiDriver_WIN::check_error(MMRESULT result) {
|
||||||
|
char buf[200];
|
||||||
|
if (result != MMSYSERR_NOERROR) {
|
||||||
|
midiOutGetErrorText(result, buf, 200);
|
||||||
|
warning("MM System Error '%s'", buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Plugin interface
|
||||||
|
|
||||||
|
class WindowsMusicPlugin : public MusicPluginObject {
|
||||||
|
public:
|
||||||
|
const char *getName() const {
|
||||||
|
return "Windows MIDI";
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *getId() const {
|
||||||
|
return "windows";
|
||||||
|
}
|
||||||
|
|
||||||
|
MusicDevices getDevices() const;
|
||||||
|
Common::Error createInstance(MidiDriver **mididriver) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
MusicDevices WindowsMusicPlugin::getDevices() const {
|
||||||
|
MusicDevices devices;
|
||||||
|
// TODO: Return a different music type depending on the configuration
|
||||||
|
// TODO: List the available devices
|
||||||
|
devices.push_back(MusicDevice(this, "", MT_GM));
|
||||||
|
return devices;
|
||||||
|
}
|
||||||
|
|
||||||
|
Common::Error WindowsMusicPlugin::createInstance(MidiDriver **mididriver) const {
|
||||||
|
*mididriver = new MidiDriver_WIN();
|
||||||
|
|
||||||
|
return Common::kNoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
MidiDriver *MidiDriver_WIN_create() {
|
||||||
|
MidiDriver *mididriver;
|
||||||
|
|
||||||
|
WindowsMusicPlugin p;
|
||||||
|
p.createInstance(&mididriver);
|
||||||
|
|
||||||
|
return mididriver;
|
||||||
|
}
|
||||||
|
|
||||||
|
//#if PLUGIN_ENABLED_DYNAMIC(WINDOWS)
|
||||||
|
//REGISTER_PLUGIN_DYNAMIC(WINDOWS, PLUGIN_TYPE_MUSIC, WindowsMusicPlugin);
|
||||||
|
//#else
|
||||||
|
REGISTER_PLUGIN_STATIC(WINDOWS, PLUGIN_TYPE_MUSIC, WindowsMusicPlugin);
|
||||||
|
//#endif
|
||||||
|
|
||||||
|
#endif
|
Loading…
Add table
Add a link
Reference in a new issue