aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMax Horn2007-11-02 16:01:52 +0000
committerMax Horn2007-11-02 16:01:52 +0000
commit8eb73f269492e5123e86236f5398a14e6aa991b5 (patch)
tree3de5e82f071d3278a73c8f0894aff941997acfb8
parent231a7875bdae36a9e17ae8a51b870e37244fa02e (diff)
downloadscummvm-rg350-8eb73f269492e5123e86236f5398a14e6aa991b5.tar.gz
scummvm-rg350-8eb73f269492e5123e86236f5398a14e6aa991b5.tar.bz2
scummvm-rg350-8eb73f269492e5123e86236f5398a14e6aa991b5.zip
Patch #1815526: Add TiMidity++ MIDI server support
svn-id: r29377
-rw-r--r--COPYRIGHT1
-rw-r--r--NEWS1
-rw-r--r--README48
-rw-r--r--backends/midi/timidity.cpp512
-rw-r--r--backends/module.mk1
-rw-r--r--sound/mididrv.cpp6
-rw-r--r--sound/mididrv.h4
7 files changed, 558 insertions, 15 deletions
diff --git a/COPYRIGHT b/COPYRIGHT
index bff52fa8eb..80dfadfed4 100644
--- a/COPYRIGHT
+++ b/COPYRIGHT
@@ -128,6 +128,7 @@ Duncan Lock "dflock"
Mark Lodato "itsr0y"
Fridvin Logi "phillip_j_fry"
Georg Lukas "ge0rg"
+Dmitry Marakasov "amdmi3
Markus "meist3r"
Connor McLeod "mcleod2032"
Mickey McMurray "metafox"
diff --git a/NEWS b/NEWS
index f6765d605b..d71b8356a6 100644
--- a/NEWS
+++ b/NEWS
@@ -14,6 +14,7 @@ For a more comprehensive changelog for the latest experimental SVN code, see:
- Improved the way keyboard input is handled internally, resolving
some odd quirks in some game / port combinations.
- Added optional 'confirm exit' dialog to the SDL backend.
+ - Added support for TiMidity++ MIDI server.
AGOS:
- Fixed crashes related to OmniTV playback in The Feeble Files.
diff --git a/README b/README
index df5b631fc9..e70b57d65a 100644
--- a/README
+++ b/README
@@ -46,8 +46,9 @@ Table of Contents:
* 7.4 MIDI emulation
* 7.5 Native MIDI support
* 7.6 UNIX native and ALSA sequencer support
- * 7.7 Using compressed audio files (MP3, Ogg Vorbis, Flac)
- * 7.8 Output sample rate
+ * 7.7 TiMidity++ MIDI server support
+ * 7.8 Using compressed audio files (MP3, Ogg Vorbis, Flac)
+ * 7.9 Output sample rate
8.0) Configuration Files
9.0) Compiling
@@ -1122,6 +1123,7 @@ your operating system and configuration.
a hardware MIDI synthesizer.
qt - Quicktime sound, for Macintosh users.
seq - Use /dev/sequencer for MIDI, *nix users. See below.
+ timidity - Connect to TiMidity++ MIDI server. See below.
windows - Windows MIDI. Uses built-in sequencer, for Windows users
To select a sound driver, select it in the Launcher, or pass its name
@@ -1225,8 +1227,8 @@ For these games, you may wish to specify --multi-midi in order to combine
MIDI music with Adlib sound effects.
-7.6.0) Playing sound with Sequencer MIDI: [UNIX ONLY]
------- ----------------------------------
+7.6) Playing sound with Sequencer MIDI: [UNIX ONLY]
+---- ----------------------------------
If your soundcard driver supports a sequencer, you may set the environment
variable "SCUMMVM_MIDI" to your sequencer device -- for example, to
/dev/sequencer
@@ -1289,7 +1291,25 @@ Once either TiMidity or FluidSynth are running, use the 'aconnect -o -l'
command as described earlier in this section.
-7.7.0) Using MP3 files for CD audio:
+7.7) Using TiMidity++ MIDI server:
+---- -----------------------------
+If you system lacks any MIDI sequencer, but you still want better MIDI quality
+than default Adlib emulation can offer, you can try TiMidity++ MIDI server. See
+http://timidity.sourceforge.net/ for download and install instructions.
+
+First, you need to start a daemon:
+
+timidity -ir 7777
+
+Now you can start scummvm and try selection TiMidity music output. By default,
+it will connect to localhost:7777, but you can change host/port by defining
+"TIMIDITY_HOST" environment variable.
+
+
+7.8) Using compressed audio files
+---- ----------------------------
+
+7.8.0) Using MP3 files for CD audio:
------ -----------------------------
Use LAME or some other MP3 encoder to rip the cd audio tracks to files. Name
the files track1.mp3 track2.mp3 etc. ScummVM must be compiled with MAD support
@@ -1300,7 +1320,7 @@ following LAME command line:
lame -t -q 0 -b 96 track1.wav track1.mp3
-7.7.1) Using Ogg Vorbis files for CD audio:
+7.8.1) Using Ogg Vorbis files for CD audio:
------ ------------------------------------
Use oggenc or some other vorbis encoder to encode the audio tracks to files.
Name the files track1.ogg track2.ogg etc. ScummVM must be compiled with vorbis
@@ -1312,7 +1332,7 @@ command line with the value after q specifying the desired quality from
oggenc -q 5 track1.wav
-7.7.2) Using Flac files for CD audio:
+7.8.2) Using Flac files for CD audio:
------ ------------------------------------
Use flac or some other flac encoder to encode the audio tracks to files.
Name the files track1.flac track2.flac etc. In your filesystem only allows
@@ -1327,7 +1347,7 @@ Remember that the quality is always the same, varying encoder options will only
affect the encoding time and resulting filesize.
-7.7.3) Compressing MONSTER.SOU with MP3:
+7.8.3) Compressing MONSTER.SOU with MP3:
------ ---------------------------------
You need LAME, and our 'compress_scumm_sou' utility from the scummvm-tools
package to perform this task, and ScummVM must be compiled with MAD support.
@@ -1338,7 +1358,7 @@ Eventually you will have a much smaller monster.so3 file, copy this file
to your game directory. You can safely remove the monster.sou file.
-7.7.4) Compressing MONSTER.SOU with Ogg Vorbis:
+7.8.4) Compressing MONSTER.SOU with Ogg Vorbis:
------ ----------------------------------------
As above, but ScummVM must be compiled with OGG support. Run:
@@ -1349,7 +1369,7 @@ game directory. Ogg encoding may take a considerable longer amount of time
than MP3, so have a good book handy.
-7.7.5) Compressing MONSTER.SOU with Flac:
+7.8.5) Compressing MONSTER.SOU with Flac:
------ ----------------------------------------
As above, but ScummVM must be compiled with Flac support. Run:
@@ -1363,7 +1383,7 @@ filesize -- 1152 seems to be a good value for those kind of soundfiles. Be sure
to read the encoder documentation before you use other values.
-7.7.6) Compressing music/sfx/speech in AGOS games
+7.8.6) Compressing music/sfx/speech in AGOS games
------ -----------------------------------------------------------------
Use our 'compress_agos' utility from the scummvm-tools package to perform this
task. You can choose between multiple target formats, but note that you can
@@ -1398,7 +1418,7 @@ Eventually you will have a much smaller *.mp3, *.ogg or *.fla file, copy this
file to your game directory. You can safely remove the old file.
-7.7.7) Compressing speech/music in Broken Sword 1
+7.8.7) Compressing speech/music in Broken Sword 1
------ ------------------------------------------
The 'compress_sword1' tool from the scummvm-tools package can encode music and
speech to MP3 as well as Ogg Vorbis.
@@ -1417,7 +1437,7 @@ instead of MP3.
Use "compress_sword1 --help" to get a full list of the options.
-7.7.8) Compressing speech/music in Broken Sword 2
+7.8.8) Compressing speech/music in Broken Sword 2
------ ------------------------------------------
Use our 'compress_sword2' utility from the scummvm-tools package to perform this
task. You can choose between multiple target formats, but note that you can
@@ -1442,7 +1462,7 @@ files in Broken Sword 2. It will not work with any of the other *.clu files,
nor will it work with the speech files from Broken Sword 1.
-7.8) Output sample rate:
+7.9) Output sample rate:
---- -------------------
The output sample rate tells ScummVM how many sound samples to play per channel
per second. There is much that could be said on this subject, but most of it
diff --git a/backends/midi/timidity.cpp b/backends/midi/timidity.cpp
new file mode 100644
index 0000000000..132ebd7c49
--- /dev/null
+++ b/backends/midi/timidity.cpp
@@ -0,0 +1,512 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * 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 "sound/mpu401.h"
+#include "common/util.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>
+
+#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, ...);
+
+ /* 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) {
+ fprintf(stderr, "Can't connect timidity: %s\t(host=%s, port=%d)\n", 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\n", 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];
+ int status, len;
+ va_list ap;
+
+ if (fmt != NULL) {
+ /* if argumends are present, write them to control connection */
+ va_start(ap, fmt);
+ 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 */
+ 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 */
+ 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;
+ *(short *)&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[1024];
+ int position = 0;
+ const byte *chr = msg;
+
+ assert(length + 2 <= 256);
+
+ 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);
+}
+
+MidiDriver *MidiDriver_TIMIDITY_create() {
+ return new MidiDriver_TIMIDITY();
+}
+
+#endif // defined (UNIX)
diff --git a/backends/module.mk b/backends/module.mk
index c5edbe6161..2f889a1951 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -9,6 +9,7 @@ MODULE_OBJS := \
midi/morphos.o \
midi/quicktime.o \
midi/seq.o \
+ midi/timidity.o \
midi/dmedia.o \
midi/windows.o \
plugins/dc/dc-provider.o \
diff --git a/sound/mididrv.cpp b/sound/mididrv.cpp
index 6cd6934774..0913ae00bc 100644
--- a/sound/mididrv.cpp
+++ b/sound/mididrv.cpp
@@ -84,6 +84,9 @@ static const MidiDriverDescription s_musicDrivers[] = {
{"pcspk", "PC Speaker", MD_PCSPK, MDT_PCSPK},
{"pcjr", "IBM PCjr", MD_PCJR, MDT_PCSPK},
{"towns", "FM Towns", MD_TOWNS, MDT_TOWNS},
+#if defined(UNIX)
+ {"timidity", "TiMidity", MD_TIMIDITY, MDT_MIDI},
+#endif
{0, 0, MD_NULL, MDT_NONE}
};
@@ -251,6 +254,9 @@ MidiDriver *MidiDriver::createMidi(int midiDriver) {
#if defined(UNIX) && !defined(__BEOS__) && !defined(MACOSX) && !defined(__MAEMO__)
case MD_SEQ: return MidiDriver_SEQ_create();
#endif
+#if defined(UNIX)
+ case MD_TIMIDITY: return MidiDriver_TIMIDITY_create();
+#endif
#if defined(IRIX)
case MD_DMEDIA: return MidiDriver_DMEDIA_create();
#endif
diff --git a/sound/mididrv.h b/sound/mididrv.h
index 6a18b441cc..7775387d39 100644
--- a/sound/mididrv.h
+++ b/sound/mididrv.h
@@ -78,7 +78,8 @@ enum MidiDriverType {
MD_ADLIB,
MD_PCSPK,
MD_PCJR,
- MD_TOWNS
+ MD_TOWNS,
+ MD_TIMIDITY
};
/**
@@ -271,6 +272,7 @@ extern MidiDriver *MidiDriver_NULL_create();
extern MidiDriver *MidiDriver_ADLIB_create(Audio::Mixer *mixer);
extern MidiDriver *MidiDriver_WIN_create();
extern MidiDriver *MidiDriver_SEQ_create();
+extern MidiDriver *MidiDriver_TIMIDITY_create();
extern MidiDriver *MidiDriver_QT_create();
extern MidiDriver *MidiDriver_CORE_create();
extern MidiDriver *MidiDriver_CoreMIDI_create();