summaryrefslogtreecommitdiffstats
path: root/libkmid
diff options
context:
space:
mode:
Diffstat (limited to 'libkmid')
-rw-r--r--libkmid/Makefile.am24
-rw-r--r--libkmid/alsaout.cc571
-rw-r--r--libkmid/alsaout.h231
-rw-r--r--libkmid/awe_sup.h50
-rw-r--r--libkmid/configure.in.in128
-rw-r--r--libkmid/dattypes.cc110
-rw-r--r--libkmid/dattypes.h55
-rw-r--r--libkmid/deviceman.cc830
-rw-r--r--libkmid/deviceman.h537
-rw-r--r--libkmid/fmout.cc354
-rw-r--r--libkmid/fmout.h155
-rw-r--r--libkmid/gusout.cc691
-rw-r--r--libkmid/gusout.h180
-rw-r--r--libkmid/gusvoices.h286
-rw-r--r--libkmid/libkmid.cc263
-rw-r--r--libkmid/libkmid.h226
-rw-r--r--libkmid/midfile.cc460
-rw-r--r--libkmid/midfile.h99
-rw-r--r--libkmid/midimapper.cc456
-rw-r--r--libkmid/midimapper.h210
-rw-r--r--libkmid/midiout.cc301
-rw-r--r--libkmid/midiout.h251
-rw-r--r--libkmid/midispec.h60
-rw-r--r--libkmid/midistat.cc115
-rw-r--r--libkmid/midistat.h143
-rw-r--r--libkmid/mt32togm.cc18
-rw-r--r--libkmid/mt32togm.h31
-rw-r--r--libkmid/notearray.cc122
-rw-r--r--libkmid/notearray.h145
-rw-r--r--libkmid/player.cc959
-rw-r--r--libkmid/player.h396
-rw-r--r--libkmid/sndcard.h88
-rw-r--r--libkmid/synthout.cc211
-rw-r--r--libkmid/synthout.h118
-rw-r--r--libkmid/tests/Kathzy.midbin0 -> 9083 bytes
-rw-r--r--libkmid/tests/Makefile.am14
-rw-r--r--libkmid/tests/apitest.cc25
-rw-r--r--libkmid/tests/ctest.c54
-rw-r--r--libkmid/tests/notesoff.cc24
-rw-r--r--libkmid/track.cc566
-rw-r--r--libkmid/track.h237
-rw-r--r--libkmid/voiceman.cc279
-rw-r--r--libkmid/voiceman.h172
43 files changed, 10245 insertions, 0 deletions
diff --git a/libkmid/Makefile.am b/libkmid/Makefile.am
new file mode 100644
index 000000000..5d64b0fcf
--- /dev/null
+++ b/libkmid/Makefile.am
@@ -0,0 +1,24 @@
+
+INCLUDES = -I$(srcdir)/.. $(all_includes)
+
+# For the future: examine if condensing the tons of *_LDFLAGS variables
+# into $(all_libraries) isn't better
+AM_LDFLAGS = $(LDFLAGS_AS_NEEDED) $(LDFLAGS_NEW_DTAGS)
+
+libkmidincludedir = $(includedir)/libkmid
+libkmidinclude_HEADERS = midiout.h player.h track.h midimapper.h \
+ midfile.h dattypes.h midistat.h deviceman.h synthout.h \
+ fmout.h gusout.h alsaout.h voiceman.h notearray.h mt32togm.h \
+ midispec.h libkmid.h
+
+lib_LTLIBRARIES = libkmid.la
+libkmid_la_SOURCES = midiout.cc player.cc track.cc midimapper.cc \
+ midfile.cc dattypes.cc midistat.cc deviceman.cc synthout.cc \
+ fmout.cc gusout.cc alsaout.cc voiceman.cc mt32togm.cc notearray.cc \
+ libkmid.cc
+
+libkmid_la_LDFLAGS = $(KDE_MT_LDFLAGS) -version-info 0:95 -no-undefined
+libkmid_la_LIBADD = $(LIBASOUND) ../kdecore/libkdecore.la
+
+DOXYGEN_REFERENCES = kdecore
+include ../admin/Doxyfile.am
diff --git a/libkmid/alsaout.cc b/libkmid/alsaout.cc
new file mode 100644
index 000000000..73effc2e3
--- /dev/null
+++ b/libkmid/alsaout.cc
@@ -0,0 +1,571 @@
+/**************************************************************************
+
+ alsaout.cc - class AlsaOut which represents an alsa client/port pair
+ This file is part of LibKMid 0.9.5
+ Copyright (C) 2000 Antonio Larrosa Jimenez
+ LibKMid's homepage : http://www.arrakis.es/~rlarrosa/libkmid.html
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ Send comments and bug fixes to Antonio Larrosa <larrosa@kde.org>
+
+***************************************************************************/
+#include "alsaout.h"
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include "sndcard.h"
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/param.h>
+#include "midispec.h"
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_ALSA_ASOUNDLIB_H
+# include <alsa/asoundlib.h>
+#elif defined(HAVE_SYS_ASOUNDLIB_H)
+# include <sys/asoundlib.h>
+#endif
+
+#ifdef HAVE_LIBASOUND2
+# define HAVE_ALSA_SEQ 1
+# define snd_seq_flush_output(x) snd_seq_drain_output(x)
+#elif defined(HAVE_LIBASOUND)
+# define HAVE_ALSA_SEQ 1
+# include <linux/asequencer.h>
+#endif
+
+
+SEQ_USE_EXTBUF();
+
+class AlsaOut::AlsaOutPrivate
+{
+public:
+#ifdef HAVE_ALSA_SEQ
+ AlsaOutPrivate(int _client, int _port, const char *cname,const char *pname)
+ {
+ handle=0L;
+ src=tgt=0L;
+ queue=0;
+ tPCN=1;
+ tgtclient=_client;
+ tgtport=_port;
+ tgtname=new char[strlen(cname)+strlen(pname)+3];
+ strcpy(tgtname, cname);
+ strcat(tgtname, " ");
+ strcat(tgtname, pname);
+ ev=new snd_seq_event_t;
+ timerStarted=false;
+ }
+#else
+ AlsaOutPrivate(int, int, const char *,const char *)
+ {
+ }
+#endif
+
+ ~AlsaOutPrivate()
+ {
+#ifdef HAVE_ALSA_SEQ
+ delete ev;
+ delete tgtname;
+#endif
+ }
+
+#ifdef HAVE_ALSA_SEQ
+ snd_seq_t *handle;
+ int client;
+ int queue;
+ snd_seq_addr_t *src;
+ snd_seq_addr_t *tgt;
+
+ snd_seq_event_t *ev;
+ int tPCN;
+
+ int tgtclient;
+ int tgtport;
+ char *tgtname;
+
+ bool timerStarted;
+
+#endif
+};
+
+AlsaOut::AlsaOut(int d,int _client, int _port, const char *cname,const char *pname) : MidiOut (d)
+{
+ di = new AlsaOutPrivate( _client, _port, cname, pname);
+ seqfd = 0;
+ devicetype=KMID_ALSA;
+ device= d;
+
+ volumepercentage=100;
+#ifdef HAVE_ALSA_SEQ
+// printf("%d %d %d (%s)\n",device, di->tgtclient, di->tgtport, di->tgtname);
+#endif
+
+ _ok=1;
+}
+
+AlsaOut::~AlsaOut()
+{
+ closeDev();
+ delete di;
+}
+
+void AlsaOut::openDev (int)
+{
+#ifndef HAVE_ALSA_SEQ
+ return;
+#else
+ _ok=1;
+#ifdef HAVE_LIBASOUND2
+ if (snd_seq_open(&di->handle, "hw", SND_SEQ_OPEN_DUPLEX, 0) < 0)
+ fprintf(stderr, "Couldn't open sequencer: %s", snd_strerror(errno));
+#else
+ if (snd_seq_open(&di->handle, SND_SEQ_OPEN) < 0)
+ fprintf(stderr, "Couldn't open sequencer: %s", snd_strerror(errno));
+#endif
+
+ di->queue = snd_seq_alloc_queue(di->handle);
+ if (di->queue < 0) {fprintf(stderr, "Couldn't allocate queue"); return; };
+ di->client = snd_seq_client_id(di->handle);
+ if (di->client < 0) {fprintf(stderr, "Couldn't get client id"); return; };
+ di->tgt = new snd_seq_addr_t;
+ di->tgt->client=di->tgtclient;
+ di->tgt->port=di->tgtport;
+
+ di->src = new snd_seq_addr_t;
+ di->src->client = di->client;
+ int port = snd_seq_create_simple_port(di->handle, NULL,
+ SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE
+ | SND_SEQ_PORT_CAP_READ, SND_SEQ_PORT_TYPE_MIDI_GENERIC);
+ if ( port < 0 )
+ {
+ delete di->src;
+ delete di->tgt;
+ di->src=0;
+ di->tgt=0;
+ _ok=0;
+ time=0;
+ snd_seq_free_queue(di->handle, di->queue);
+ snd_seq_close(di->handle);
+ fprintf(stderr, "Cannot connect to %d:%d\n",di->tgtclient,di->tgtport);
+ return;
+ }
+ di->src->port = port;
+
+
+ int r=snd_seq_connect_to(di->handle, di->src->port, di->tgt->client, di->tgt->port);
+ if (r < 0) { _ok=0; fprintf(stderr, "Cannot connect to %d:%d\n",di->tgtclient,di->tgtport); }
+ time=0;
+#endif
+}
+
+void AlsaOut::closeDev (void)
+{
+ if (!ok()) return;
+#ifdef HAVE_ALSA_SEQ
+ if (di->handle)
+ {
+ if (di->src)
+ {
+ snd_seq_delete_simple_port(di->handle,di->src->port);
+ delete di->src;
+ di->src=0;
+ }
+ if (di->tgt)
+ {
+ delete di->tgt;
+ di->tgt=0;
+ }
+ if (di->queue)
+ {
+ snd_seq_free_queue(di->handle, di->queue);
+ snd_seq_close(di->handle);
+ }
+ di->handle=0;
+ }
+
+#endif
+}
+
+void AlsaOut::initDev (void)
+{
+#ifdef HAVE_ALSA_SEQ
+ int chn;
+ if (!ok()) return;
+ uchar gm_reset[5]={0x7e, 0x7f, 0x09, 0x01, 0xf7};
+ sysex(gm_reset, sizeof(gm_reset));
+ for (chn=0;chn<16;chn++)
+ {
+ chnmute[chn]=0;
+ if (chn!=9) chnPatchChange(chn,0);
+ chnPressure(chn,64);
+ chnPitchBender(chn, 0x00, 0x40);
+ chnController(chn, CTL_MAIN_VOLUME,110*volumepercentage);
+ chnController(chn, CTL_EXT_EFF_DEPTH, 0);
+ chnController(chn, CTL_CHORUS_DEPTH, 0);
+ chnController(chn, 0x4a, 127);
+ }
+#endif
+}
+
+#ifdef HAVE_ALSA_SEQ
+void AlsaOut::eventInit(snd_seq_event_t *ev)
+{
+ snd_seq_ev_clear(ev);
+ snd_seq_real_time_t tmp;
+ tmp.tv_sec=(time)/1000;
+ tmp.tv_nsec=(time%1000)*1000000;
+// printf("time : %d %d %d\n",(int)time,(int)tmp.tv_sec, (int)tmp.tv_nsec);
+ if (!di->src) { fprintf(stderr,"AlsaOut::eventInit : no source\n"); return; }
+ ev->source = *di->src;
+ if (!di->tgt) { fprintf(stderr,"AlsaOut::eventInit : no target\n"); return; }
+ ev->dest = *di->tgt;
+
+ snd_seq_ev_schedule_real(ev, di->queue, 0, &tmp);
+}
+
+void AlsaOut::eventSend(snd_seq_event_t *ev)
+{
+ /*int err = */ snd_seq_event_output(di->handle, ev);
+/* if (err < 0)
+ return;
+*/
+//#ifndef SND_SEQ_IOCTL_GET_CLIENT_POOL
+ /*
+ * If this is not defined then block mode writes will not be
+ * working correctly. Therefore loop until all events are flushed
+ * out.
+ */
+/* err = 0;
+ do {
+ err = snd_seq_flush_output(di->handle);
+ if (err > 0)
+ usleep(2000);
+ } while (err > 0);
+
+#endif
+
+ return ;
+*/
+}
+
+void AlsaOut::timerEventSend(int type)
+{
+ snd_seq_event_t ev;
+
+ ev.queue = di->queue;
+ ev.dest.client = SND_SEQ_CLIENT_SYSTEM;
+ ev.dest.port = SND_SEQ_PORT_SYSTEM_TIMER;
+
+ ev.data.queue.queue = di->queue;
+
+ ev.flags = SND_SEQ_TIME_STAMP_REAL | SND_SEQ_TIME_MODE_REL;
+ ev.time.time.tv_sec = 0;
+ ev.time.time.tv_nsec = 0;
+
+ ev.type = type;
+
+ snd_seq_event_output(di->handle, &ev);
+ snd_seq_flush_output(di->handle);
+}
+
+#endif // HAVE_ALSA_SEQ
+
+#ifndef HAVE_ALSA_SEQ
+void AlsaOut::noteOn (uchar , uchar , uchar )
+{
+#else
+void AlsaOut::noteOn (uchar chn, uchar note, uchar vel)
+{
+ if (vel==0)
+ {
+ noteOff(chn,note,vel);
+ }
+ else
+ {
+ eventInit(di->ev);
+ snd_seq_ev_set_noteon(di->ev,map->channel(chn), map->key(chn,chnpatch[chn],note), vel);
+ eventSend(di->ev);
+ }
+#endif
+#ifdef MIDIOUTDEBUG
+ printfdebug("Note ON >\t chn : %d\tnote : %d\tvel: %d\n",chn,note,vel);
+#endif
+}
+
+#ifndef HAVE_ALSA_SEQ
+void AlsaOut::noteOff (uchar , uchar , uchar )
+{
+#else
+void AlsaOut::noteOff (uchar chn, uchar note, uchar vel)
+{
+ eventInit(di->ev);
+ snd_seq_ev_set_noteoff(di->ev,map->channel(chn), map->key(chn,chnpatch[chn],note), vel);
+ eventSend(di->ev);
+#endif
+#ifdef MIDIOUTDEBUG
+ printfdebug("Note OFF >\t chn : %d\tnote : %d\tvel: %d\n",chn,note,vel);
+#endif
+}
+
+#ifndef HAVE_ALSA_SEQ
+void AlsaOut::keyPressure (uchar , uchar , uchar )
+{
+#else
+void AlsaOut::keyPressure (uchar chn, uchar note, uchar vel)
+{
+ eventInit(di->ev);
+ snd_seq_ev_set_keypress(di->ev,map->channel(chn), map->key(chn,chnpatch[chn],note), vel);
+ eventSend(di->ev);
+#endif
+}
+
+#ifndef HAVE_ALSA_SEQ
+void AlsaOut::chnPatchChange (uchar , uchar )
+{
+#else
+void AlsaOut::chnPatchChange (uchar chn, uchar patch)
+{
+#ifdef MIDIOUTDEBUG
+ printfdebug("PATCHCHANGE [%d->%d] %d -> %d\n",
+ chn,map->channel(chn),patch,map->patch(chn,patch));
+#endif
+ eventInit(di->ev);
+ snd_seq_ev_set_pgmchange(di->ev,map->channel(chn), map->patch(chn,patch));
+ eventSend(di->ev);
+ chnpatch[chn]=patch;
+#endif
+}
+
+#ifndef HAVE_ALSA_SEQ
+void AlsaOut::chnPressure (uchar , uchar )
+{
+#else
+void AlsaOut::chnPressure (uchar chn, uchar vel)
+{
+ eventInit(di->ev);
+ snd_seq_ev_set_chanpress(di->ev,map->channel(chn), vel);
+ eventSend(di->ev);
+
+ chnpressure[chn]=vel;
+#endif
+}
+
+#ifndef HAVE_ALSA_SEQ
+void AlsaOut::chnPitchBender(uchar ,uchar , uchar )
+{
+#else
+void AlsaOut::chnPitchBender(uchar chn,uchar lsb, uchar msb)
+{
+ map->pitchBender(chn,lsb,msb);
+ chnbender[chn]=((short)msb<<7) | (lsb & 0x7F);
+ chnbender[chn]=chnbender[chn]-0x2000;
+
+ eventInit(di->ev);
+ snd_seq_ev_set_pitchbend(di->ev,map->channel(chn), chnbender[chn]);
+ eventSend(di->ev);
+#endif
+}
+
+#ifndef HAVE_ALSA_SEQ
+void AlsaOut::chnController (uchar , uchar , uchar )
+{
+#else
+void AlsaOut::chnController (uchar chn, uchar ctl, uchar v)
+{
+ map->controller(chn,ctl,v);
+ if ((ctl==11)||(ctl==7))
+ {
+ v=(v*volumepercentage)/100;
+ if (v>127) v=127;
+ }
+
+ eventInit(di->ev);
+ snd_seq_ev_set_controller(di->ev,map->channel(chn), ctl, v);
+ eventSend(di->ev);
+
+ chncontroller[chn][ctl]=v;
+#endif
+}
+
+#ifndef HAVE_ALSA_SEQ
+void AlsaOut::sysex(uchar *, ulong )
+{
+#else
+void AlsaOut::sysex(uchar *data, ulong size)
+{
+ eventInit(di->ev);
+ snd_seq_ev_set_sysex(di->ev, size, data);
+ eventSend(di->ev);
+#endif
+
+#ifdef MIDIOUTDEBUG
+ printfdebug("sysex\n");
+#endif
+}
+
+#ifndef HAVE_ALSA_SEQ
+void AlsaOut::channelSilence (uchar )
+{
+#else
+void AlsaOut::channelSilence (uchar chn)
+{
+ uchar i;
+ for ( i=0; i<127; i++)
+ {
+ noteOff(chn,i,0);
+ }
+#endif
+}
+
+#ifndef HAVE_ALSA_SEQ
+void AlsaOut::channelMute(uchar , int )
+{
+#else
+void AlsaOut::channelMute(uchar chn, int a)
+{
+ if (a==1)
+ {
+ chnmute[chn]=a;
+ channelSilence(chn);
+ }
+ else if (a==0)
+ {
+ chnmute[chn]=a;
+ }
+ /* else ignore the call to this procedure */
+#endif
+}
+
+void AlsaOut::seqbuf_dump (void)
+{
+ printf("You shouldn't be here.\n");
+}
+
+void AlsaOut::seqbuf_clean(void)
+{
+ printf("You shouldn't be here neither.\n");
+}
+
+void AlsaOut::wait(double ticks)
+{
+// SEQ_WAIT_TIME(((int)(ticks/convertrate)));
+ time=(long int)ticks;
+
+#ifdef MIDIOUTDEBUG
+ printfdebug("Wait >\t ticks: %g\n",ticks);
+#endif
+}
+
+#ifndef HAVE_ALSA_SEQ
+void AlsaOut::tmrSetTempo(int )
+{
+#else
+void AlsaOut::tmrSetTempo(int v)
+{
+ eventInit(di->ev);
+ di->ev->type = SND_SEQ_EVENT_TEMPO;
+ snd_seq_ev_set_direct(di->ev);
+ di->ev->data.queue.queue = di->queue;
+ di->ev->data.queue.param.value = v;
+ di->ev->dest.client = SND_SEQ_CLIENT_SYSTEM;
+ di->ev->dest.port = SND_SEQ_PORT_SYSTEM_TIMER;
+ snd_seq_event_output_direct(di->handle, di->ev);
+#ifdef MIDIOUTDEBUG
+ printfdebug("SETTEMPO >\t tempo: %d\n",v);
+#endif
+#endif
+}
+
+#ifndef HAVE_ALSA_SEQ
+void AlsaOut::sync(int )
+{
+#else
+void AlsaOut::sync(int i)
+{
+ if (i==1)
+ {
+ snd_seq_flush_output(di->handle);
+ }
+
+ if (di->timerStarted && di->src)
+ {
+ eventInit(di->ev);
+ di->ev->dest = *di->src;
+ eventSend(di->ev);
+ snd_seq_flush_output(di->handle);
+ snd_seq_event_input(di->handle,&di->ev);
+ }
+
+#endif
+}
+
+#ifndef HAVE_ALSA_SEQ
+void AlsaOut::tmrStart(int )
+{
+#else
+void AlsaOut::tmrStart(int tpcn)
+{
+ int ret;
+ di->timerStarted=true;
+ di->tPCN=tpcn;
+
+#ifdef HAVE_LIBASOUND2
+ snd_seq_queue_tempo_t *queuetempo;
+ snd_seq_queue_tempo_alloca(&queuetempo);
+ snd_seq_queue_tempo_set_ppq(queuetempo, tpcn);
+ snd_seq_queue_tempo_set_tempo(queuetempo, 60*1000000/120);
+ ret = snd_seq_set_queue_tempo(di->handle, di->queue, queuetempo);
+#else
+ snd_seq_queue_tempo_t queuetempo;
+ memset(&queuetempo, 0, sizeof(queuetempo));
+ queuetempo.queue = di->queue;
+ queuetempo.ppq = tpcn;
+ queuetempo.tempo = 60*1000000/120;
+ ret = snd_seq_set_queue_tempo(di->handle, di->queue, &queuetempo);
+#endif
+
+ timerEventSend(SND_SEQ_EVENT_START);
+ snd_seq_start_queue(di->handle,di->queue,NULL);
+#endif
+}
+
+void AlsaOut::tmrStop(void)
+{
+#ifdef HAVE_ALSA_SEQ
+ di->timerStarted=false;
+ timerEventSend(SND_SEQ_EVENT_STOP);
+#endif
+}
+
+void AlsaOut::tmrContinue(void)
+{
+}
+
+const char * AlsaOut::deviceName(void) const
+{
+#ifdef HAVE_ALSA_SEQ
+ return di->tgtname;
+#else
+ return 0L;
+#endif
+}
diff --git a/libkmid/alsaout.h b/libkmid/alsaout.h
new file mode 100644
index 000000000..fd4a46579
--- /dev/null
+++ b/libkmid/alsaout.h
@@ -0,0 +1,231 @@
+/* alsaout.cc - class AlsaOut which represents an alsa client/port pair
+ This file is part of LibKMid 0.9.5
+ Copyright (C) 1997,98,99,2000 Antonio Larrosa Jimenez
+ LibKMid's homepage : http://www.arrakis.es/~rlarrosa/libkmid.html
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ Send comments and bug fixes to Antonio Larrosa <larrosa@kde.org>
+
+***************************************************************************/
+#ifndef _ALSAOUT_H
+#define _ALSAOUT_H
+
+#include <libkmid/midiout.h>
+
+struct snd_seq_event;
+typedef struct snd_seq_event snd_seq_event_t;
+
+/**
+ * @short Sends MIDI events to a MIDI devices using ALSA
+ * @version 0.9.5 17/01/2000
+ * @author Antonio Larrosa Jimenez <larrosa@kde.org>
+ */
+class AlsaOut : public MidiOut
+{
+ friend class DeviceManager;
+
+ protected:
+
+/**
+ * @internal
+ * Total number of devices.
+ */
+ int ndevs;
+
+/**
+ * @internal
+ * Total number of midi ports
+ */
+ int nmidiports;
+
+ double count;
+ double lastcount;
+ double lasttime;
+ double begintime;
+ int m_rate;
+
+/**
+ * @internal
+ * A "constant" used to convert from milliseconds to the computer rate
+ */
+ double convertrate;
+
+ long int time;
+
+ virtual void seqbuf_dump (void);
+ virtual void seqbuf_clean(void);
+ void eventInit(snd_seq_event_t *ev);
+ void eventSend(snd_seq_event_t *ep);
+ void timerEventSend(int type);
+
+ public:
+
+ /**
+ * Constructor. After constructing a MidiOut device, you must open it
+ * (using openDev() ). Additionally you may want to initialize it
+ * (with initDev() ),
+ */
+ AlsaOut(int d, int client=64, int port=0, const char *cname="", const char *pname="");
+
+ /**
+ * Destructor. It doesn't matter if you close the device ( closeDev() )
+ * before you destruct the object because in other case, it will be closed
+ * here.
+ */
+ virtual ~AlsaOut();
+
+ /**
+ * Opens the device. This is generally called from DeviceManager , so you
+ * shouldn't call this yourself (except if you created the MidiOut object
+ * yourself.
+ * @param sqfd a file descriptor of /dev/sequencer
+ * @see closeDev
+ * @see initDev
+ */
+ virtual void openDev (int sqfd);
+
+ /**
+ * Closes the device. It basically tells the device (the file descriptor)
+ * is going to be closed.
+ * @see openDev
+ */
+ virtual void closeDev ();
+
+ /**
+ * Initializes the device sending generic standard midi events and controllers,
+ * such as changing the patches of each channel to an Acoustic Piano (000),
+ * setting the volume to a normal value, etc.
+ */
+ virtual void initDev ();
+
+ /**
+ * @return the device type of the object. This is to identify the
+ * inherited class that a given object is polymorphed to.
+ * The returned value is one of these :
+ *
+ * @li KMID_EXTERNAL_MIDI if it's a MidiOut object
+ * @li KMID_SYNTH if it's a SynthOut object (as an AWE device)
+ * @li KMID_FM if it's a FMOut object
+ * @li KMID_GUS if it's a GUSOut object
+ *
+ * which are defined in midispec.h
+ *
+ * @see deviceName
+ */
+ int deviceType () const { return devicetype; }
+
+ /**
+ * Returns the name and type of this MIDI device.
+ * @see deviceType
+ */
+ virtual const char * deviceName (void) const;
+
+ /**
+ * @internal
+ */
+ int rate (void) { return m_rate; }
+
+ /**
+ * See DeviceManager::noteOn()
+ */
+ virtual void noteOn ( uchar chn, uchar note, uchar vel );
+
+ /**
+ * See DeviceManager::noteOff()
+ */
+ virtual void noteOff ( uchar chn, uchar note, uchar vel );
+
+ /**
+ * See DeviceManager::keyPressure()
+ */
+ virtual void keyPressure ( uchar chn, uchar note, uchar vel );
+
+ /**
+ * See DeviceManager::chnPatchChange()
+ */
+ virtual void chnPatchChange ( uchar chn, uchar patch );
+
+ /**
+ * See DeviceManager::chnPressure()
+ */
+ virtual void chnPressure ( uchar chn, uchar vel );
+
+ /**
+ * See DeviceManager::chnPitchBender()
+ */
+ virtual void chnPitchBender ( uchar chn, uchar lsb, uchar msb );
+
+ /**
+ * See DeviceManager::chnController()
+ */
+ virtual void chnController ( uchar chn, uchar ctl , uchar v );
+
+ /**
+ * See DeviceManager::sysex()
+ */
+ virtual void sysex ( uchar *data,ulong size);
+
+ /**
+ * Mutes all notes being played on a given channel.
+ */
+ virtual void channelSilence ( uchar chn );
+
+ /**
+ * Mute or "unmute" a given channel .
+ * @param chn channel to work on
+ * @param b if true, the device will ignore subsequent notes played on the chn
+ * channel, and mute all notes being played on it. If b is false, the channel
+ * is back to work.
+ */
+ virtual void channelMute ( uchar chn, int b );
+
+ /**
+ * Change all channel volume events multiplying it by this percentage correction
+ * Instead of forcing a channel to a fixed volume, this method allows to
+ * music to fade out even when it was being played softly.
+ * @param volper is an integer value, where 0 is quiet, 100 is used to send
+ * an unmodified value, 200 play music twice louder than it should, etc.
+ */
+ virtual void setVolumePercentage ( int volper )
+ { volumepercentage = volper; }
+
+ /**
+ * Returns true if everything's ok and false if there has been any problem
+ */
+ int ok (void)
+ { if (seqfd<0) return 0;
+ return (_ok>0);
+ }
+
+ virtual void wait (double ticks);
+ virtual void tmrSetTempo (int v);
+ virtual void tmrStart (int tpcn);
+ virtual void tmrStart () { tmrStart(-1); }
+ virtual void tmrStop ();
+ virtual void tmrContinue ();
+ /**
+ * @internal
+ * If i==1 syncronizes by cleaning the buffer instead of sending it (in fact,
+ * this is what synchronizing really means :-) )
+ */
+ void sync (int i=0);
+
+ class AlsaOutPrivate;
+ AlsaOutPrivate *di;
+};
+
+#endif // _ALSAOUT_H
diff --git a/libkmid/awe_sup.h b/libkmid/awe_sup.h
new file mode 100644
index 000000000..5602cf15f
--- /dev/null
+++ b/libkmid/awe_sup.h
@@ -0,0 +1,50 @@
+/* awe_sup.h - A wrapper on awe_voice.h
+ This file is part of LibKMid 0.9.5
+ Copyright (C) 1997,98,99,2000 Antonio Larrosa Jimenez
+ LibKMid's homepage : http://www.arrakis.es/~rlarrosa/libkmid.html
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ Send comments and bug fixes to Antonio Larrosa <larrosa@kde.org>
+
+***************************************************************************/
+#ifndef _AWE_SUP_H
+#define _AWE_SUP_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_AWE_VOICE_H
+#include <awe_voice.h>
+#define HAVE_AWE32
+#elif defined(HAVE_LINUX_AWE_VOICE_H)
+#include <linux/awe_voice.h>
+#define HAVE_AWE32
+#elif defined(HAVE__USR_SRC_SYS_I386_ISA_SOUND_AWE_VOICE_H)
+#include "/usr/src/sys/i386/isa/sound/awe_voice.h"
+#define HAVE_AWE32
+#elif defined(HAVE__USR_SRC_SYS_GNU_I386_ISA_SOUND_AWE_VOICE_H)
+#include "/usr/src/sys/gnu/i386/isa/sound/awe_voice.h"
+#define HAVE_AWE32
+#endif
+
+#ifndef AWE_SET_CHANNEL_MODE
+// AWE32 doesn't work if AWE_SET_CHANNEL_MODE isn't defined.
+#undef HAVE_AWE32
+#endif
+
+#endif
diff --git a/libkmid/configure.in.in b/libkmid/configure.in.in
new file mode 100644
index 000000000..7500e3455
--- /dev/null
+++ b/libkmid/configure.in.in
@@ -0,0 +1,128 @@
+dnl libkmid's specific checks
+
+#AC_MSG_CHECKING(if libkmid would compile)
+#AC_CACHE_VAL(kde_libkmid_compiles,
+#[
+#AC_TRY_COMPILE([
+##ifndef __FreeBSD__
+##include <sys/soundcard.h>
+##else
+##include <machine/soundcard.h>
+##endif
+#],
+#[
+#],
+# kde_libmid_compiles=yes,
+#kde_libmid_compiles=no)
+#])
+#AC_MSG_RESULT($kde_libmid_compiles)
+#if test $kde_libmid_compiles = no; then
+# DO_NOT_COMPILE="$DO_NOT_COMPILE libkmid"
+#fi
+
+#AC_MSG_CHECKING([for OSS support])
+#AC_CACHE_VAL(ac_cv_header_soundcard_h,
+#[
+#AC_TRY_COMPILE([
+##include <unistd.h>
+#],
+#[ ],
+#ac_cv_header_soundcard_h=yes,
+#ac_cv_header_soundcard_h=no)
+#])
+#AC_MSG_RESULT($ac_cv_header_soundcard_h)
+#if eval "test \"`echo $ac_cv_header_soundcard_h`\" = yes"; then
+# AC_DEFINE(HAVE_GETHOSTNAME, 1, [Define if you have getdomainname])
+#fi
+#CXXFLAGS="$save_CXXFLAGS"
+#])
+
+
+AC_CHECK_HEADERS(sys/soundcard.h machine/soundcard.h linux/awe_voice.h awe_voice.h /usr/src/sys/i386/isa/sound/awe_voice.h /usr/src/sys/gnu/i386/isa/sound/awe_voice.h)
+
+dnl check for ALSA audio support
+
+kde_with_alsa=yes
+AC_ARG_WITH(alsa, AC_HELP_STRING([--with-alsa],[enable libKMid ALSA support]),
+[kde_with_alsa=$withval])
+
+if test "$kde_with_alsa" = "yes"; then
+AC_DEFUN([AC_CHECK_LIBASOUND],
+[
+ ac_ldflags_save="$LDFLAGS"
+ LDFLAGS="$all_libraries $LDFLAGS"
+ kde_has_asoundlib=no
+
+ AC_CHECK_HEADERS([ sys/asoundlib.h alsa/asoundlib.h ],
+ [
+ kde_has_asoundlib=yes
+ ])
+
+ dnl trial and error version check for ALSA 0.5.x / ALSA 0.9.x
+ AC_LANG_SAVE
+ AC_LANG_C
+ if test "x$kde_has_asoundlib" = "xyes"; then
+ AC_TRY_COMPILE([
+ #include "confdefs.h"
+ #ifdef HAVE_SYS_ASOUNDLIB_H
+ #include <sys/asoundlib.h>
+ #endif
+ #ifdef HAVE_ALSA_ASOUNDLIB_H
+ #include <alsa/asoundlib.h>
+ #endif
+ ],[
+ #if ((SND_LIB_MAJOR == 0) && (SND_LIB_MINOR == 9)) || (SND_LIB_MAJOR == 1)
+ /* we have ALSA 0.9.x or 1.x */
+ #else
+ #error not ALSA 0.9.x
+ #endif
+ ],
+ kde_has_alsa_0_9=yes,
+ kde_has_alsa_0_9=no)
+ fi
+
+ if test "x$kde_has_asoundlib" = "xyes"; then
+ AC_TRY_COMPILE([
+ #include "confdefs.h"
+ #ifdef HAVE_SYS_ASOUNDLIB_H
+ #include <sys/asoundlib.h>
+ #endif
+ #ifdef HAVE_ALSA_ASOUNDLIB_H
+ #include <alsa/asoundlib.h>
+ #endif
+ ],[
+ #if (SND_LIB_MAJOR == 0) && (SND_LIB_MINOR == 5)
+ /* we have ALSA 0.5.x */
+ #else
+ #error not ALSA 0.5.x
+ #endif
+ ],
+ kde_has_alsa_0_5=yes,
+ kde_has_alsa_0_5=no)
+ fi
+ AC_LANG_RESTORE
+
+ if test "x$kde_has_asoundlib" = "xyes"; then
+ AC_CHECK_LIB(asound,snd_seq_create_simple_port,[
+ if test "x$kde_has_alsa_0_5" = "xyes"; then
+ LIBASOUND="-lasound"
+ AC_DEFINE(HAVE_LIBASOUND, 1,
+ [Define if you have libasound.so.1 (required for ALSA 0.5.x support)])
+ fi
+ if test "x$kde_has_alsa_0_9" = "xyes"; then
+ LIBASOUND="-lasound"
+ AC_DEFINE(HAVE_LIBASOUND2, 1,
+ [Define if you have libasound.so.2 (required for ALSA 0.9.x support)])
+ AC_CHECK_LIB(asound,snd_pcm_resume,[
+ AC_DEFINE(HAVE_SND_PCM_RESUME, 1,
+ [Define if libasound has snd_pcm_resume()])])
+ fi
+ ])
+ fi
+ AC_SUBST(LIBASOUND)
+ LDFLAGS="$ac_ldflags_save"
+])
+AC_CHECK_LIBASOUND
+fi
+
+AC_SUBST(LIBASOUND)
diff --git a/libkmid/dattypes.cc b/libkmid/dattypes.cc
new file mode 100644
index 000000000..821abd00a
--- /dev/null
+++ b/libkmid/dattypes.cc
@@ -0,0 +1,110 @@
+/**************************************************************************
+
+ dattypes.cc - Some always useful definitions and functions
+ This file is part of LibKMid 0.9.5
+ Copyright (C) 1997,98,99,2000 Antonio Larrosa Jimenez
+ LibKMid's homepage : http://www.arrakis.es/~rlarrosa/libkmid.html
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ Send comments and bug fixes to Antonio Larrosa <larrosa@kde.org>
+
+***************************************************************************/
+#include "dattypes.h"
+#include <stdio.h>
+
+ushort readShort(FILE *fh)
+{
+ uchar c1;
+ uchar c2;
+
+ fread(&c1,1,1,fh);
+ fread(&c2,1,1,fh);
+ return (c1<<8)|c2;
+}
+
+ulong readLong(FILE *fh)
+{
+ uchar c1;
+ uchar c2;
+ uchar c3;
+ uchar c4;
+ ulong l;
+
+ fread(&c1,1,1,fh);
+ fread(&c2,1,1,fh);
+ fread(&c3,1,1,fh);
+ fread(&c4,1,1,fh);
+ l=((c1<<24)|(c2<<16)|(c3<<8)|c4);
+ return l;
+}
+
+#ifdef DEBUG
+
+void printfdebug(const char *format, int a, int b, int c)
+{
+ char *s=(char *)format;
+ int i=0;
+ while (*s!=0)
+ {
+ if (*s=='%') i++;
+ s++;
+ }
+ switch (i)
+ {
+ case (1) : fprintf(stderr,format,a); break;
+ case (2) : fprintf(stderr,format,a,b); break;
+ case (3) : fprintf(stderr,format,a,b,c); break;
+ default : fprintf(stderr,format); break;
+ }
+
+}
+
+void printfdebug(const char *format, int a, long b)
+{
+ fprintf(stderr,format,a,b);
+}
+
+void printfdebug(const char *format, double a, double b, double c)
+{
+ char *s=(char *)format;
+ int i=0;
+ while (*s!=0)
+ {
+ if (*s=='%') i++;
+ s++;
+ }
+ switch (i)
+ {
+ case (1) : fprintf(stderr,format,a); break;
+ case (2) : fprintf(stderr,format,a,b); break;
+ case (3) : fprintf(stderr,format,a,b,c); break;
+ default : fprintf(stderr,format); break;
+ }
+
+}
+#else
+
+void printfdebug(const char *, int , int , int )
+{
+}
+void printfdebug(const char *, int , long )
+{
+}
+void printfdebug(const char *, double , double , double )
+{
+}
+#endif
diff --git a/libkmid/dattypes.h b/libkmid/dattypes.h
new file mode 100644
index 000000000..0e647980f
--- /dev/null
+++ b/libkmid/dattypes.h
@@ -0,0 +1,55 @@
+/* dattypes.h - Some useful definitions and functions
+ This file is part of LibKMid 0.9.5
+ Copyright (C) 1997,98,99,2000 Antonio Larrosa Jimenez
+ LibKMid's homepage : http://www.arrakis.es/~rlarrosa/libkmid.html
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ Send comments and bug fixes to Antonio Larrosa <larrosa@kde.org>
+
+***************************************************************************/
+#ifndef _DATTYPES_H
+#define _DATTYPES_H
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#undef uchar
+#undef ushort
+#undef ulong
+
+/**
+ * Unsigned char
+ */
+typedef unsigned char uchar;
+
+/**
+ * Unsigned short
+ */
+typedef unsigned short ushort;
+
+/**
+ * Unsigned long
+ */
+typedef unsigned long ulong;
+
+ushort readShort(FILE *fh);
+ulong readLong (FILE *fh);
+
+void printfdebug(const char *s,int a=0,int b=0, int c=0);
+void printfdebug(const char *s,int a,long b);
+void printfdebug(const char *s,double a,double b=0, double c=0);
+
+#endif
diff --git a/libkmid/deviceman.cc b/libkmid/deviceman.cc
new file mode 100644
index 000000000..7c8e0f48c
--- /dev/null
+++ b/libkmid/deviceman.cc
@@ -0,0 +1,830 @@
+/**************************************************************************
+
+ deviceman.cc - The device manager, that hides the use of midiOut
+ This file is part of LibKMid 0.9.5
+ Copyright (C) 1997,98,99,2000 Antonio Larrosa Jimenez
+ LibKMid's homepage : http://www.arrakis.es/~rlarrosa/libkmid.html
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ $Id$
+
+ Send comments and bug fixes to Antonio Larrosa <larrosa@kde.org>
+
+***************************************************************************/
+#include "deviceman.h"
+#include "midiout.h"
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include "sndcard.h"
+#include "synthout.h"
+#include "fmout.h"
+#include "gusout.h"
+#include "alsaout.h"
+#include "midimapper.h"
+#include "midispec.h"
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#ifdef HAVE_ALSA_ASOUNDLIB_H
+# define HAVE_ALSA_SUPPORT
+# include <alsa/asoundlib.h>
+#elif defined(HAVE_SYS_ASOUNDLIB_H)
+# define HAVE_ALSA_SUPPORT
+# include <sys/asoundlib.h>
+#else
+#ifdef HAVE_LIBASOUND2
+# define HAVE_ALSA_SUPPORT
+# include <sound/asound.h>
+# include <sound/asequencer.h>
+#elif defined(HAVE_LIBASOUND)
+# define HAVE_ALSA_SUPPORT
+# include <linux/asequencer.h>
+#endif
+#endif
+
+#if 1
+#include <kinstance.h>
+#include <kglobal.h>
+#include <kconfig.h>
+#endif
+
+//#define DEVICEMANDEBUG
+//#define GENERAL_DEBUG_MESSAGES
+
+SEQ_DEFINEBUF (4096);
+
+#define CONTROLTIMER
+
+#ifdef GENERAL_DEBUG_MESSAGES
+void DEBUGPRINTF(const char *format)
+{
+ printf(format);
+}
+
+void DEBUGPRINTF(const char *format,int i)
+{
+ printf(format,i);
+}
+
+void DEBUGPRINTF(const char *format,const char *s)
+{
+ printf(format,s);
+}
+
+#else
+
+void DEBUGPRINTF(const char *) { }
+void DEBUGPRINTF(const char *,int ) { }
+void DEBUGPRINTF(const char *,const char * ) { }
+
+#endif
+
+
+DeviceManager::DeviceManager(int def)
+{
+#if 1
+ if (def==-1)
+ {
+ KInstance *tmp_instance=0L;
+ if (!KGlobal::_instance) tmp_instance=new KInstance("nonKDEapp");
+ KConfig *config = new KConfig("kcmmidirc", true);
+
+ config->setGroup("Configuration");
+ default_dev=config->readNumEntry("midiDevice",0);
+ if ( default_dev < 0 )
+ default_dev=0;
+ QString mapurl(config->readPathEntry("mapFilename"));
+ if ((config->readBoolEntry("useMidiMapper", false))&&(!mapurl.isEmpty()))
+ {
+ mapper_tmp = new MidiMapper( mapurl.mid(mapurl.find(":")+1 ).local8Bit() );
+ }
+ else
+ mapper_tmp = 0L;
+
+ delete config;
+ delete tmp_instance;
+ }
+ else
+#endif
+ {
+ default_dev = def;
+ mapper_tmp = 0L;
+ }
+
+ initialized=0;
+ _ok=1;
+ alsa=false;
+ device = 0L;
+ m_rate=0;
+ convertrate=10;
+ seqfd=-1;
+ timerstarted=0;
+ n_midi=0;
+ n_synths=0;
+ n_total=0;
+ midiinfo=0L;
+ synthinfo=0L;
+ for (int i=0;i<16;i++) chn2dev[i]=default_dev;
+}
+
+DeviceManager::~DeviceManager(void)
+{
+ closeDev();
+ if (device)
+ {
+ for (int i=0;i<n_total;i++)
+ delete device[i];
+ delete[] device;
+ device=0L;
+ }
+#ifdef HAVE_OSS_SUPPORT
+ delete[] midiinfo;
+ delete[] synthinfo;
+#endif
+}
+
+int DeviceManager::ok(void)
+{
+ int r=_ok;
+ _ok=1;
+ return r;
+}
+
+int DeviceManager::checkInit(void)
+{
+ if (initialized==0)
+ {
+ int r=initManager();
+ if (default_dev>=n_total) default_dev=0;
+ DEBUGPRINTF("check : %d\n",r);
+ return r;
+ }
+ return 0;
+}
+
+void DeviceManager::checkAlsa(void)
+{
+#ifdef HAVE_SYS_STAT_H
+ struct stat buf;
+ stat("/proc/asound", &buf);
+ if ((stat("/proc/asound", &buf) == 0 ) && (S_ISDIR(buf.st_mode)))
+ alsa=true;
+ else
+ alsa=false;
+#else
+#warning "ALSA won't be found at runtime"
+ alsa=false;
+#endif
+}
+
+int DeviceManager::initManager(void)
+{
+ checkAlsa();
+
+ if (!alsa) // We are using OSS
+ {
+#ifdef HAVE_OSS_SUPPORT
+ n_synths=0;
+ n_midi=0;
+ n_total=0;
+
+ seqfd = open("/dev/sequencer", O_WRONLY | O_NONBLOCK, 0);
+ if (seqfd==-1)
+ {
+ fprintf(stderr,"ERROR: Couldn't open /dev/sequencer to get some information\n");
+ _ok=0;
+ return -1;
+ }
+ ioctl(seqfd,SNDCTL_SEQ_NRSYNTHS,&n_synths);
+ ioctl(seqfd,SNDCTL_SEQ_NRMIDIS,&n_midi);
+ n_total=n_midi+n_synths;
+
+
+ if (n_midi==0)
+ {
+ fprintf(stderr,"ERROR: There's no midi port\n");
+ /* This could be a problem if the user don't have a synth neither,
+ but not having any of both things is unusual */
+ // _ok=0;
+ // return 1;
+ }
+
+ device=new MidiOut*[n_total];
+ midiinfo=new midi_info[n_midi];
+ synthinfo=new synth_info[n_synths];
+
+ int i;
+ for (i=0;i<n_midi;i++)
+ {
+ midiinfo[i].device=i;
+ if (ioctl(seqfd,SNDCTL_MIDI_INFO,&midiinfo[i])!=-1)
+ {
+#ifdef GENERAL_DEBUG_MESSAGES
+ printf("----\n");
+ printf("Device : %d\n",i);
+ printf("Name : %s\n",midiinfo[i].name);
+ printf("Device type : %d\n",midiinfo[i].dev_type);
+#endif
+ }
+ device[i]=new MidiOut(i);
+ }
+
+ for (i=0;i<n_synths;i++)
+ {
+ synthinfo[i].device=i;
+ if (ioctl(seqfd,SNDCTL_SYNTH_INFO,&synthinfo[i])!=-1)
+ {
+#ifdef GENERAL_DEBUG_MESSAGES
+ printf("----\n");
+ printf("Device : %d\n",i);
+ printf("Name : %s\n",synthinfo[i].name);
+ switch (synthinfo[i].synth_type)
+ {
+ case (SYNTH_TYPE_FM) : printf("FM\n");break;
+ case (SYNTH_TYPE_SAMPLE) : printf("Sample\n");break;
+ case (SYNTH_TYPE_MIDI) : printf("Midi\n");break;
+ default : printf("default type\n");break;
+ };
+ switch (synthinfo[i].synth_subtype)
+ {
+ case (FM_TYPE_ADLIB) : printf("Adlib\n");break;
+ case (FM_TYPE_OPL3) : printf("Opl3\n");break;
+ case (MIDI_TYPE_MPU401) : printf("Mpu-401\n");break;
+ case (SAMPLE_TYPE_GUS) : printf("Gus\n");break;
+ default : printf("default subtype\n");break;
+ }
+#endif
+ if (synthinfo[i].synth_type==SYNTH_TYPE_FM)
+ device[i+n_midi]=new FMOut(i,synthinfo[i].nr_voices);
+ else if ((synthinfo[i].synth_type==SYNTH_TYPE_SAMPLE)&&
+ (synthinfo[i].synth_subtype==SAMPLE_TYPE_GUS))
+ device[i+n_midi]=new GUSOut(i,synthinfo[i].nr_voices);
+ else
+ device[i+n_midi]=new SynthOut(i);
+ }
+ }
+
+ close(seqfd);
+#else // There's no OSS support and ALSA wasn't detected
+ // It must be one of those systems coolo is using :-)
+
+ n_synths=0;
+ n_midi=0;
+ n_total=0;
+ device=0L;
+ midiinfo=0L;
+ synthinfo=0L;
+
+#endif
+
+ }
+ else
+ { // We are using ALSA
+
+#ifdef HAVE_ALSA_SUPPORT
+ int client, port;
+#ifdef HAVE_LIBASOUND2
+ snd_seq_t *handle=0;
+ snd_seq_client_info_t *clienti;
+ snd_seq_client_info_malloc(&clienti);
+ snd_seq_port_info_t *porti;
+ snd_seq_port_info_malloc(&porti);
+
+ snd_seq_open(&handle, "hw", SND_SEQ_OPEN_DUPLEX, 0);
+ if (!handle) { printf("handle==0\n"); return -1; };
+ snd_seq_system_info_t *info;
+ snd_seq_system_info_malloc(&info);
+ if (!info) { printf("info==0\n"); return -1; };
+ snd_seq_system_info(handle, info);
+
+ n_total=0;
+ n_midi=0;
+ n_synths=0;
+
+ device=new MidiOut*[snd_seq_system_info_get_clients(info)*snd_seq_system_info_get_ports(info)];
+ unsigned int k=SND_SEQ_PORT_CAP_SUBS_WRITE | SND_SEQ_PORT_CAP_WRITE ;
+ for (client=0 ; client<snd_seq_system_info_get_clients(info) ; client++)
+ {
+ snd_seq_get_any_client_info(handle, client, clienti);
+ for (port=0 ; port<snd_seq_client_info_get_num_ports(clienti) ; port++)
+ {
+ snd_seq_get_any_port_info(handle, client, port, porti);
+ if (( snd_seq_port_info_get_capability(porti) & k ) == k)
+ {
+ device[n_midi]=new AlsaOut(n_midi,client, port, snd_seq_client_info_get_name(clienti), snd_seq_port_info_get_name(porti));
+ n_midi++;
+ };
+ }
+ }
+ snd_seq_client_info_free(clienti);
+ snd_seq_port_info_free(porti);
+ snd_seq_system_info_free(info);
+#else
+ snd_seq_t *handle=0;
+ snd_seq_client_info_t clienti;
+ snd_seq_port_info_t porti;
+
+ snd_seq_open(&handle, SND_SEQ_OPEN);
+ if (!handle) { printf("handle(2)==0\n"); return -1; };
+
+ snd_seq_system_info_t info;
+ info.clients=info.ports=0;
+ snd_seq_system_info(handle, &info);
+
+ n_total=0;
+ n_midi=0;
+ n_synths=0;
+
+ device=new MidiOut*[info.clients*info.ports];
+ unsigned int k=SND_SEQ_PORT_CAP_SUBS_WRITE | SND_SEQ_PORT_CAP_WRITE ;
+ for (client=0 ; client<info.clients ; client++)
+ {
+ snd_seq_get_any_client_info(handle, client, &clienti);
+ for (port=0 ; port<clienti.num_ports ; port++)
+ {
+ snd_seq_get_any_port_info(handle, client, port, &porti);
+ if (( porti.capability & k ) == k)
+ {
+ device[n_midi]=new AlsaOut(n_midi,client, port, clienti.name, porti.name);
+ n_midi++;
+ };
+ }
+ }
+#endif
+ n_total=n_midi;
+
+ snd_seq_close(handle);
+#else
+
+ // Note: Please don't add i18n for the text below, thanks :)
+
+ fprintf(stderr,"Sorry, this KMid version was compiled without \n");
+ fprintf(stderr,"ALSA support but you're using ALSA . \n");
+ fprintf(stderr,"Please compile KMid for yourself or tell the people\n");
+ fprintf(stderr,"at your Linux distribution to compile it themselves\n");
+#endif
+ }
+
+ if (mapper_tmp!=0L) setMidiMap(mapper_tmp);
+
+ initialized=1;
+
+ return 0;
+}
+
+void DeviceManager::openDev(void)
+{
+ if (checkInit()<0)
+ {
+ DEBUGPRINTF("DeviceManager::openDev : Not initialized\n");
+ _ok = 0;
+ return;
+ }
+ _ok=1;
+
+ if (!alsa)
+ {
+#ifdef HAVE_OSS_SUPPORT
+ seqfd = open("/dev/sequencer", O_WRONLY | O_NONBLOCK, 0);
+ if (seqfd==-1)
+ {
+ fprintf(stderr,"Couldn't open the MIDI sequencer device (/dev/sequencer)\n");
+ _ok=0;
+ return;
+ }
+ _seqbufptr = 0;
+ ioctl(seqfd,SNDCTL_SEQ_RESET);
+ //ioctl(seqfd,SNDCTL_SEQ_PANIC);
+ m_rate=0;
+ int r=ioctl(seqfd,SNDCTL_SEQ_CTRLRATE,&m_rate);
+ if ((r==-1)||(m_rate<=0)) m_rate=HZ;
+
+ convertrate=1000/m_rate;
+
+#endif
+ }
+ else seqfd=0L; // ALSA
+
+// DEBUGPRINTF("Opening devices : ");
+ for (int i=0;i<n_total;i++)
+ {
+ device[i]->openDev(seqfd);
+// DEBUGPRINTF("%s ",device[i]->deviceName());
+ }
+// DEBUGPRINTF("\n");
+ for (int i=0;i<n_total;i++) if (!device[i]->ok()) _ok=0;
+ if (_ok==0)
+ {
+ for (int i=0;i<n_total;i++) device[i]->closeDev();
+// DEBUGPRINTF("DeviceMan :: ERROR : Closing devices\n");
+ return;
+ }
+
+// DEBUGPRINTF("Devices opened\n");
+}
+
+void DeviceManager::closeDev(void)
+{
+ if (alsa)
+ {
+ if (device)
+ for (int i=0;i<n_total;i++)
+ if (device[i]) device[i]->closeDev();
+
+ return;
+ }
+
+#ifdef HAVE_OSS_SUPPORT
+ if (seqfd==-1) return;
+ tmrStop();
+ if (device)
+ for (int i=0;i<n_total;i++)
+ if (device[i]) device[i]->closeDev();
+ /*
+ DEBUGPRINTF("Closing devices : ");
+ if (device!=NULL) for (int i=0;i<n_total;i++)
+ {
+ device[i]->initDev();
+ DEBUGPRINTF("%s ",device[i]->deviceName());
+
+ // device[i]->closeDev();
+ };
+ DEBUGPRINTF("\n");
+ */
+ close(seqfd);
+ seqfd=-1;
+#endif
+}
+
+void DeviceManager::initDev(void)
+{
+ if (device!=0L)
+ {
+// DEBUGPRINTF("Initializing devices :");
+ for (int i=0;i<n_total;i++)
+ {
+ device[i]->initDev();
+ DEBUGPRINTF("%s ",device[i]->deviceName());
+ }
+ DEBUGPRINTF("\n");
+ }
+}
+
+void DeviceManager::noteOn ( uchar chn, uchar note, uchar vel )
+{
+ MidiOut *midi=chntodev(chn);
+ if (midi) midi->noteOn(chn,note,vel);
+}
+void DeviceManager::noteOff ( uchar chn, uchar note, uchar vel )
+{
+ MidiOut *midi=chntodev(chn);
+ if (midi) midi->noteOff(chn,note,vel);
+}
+void DeviceManager::keyPressure ( uchar chn, uchar note, uchar vel )
+{
+ MidiOut *midi=chntodev(chn);
+ if (midi) midi->keyPressure(chn,note,vel);
+}
+void DeviceManager::chnPatchChange ( uchar chn, uchar patch )
+{
+ MidiOut *midi=chntodev(chn);
+ if (midi) midi->chnPatchChange(chn,patch);
+}
+void DeviceManager::chnPressure ( uchar chn, uchar vel )
+{
+ MidiOut *midi=chntodev(chn);
+ if (midi) midi->chnPressure(chn,vel);
+}
+void DeviceManager::chnPitchBender ( uchar chn, uchar lsb, uchar msb )
+{
+ MidiOut *midi=chntodev(chn);
+ if (midi) midi->chnPitchBender(chn,lsb,msb);
+}
+void DeviceManager::chnController ( uchar chn, uchar ctl , uchar v )
+{
+ MidiOut *midi=chntodev(chn);
+ if (midi) midi->chnController(chn,ctl,v);
+}
+void DeviceManager::sysEx ( uchar *data,ulong size)
+{
+ for (int i=0;i<n_midi;i++)
+ device[i]->sysex(data,size);
+}
+
+void DeviceManager::wait (double ticks)
+{
+#ifdef HAVE_ALSA_SUPPORT
+ if (alsa) { ((AlsaOut *)device[default_dev])->wait(ticks); return; };
+#endif
+
+#ifdef HAVE_OSS_SUPPORT
+ unsigned long int t=(unsigned long int)(ticks/convertrate);
+ if (lastwaittime==t) return;
+ lastwaittime=t;
+ SEQ_WAIT_TIME(t);
+ SEQ_DUMPBUF();
+#endif
+}
+
+//void DeviceManager::tmrSetTempo(int v)
+void DeviceManager::tmrSetTempo(int v)
+{
+#ifdef HAVE_ALSA_SUPPORT
+ if (alsa) { ((AlsaOut *)device[default_dev])->tmrSetTempo(v); return; }
+#endif
+
+#ifdef HAVE_OSS_SUPPORT
+ SEQ_SET_TEMPO(v);
+ SEQ_DUMPBUF();
+#endif
+}
+
+void DeviceManager::tmrStart(long int
+#ifdef HAVE_ALSA_SUPPORT
+tpcn /*name the argument only if it is used*/
+#endif
+)
+{
+#ifdef HAVE_ALSA_SUPPORT
+ if (alsa) { ((AlsaOut *)device[default_dev])->tmrStart(tpcn); return; }
+#endif
+
+#ifdef HAVE_OSS_SUPPORT
+#ifdef CONTROLTIMER
+ if (!timerstarted)
+ {
+ SEQ_START_TIMER();
+ SEQ_DUMPBUF();
+ timerstarted=1;
+ }
+ lastwaittime=0;
+#else
+ SEQ_START_TIMER();
+ SEQ_DUMPBUF();
+#endif
+#endif
+}
+
+void DeviceManager::tmrStop(void)
+{
+#ifdef HAVE_ALSA_SUPPORT
+ if (alsa) { ((AlsaOut *)device[default_dev])->tmrStop(); return; }
+#endif
+
+#ifdef HAVE_OSS_SUPPORT
+#ifdef CONTROLTIMER
+ if (timerstarted)
+ {
+ SEQ_STOP_TIMER();
+ SEQ_DUMPBUF();
+ timerstarted=0;
+ }
+#else
+ SEQ_STOP_TIMER();
+ SEQ_DUMPBUF();
+#endif
+#endif
+}
+
+void DeviceManager::tmrContinue(void)
+{
+#ifdef HAVE_ALSA_SUPPORT
+ if (alsa) { ((AlsaOut *)device[default_dev])->tmrContinue(); return; }
+#endif
+
+#ifdef HAVE_OSS_SUPPORT
+#ifdef CONTROLTIMER
+ if (timerstarted)
+ {
+ SEQ_CONTINUE_TIMER();
+ SEQ_DUMPBUF();
+ }
+#else
+ SEQ_CONTINUE_TIMER();
+ SEQ_DUMPBUF();
+#endif
+#endif
+}
+
+void DeviceManager::sync(bool f)
+{
+#ifdef HAVE_ALSA_SUPPORT
+ if (alsa) { ((AlsaOut *)device[default_dev])->sync(f); return ; };
+#endif
+
+#ifdef HAVE_OSS_SUPPORT
+#ifdef DEVICEMANDEBUG
+ printf("Sync %d\n",f);
+#endif
+ if (f)
+ {
+ seqbuf_clean();
+ /* If you have any problem, try removing the next 2 lines,
+ I though they would be useful here but the may have side effects */
+ ioctl(seqfd,SNDCTL_SEQ_RESET);
+ ioctl(seqfd,SNDCTL_SEQ_PANIC);
+ }
+ else
+ {
+ seqbuf_dump();
+ ioctl(seqfd, SNDCTL_SEQ_SYNC);
+ };
+#endif
+}
+
+void DeviceManager::seqbuf_dump (void)
+{
+ if (!alsa)
+ {
+#ifdef HAVE_OSS_SUPPORT
+ if (_seqbufptr)
+ {
+ int r=0;
+ unsigned char *sb=_seqbuf;
+ int w=_seqbufptr;
+ r=write (seqfd, _seqbuf, _seqbufptr);
+#ifdef DEVICEMANDEBUG
+ printf("%d == %d\n",r,w);
+ printf("%d\n",(errno==EAGAIN)? 1 : 0);
+#endif
+ while (((r == -1)&&(errno==EAGAIN))||(r != w))
+ {
+ if ((r==-1)&&(errno==EAGAIN))
+ {
+ usleep(1);
+ }
+ else if ((r>0)&&(r!=w))
+ {
+ w-=r;
+ sb+=r;
+ }
+ r=write (seqfd, sb, w);
+#ifdef DEVICEMANDEBUG
+ printf("%d == %d\n",r,w);
+ printf("%d\n",(errno==EAGAIN)? 1 : 0);
+#endif
+ }
+ }
+ /*
+ * if (_seqbufptr)
+ * if (write (seqfd, _seqbuf, _seqbufptr) == -1)
+ * {
+ * printf("Error writing to /dev/sequencer in deviceManager::seqbuf_dump\n");
+ * perror ("write /dev/sequencer in seqbuf_dump\n");
+ * exit (-1);
+ * }
+ */
+ _seqbufptr = 0;
+#endif
+ }
+}
+
+void DeviceManager::seqbuf_clean(void)
+{
+#ifdef HAVE_ALSA_SUPPORT
+ if (alsa)
+ ((AlsaOut *)device[default_dev])->seqbuf_clean();
+ else
+#endif
+#ifdef HAVE_OSS_SUPPORT
+ _seqbufptr=0;
+#endif
+}
+
+
+const char *DeviceManager::name(int i)
+{
+#ifdef HAVE_OSS_SUPPORT
+ if (checkInit()<0) {_ok = 0; return NULL;}
+
+ if (alsa)
+ {
+ if (i<n_midi) return device[i]->deviceName();
+ }
+ else
+ {
+ if (i<n_midi) return midiinfo[i].name;
+ if (i<n_midi+n_synths) return synthinfo[i-n_midi].name;
+ };
+#endif
+ return (char *)"";
+}
+
+const char *DeviceManager::type(int i)
+{
+#ifdef HAVE_OSS_SUPPORT
+ if (checkInit()<0) {_ok = 0; return NULL;}
+
+ if (alsa)
+ {
+ if (i<n_midi) return "ALSA device";
+ }
+ else
+ {
+ if (i<n_midi)
+ {
+ return "External Midi Port";
+ }
+ if (i<n_midi+n_synths)
+ {
+ switch (synthinfo[i-n_midi].synth_subtype)
+ {
+ case (FM_TYPE_ADLIB) : return "Adlib";break;
+ case (FM_TYPE_OPL3) : return "FM";break;
+ case (MIDI_TYPE_MPU401) : return "MPU 401";break;
+ case (SAMPLE_TYPE_GUS) : return "GUS";break;
+ }
+ }
+ }
+#endif
+ return "";
+}
+
+int DeviceManager::defaultDevice(void)
+{
+ return default_dev;
+}
+
+void DeviceManager::setDefaultDevice(int i)
+{
+ if (i>=n_total) return;
+ default_dev=i;
+ for (int i=0;i<16;i++) chn2dev[i]=default_dev;
+}
+
+const char *DeviceManager::midiMapFilename(void)
+{
+ if (device==0L) return "";
+ if (default_dev>=n_total) return "";
+ return (device[default_dev]!=NULL) ?
+ device[default_dev]->midiMapFilename() : "";
+}
+
+void DeviceManager::setMidiMap(MidiMapper *map)
+{
+ if (map==NULL) return;
+ mapper_tmp=map;
+ if (default_dev>=n_total) {default_dev=0;return;};
+ if ((device==0L)||(device[default_dev]==NULL))
+ return;
+ device[default_dev]->setMidiMapper(map);
+}
+
+int DeviceManager::setPatchesToUse(int *patchesused)
+{
+ if (checkInit()<0) return -1;
+ if ((device==0L)||(device[default_dev]==NULL))
+ return 0;
+
+ if ((device[default_dev]->deviceType())==KMID_GUS)
+ {
+ GUSOut *gus=(GUSOut *)device[default_dev];
+ gus->setPatchesToUse(patchesused);
+ }
+ return 0;
+}
+
+void DeviceManager::setVolumePercentage(int v)
+{
+ if (device!=0L)
+ {
+ for (int i=0;i<n_total;i++)
+ {
+ device[i]->setVolumePercentage(v);
+ }
+ }
+}
+
+void DeviceManager::setDeviceNumberForChannel(int chn, int dev)
+{
+ chn2dev[chn]=dev;
+}
+
+void DeviceManager::allNotesOff(void)
+{
+ for (int i=0;i<n_midi;i++)
+ device[i]->allNotesOff();
+}
diff --git a/libkmid/deviceman.h b/libkmid/deviceman.h
new file mode 100644
index 000000000..463bbfba7
--- /dev/null
+++ b/libkmid/deviceman.h
@@ -0,0 +1,537 @@
+/* deviceman.h - The device manager, that hides the use of midiOut
+ This file is part of LibKMid 0.9.5
+ Copyright (C) 1997,98,99,2000 Antonio Larrosa Jimenez
+ LibKMid's homepage : http://www.arrakis.es/~rlarrosa/libkmid.html
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ Send comments and bug fixes to Antonio Larrosa <larrosa@kde.org>
+
+***************************************************************************/
+#ifndef DEVICEMAN_H
+#define DEVICEMAN_H
+
+#include <libkmid/dattypes.h>
+#include <kdelibs_export.h>
+
+class MidiOut;
+class MidiMapper;
+
+/**
+ * MIDI Device Manager class . This class is the one you should use to
+ * send MIDI events to any device, as it creates and manages the *Out classes.
+ *
+ * This class is usually used by creating a DeviceManager object, then call
+ * openDev() and initDev() . Then, use numberOfMidiPorts(),
+ * numberOfSynthDevices(), name() and type() to choose which
+ * device to play MIDI events to and then use defaultDevice() to set the
+ * MIDI device to play.
+ *
+ * @short Manages all MIDI devices and redirects MIDI events to each one as
+ * configured.
+ * @version 0.9.5 17/01/2000
+ * @author Antonio Larrosa Jimenez <larrosa@kde.org>
+ */
+class KMID_EXPORT DeviceManager
+{
+ protected:
+
+ /**
+ * @internal
+ * The midi devices objects
+ */
+ MidiOut **device;
+
+ /**
+ * @internal
+ * Midi info
+ */
+ struct midi_info *midiinfo;
+
+ /**
+ * @internal
+ * Synth info
+ */
+ struct synth_info *synthinfo;
+
+ /**
+ * @internal
+ * Stores the device thru which a channel will be sent
+ */
+ int chn2dev[16];
+
+ /**
+ * @internal
+ * Number of synths devices
+ */
+ int n_synths;
+
+ /**
+ * @internal
+ * Number of midi ports
+ */
+ int n_midi;
+
+ /**
+ * @internal
+ * n_midi + n_synths
+ */
+ int n_total;
+
+ /**
+ * @internal
+ * rate
+ */
+ int m_rate;
+
+ /**
+ * @internal
+ * A "constant" used to convert from milliseconds to the computer rate.
+ */
+ double convertrate;
+
+ /**
+ * @internal
+ * Newest kernels don't want me to stop a timer that hasn't been started :-)
+ */
+ int timerstarted;
+
+ /**
+ * @internal
+ * Last time waited for in wait(double)
+ */
+ double lastwaittime;
+
+ /**
+ * @internal
+ * Keeps a pointer to the mapper so that if devices weren't initialized when
+ * first called setMidiMap then, when they get initialized, they use the
+ * proper mapper
+ */
+ MidiMapper *mapper_tmp;
+
+ int initialized;
+
+ /**
+ * @internal
+ * The real file handler for /dev/sequencer, that is opened and closed.
+ */
+ int seqfd;
+
+ /**
+ * @internal
+ * The device to which timer events will be sent
+ */
+ int default_dev;
+
+ /**
+ * @internal
+ */
+ int _ok;
+
+ /**
+ * @internal
+ * True if the user is running ALSA. False if (s)he's using OSS
+ */
+ bool alsa;
+
+ /**
+ * @internal
+ */
+ void seqbuf_dump (void);
+
+ /**
+ * @internal
+ */
+ void seqbuf_clean (void);
+
+ /**
+ * @internal
+ */
+ void checkAlsa (void);
+ public:
+ /**
+ * Constructor. It just initializes internal variables, before playing any
+ * music, you should call initManager(), setMidiMap()
+ * (optional), openDev(), initDev(), setPatchesToUse()
+ * (not required, unless you're playing to a GUS device, which must load
+ * the patches), tmrStart(), and finally, play the music.
+ */
+ DeviceManager(int def=-1);
+
+ /**
+ * Destructor. It closes the device (calling closeDev() ) if it wasn't
+ * closed before.
+ */
+ ~DeviceManager(void);
+
+ /**
+ * Initializes the MIDI Device Manager object.
+ *
+ * The /dev/sequencer and/or /dev/snd/seq files are opened, available
+ * devices are analyzed and *Out objects are created. Then, the
+ * device files are closed.
+ *
+ * @return 0 if everything was OK, or -1 if there was an error and it
+ * couldn't be initialized (for example, because it couldn't open the
+ * /dev/sequencer file)
+ */
+ int initManager(void);
+
+ /**
+ * Checks if the device manager has been initialized (with @p initManager),
+ * and in case it wasn't, initializes it.
+ *
+ * @return 0 if it was (or has just been) correctly initialized, and -1 if
+ * there was an error.
+ */
+ int checkInit(void);
+
+ /**
+ * \obsolete Please use deviceForChannel() instead.
+ *
+ */
+ MidiOut *chntodev(int chn)
+ { return deviceForChannel(chn); }
+
+ /**
+ * It's possible to send different MIDI channels to different MIDI devices,
+ * so that you can for example send channel 1 to an external synthesizer,
+ * channel 2 to a FM device and channel 10 to an AWE synth.
+ *
+ * @return the device to which MIDI events goind to channel @p chn should
+ * be sent.
+ */
+ MidiOut *deviceForChannel(int chn)
+ { return (device!=0L) ? device[chn2dev[chn]] : 0L ; }
+
+ /**
+ * Returns the device number associated with a given channel.
+ */
+ int deviceNumberForChannel(int chn) { return chn2dev[chn]; }
+
+ /**
+ * Sets the device number associated with a given channel.
+ */
+ void setDeviceNumberForChannel(int chn, int dev);
+
+ /**
+ * @return 0 if there was a problem and 1 if everything was OK. Note that the
+ * return value is changed after you check it, so you can only check it once.
+ */
+ int ok(void);
+
+ /**
+ * Returns true if it's running ALSA and false if OSS is being run
+ */
+ int usingAlsa(void) { return alsa; }
+
+ // The following function are here to emulate a midi, so that the
+ // DeviceManager sends the events to the appropriate devices.
+
+ /**
+ * Open the devices. It first initializes the manager it that wasn't done
+ * yet (you should do it yourself, to be able to choose the MIDI output
+ * device, as it will be set to an external synth by default, if available).
+ *
+ * Then /dev/sequencer is opened and the MIDI devices are opened
+ * (calling MidiOut::openDev() ).
+ * @see ok() to check if there was any problem
+ * @see closeDev()
+ * @see initDev()
+ */
+ void openDev (void);
+
+ /**
+ * Closes the devices, and /dev/sequencer.
+ *
+ * @see openDev()
+ */
+ void closeDev (void);
+
+ /**
+ * Calls MidiOut::initDev() in turn in each of the available devices.
+ *
+ * @see MidiOut::initDev()
+ */
+ void initDev (void);
+
+ /**
+ * Sends a Note On MIDI event.
+ *
+ * @param chn the MIDI channel (0 to 15) to play the note on.
+ * @param note the key of the note to play (0 to 127).
+ * @param vel the velocity of the note (0 to 127).
+ *
+ * @see noteOff()
+ */
+ void noteOn ( uchar chn, uchar note, uchar vel );
+
+ /**
+ * Sends a Note Off MIDI event. This is equivalent to send a Note On event
+ * with a vel value of 0.
+ *
+ * @param chn the MIDI channel (0 to 15) to play the note on.
+ * @param note the key of the note to play (0 to 127).
+ * @param vel the velocity of the note (0 to 127).
+ *
+ * @see noteOn()
+ */
+ void noteOff ( uchar chn, uchar note, uchar vel );
+
+ /**
+ * Sends a Key Pressure (or Aftertouch) MIDI event.
+ * This event changes the pressure over a key after this key has been played.
+ *
+ * @param chn the MIDI channel (0 to 15) where the note is being played.
+ * @param note the key of the note (0 to 127).
+ * @param vel the new velocity (or pressure) of the note (0 to 127).
+ */
+ void keyPressure ( uchar chn, uchar note, uchar vel );
+
+ /**
+ * Changes the patch (instrument) on a MIDI channel.
+ *
+ * @see setPatchesToUse()
+ *
+ * @param chn the MIDI channel (0 to 15) .
+ * @param patch the General Midi patch (0 to 127) to use on the channel chn.
+ */
+ void chnPatchChange ( uchar chn, uchar patch );
+
+ /**
+ * Changes the Pressure (Aftertouch) on a MIDI channel. Keep in mind that
+ * some synthesizers don't like this events, and it's better not to send it.
+ *
+ * @param chn the MIDI channel (0 to 15) to change.
+ * @param vel the velocity (0 to 127) to use on the channel chn.
+ */
+ void chnPressure ( uchar chn, uchar vel );
+
+ /**
+ * Changes the Pitch Bender value on a MIDI channel. This bends the tone of
+ * each note played on this channel.
+ *
+ * @param chn the MIDI channel (0 to 15) to use.
+ * @param lsb and @p msb the less significant byte and the most significant
+ * byte (0 to 127 each) of the number by which notes will be bend. a 0x4000
+ * value means not to bend.
+ * @param msb the most significant byte
+ */
+ void chnPitchBender ( uchar chn, uchar lsb, uchar msb );
+
+ /**
+ * Sends a Controller event to a MIDI channel. This can be used for example
+ * to change the volume, set a XG patch, etc. Look for any General Midi
+ * resource page on the net for more information about the available
+ * controller events.
+ *
+ * For example, to set the tremolo value to a maximum on the MIDI channel
+ * number one, you should pass 1 to @p chn, 1 to @p ctl and 127 to @p v.
+ *
+ * @param chn the MIDI channel (0 to 15) to send the event to.
+ * @param ctl the controller (0 to 15) to send.
+ * @param v the value (data) of the controller.
+ */
+ void chnController ( uchar chn, uchar ctl , uchar v );
+
+ /**
+ * Sends a SYStem EXclusive message to the default MIDI device (usually,
+ * external MIDI synths, as most internal synths do not support sysex
+ * messages)
+ *
+ * @param data the array of bytes that comform the system exclusive message.
+ * Without the initial 0xF0 char, and including the final 0xF7 char (end of
+ * exclusive message)
+ * @param size the size in bytes of the data to send
+ *
+ * @see setDefaultDevice()
+ */
+ void sysEx ( uchar *data,ulong size);
+
+ /**
+ * Sets the number of milliseconds at which the next event will be sent.
+ * This way, you can schedule notes and events to send to the MIDI device.
+ * @see tmrStart()
+ */
+ void wait (double ms);
+
+ /**
+ * Sets the tempo which will be used to convert between ticks and
+ * milliseconds.
+ */
+ void tmrSetTempo(int v);
+
+ /**
+ * Starts the timer. You must call tmrStart before using wait()
+ */
+ void tmrStart(long int tpcn);
+
+ /**
+ * Stops the timer. This will be called by closeDev() before closing
+ * the device
+ */
+ void tmrStop(void);
+
+ /**
+ * Continue the stopped timer . It is the same than starting a new timer, but
+ * without resetting it.
+ */
+ void tmrContinue(void);
+
+ /**
+ * Sends an all notes off event
+ */
+ void allNotesOff(void);
+
+ /**
+ * Synchronizes with the MIDI buffer. Midi events are put into a buffer,
+ * along with timer delays (see wait() ). sync returns when the buffer
+ * is empty.
+ *
+ * @param f if false, it syncronizes by waiting for the buffer to be sent.
+ * If true, it forces the synchronization by clearing the buffer
+ * inmediately. The "force" method is, of course, not recommended, except
+ * in rare situations.
+ */
+ void sync(bool f=0);
+
+ /**
+ * Changes the "master" volume of the played events by altering next volume
+ * controller events. The parameter @p i should be in the range of 0
+ * (nothing is heard) to 150 (music is played at a 150% of the original
+ * volume).
+ *
+ * Keep in mind that as most MIDI files already play music at near the
+ * maximum volume, an @p i value greater than 100 is very probably ignored
+ * most of the times.
+ */
+ void setVolumePercentage(int i);
+
+ /**
+ * Returns the device to which the MIDI events will be sent.
+ * Returns -1 if there's no available device.
+ *
+ * @see setDefaultDevice()
+ */
+ int defaultDevice(void);
+
+ /**
+ * Sets the device to send the MIDI events to.
+ *
+ * By using midiPorts(), synthDevices(), name() and
+ * type(), you should choose which device to use (note that they are
+ * numbered with midi ports being first and synth devices next)
+ *
+ * @see defaultDevice()
+ */
+ void setDefaultDevice(int i);
+
+ /**
+ * Loads the patches you're going to use . This has effect only for GUS
+ * cards, although, if you use this function when defaultDevice() is
+ * not a GUS device, it will be ignored.
+ *
+ * The parameter is an int [256] array, which contain the following:
+ *
+ * The first 0..127 integers, are the number of times each General MIDI patch
+ * will be used, and -1 when the corresponding patch won't be used.
+ *
+ * The 128..255 integers are the number of times each drum voice (each note
+ * on the drum channel) will be used, and -1 when the corresponding
+ * percussion won't be used.
+ *
+ * This is done this way so that if the user has very little memory on his
+ * GUS card, and not all patches will be loaded, they are at least
+ * reordered, so that it first loads the one you're going to use most.
+ *
+ * In case you don't worry about such users, or you don't know "a priori"
+ * the number of notes you're going to play, you can just use 1 for each
+ * patch you want to load and -1 in the rest.
+ *
+ * @see GUSOut::setPatchesToUse()
+ * @see GUSOut::loadPatch()
+ *
+ * @return 0 if ok, and -1 if there wasn't enough memory to load the patches
+ * in the card's memory.
+ */
+ int setPatchesToUse(int *patchesused);
+
+ /**
+ * Returns the filename where the Midi Mapper was loaded from, or "" if no
+ * MIDI Mapper is in use.
+ *
+ * @see setMidiMap()
+ */
+ const char *midiMapFilename(void);
+
+ /**
+ * Sets a MidiMapper object to use. This object should already have
+ * loaded the configuration. See the description of MidiMapper for
+ * more information.
+ *
+ * @see MidiMapper::MidiMapper()
+ * @see midiMapFilename()
+ */
+ void setMidiMap(MidiMapper *map);
+
+ /**
+ * Returns the SNDCTL_SEQ_CTRLRATE ioctl value
+ */
+ int rate(void) { return m_rate; }
+
+ /**
+ * Returns the number of MIDI ports available on the system. It's common that
+ * users have MIDI ports available, but there are no external synthesizers
+ * connected to these ports, so sending MIDI events to these ports will not
+ * produce any music in this case.
+ *
+ * @see synthDevices()
+ * @see setDefaultDevice()
+ */
+ int midiPorts(void) { return n_midi; }
+
+ /**
+ * Returns the number of internal synthesizers available on the system. Some
+ * of these devices will need special configuration, for example, to load
+ * sound patches.
+ *
+ * @see midiPorts()
+ * @see setDefaultDevice()
+ * @see setPatchesToUse()
+ */
+ int synthDevices(void) { return n_synths; }
+
+ /**
+ * Returns the name of the @p i-th device . In case the DeviceManager wasn't
+ * yet initialized ( see checkInit() ), the return value is NULL, and
+ * in case the parameter has a value out of the valid range ( 0 to
+ * midiPorts() + synthDevices() ) it returns an empty string.
+ */
+ const char *name(int i);
+
+ /**
+ * Returns the type of device the @p i-th device is , in a user-friendly
+ * string . For example, "External Midi Port" for midi ports, "FM" for FM
+ * synthesizers, "GUS" for Gravis Ultrasound devices, etc.
+ */
+ const char *type(int i);
+
+ private:
+ class DeviceManagerPrivate;
+ DeviceManagerPrivate *d;
+};
+
+#endif
diff --git a/libkmid/fmout.cc b/libkmid/fmout.cc
new file mode 100644
index 000000000..c4af93dcd
--- /dev/null
+++ b/libkmid/fmout.cc
@@ -0,0 +1,354 @@
+/**************************************************************************
+
+ fmout.cc - class fmOut which handles the /dev/sequencer device
+ for fm synths
+ This file is part of LibKMid 0.9.5
+ Copyright (C) 1998,99,2000 Antonio Larrosa Jimenez
+ LibKMid's homepage : http://www.arrakis.es/~rlarrosa/libkmid.html
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ Send comments and bug fixes to Antonio Larrosa <larrosa@kde.org>
+
+***************************************************************************/
+#include "fmout.h"
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include "sndcard.h"
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/param.h>
+#include <stdlib.h>
+#include <limits.h>
+#include "midispec.h"
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+SEQ_USE_EXTBUF();
+
+FMOut::FMOut( int d, int total )
+{
+ seqfd = -1;
+ devicetype = KMID_FM;
+ device = d;
+ _ok = 1;
+ // Put opl=3 for opl/3 (better quality/ 6 voices)
+ // or opl=2 for fm output (less quality/ 18 voices, which is better imho) :
+ opl = 2;
+ // But be aware that opl=3 is not intended to be fully supported by now
+
+ nvoices = total;
+ vm = new VoiceManager (nvoices);
+}
+
+FMOut::~FMOut()
+{
+ closeDev();
+ delete vm;
+ if (deleteFMPatchesDirectory)
+ {
+ free((char *)FMPatchesDirectory);
+ deleteFMPatchesDirectory = 0;
+ FMPatchesDirectory="/etc";
+ }
+}
+
+void FMOut::openDev (int sqfd)
+{
+#ifdef HAVE_OSS_SUPPORT
+ _ok=1;
+ seqfd = sqfd;
+ //vm->clearLists();
+ if ( seqfd == -1 )
+ {
+ printfdebug("ERROR: Could not open /dev/sequencer\n");
+ return;
+ }
+
+ loadFMPatches();
+#endif
+
+}
+
+void FMOut::closeDev (void)
+{
+ if (!ok()) return;
+ vm->clearLists();
+ //if (seqfd>=0) close(seqfd);
+ seqfd = -1;
+}
+
+void FMOut::initDev (void)
+{
+#ifdef HAVE_OSS_SUPPORT
+ int chn;
+ if (!ok()) return;
+ uchar gm_reset[5]={0x7e, 0x7f, 0x09, 0x01, 0xf7};
+ sysex(gm_reset, sizeof(gm_reset));
+ for (chn=0;chn<16;chn++)
+ {
+ chnmute[chn]=0;
+ chnPatchChange(chn,0);
+ chnPressure(chn,127);
+ chnPitchBender(chn, 0x00, 0x40);
+ chnController(chn, CTL_MAIN_VOLUME,127);
+ chnController(chn, CTL_EXT_EFF_DEPTH, 0);
+ chnController(chn, CTL_CHORUS_DEPTH, 0);
+ chnController(chn, 0x4a, 127);
+ }
+
+ if (opl==3) ioctl(seqfd, SNDCTL_FM_4OP_ENABLE, &device);
+ SEQ_VOLUME_MODE(device,VOL_METHOD_LINEAR);
+
+ for (int i = 0; i < nvoices; i++)
+ {
+ SEQ_CONTROL(device, i, SEQ_VOLMODE, VOL_METHOD_LINEAR);
+ SEQ_STOP_NOTE(device, i, vm->note(i), 64);
+ }
+#endif
+}
+
+void FMOut::loadFMPatches(void)
+{
+#ifdef HAVE_OSS_SUPPORT
+ char patchesfile[PATH_MAX];
+ char drumsfile[PATH_MAX];
+ int size;
+ struct sbi_instrument instr;
+ char tmp[60];
+ int i,j;
+ for ( i=0; i<256; i++ )
+ patchloaded[i] = 0;
+ int stereoeffect=rand()%3;
+ FILE *fh;
+ int datasize;
+
+ if (opl==3)
+ {
+ snprintf(patchesfile, PATH_MAX, "%s/std.o3",FMPatchesDirectory);
+ size=60;
+ }
+ else
+ {
+ snprintf(patchesfile, PATH_MAX, "%s/std.sb",FMPatchesDirectory);
+ size=52;
+ }
+ fh=fopen(patchesfile,"rb");
+ if (fh==NULL) return;
+
+ for (i=0;i<128;i++)
+ {
+ fread(tmp,size,1,fh);
+ patchloaded[i]=1;
+ instr.key = ((strncmp(tmp, "4OP", 3) == 0))? OPL3_PATCH : FM_PATCH;
+ datasize = (strncmp(tmp, "4OP", 3) == 0)? 22 : 11;
+ instr.device=device;
+ instr.channel = i;
+ // Let's get some stereo effect ...
+ tmp[46] = (tmp[46] & 0xcf) | ((++stereoeffect)<<4);
+ stereoeffect=stereoeffect%3;
+ for (j=0; j<22; j++)
+ instr.operators[j] = tmp[j+36];
+ SEQ_WRPATCH(&instr,sizeof(instr));
+ }
+ fclose(fh);
+
+ if (opl==3)
+ {
+ snprintf(drumsfile, PATH_MAX, "%s/drums.o3",FMPatchesDirectory);
+ }
+ else
+ {
+ snprintf(drumsfile, PATH_MAX, "%s/drums.sb",FMPatchesDirectory);
+ }
+
+ fh=fopen(drumsfile,"rb");
+ if (fh==NULL) return;
+
+ for (i=128;i<175;i++)
+ {
+ fread(tmp,size,1,fh);
+ patchloaded[i]=1;
+ instr.key = (strncmp(tmp, "4OP", 3) == 0)? OPL3_PATCH : FM_PATCH;
+ datasize = (strncmp(tmp, "4OP", 3) == 0)? 22 : 11;
+ instr.device=device;
+ instr.channel = i;
+ // Let's get some stereo effect ...
+ tmp[46] = (tmp[46] & 0xcf) | ((++stereoeffect)<<4);
+ stereoeffect=stereoeffect%3;
+ for (j=0; j<22; j++)
+ instr.operators[j] = tmp[j+36];
+ SEQ_WRPATCH(&instr,sizeof(instr));
+ }
+ fclose(fh);
+
+#ifdef FMOUTDEBUG
+ printfdebug("Patches loaded\n");
+#endif
+#endif
+}
+
+int FMOut::patch(int p)
+{
+ if (patchloaded[p]==1) return p;
+#ifdef FMOUTDEBUG
+ printfdebug("Not loaded %d!\n",p);
+#endif
+ p=0;
+ while ((p<256)&&(patchloaded[p]==0)) p++;
+ return p;
+}
+
+void FMOut::noteOn (uchar chn, uchar note, uchar vel)
+{
+ if (vel==0)
+ {
+ noteOff(chn,note,vel);
+ }
+ else
+ {
+ if (chn==PERCUSSION_CHANNEL)
+ {
+ if (patchloaded[note+128]==0) return;
+ else
+ if (patchloaded[chnpatch[chn]]==0) return;
+ }
+ int v=vm->allocateVoice(chn,note);
+ int p;
+ if (chn==PERCUSSION_CHANNEL)
+ SEQ_SET_PATCH(device,v ,p=patch(note+128))
+ else
+ SEQ_SET_PATCH(device,v ,p=map->patch(chn,chnpatch[chn]));
+ SEQ_BENDER(device, v, chnbender[chn]);
+
+ SEQ_START_NOTE(device, v, note, vel);
+ // SEQ_CONTROL(device, v, CTL_MAIN_VOLUME, chncontroller[chn][CTL_MAIN_VOLUME]);
+
+ SEQ_CHN_PRESSURE(device, v , chnpressure[chn]);
+ }
+
+#ifdef FMOUTDEBUG
+ printfdebug("Note ON >\t chn : %d\tnote : %d\tvel: %d\n",chn,note,vel);
+#endif
+}
+
+void FMOut::noteOff (uchar chn, uchar note, uchar vel)
+{
+ int i;
+ vm->initSearch();
+ while ((i=vm->search(chn,note))!=-1)
+ {
+ SEQ_STOP_NOTE(device, i, note, vel);
+ vm->deallocateVoice(i);
+ }
+
+#ifdef FMOUTDEBUG
+ printfdebug("Note OFF >\t chn : %d\tnote : %d\tvel: %d\n",chn,note,vel);
+#endif
+}
+
+void FMOut::keyPressure (uchar chn, uchar note, uchar vel)
+{
+ int i;
+ vm->initSearch();
+ while ((i=vm->search(chn,note))!=-1)
+ SEQ_KEY_PRESSURE(device, i, note,vel);
+}
+
+void FMOut::chnPatchChange (uchar chn, uchar patch)
+{
+ if (chn==PERCUSSION_CHANNEL) return;
+ int i;
+ vm->initSearch();
+ while ((i=vm->search(chn))!=-1)
+ SEQ_SET_PATCH(device,i,map->patch(chn,patch));
+
+ chnpatch[chn]=patch;
+}
+
+void FMOut::chnPressure (uchar chn, uchar vel)
+{
+ int i;
+ vm->initSearch();
+ while ((i=vm->search(chn))!=-1)
+ SEQ_CHN_PRESSURE(device, i , vel);
+
+ chnpressure[chn]=vel;
+}
+
+void FMOut::chnPitchBender(uchar chn,uchar lsb, uchar msb)
+{
+ chnbender[chn]=((int)msb<<7) | (lsb & 0x7F);
+
+ int i;
+ vm->initSearch();
+ while ((i=vm->search(chn))!=-1)
+ SEQ_BENDER(device, i, chnbender[chn]);
+
+}
+
+void FMOut::chnController (uchar chn, uchar ctl, uchar v)
+{
+ if ((ctl==11)||(ctl==7))
+ {
+ v=(v*volumepercentage)/100;
+ if (v>127) v=127;
+ }
+ int i;
+ vm->initSearch();
+ while ((i=vm->search(chn))!=-1)
+ SEQ_CONTROL(device, i, ctl, v);
+
+ chncontroller[chn][ctl]=v;
+}
+
+void FMOut::sysex(uchar *, ulong )
+{
+
+}
+
+void FMOut::setFMPatchesDirectory(const char *dir)
+{
+ if ((dir==NULL)||(dir[0]==0)) return;
+ if (deleteFMPatchesDirectory)
+ free((char *)FMPatchesDirectory);
+
+ FMPatchesDirectory = strdup(dir);
+
+ deleteFMPatchesDirectory=1;
+}
+
+void FMOut::setVolumePercentage ( int i )
+{
+#ifdef HAVE_OSS_SUPPORT
+ int fd=open("/dev/mixer0",O_RDWR,0);
+ if (fd==-1) return;
+ int a=i*255/100;
+ if (a>255) a=255;
+ a=(a<<8) | a;
+ if (ioctl(fd,MIXER_WRITE(SOUND_MIXER_SYNTH),&a) == -1)
+ printfdebug("ERROR writing to mixer\n");
+ close(fd);
+#endif
+ volumepercentage=i;
+}
+
+
+const char *FMOut::FMPatchesDirectory = "/etc";
+int FMOut::deleteFMPatchesDirectory = 0;
diff --git a/libkmid/fmout.h b/libkmid/fmout.h
new file mode 100644
index 000000000..328ca5626
--- /dev/null
+++ b/libkmid/fmout.h
@@ -0,0 +1,155 @@
+/* fmout.h - class fmOut which handles the /dev/sequencer device
+ for FM synths
+ This file is part of LibKMid 0.9.5
+ Copyright (C) 1998,99,2000 Antonio Larrosa Jimenez
+ LibKMid's homepage : http://www.arrakis.es/~rlarrosa/libkmid.html
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ Send comments and bug fixes to Antonio Larrosa <larrosa@kde.org>
+
+***************************************************************************/
+#ifndef _FMOUT_H
+#define _FMOUT_H
+
+#include <libkmid/midiout.h>
+#include <libkmid/voiceman.h>
+
+/**
+ * FM device output class . FMOut is used to send MIDI events to
+ * FM devices, such as AdLib cards, or OPL3 synthesizers.
+ *
+ * FMOut inherits MidiOut and supports the same simple API.
+ *
+ * The preferred way to use this class is by selecting a FM device
+ * on the MidiManager and using a MidiManager object directly
+ *
+ * @short Sends MIDI events to FM devices
+ * @version 0.9.5 17/01/2000
+ * @author Antonio Larrosa Jimenez <larrosa@kde.org>
+ */
+class KMID_EXPORT FMOut : public MidiOut
+{
+ private:
+ class FMOutPrivate;
+ FMOutPrivate *di;
+
+ int patchloaded[256];
+ /**
+ * Takes a value of 2 or 3, for FM or OPL3 support
+ */
+ int opl;
+ int nvoices;
+
+ VoiceManager *vm;
+
+ void modifyPatch(char *buf, int key);
+ void loadFMPatches (void);
+
+ public:
+ /**
+ * Constructor. See MidiOut::MidiOut() for more information.
+ */
+ FMOut ( int d=0, int total =12 );
+
+ /**
+ * Destructor.
+ */
+ ~FMOut ();
+
+ /**
+ * See MidiOut::openDev()
+ */
+ virtual void openDev ( int sqfd );
+
+ /**
+ * See MidiOut::closeDev()
+ */
+ virtual void closeDev ( void );
+
+ /**
+ * See MidiOut::initDev()
+ */
+ virtual void initDev ( void );
+
+ /**
+ * See MidiOut::noteOn()
+ */
+ virtual void noteOn ( uchar chn, uchar note, uchar vel );
+
+ /**
+ * See MidiOut::noteOff()
+ */
+ virtual void noteOff ( uchar chn, uchar note, uchar vel );
+
+ /**
+ * See MidiOut::keyPressure()
+ */
+ virtual void keyPressure ( uchar chn, uchar note, uchar vel );
+
+ /**
+ * See MidiOut::chnPatchChange()
+ */
+ virtual void chnPatchChange ( uchar chn, uchar patch );
+
+ /**
+ * See MidiOut::chnPressure()
+ */
+ virtual void chnPressure ( uchar chn, uchar vel );
+
+ /**
+ * See MidiOut::chnPitchBender()
+ */
+ virtual void chnPitchBender ( uchar chn, uchar lsb, uchar msb );
+
+ /**
+ * See MidiOut::chnController()
+ */
+ virtual void chnController ( uchar chn, uchar ctl , uchar v );
+
+ /**
+ * It's an empty function, as FM devices don't support System Exclusive
+ * messages
+ */
+ virtual void sysex ( uchar *data,ulong size);
+
+ /**
+ * See MidiOut::setVolumePercentage()
+ */
+ virtual void setVolumePercentage ( int i );
+
+ /**
+ * Returns @p p if the patch p has been loaded, or another patch (already loaded)
+ * if @p p hasn't been loaded.
+ */
+ int patch(int p);
+
+ private:
+ static const char *FMPatchesDirectory;
+ static int deleteFMPatchesDirectory;
+
+ public:
+ /**
+ * Sets the directory where the FM patches are stored, that is, where the
+ * std.o3, std.sb, drums.o3 and drums.sb files can be found.
+ *
+ * It will store a copy of the parameter, so you should delete the memory
+ * used by the parameter you passed.
+ */
+ static void setFMPatchesDirectory(const char *dir);
+
+};
+
+#endif
diff --git a/libkmid/gusout.cc b/libkmid/gusout.cc
new file mode 100644
index 000000000..6b207956b
--- /dev/null
+++ b/libkmid/gusout.cc
@@ -0,0 +1,691 @@
+/**************************************************************************
+
+ gusout.cc - class GUSOut which implements support for Gravis
+ Ultrasound cards through a /dev/sequencer device
+ This file is part of LibKMid 0.9.5
+ Copyright (C) 1998,99,2000 Antonio Larrosa Jimenez
+ LibKMid's homepage : http://www.arrakis.es/~rlarrosa/libkmid.html
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ Send comments and bug fixes to Antonio Larrosa <larrosa@kde.org>
+
+***************************************************************************/
+#include "gusout.h"
+#include "sndcard.h"
+#include "midispec.h"
+#include "gusvoices.h"
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/param.h>
+#include <stdlib.h>
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+SEQ_USE_EXTBUF();
+
+#ifdef HAVE_OSS_SUPPORT
+struct pat_header
+{
+ char magic[12];
+ char version[10];
+ char description[60];
+ unsigned char instruments;
+ char voices;
+ char channels;
+ unsigned short nr_waveforms;
+ unsigned short master_volume;
+ unsigned long data_size;
+};
+struct sample_header
+{
+ char name[7];
+ unsigned char fractions;
+ long len;
+ long loop_start;
+ long loop_end;
+ unsigned short base_freq;
+ long low_note;
+ long high_note;
+ long base_note;
+ short detune;
+ unsigned char panning;
+
+ unsigned char envelope_rate[6];
+ unsigned char envelope_offset[6];
+
+ unsigned char tremolo_sweep;
+ unsigned char tremolo_rate;
+ unsigned char tremolo_depth;
+
+ unsigned char vibrato_sweep;
+ unsigned char vibrato_rate;
+ unsigned char vibrato_depth;
+
+ char modes;
+
+ short scale_frequency;
+ unsigned short scale_factor;
+};
+
+int get_dint(unsigned char *p)
+{
+ unsigned int v=0;
+
+ for (int i=0;i<4;i++)
+ {
+ v |= (p[i] << (i*8));
+ }
+ return (int)v;
+}
+
+unsigned short get_word(unsigned char *p)
+{
+ unsigned short v=0;
+
+ for (int i=0;i<2;i++)
+ v |= (*p++ << (i*8));
+ return (short)v;
+}
+
+#endif
+
+GUSOut::GUSOut(int d,int total)
+{
+ seqfd = -1;
+ devicetype=KMID_GUS;
+ device= d;
+ _ok=1;
+
+ use8bit=0;
+ nvoices=total;
+ vm=new VoiceManager(nvoices);
+}
+
+GUSOut::~GUSOut()
+{
+ closeDev();
+
+ delete vm;
+ if (delete_GUS_patches_directory)
+ {
+ free((char *)GUS_patches_directory);
+ delete_GUS_patches_directory = 0;
+ GUS_patches_directory="/etc";
+ }
+}
+
+void GUSOut::openDev (int sqfd)
+{
+ _ok=1;
+ seqfd = sqfd;
+ //vm->clearLists();
+ if (seqfd==-1)
+ {
+ printfdebug("ERROR: Could not open /dev/sequencer\n");
+ return;
+ }
+
+#ifdef HAVE_OSS_SUPPORT
+
+ //seqbuf_clean();
+ //ioctl(seqfd,SNDCTL_SEQ_RESET);
+ //ioctl(seqfd,SNDCTL_SEQ_PANIC);
+
+ if (ioctl(seqfd, SNDCTL_SEQ_RESETSAMPLES, &device)==-1)
+ {
+ printfdebug("Error reseting gus samples. Please report\n");
+ };
+ use8bit=0;
+ totalmemory = device;
+ ioctl(seqfd, SNDCTL_SYNTH_MEMAVL, &totalmemory);
+ freememory = device;
+ ioctl(seqfd, SNDCTL_SYNTH_MEMAVL, &freememory);
+
+#endif
+
+
+}
+
+void GUSOut::closeDev (void)
+{
+ if (!ok()) return;
+ vm->clearLists();
+ //if (seqfd>=0)
+ // close(seqfd);
+ seqfd=-1;
+}
+
+void GUSOut::initDev (void)
+{
+#ifdef HAVE_OSS_SUPPORT
+ int chn;
+ if (!ok()) return;
+ uchar gm_reset[5]={0x7e, 0x7f, 0x09, 0x01, 0xf7};
+ sysex(gm_reset, sizeof(gm_reset));
+ for (chn=0;chn<16;chn++)
+ {
+ chnmute[chn]=0;
+ chnPatchChange(chn,0);
+ // chnPressure(chn,127);
+ chnPitchBender(chn, 0x00, 0x40);
+ chnController(chn, CTL_MAIN_VOLUME,127);
+ chnController(chn, CTL_EXT_EFF_DEPTH, 0);
+ chnController(chn, CTL_CHORUS_DEPTH, 0);
+ chnController(chn, 0x4a, 127);
+ }
+
+
+ for (int i = 0; i < nvoices; i++)
+ {
+ SEQ_CONTROL(device, i, SEQ_VOLMODE, VOL_METHOD_LINEAR);
+ SEQ_STOP_NOTE(device, i, vm->note(i), 64);
+ }
+
+#endif
+}
+
+
+int GUSOut::patch(int p)
+{
+ if (patchloaded[p]==1) return p;
+ printfdebug("Not loaded %d!\n",p);
+ p=0;
+ while ((p<256)&&(patchloaded[p]==0)) p++;
+ return p;
+}
+
+void GUSOut::noteOn (uchar chn, uchar note, uchar vel)
+{
+ if (vel==0)
+ {
+ noteOff(chn,note,vel);
+ }
+ else
+ {
+ if (chn==PERCUSSION_CHANNEL)
+ {
+ if (patchloaded[note+128]==0) return;
+ else
+ if (patchloaded[chnpatch[chn]]==0) return;
+ };
+ int v=vm->allocateVoice(chn,note);
+ int p;
+ if (chn==PERCUSSION_CHANNEL)
+ SEQ_SET_PATCH(device,v ,p=patch(note+128))
+ else
+ SEQ_SET_PATCH(device,v ,p=map->patch(chn,chnpatch[chn]));
+ SEQ_BENDER(device, v, chnbender[chn]);
+
+ SEQ_START_NOTE(device, v, note, vel);
+ // SEQ_CONTROL(device, v, CTL_MAIN_VOLUME, chncontroller[chn][CTL_MAIN_VOLUME]);
+ SEQ_CHN_PRESSURE(device, v , chnpressure[chn]);
+ }
+
+ printfdebug("Note ON >\t chn : %d\tnote : %d\tvel: %d\n",chn,note,vel);
+}
+
+void GUSOut::noteOff (uchar chn, uchar note, uchar vel)
+{
+ int i;
+ vm->initSearch();
+ while ((i=vm->search(chn,note))!=-1)
+ {
+ SEQ_STOP_NOTE(device, i, note, vel);
+ vm->deallocateVoice(i);
+ }
+
+#ifdef GUSOUTDEBUG
+ printf("Note OFF >\t chn : %d\tnote : %d\tvel: %d\n",chn,note,vel);
+#endif
+}
+
+void GUSOut::keyPressure (uchar chn, uchar note, uchar vel)
+{
+ int i;
+ vm->initSearch();
+ while ((i=vm->search(chn,note))!=-1)
+ SEQ_KEY_PRESSURE(device, i, note,vel);
+}
+
+void GUSOut::chnPatchChange (uchar chn, uchar patch)
+{
+ if (chn==PERCUSSION_CHANNEL) return;
+ int i;
+ vm->initSearch();
+ while ((i=vm->search(chn))!=-1)
+ SEQ_SET_PATCH(device,i,map->patch(chn,patch));
+ chnpatch[chn]=patch;
+
+}
+
+void GUSOut::chnPressure (uchar /*chn*/, uchar /*vel*/)
+{
+ /* int i;
+ vm->initSearch();
+ while ((i=vm->search(chn))!=-1)
+ SEQ_CHN_PRESSURE(device, i , vel);
+ chnpressure[chn]=vel;
+ */
+}
+
+void GUSOut::chnPitchBender(uchar chn,uchar lsb, uchar msb)
+{
+ chnbender[chn]=((int)msb<<7) | (lsb & 0x7F);
+
+ int i;
+ vm->initSearch();
+ while ((i=vm->search(chn))!=-1)
+ SEQ_BENDER(device, i, chnbender[chn]);
+}
+
+void GUSOut::chnController (uchar chn, uchar ctl, uchar v)
+{
+ if ((ctl==11)||(ctl==7))
+ {
+ v=(v*volumepercentage)/100;
+ if (v>127) v=127;
+ };
+
+ int i;
+ vm->initSearch();
+ while ((i=vm->search(chn))!=-1)
+ SEQ_CONTROL(device, i, ctl, v);
+
+ chncontroller[chn][ctl]=v;
+}
+
+void GUSOut::sysex(uchar *, ulong )
+{
+
+}
+
+void GUSOut::setGUSPatchesDirectory(const char *dir)
+{
+ if ((dir==NULL)||(dir[0]==0)) return;
+ if (delete_GUS_patches_directory)
+ free((char *)GUS_patches_directory);
+
+ GUS_patches_directory = strdup(dir);
+ delete_GUS_patches_directory=1;
+}
+
+const char *GUSOut::patchName(int pgm)
+{
+ return GUS_voice_names[pgm];
+}
+
+
+int GUSOut::loadPatch(int pgm)
+{
+#ifdef HAVE_OSS_SUPPORT
+ struct pat_header header;
+ struct sample_header sample;
+ if (patchloaded[pgm]==1)
+ {
+#ifdef GUSOUTDEBUG
+ printf("Trying to reload a patch. This should never happen, please report.\n");
+#endif
+ return 0;
+ }
+ if ((patchName(pgm)==NULL)||((patchName(pgm))[0]==0))
+ {
+#ifdef GUSOUTDEBUG
+ printf("Couldn't guess patch name for patch number %d\n",pgm);
+#endif
+ return -1;
+ }
+ char *s=new char[strlen(GUS_patches_directory)+strlen(patchName(pgm))+10];
+ if (s==NULL) return -1;
+ sprintf(s,"%s/%s.pat",GUS_patches_directory,patchName(pgm));
+#ifdef GUSOUTDEBUG
+ printf("Loading patch : %s\n",s);
+#endif
+ struct patch_info *patch=NULL;
+ struct stat info;
+ if (stat(s, &info)==-1)
+ {
+#ifdef GUSOUTDEBUG
+ printf("File %s doesn't exist\n",s);
+#endif
+ return -1;
+ }
+
+ FILE *fh=fopen(s,"rb");
+ if (fh==NULL)
+ {
+#ifdef GUSOUTDEBUG
+ printf("Couldn't open patch %s\n",s);
+#endif
+ return -1;
+ }
+
+ unsigned char tmp[256];
+ if (fread(tmp,1,0xef,fh)!=0xef)
+ {
+ fclose(fh);
+#ifdef GUSOUTDEBUG
+ printf("Short file ! \n");
+#endif
+ return -1;
+ }
+ memcpy ((char *) &header, tmp, sizeof (header));
+
+ if (strncmp(header.magic,"GF1PATCH110",12)!=0)
+ {
+#ifdef GUSOUTDEBUG
+ printf("File %s is corrupted or it isn't a patch file\n",s);
+#endif
+ return -1;
+ }
+ if (strncmp(header.version,"ID#000002",10)!=0)
+ {
+#ifdef GUSOUTDEBUG
+ printf("File %s's version is not supported\n",s);
+#endif
+ return -1;
+ }
+ unsigned short nWaves= *(unsigned short *)&tmp[85];
+#ifdef GUSOUTDEBUG
+ unsigned short masterVolume= *(unsigned short *)&tmp[87];
+ printf("nWaves: %d\n",nWaves);
+ printf("masterVolume : %d\n",masterVolume);
+#endif
+
+ unsigned short i;
+ int offset=0xef;
+ for (i=0;i<nWaves;i++)
+ {
+ fseek(fh,offset,SEEK_SET);
+
+ if (fread(tmp,1,sizeof(sample),fh) != sizeof(sample))
+ {
+ fclose(fh);
+#ifdef GUSOUTDEBUG
+ printf("Short file\n");
+#endif
+ return -1;
+ }
+ memcpy ((char *) &sample, tmp, sizeof (sample));
+ sample.fractions = (char)tmp[7];
+ sample.len = get_dint(&tmp[8]);
+ sample.loop_start = get_dint(&tmp[12]);
+ sample.loop_end = get_dint(&tmp[16]);
+ sample.base_freq = get_word(&tmp[20]);
+ sample.low_note = get_dint(&tmp[22]);
+ sample.high_note = get_dint(&tmp[26]);
+ sample.base_note = get_dint(&tmp[30]);
+ sample.detune = (short)get_word(&tmp[34]);
+ sample.panning = (unsigned char) tmp[36];
+
+ memcpy (sample.envelope_rate, &tmp[37], 6);
+ memcpy (sample.envelope_offset, &tmp[43], 6);
+
+ sample.tremolo_sweep = (unsigned char) tmp[49];
+ sample.tremolo_rate = (unsigned char) tmp[50];
+ sample.tremolo_depth = (unsigned char) tmp[51];
+
+ sample.vibrato_sweep = (unsigned char) tmp[52];
+ sample.vibrato_rate = (unsigned char) tmp[53];
+ sample.vibrato_depth = (unsigned char) tmp[54];
+ sample.modes = (unsigned char) tmp[55];
+ sample.scale_frequency = (short)get_word(&tmp[56]);
+ sample.scale_factor = get_word(&tmp[58]);
+
+ offset = offset + 96;
+
+ patch = (struct patch_info *) malloc(sizeof (*patch) + sample.len);
+ if (patch == NULL)
+ {
+#ifdef GUSOUTDEBUG
+ printf("Not enough memory\n");
+#endif
+ return -1;
+ }
+ patch->key = GUS_PATCH;
+ patch->device_no = device;
+ patch->instr_no = pgm;
+ patch->mode = sample.modes | WAVE_TREMOLO | WAVE_VIBRATO | WAVE_SCALE;
+ patch->len = sample.len;
+ patch->loop_start = sample.loop_start;
+ patch->loop_end = sample.loop_end;
+ patch->base_note = sample.base_note;
+ patch->high_note = sample.high_note;
+ patch->low_note = sample.low_note;
+ patch->base_freq = sample.base_freq;
+ patch->detuning = sample.detune;
+ patch->panning = (sample.panning - 7) * 16;
+
+ memcpy (patch->env_rate, sample.envelope_rate, 6);
+ memcpy (patch->env_offset, sample.envelope_offset, 6);
+
+ patch->tremolo_sweep = sample.tremolo_sweep;
+ patch->tremolo_rate = sample.tremolo_rate;
+ patch->tremolo_depth = sample.tremolo_depth;
+
+ patch->vibrato_sweep = sample.vibrato_sweep;
+ patch->vibrato_rate = sample.vibrato_rate;
+ patch->vibrato_depth = sample.vibrato_depth;
+
+ patch->scale_frequency = sample.scale_frequency;
+ patch->scale_factor = sample.scale_factor;
+
+ patch->volume = header.master_volume;
+
+ if (fseek (fh, offset, 0) == -1)
+ {
+ fclose(fh);
+ return -1;
+ }
+
+ if ((long)fread (patch->data, 1,sample.len,fh) != sample.len)
+ {
+#ifdef GUSOUTDEBUG
+ printf ("Short file\n");
+#endif
+ return -1;
+ }
+
+ SEQ_WRPATCH (patch, sizeof (*patch) + sample.len);
+
+ offset = offset + sample.len;
+
+ }
+ patchloaded[pgm]=1;
+
+ fclose(fh);
+ free(patch); // Shouldn't this 'free' be within the 'for' loop ?
+ delete s;
+ freememory = device;
+ ioctl(seqfd, SNDCTL_SYNTH_MEMAVL, &freememory);
+#endif
+ return 0;
+}
+
+
+void GUSOut::setPatchesToUse(int *patchesused)
+{
+#ifdef HAVE_OSS_SUPPORT
+ int k;
+ for (k=0;k<256;k++) patchloaded[k]=0;
+
+ int patchesordered[256]; //This holds the pgm used ordered by a method which
+ // put first the patches more oftenly used, and then the least
+ // In example, if a song only uses a piano and a splash cymbal,
+ // This is set to : 0,188,-1,-1,-1,-1 ...
+ patchesLoadingOrder(patchesused,patchesordered);
+
+ // If above line doesn't work, perhaps you could try this ? :
+ // for (int j=0;j<256;j++) patchesordered[j]=patchesused[j];
+#ifdef GUSOUTDEBUG
+ printf("Patches used : \n");
+ for (k=0;k<256;k++)
+ {
+ if (patchesused[k]!=-1) printf("%d,",patchesused[k]);
+ }
+ printf("\n Patches used, sorted :\n");
+ for (k=0;k<256;k++)
+ {
+ if (patchesordered[k]!=-1) printf("%d,",patchesordered[k]);
+ }
+#endif
+
+ int i=0;
+ while (patchesordered[i]!=-1)
+ {
+#ifdef GUSOUTDEBUG
+ printf("Load Patch : %d\n",patchesordered[i]);
+#endif
+ loadPatch(patchesordered[i]);
+ i++;
+ }
+#endif
+}
+
+int compare_decreasing(const void *a,const void *b)
+{
+ struct instr_gm
+ {
+ int used;
+ int pgm;
+ };
+ instr_gm *ai=(instr_gm *)a;
+ instr_gm *bi=(instr_gm *)b;
+ return ai->used<bi->used;
+}
+
+
+void GUSOut::patchesLoadingOrder(int *patchesused,int *patchesordered)
+{
+ struct instr_gm
+ {
+ int used;
+ int pgm;
+ };
+
+ instr_gm tempmelody[128];
+ instr_gm tempdrums[128];
+ int i,j;
+ for (i=0,j=128;i<128;i++,j++)
+ {
+ tempmelody[i].used=patchesused[i];
+ tempmelody[i].pgm=i;
+ tempdrums[i].used=patchesused[j];
+ tempdrums[i].pgm=j;
+ }
+ /* SORT */ // Decreasing order (first most used patch, then less used patch)
+ qsort(&tempmelody[0],128,sizeof(instr_gm),compare_decreasing);
+ qsort(&tempdrums[0],128,sizeof(instr_gm),compare_decreasing);
+
+ /* Once they are sorted, the result is put on patchesordered in the following
+ * way : If tempmelody is : M0 M1 M2 M3 ... M127 and tempdrums is :
+ * D0 D1 D2 D3 ... D127, the result is :
+ * M0 D0 M1 M2 D1 M3 M4 D2 M5 M6 D3 ...
+ * P0 P1 P2 P3 P4 P5 P6 P7 P8 P9 P10 ...
+ */
+
+#ifdef GUSOUTDEBUG
+ for (int k=0;k<128;k++)
+ {
+ printf("%d - %d\n",tempmelody[k].used,tempmelody[k].pgm);
+ }
+ for (int k=0;k<128;k++)
+ {
+ printf("%d : %d\n",tempdrums[k].used,tempdrums[k].pgm);
+ }
+#endif
+
+ i=0;
+ int totalmelody=0;
+ while ((i<128)&&(tempmelody[i].used!=0))
+ {
+ totalmelody++;
+ i++;
+ }
+ i=0;
+ int totaldrums=0;
+ while ((i<128)&&(tempdrums[i].used!=0))
+ {
+ totaldrums++;
+ i++;
+ }
+#ifdef GUSOUTDEBUG
+ printf("Totalmelody : %d,totaldrums : %d\n",totalmelody,totaldrums);
+#endif
+ int tgt=0;
+
+ int tm=totalmelody;
+ int td=totaldrums;
+ int cm,cd;
+ cm=cd=0;
+ if ((tm!=0)&&(td!=0))
+ {
+ patchesordered[0]=tempmelody[0].pgm;
+ patchesordered[1]=tempdrums[0].pgm;
+ tm--;td--;
+ cm++;cd++;
+ tgt+=2;
+ while ((tm>0)&&(td>0))
+ {
+ if (((tgt-1)%3)==0)
+ {
+ patchesordered[tgt]=tempdrums[cd].pgm;
+ cd++;
+ td--;
+ }
+ else
+ {
+ patchesordered[tgt]=tempmelody[cm].pgm;
+ cm++;
+ tm--;
+ }
+ tgt++;
+ }
+ }
+ while (tm>0)
+ {
+ patchesordered[tgt]=tempmelody[cm].pgm;
+ tgt++;
+ cm++;
+ tm--;
+ }
+ while (td>0)
+ {
+ patchesordered[tgt]=tempdrums[cd].pgm;
+ tgt++;
+ cd++;
+ td--;
+ }
+
+ // Now we put as not used (-1) the rest of the array
+ while (tgt<256)
+ {
+ patchesordered[tgt]=-1;
+ tgt++;
+ }
+}
+
+//char *GUSOut::GUS_patches_directory="/mnt/dosc/gravis/patches";
+const char *GUSOut::GUS_patches_directory="/usr/share/ultrasnd";
+
+int GUSOut::delete_GUS_patches_directory = 0;
+/* No, this doesn't delete any file :-) it's just for internal use */
diff --git a/libkmid/gusout.h b/libkmid/gusout.h
new file mode 100644
index 000000000..1fd8a3cbd
--- /dev/null
+++ b/libkmid/gusout.h
@@ -0,0 +1,180 @@
+/* gusout.h - class gusOut which implements support for Gravis
+ Ultrasound cards through a /dev/sequencer device
+ This file is part of LibKMid 0.9.5
+ Copyright (C) 1998,99,2000 Antonio Larrosa Jimenez
+ LibKMid's homepage : http://www.arrakis.es/~rlarrosa/libkmid.html
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ Send comments and bug fixes to Antonio Larrosa <larrosa@kde.org>
+
+***************************************************************************/
+#ifndef _GUSOUT_H
+#define _GUSOUT_H
+
+#include <libkmid/midiout.h>
+#include <libkmid/voiceman.h>
+
+/**
+ * Gravis Ultrasound synthesizer output class . This class is used to send midi
+ * events to synthesizers on GUS cards.
+ *
+ * GUSOut inherits MidiOut and supports the same simple API.
+ *
+ * The recommended way to use this class is by using a DeviceManager
+ * object, and use the DeviceManager::setPatchesToUse() member which will
+ * call the setPatchesToUse() member in this class.
+ *
+ * @short Sends MIDI events to GUS synths
+ * @version 0.9.5 17/01/2000
+ * @author Antonio Larrosa Jimenez <larrosa@kde.org>
+ */
+class GUSOut : public MidiOut
+{
+ private:
+ class GUSOutPrivate;
+ GUSOutPrivate *di;
+
+ int patchloaded[256];
+ int nvoices;
+
+ int use8bit; // Use 8 bit patches, instead of 16 bits to use less memory
+ VoiceManager *vm;
+
+ int totalmemory; // Total memory in soundcard
+ int freememory; // Free memory
+
+
+ void patchesLoadingOrder(int *patchesused,int *patchesordered);
+ const char *patchName(int pgm);
+
+ public:
+ /**
+ * Constructor. See MidiOut::MidiOut() for more information.
+ */
+ GUSOut(int d=0,int total =12);
+
+ /**
+ * Destructor.
+ */
+ ~GUSOut();
+
+ /**
+ * See MidiOut::openDev()
+ */
+ virtual void openDev (int sqfd);
+
+ /**
+ * See MidiOut::closeDev()
+ */
+ virtual void closeDev (void);
+
+ /**
+ * See MidiOut::initDev()
+ */
+ virtual void initDev (void);
+
+ /**
+ * See MidiOut::noteOn()
+ */
+ virtual void noteOn ( uchar chn, uchar note, uchar vel );
+
+ /**
+ * See MidiOut::noteOff()
+ */
+ virtual void noteOff ( uchar chn, uchar note, uchar vel );
+
+ /**
+ * See MidiOut::keyPressure()
+ */
+ virtual void keyPressure ( uchar chn, uchar note, uchar vel );
+
+ /**
+ * See MidiOut::chnPatchChange()
+ */
+ virtual void chnPatchChange ( uchar chn, uchar patch );
+
+ /**
+ * See MidiOut::chnPressure()
+ */
+ virtual void chnPressure ( uchar chn, uchar vel );
+
+ /**
+ * See MidiOut::chnPitchBender()
+ */
+ virtual void chnPitchBender ( uchar chn, uchar lsb, uchar msb );
+
+ /**
+ * See MidiOut::chnController()
+ */
+ virtual void chnController ( uchar chn, uchar ctl , uchar v );
+
+ /**
+ * It's an empty function, as GUS synths don't support System Exclusive
+ * messages
+ */
+ virtual void sysex ( uchar *data,ulong size);
+
+ /**
+ * See DeviceManager::setPatchesToUse() . All the information about this
+ * member is explained there because it's (for now) just a simple call to this
+ * function when the device used is a GUS device, and you're supposed to use
+ * a DeviceManager object instead of a GUSOut object except in rare ocassions.
+ *
+ * @see patch()
+ * @see loadPatch()
+ */
+ void setPatchesToUse(int *patchesused);
+
+ /**
+ * Loads a single patch on the synthesizer memory.
+ * @param pgm is the number of the GM patch when pgm is between 0 and 127.
+ * Values from 128 to 255 are used to represent the percussion instruments.
+ * @return 0 if OK and -1 if there was an error (patch not found, not enough
+ * memory, etc.)
+ *
+ * @see patch()
+ * @see setPatchesToUse()
+ */
+ int loadPatch (int pgm);
+
+ /**
+ * Returns p if the patch with number p has been correctly loaded.
+ * In the case it hasn't been loaded, it returns the number of another patch
+ * that is loaded and that should be used instead.
+ *
+ * @see loadPatch()
+ * @see setPatchesToUse()
+ */
+ int patch(int p);
+
+ private:
+ static const char *GUS_patches_directory;
+ static int delete_GUS_patches_directory;
+
+ public:
+ /**
+ * Sets the directory where the GUS patches are stored, that is, where the
+ * acpiano.pat, ... files can be found.
+ *
+ * It will store a copy of the parameter, so you should delete the memory
+ * used by the parameter you passed.
+ */
+ static void setGUSPatchesDirectory(const char *dir);
+
+};
+
+#endif
diff --git a/libkmid/gusvoices.h b/libkmid/gusvoices.h
new file mode 100644
index 000000000..da364eb18
--- /dev/null
+++ b/libkmid/gusvoices.h
@@ -0,0 +1,286 @@
+/* gusvoices.h - struct with Gravis Ultrasound patches' names
+ This file is part of LibKMid 0.9.5
+ Copyright (C) 1998,99,2000 Antonio Larrosa Jimenez
+ LibKMid's homepage : http://www.arrakis.es/~rlarrosa/libkmid.html
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ Send comments and bug fixes to Antonio Larrosa <larrosa@kde.org>
+
+***************************************************************************/
+const char GUS_voice_names[256][9] =
+{
+ /* 0 */ "acpiano",
+ /* 1 */ "britepno",
+ /* 2 */ "synpiano",
+ /* 3 */ "honky",
+ /* 4 */ "epiano1",
+ /* 5 */ "epiano2",
+ /* 6 */ "hrpschrd",
+ /* 7 */ "clavinet",
+ /* 8 */ "celeste",
+ /* 9 */ "glocken",
+ /* 10 */ "musicbox",
+ /* 11 */ "vibes",
+ /* 12 */ "marimba",
+ /* 13 */ "xylophon",
+ /* 14 */ "tubebell",
+ /* 15 */ "santur",
+ /* 16 */ "homeorg",
+ /* 17 */ "percorg",
+ /* 18 */ "rockorg",
+ /* 19 */ "church",
+ /* 20 */ "reedorg",
+ /* 21 */ "accordn",
+ /* 22 */ "harmonca",
+ /* 23 */ "concrtna",
+ /* 24 */ "nyguitar",
+ /* 25 */ "acguitar",
+ /* 26 */ "jazzgtr",
+ /* 27 */ "cleangtr",
+ /* 28 */ "mutegtr",
+ /* 29 */ "odguitar",
+ /* 30 */ "distgtr",
+ /* 31 */ "gtrharm",
+ /* 32 */ "acbass",
+ /* 33 */ "fngrbass",
+ /* 34 */ "pickbass",
+ /* 35 */ "fretless",
+ /* 36 */ "slapbas1",
+ /* 37 */ "slapbas2",
+ /* 38 */ "synbass1",
+ /* 39 */ "synbass2",
+ /* 40 */ "violin",
+ /* 41 */ "viola",
+ /* 42 */ "cello",
+ /* 43 */ "contraba",
+// /* 44 */ "marcato",
+ /* 44 */ "tremstr",
+ /* 45 */ "pizzcato",
+ /* 46 */ "harp",
+ /* 47 */ "timpani",
+ /* 48 */ "marcato",
+ /* 49 */ "slowstr",
+ /* 50 */ "synstr1",
+ /* 51 */ "synstr2",
+ /* 52 */ "choir",
+ /* 53 */ "doo",
+ /* 54 */ "voices",
+ /* 55 */ "orchhit",
+ /* 56 */ "trumpet",
+ /* 57 */ "trombone",
+ /* 58 */ "tuba",
+ /* 59 */ "mutetrum",
+ /* 60 */ "frenchrn",
+ /* 61 */ "hitbrass",
+ /* 62 */ "synbras1",
+ /* 63 */ "synbras2",
+ /* 64 */ "sprnosax",
+ /* 65 */ "altosax",
+ /* 66 */ "tenorsax",
+ /* 67 */ "barisax",
+ /* 68 */ "oboe",
+ /* 69 */ "englhorn",
+ /* 70 */ "bassoon",
+ /* 71 */ "clarinet",
+ /* 72 */ "piccolo",
+ /* 73 */ "flute",
+ /* 74 */ "recorder",
+ /* 75 */ "woodflut",
+ /* 76 */ "bottle",
+ /* 77 */ "shakazul",
+ /* 78 */ "whistle",
+ /* 79 */ "ocarina",
+ /* 80 */ "sqrwave",
+ /* 81 */ "sawwave",
+ /* 82 */ "calliope",
+ /* 83 */ "chiflead",
+// /* 84 */ "voxlead",
+ /* 84 */ "charang",
+ /* 85 */ "voxlead",
+ /* 86 */ "lead5th",
+ /* 87 */ "basslead",
+ /* 88 */ "fantasia",
+ /* 89 */ "warmpad",
+ /* 90 */ "polysyn",
+ /* 91 */ "ghostie",
+ /* 92 */ "bowglass",
+ /* 93 */ "metalpad",
+ /* 94 */ "halopad",
+ /* 95 */ "sweeper",
+ /* 96 */ "aurora",
+ /* 97 */ "soundtrk",
+ /* 98 */ "crystal",
+ /* 99 */ "atmosphr",
+ /* 100 */ "freshair",
+ /* 101 */ "unicorn",
+// /* 102 */ "sweeper",
+ /* 102 */ "echovox",
+ /* 103 */ "startrak",
+ /* 104 */ "sitar",
+ /* 105 */ "banjo",
+ /* 106 */ "shamisen",
+ /* 107 */ "koto",
+ /* 108 */ "kalimba",
+ /* 109 */ "bagpipes",
+ /* 110 */ "fiddle",
+ /* 111 */ "shannai",
+ /* 112 */ "carillon",
+ /* 113 */ "agogo",
+ /* 114 */ "steeldrm",
+ /* 115 */ "woodblk",
+ /* 116 */ "taiko",
+ /* 117 */ "toms",
+ /* 118 */ "syntom",
+ /* 119 */ "revcym",
+ /* 120 */ "fx-fret",
+ /* 121 */ "fx-blow",
+ /* 122 */ "seashore",
+ /* 123 */ "jungle",
+ /* 124 */ "telephon",
+ /* 125 */ "helicptr",
+ /* 126 */ "applause",
+ /* 127 */ "pistol",
+
+ "", /* 128 = drum 0*/
+ "", /* 129 = drum 1*/
+ "", /* 130 = drum 2*/
+ "", /* 131 = drum 3*/
+ "", /* 132 = drum 4*/
+ "", /* 133 = drum 5*/
+ "", /* 134 = drum 6*/
+ "", /* 135 = drum 7*/
+ "", /* 136 = drum 8*/
+ "", /* 137 = drum 9*/
+ "", /* 138 = drum 10*/
+ "", /* 139 = drum 11*/
+ "", /* 140 = drum 12*/
+ "", /* 141 = drum 13*/
+ "", /* 142 = drum 14*/
+ "", /* 143 = drum 15*/
+ "", /* 144 = drum 16*/
+ "", /* 145 = drum 17*/
+ "", /* 146 = drum 18*/
+ "", /* 147 = drum 19*/
+ "", /* 148 = drum 20*/
+ "", /* 149 = drum 21*/
+ "", /* 150 = drum 22*/
+ "", /* 151 = drum 23*/
+ "", /* 152 = drum 24*/
+ "", /* 153 = drum 25*/
+ "", /* 154 = drum 26*/
+ "highq", /* 155 = drum 27*/
+ "slap", /* 156 = drum 28*/
+ "scratch1", /* 157 = drum 29*/
+ "scratch2", /* 158 = drum 30*/
+ "sticks", /* 159 = drum 31*/
+ "sqrclick", /* 160 = drum 32*/
+ "metclick", /* 161 = drum 33*/
+ "metbell", /* 162 = drum 34*/
+ "kick1", /* 163 = drum 35*/
+ "kick2", /* 164 = drum 36*/
+ "stickrim", /* 165 = drum 37*/
+ "snare1", /* 166 = drum 38*/
+ "claps", /* 167 = drum 39*/
+ "snare2", /* 168 = drum 40*/
+ "tomlo2", /* 169 = drum 41*/
+ "hihatcl", /* 170 = drum 42*/
+ "tomlo1", /* 171 = drum 43*/
+ "hihatpd", /* 172 = drum 44*/
+ "tommid2", /* 173 = drum 45*/
+ "hihatop", /* 174 = drum 46*/
+ "tommid1", /* 175 = drum 47*/
+ "tomhi2", /* 176 = drum 48*/
+ "cymcrsh1", /* 177 = drum 49*/
+ "tomhi1", /* 178 = drum 50*/
+ "cymride1", /* 179 = drum 51*/
+ "cymchina", /* 180 = drum 52*/
+ "cymbell", /* 181 = drum 53*/
+ "tamborin", /* 182 = drum 54*/
+ "cymsplsh", /* 183 = drum 55*/
+ "cowbell", /* 184 = drum 56*/
+ "cymcrsh2", /* 185 = drum 57*/
+ "vibslap", /* 186 = drum 58*/
+ "cymride2", /* 187 = drum 59*/
+ "bongohi", /* 188 = drum 60*/
+ "bongolo", /* 189 = drum 61*/
+ "congahi1", /* 190 = drum 62*/
+ "congahi2", /* 191 = drum 63*/
+ "congalo", /* 192 = drum 64*/
+ "timbaleh", /* 193 = drum 65*/
+ "timbalel", /* 194 = drum 66*/
+ "agogohi", /* 195 = drum 67*/
+ "agogolo", /* 196 = drum 68*/
+ "cabasa", /* 197 = drum 69*/
+ "maracas", /* 198 = drum 70*/
+ "whistle1", /* 199 = drum 71*/
+ "whistle2", /* 200 = drum 72*/
+ "guiro1", /* 201 = drum 73*/
+ "guiro2", /* 202 = drum 74*/
+ "clave", /* 203 = drum 75*/
+ "woodblk1", /* 204 = drum 76*/
+ "woodblk2", /* 205 = drum 77*/
+ "cuica1", /* 206 = drum 78*/
+ "cuica2", /* 207 = drum 79*/
+ "triangl1", /* 208 = drum 80*/
+ "triangl2", /* 209 = drum 81*/
+ "shaker", /* 210 = drum 82*/
+ "jingles", /* 211 = drum 83*/
+ "belltree", /* 212 = drum 84*/
+ "castinet", /* 213 = drum 85*/
+ "surdo1", /* 214 = drum 86*/
+ "surdo2", /* 215 = drum 87*/
+ "", /* 216 = drum 88*/
+ "", /* 217 = drum 89*/
+ "", /* 218 = drum 90*/
+ "", /* 219 = drum 91*/
+ "", /* 220 = drum 92*/
+ "", /* 221 = drum 93*/
+ "", /* 222 = drum 94*/
+ "", /* 223 = drum 95*/
+ "", /* 224 = drum 96*/
+ "", /* 225 = drum 97*/
+ "", /* 226 = drum 98*/
+ "", /* 227 = drum 99*/
+ "", /* 228 = drum 100*/
+ "", /* 229 = drum 101*/
+ "", /* 230 = drum 102*/
+ "", /* 231 = drum 103*/
+ "", /* 232 = drum 104*/
+ "", /* 233 = drum 105*/
+ "", /* 234 = drum 106*/
+ "", /* 235 = drum 107*/
+ "", /* 236 = drum 108*/
+ "", /* 237 = drum 109*/
+ "", /* 238 = drum 110*/
+ "", /* 239 = drum 111*/
+ "", /* 240 = drum 112*/
+ "", /* 241 = drum 113*/
+ "", /* 242 = drum 114*/
+ "", /* 243 = drum 115*/
+ "", /* 244 = drum 116*/
+ "", /* 245 = drum 117*/
+ "", /* 246 = drum 118*/
+ "", /* 247 = drum 119*/
+ "", /* 248 = drum 120*/
+ "", /* 249 = drum 121*/
+ "", /* 250 = drum 122*/
+ "", /* 251 = drum 123*/
+ "", /* 252 = drum 124*/
+ "", /* 253 = drum 125*/
+ "", /* 254 = drum 126*/
+ "" /* 255 = drum 127*/
+};
diff --git a/libkmid/libkmid.cc b/libkmid/libkmid.cc
new file mode 100644
index 000000000..59e96ad0f
--- /dev/null
+++ b/libkmid/libkmid.cc
@@ -0,0 +1,263 @@
+/**************************************************************************
+
+ libkmid.cc - class KMidSimpleAPI that makes it easy to use libkmid
+ and a C wrapper.
+ This file is part of LibKMid 0.9.5
+ Copyright (C) 2000 Antonio Larrosa Jimenez
+ LibKMid's homepage : http://www.arrakis.es/~rlarrosa/libkmid.html
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ Send comments and bug fixes to Antonio Larrosa <larrosa@kde.org>
+
+***************************************************************************/
+
+#include "libkmid.h"
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/shm.h>
+
+#include "deviceman.h"
+#include "player.h"
+#include "midimapper.h"
+
+struct kMidData kMid;
+
+int KMidSimpleAPI::kMidInit(void)
+{
+ kMid.midi = new DeviceManager();
+ if ( kMid.midi == 0L ) return 1;
+ kMid.midi->initManager();
+ if (!kMid.midi->ok()) return 1;
+
+ kMid.pctlsmID=shmget(getpid(),sizeof(PlayerController),0600 | IPC_CREAT);
+ if (kMid.pctlsmID==-1) return 1;
+ kMid.pctl=(PlayerController *)shmat(kMid.pctlsmID,NULL,0);
+ if (kMid.pctl==NULL) return 1;
+
+ kMid.player=new MidiPlayer(kMid.midi,kMid.pctl);
+ if ( kMid.player == 0L )
+ {
+ delete kMid.midi;
+ return 1;
+ }
+
+ kMid.player->setParseSong(false);
+
+ kMid.pctl->message=0;
+ kMid.pctl->gm=1;
+ kMid.pctl->error=0;
+ kMid.pctl->ratioTempo=1.0;
+ kMid.pctl->tempo=500000;
+ kMid.pctl->volumepercentage=100;
+ for (int i=0;i<16;i++)
+ {
+ kMid.pctl->forcepgm[i]=0;
+ kMid.pctl->pgm[i]=0;
+ }
+
+ return 0;
+}
+
+int KMidSimpleAPI::kMidLoad(const char *filename)
+{
+ if (kMidDevices()==0) return 0;
+ return kMid.player->loadSong(filename);
+}
+
+int KMidSimpleAPI::kMidPlay(int loop)
+{
+ if (kMidDevices()==0) return 4;
+ if (!kMid.player->isSongLoaded()) return 1;
+ if (kMid.pctl->playing==1) return 2;
+ if (kMid.midi->checkInit()==-1) return 3;
+ kMid.pctl->message=0;
+ kMid.pctl->playing=0;
+ kMid.pctl->finished=0;
+ kMid.pctl->error=0;
+ kMid.pctl->SPEVplayed=0;
+ kMid.pctl->SPEVprocessed=0;
+ kMid.pctl->millisecsPlayed=0;
+ if ((kMid.pid=fork())==0)
+ {
+ if (loop)
+ {
+ while (1)
+ {
+ kMid.player->play();
+ if (kMid.pctl->error) return 5;
+ kMid.pctl->message=0;
+ kMid.pctl->playing=0;
+ kMid.pctl->finished=0;
+ kMid.pctl->error=0;
+ kMid.pctl->SPEVplayed=0;
+ kMid.pctl->SPEVprocessed=0;
+ kMid.pctl->millisecsPlayed=0;
+ }
+
+ } else {
+ kMid.player->play();
+ if (kMid.pctl->error) return 5;
+ }
+ _exit(0);
+ } else return 4;
+ return 0;
+}
+
+int KMidSimpleAPI::kMidStop(void)
+{
+ if (kMidDevices()==0) return 4;
+ if (kMid.pctl->playing==0) return 1;
+ if (kMid.pid!=0)
+ {
+ kill(kMid.pid,SIGTERM);
+ waitpid(kMid.pid, NULL, 0);
+ kMid.pid=0;
+ } else return 2;
+
+ kMid.pctl->playing=0;
+ return 0;
+}
+
+void KMidSimpleAPI::kMidDestruct(void)
+{
+ delete kMid.midi;
+ kMid.midi=0L;
+ delete kMid.player;
+ kMid.player=0L;
+ delete kMid.map;
+ shmdt((char *)kMid.pctl);
+ shmctl(kMid.pctlsmID, IPC_RMID, 0L);
+}
+
+int KMidSimpleAPI::kMidIsPlaying(void)
+{
+ return kMid.pctl->playing;
+}
+
+int KMidSimpleAPI::kMidDevices(void)
+{
+ return kMid.midi->midiPorts()+kMid.midi->synthDevices();
+}
+
+const char * KMidSimpleAPI::kMidName(int i)
+{
+ return kMid.midi->name(i);
+}
+
+const char * KMidSimpleAPI::kMidType(int i)
+{
+ return kMid.midi->type(i);
+}
+
+void KMidSimpleAPI::kMidSetDevice(int i)
+{
+ kMid.midi->setDefaultDevice(i);
+}
+
+void KMidSimpleAPI::kMidSetMidiMapper(const char *mapfilename)
+{
+ if (kMidDevices()==0) return;
+ kMid.map=new MidiMapper(mapfilename);
+ if ((kMid.map->ok() == 0L)||(!kMid.map->ok())) return;
+ kMid.midi->setMidiMap(kMid.map);
+}
+
+const char *KMidSimpleAPI::kMidVersion(void)
+{
+ return "0.9.5";
+}
+
+const char *KMidSimpleAPI::kMidCopyright(void)
+{
+ return "LibKMid 0.9.5 (C)1997-2000 Antonio Larrosa Jimenez <larrosa@kde.org>.Malaga(es)";
+}
+
+/* * * * * *
+
+ Under this line (------) there's only a C wrapper for the KMidSimpleAPI class
+
+* * * * * */
+
+
+int kMidInit(void)
+{
+ return KMidSimpleAPI::kMidInit();
+}
+
+int kMidLoad(const char *filename)
+{
+ return KMidSimpleAPI::kMidLoad(filename);
+}
+
+int kMidPlay(void)
+{
+ return KMidSimpleAPI::kMidPlay();
+}
+
+int kMidStop(void)
+{
+ return KMidSimpleAPI::kMidStop();
+}
+
+void kMidDestruct(void)
+{
+ KMidSimpleAPI::kMidDestruct();
+}
+
+int kMidIsPlaying(void)
+{
+ return KMidSimpleAPI::kMidIsPlaying();
+}
+
+int kMidDevices(void)
+{
+ return KMidSimpleAPI::kMidDevices();
+}
+
+const char *kMidName(int i)
+{
+ return KMidSimpleAPI::kMidName(i);
+}
+
+const char *kMidType(int i)
+{
+ return KMidSimpleAPI::kMidType(i);
+}
+
+void kMidSetDevice(int i)
+{
+ KMidSimpleAPI::kMidSetDevice(i);
+}
+
+void kMidSetMidiMapper(const char *mapfilename)
+{
+ KMidSimpleAPI::kMidSetMidiMapper(mapfilename);
+}
+
+const char *kMidVersion(void)
+{
+ return KMidSimpleAPI::kMidVersion();
+}
+
+const char *kMidCopyright(void)
+{
+ return KMidSimpleAPI::kMidCopyright();
+}
+
diff --git a/libkmid/libkmid.h b/libkmid/libkmid.h
new file mode 100644
index 000000000..45f41bd79
--- /dev/null
+++ b/libkmid/libkmid.h
@@ -0,0 +1,226 @@
+/* libkmid.h - class KMidSimpleAPI that makes it easy to use libkmid
+ and a C wrapper.
+ This file is part of LibKMid 0.9.5
+ Copyright (C) 2000 Antonio Larrosa Jimenez
+ LibKMid's homepage : http://www.arrakis.es/~rlarrosa/libkmid.html
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ Send comments and bug fixes to Antonio Larrosa <larrosa@kde.org>
+
+***************************************************************************/
+#ifndef _LIBKMID_H
+#define _LIBKMID_H
+
+#ifdef __cplusplus
+
+#include <kdelibs_export.h>
+
+/**
+ * Simple API covering most of the uses of libkmid.
+ *
+ * You can use the members of this class in pure C applications, just by using
+ * the same name as the corresponding function member.
+ *
+ * Suppose you're developing a game and you want to play some background music
+ * while the user is playing. You only have to call :
+ *
+ * @li kMidInit();
+ * @li kMidLoad("RideOfTheValkyries.mid");
+ * @li kMidPlay();
+ *
+ * When the user decides to quit the game, use
+ *
+ * @li kMidStop();
+ * @li kMidDestruct();
+ *
+ * to stop the music and release the memory allocated by libkmid.
+ *
+ * @short A very simple API around the rest of libkmid.
+ * @version 0.9.5 17/01/2000
+ * @author Antonio Larrosa Jimenez <larrosa@kde.org>
+ */
+class KMID_EXPORT KMidSimpleAPI
+{
+ private:
+ class KMidSimpleAPIPrivate;
+ KMidSimpleAPIPrivate *d;
+
+ public:
+
+ /**
+ * Initializes libkmid. Creates the DeviceManager object, and initializes
+ * some variables that will be used later.
+ *
+ * @return 0 if OK, and a positive number when there's any error (for
+ * example, because the /dev/sequencer device couldn't be opened, be it
+ * because it was already opened by another application, or because the
+ * sound card wasn't configured)
+ */
+ static int kMidInit(void);
+
+ /**
+ * Loads a song that will be played with the next call to kMidPlay().
+ */
+ static int kMidLoad(const char *filename);
+
+ /**
+ * Plays the song currently loaded with kMidLoad().
+ * kMidPlay forks in order to play the song in a different process, it
+ * exits inmediately, so that the application can follow the normal
+ * execution flow while the sone is played.
+ *
+ * If loop is 0 the song is played once and then the child process
+ * finishes. If loop is 1, the song is played repeatedly until
+ * kMidStop() is called. You can call kMidStop() anytime you want
+ * (also if loop is 0) to stop the song and kill the child process.
+ *
+ * @see kMidStop
+ * @see kMidIsPlaying
+ */
+ static int kMidPlay(int loop=0);
+
+ /**
+ * Stops playing a song inmediatly. It doesn't return until the child
+ * process that is playing the song is terminated.
+ *
+ * @see kMidPlay
+ */
+ static int kMidStop(void);
+
+ /**
+ * Releases the memory allocated by libkmid. To continue playing, you must
+ * first make a(nother) call to kMidInit().
+ */
+ static void kMidDestruct(void);
+
+ /**
+ * Returns 1 if the library is playing a song, and 0 if it's not.
+ * @see kMidPlay
+ */
+ static int kMidIsPlaying(void);
+
+ /**
+ * Returns the number of MIDI devices ( MIDI ports + synthesizers )
+ * @see DeviceManager::midiPorts
+ * @see DeviceManager::synthDevices
+ * @see kMidName
+ * @see kMidType
+ */
+ static int kMidDevices(void);
+
+ /**
+ * Returns the name of the i-th device . In case libkmid wasn't yet
+ * initialized ( see kMidInit() ), the return value is NULL, and in
+ * case the parameter has a value out of the valid range
+ * ( see kMidDevices() ) it returns an empty string.
+ *
+ * @see kMidDevices
+ * @see kMidType
+ */
+ static const char *kMidName(int i);
+
+ /**
+ * Returns the type of the i-th device . In case libkmid wasn't yet
+ * initialized ( see kMidInit() ), the return value is NULL, and in
+ * case the parameter has a value out of the valid range
+ * ( see kMidDevices() ) it returns an empty string.
+ *
+ * @see kMidDevices
+ * @see kMidName
+ */
+ static const char *kMidType(int i);
+
+ /**
+ * Sets the MIDI device to use when playing a song.
+ * @see kMidDevices
+ * @see kMidName
+ * @see DeviceManager
+ */
+ static void kMidSetDevice(int i);
+
+ /**
+ * Sets the Midi Mapper to use. Most of the users won't need a midi mapper,
+ * but there're still non-General Midi synthesizers out there, and people
+ * with one of those will get much better sound quality by using a MIDI
+ * mapper.
+ *
+ * Please have a look at KMid's documentation for more information
+ * about MIDI mappers and how to write a MIDI mapper for your keyboard.
+ */
+ static void kMidSetMidiMapper(const char *mapfilename);
+
+ /**
+ * Returns the version number of libkmid, i.e. "0.9.5" or "1.0 Beta"
+ */
+ static const char *kMidVersion(void);
+
+ /**
+ * Returns the copyright notice that applications using libkmid should print
+ * to the user in an about box or somewhere visible.
+ * I.e.
+ *
+ * "LibKMid 0.9.5 (C) 1997-2000 Antonio Larrosa Jimenez <larrosa@kde.org>. Spain"
+ */
+ static const char *kMidCopyright(void);
+
+};
+
+
+
+extern "C" {
+
+#else
+#define KMID_EXPORT
+#endif
+
+
+KMID_EXPORT int kMidInit(void);
+KMID_EXPORT int kMidLoad(const char *filename);
+KMID_EXPORT int kMidPlay(void);
+KMID_EXPORT int kMidStop(void);
+KMID_EXPORT void kMidDestruct(void);
+KMID_EXPORT int kMidIsPlaying(void);
+KMID_EXPORT int kMidDevices(void);
+KMID_EXPORT const char * kMidName(int i);
+KMID_EXPORT const char * kMidType(int i);
+KMID_EXPORT void kMidSetDevice(int i);
+KMID_EXPORT void kMidSetMidiMapper(const char *mapfilename);
+KMID_EXPORT const char * kMidVersion(void);
+KMID_EXPORT const char * kMidCopyright(void);
+
+
+
+#ifdef __cplusplus
+
+}
+
+/**
+ * @internal
+ */
+extern struct kMidData
+{
+ class DeviceManager *midi;
+ class MidiPlayer *player;
+ class MidiMapper *map;
+ struct PlayerController *pctl;
+ int pctlsmID;
+ int pid;
+} kMid;
+#endif
+
+
+#endif
diff --git a/libkmid/midfile.cc b/libkmid/midfile.cc
new file mode 100644
index 000000000..baf0a2b37
--- /dev/null
+++ b/libkmid/midfile.cc
@@ -0,0 +1,460 @@
+/**************************************************************************
+
+ midfile.cc - function which reads a midi file,and creates the track classes
+ This file is part of LibKMid 0.9.5
+ Copyright (C) 1997,98,99,2000 Antonio Larrosa Jimenez
+ LibKMid's homepage : http://www.arrakis.es/~rlarrosa/libkmid.html
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ Send comments and bug fixes to Antonio Larrosa <larrosa@kde.org>
+
+***************************************************************************/
+#include "midfile.h"
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "sndcard.h"
+#include "midispec.h"
+#include "mt32togm.h"
+#include "sys/stat.h"
+#include <config.h>
+
+#include <kprocess.h>
+#include <qfile.h>
+
+int fsearch(FILE *fh,const char *text,long *ptr);
+
+/* This function gives the metronome tempo, from a tempo data as found in
+ a midi file */
+double tempoToMetronomeTempo(ulong x)
+{
+ return 60/((double)x/1000000);
+}
+
+double metronomeTempoToTempo(ulong x)
+{
+ return ((double)60*x)/1000000;
+}
+
+int uncompressFile(const char *gzname, char *tmpname)
+ // Returns 0 if OK, 1 if error (tmpname not set)
+{
+ QString cmd("gzip -dc " + KProcess::quote(gzname));
+ FILE *infile = popen( QFile::encodeName(cmd).data(), "r");
+ if (infile==NULL) {
+ fprintf(stderr,"ERROR : popen failed : %s\n",QFile::encodeName(cmd).data());
+ return 1;
+ }
+ strcpy(tmpname, "/tmp/KMid.XXXXXXXXXX");
+ int fd = mkstemp(tmpname);
+ if (fd == -1)
+ {
+ pclose(infile);
+ return 1;
+ }
+ FILE *outfile= fdopen(fd,"wb");
+ if (outfile==NULL)
+ {
+ pclose(infile);
+ return 1;
+ }
+ int n=getc(infile);
+ if (n==EOF)
+ {
+ pclose(infile);
+ fclose(outfile);
+ unlink(tmpname);
+ return 1;
+ }
+ fputc(n,outfile);
+ int buf[BUFSIZ];
+ n = fread(buf, 1, BUFSIZ, infile);
+ while (n>0)
+ {
+ fwrite(buf, 1, n, outfile);
+ n = fread(buf, 1, BUFSIZ, infile);
+ }
+
+ pclose(infile);
+
+ //if (pclose(infile) != 0) fprintf(stderr,"Error : pclose failed\n");
+ // Is it right for pclose to always fail ?
+
+ fclose(outfile);
+ return 0;
+}
+
+MidiTrack **readMidiFile( const char *name, MidiFileInfo *info, int &ok)
+{
+ ok=1;
+ MidiTrack **tracks;
+
+ struct stat buf;
+ if (stat(name,&buf) || !S_ISREG(buf.st_mode))
+ {
+ fprintf(stderr,"ERROR: %s is not a regular file\n",name);
+ ok=-6;
+ return NULL;
+ }
+
+ FILE *fh=fopen(name,"rb");
+ if (fh==NULL)
+ {
+ fprintf(stderr,"ERROR: Can't open file %s\n",name);
+ ok=-1;
+ return NULL;
+ }
+ char text[4];
+ text[0] = 0;
+ fread(text,1,4,fh);
+ if ((strncmp(text,"MThd",4)!=0)&&(strcmp(&name[strlen(name)-3],".gz")==0))
+ {
+ fclose(fh);
+ char tempname[200];
+ fprintf(stderr,"Trying to open zipped midi file...\n");
+ if (uncompressFile(name,tempname)!=0)
+ {
+ fprintf(stderr,"ERROR: %s is not a (zipped) midi file\n",name);
+ ok=-2;
+ return NULL;
+ }
+ fh=fopen(tempname,"rb");
+ fread(text,1,4,fh);
+ unlink(tempname);
+ }
+
+ if (strncmp(text,"MThd",4)!=0)
+ {
+ fseek(fh,0,SEEK_SET);
+ long pos;
+ if (fsearch(fh,"MThd",&pos)==0)
+ {
+ fclose(fh);
+ fprintf(stderr,"ERROR: %s is not a midi file.\n",name);
+ ok=-2;
+ return NULL;
+ }
+ fseek(fh,pos,SEEK_SET);
+ fread(text,1,4,fh);
+ }
+ long header_size=readLong(fh);
+ info->format=readShort(fh);
+ info->ntracks=readShort(fh);
+ info->ticksPerCuarterNote=readShort(fh);
+ if (info->ticksPerCuarterNote<0)
+ {
+ fprintf(stderr,"ERROR: Ticks per cuarter note is negative !\n");
+ fprintf(stderr,"Please report this error to : larrosa@kde.org\n");
+ fclose(fh);
+ ok=-3;
+ return NULL;
+ }
+ if (header_size>6) fseek(fh,header_size-6,SEEK_CUR);
+ tracks=new MidiTrack*[info->ntracks];
+ if (tracks==NULL)
+ {
+ fprintf(stderr,"ERROR: Not enough memory\n");
+ fclose(fh);
+ ok=-4;
+ return NULL;
+ }
+ int i=0;
+ while (i<info->ntracks)
+ {
+ fread(text,1,4,fh);
+ if (strncmp(text,"MTrk",4)!=0)
+ {
+ fprintf(stderr,"ERROR: Not a well built midi file\n");
+ fprintf(stderr,"%s",text);
+ fclose(fh);
+ ok=-5;
+ return NULL;
+ }
+ tracks[i]=new MidiTrack(fh,info->ticksPerCuarterNote,i);
+ if (tracks[i]==NULL)
+ {
+ fprintf(stderr,"ERROR: Not enough memory");
+ fclose(fh);
+ ok=-4;
+ return NULL;
+ }
+ i++;
+ }
+
+ fclose(fh);
+
+ return tracks;
+
+}
+
+void parseInfoData(MidiFileInfo *info,MidiTrack **tracks,float ratioTempo)
+{
+
+ info->ticksTotal=0;
+ info->millisecsTotal=0.0;
+ info->ticksPlayed=0;
+ int i;
+ for (i=0;i<256;i++)
+ {
+ info->patchesUsed[i]=0;
+ }
+
+ int parsing=1;
+ int trk,minTrk;
+ ulong tempo=(ulong)(500000 * ratioTempo);
+
+#ifdef MIDFILEDEBUG
+ printf("Parsing 1 ...\n");
+#endif
+
+ int pgminchannel[16];
+ for (i=0;i<16;i++)
+ {
+ pgminchannel[i]=0;
+ }
+
+ int j;
+ for (i=0;i<info->ntracks;i++)
+ {
+ tracks[i]->init();
+ tracks[i]->changeTempo(tempo);
+ }
+ double prevms=0;
+ double minTime=0;
+ double maxTime;
+ MidiEvent *ev=new MidiEvent;
+ while (parsing)
+ {
+ prevms=minTime;
+ trk=0;
+ minTrk=0;
+ maxTime=minTime + 2 * 60000L;
+ minTime=maxTime;
+ while (trk<info->ntracks)
+ {
+ if (tracks[trk]->absMsOfNextEvent()<minTime)
+ {
+ minTrk=trk;
+ minTime=tracks[minTrk]->absMsOfNextEvent();
+ }
+ trk++;
+ }
+ if ((minTime==maxTime))
+ {
+ parsing=0;
+#ifdef MIDFILEDEBUG
+ printf("END of parsing\n");
+#endif
+ }
+ else
+ {
+ trk=0;
+ while (trk<info->ntracks)
+ {
+ tracks[trk]->currentMs(minTime);
+ trk++;
+ }
+ }
+ trk=minTrk;
+ tracks[trk]->readEvent(ev);
+
+ switch (ev->command)
+ {
+ case (MIDI_NOTEON) :
+ if (ev->chn!=PERCUSSION_CHANNEL)
+ info->patchesUsed[pgminchannel[ev->chn]]++;
+ else
+ info->patchesUsed[ev->note+128]++;
+ break;
+ case (MIDI_PGM_CHANGE) :
+ pgminchannel[ev->chn]=(ev->patch);
+ break;
+ case (MIDI_SYSTEM_PREFIX) :
+ if (((ev->command|ev->chn)==META_EVENT)&&(ev->d1==ME_SET_TEMPO))
+ {
+ tempo=(ulong)(((ev->data[0]<<16)|(ev->data[1]<<8)|(ev->data[2])) * ratioTempo);
+ for (j=0;j<info->ntracks;j++)
+ {
+ tracks[j]->changeTempo(tempo);
+ }
+ }
+ break;
+ }
+ }
+
+ delete ev;
+ info->millisecsTotal=prevms;
+
+ for (i=0;i<info->ntracks;i++)
+ {
+ tracks[i]->init();
+ }
+
+#ifdef MIDFILEDEBUG
+ printf("info.ticksTotal = %ld \n",info->ticksTotal);
+ printf("info.ticksPlayed= %ld \n",info->ticksPlayed);
+ printf("info.millisecsTotal = %g \n",info->millisecsTotal);
+ printf("info.TicksPerCN = %d \n",info->ticksPerCuarterNote);
+#endif
+
+}
+
+
+void parsePatchesUsed(MidiTrack **tracks,MidiFileInfo *info,int gm)
+{
+ int i;
+ for (i=0;i<256;i++)
+ {
+ info->patchesUsed[i]=0;
+ }
+ int parsing=1;
+ int trk,minTrk;
+ ulong tempo=500000;
+
+#ifdef MIDFILEDEBUG
+ printf("Parsing for patches ...\n");
+#endif
+
+ int j;
+ for (i=0;i<info->ntracks;i++)
+ {
+ tracks[i]->init();
+ }
+ double prevms=0;
+ double minTime=0;
+ double maxTime;
+ ulong tmp;
+ MidiEvent *ev=new MidiEvent;
+ int pgminchannel[16];
+ for (i=0;i<16;i++)
+ {
+ pgminchannel[i]=0;
+ }
+
+ while (parsing)
+ {
+ prevms=minTime;
+ trk=0;
+ minTrk=0;
+ maxTime=minTime + 2 * 60000L;
+ minTime=maxTime;
+ while (trk<info->ntracks)
+ {
+ if (tracks[trk]->absMsOfNextEvent()<minTime)
+ {
+ minTrk=trk;
+ minTime=tracks[minTrk]->absMsOfNextEvent();
+ }
+ trk++;
+ }
+ if ((minTime==maxTime))
+ {
+ parsing=0;
+#ifdef MIDFILEDEBUG
+ printf("END of parsing for patches\n");
+#endif
+ }
+ else
+ {
+ trk=0;
+ while (trk<info->ntracks)
+ {
+ tracks[trk]->currentMs(minTime);
+ trk++;
+ }
+ }
+ trk=minTrk;
+ tracks[trk]->readEvent(ev);
+ switch (ev->command)
+ {
+ case (MIDI_NOTEON) :
+ if (ev->chn!=PERCUSSION_CHANNEL)
+ info->patchesUsed[pgminchannel[ev->chn]]++;
+ else
+ info->patchesUsed[ev->note+128]++;
+ break;
+ case (MIDI_PGM_CHANGE) :
+ pgminchannel[ev->chn]=(gm==1)?(ev->patch):(MT32toGM[ev->patch]);
+ break;
+ case (MIDI_SYSTEM_PREFIX) :
+ if (((ev->command|ev->chn)==META_EVENT)&&(ev->d1==ME_SET_TEMPO))
+ {
+ if (tempoToMetronomeTempo(tmp=((ev->data[0]<<16)|(ev->data[1]<<8)|(ev->data[2])))>=8)
+ {
+ tempo=tmp;
+ // printf("setTempo %ld\n",tempo);
+ for (j=0;j<info->ntracks;j++)
+ {
+ tracks[j]->changeTempo(tempo);
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ delete ev;
+
+ for (i=0;i<info->ntracks;i++)
+ {
+ tracks[i]->init();
+ }
+
+}
+
+int fsearch(FILE *fh,const char *text,long *ptr)
+ // Search for "text" through the fh file and then returns :
+ // text MUST BE smaller than 256 characters
+ // 0 if not was found
+ // 1 if it was found and in ptr (if !=NULL) the position where text begins.
+{
+ if ((text==NULL)||(text[0]==0)) return 0;
+ char buf[1024];
+ char tmp[256];
+ long pos;
+ int l=strlen(text);
+ int i,k,r;
+ while (!feof(fh))
+ {
+ pos=ftell(fh);
+ k=fread(buf,1,1024,fh);
+ i=0;
+ while (i<k)
+ {
+ if (buf[i]==text[0])
+ {
+ if (k-i>=l)
+ r=strncmp(text,&buf[i],l);
+ else
+ {
+ fseek(fh,pos+i,SEEK_SET);
+ if (fread(tmp,1,l,fh)<(uint)l) return 0;
+ fseek(fh,pos+k,SEEK_SET);
+ r=strncmp(text,tmp,l);
+ }
+ if (r==0)
+ {
+ if (ptr!=NULL) *ptr=pos+i;
+ return 1;
+ }
+ }
+ i++;
+ }
+ }
+ return 0;
+}
diff --git a/libkmid/midfile.h b/libkmid/midfile.h
new file mode 100644
index 000000000..843e4fc44
--- /dev/null
+++ b/libkmid/midfile.h
@@ -0,0 +1,99 @@
+/* midfile.h - function which reads a midi file,and creates the track classes
+ This file is part of LibKMid 0.9.5
+ Copyright (C) 1997,98,99,2000 Antonio Larrosa Jimenez
+ LibKMid's homepage : http://www.arrakis.es/~rlarrosa/libkmid.html
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ Send comments and bug fixes to Antonio Larrosa <larrosa@kde.org>
+
+ ***************************************************************************/
+#ifndef _MIDFILE_H
+#define _MIDFILE_H
+
+#include <libkmid/dattypes.h>
+#include <libkmid/track.h>
+#include <stdio.h>
+#include <kdelibs_export.h>
+
+/**
+ * Contains all the information about a MIDI file.
+ *
+ * @short All the information about a MIDI file.
+ * @version 0.9.5 17/01/2000
+ * @author Antonio Larrosa Jimenez <larrosa@kde.org>
+ */
+struct MidiFileInfo
+{
+ /**
+ * Format of MIDI file.
+ */
+ int format;
+
+ /**
+ * Number of tracks.
+ */
+ int ntracks;
+
+ /**
+ * Ticks per cuarter note.
+ */
+ int ticksPerCuarterNote;
+
+ /**
+ * Total number of MIDI ticks
+ */
+ ulong ticksTotal;
+
+ /**
+ * Total number of milliseconds
+ */
+ double millisecsTotal;
+
+ ulong ticksPlayed;
+
+ /**
+ * Patches used in the MIDI file.
+ *
+ * In each position of the array it stores the number of times the
+ * corresponding patch is used. So, if a MIDI file plays 782 notes
+ * with a piano, patchesUsed[0] will store 782. In the same way,
+ * if it doesn't use the Music Box patch, patchesUsed[10] will be 0.
+ *
+ */
+ int patchesUsed[256];
+
+};
+
+double KMID_EXPORT tempoToMetronomeTempo(ulong x);
+double metronomeTempoToTempo(ulong x);
+
+/**
+ * Reads a midi file.
+ *
+ * @param name the filename of the midi file to load.
+ * @param info a pointer to the MidiFileInfo struct that will be
+ * filled with the information of the loaded file.
+ * @param ok return status.
+ * @return an array of MidiTrack objects with the contents of the file.
+ */
+MidiTrack **readMidiFile( const char *name, MidiFileInfo *info, int &ok);
+
+void parseInfoData( MidiFileInfo *info, MidiTrack **tracks, float ratioTempo);
+
+void parsePatchesUsed( MidiTrack **tracks, MidiFileInfo *info, int gm);
+
+#endif
diff --git a/libkmid/midimapper.cc b/libkmid/midimapper.cc
new file mode 100644
index 000000000..0b8022b8e
--- /dev/null
+++ b/libkmid/midimapper.cc
@@ -0,0 +1,456 @@
+/**************************************************************************
+
+ midimapper.cc - The midi mapper object
+ This file is part of LibKMid 0.9.5
+ Copyright (C) 1997,98,99,2000 Antonio Larrosa Jimenez
+ LibKMid's homepage : http://www.arrakis.es/~rlarrosa/libkmid.html
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ Send comments and bug fixes to Antonio Larrosa <larrosa@kde.org>
+
+***************************************************************************/
+#include "midimapper.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+MidiMapper::MidiMapper(const char *name)
+{
+ _ok=1;
+ keymaps=NULL;
+ _filename=NULL;
+ mapPitchBender=0;
+ mapExpressionToVolumeEvents=0;
+ if ((name==NULL)||(name[0]==0))
+ {
+ deallocateMaps();
+ int i;
+ for (i=0;i<16;i++)
+ {
+ channelmap[i]=i;
+ channelPatchForced[i]=-1;
+ }
+ for (i=0;i<128;i++) patchmap[i]=i;
+ }
+ else
+ loadFile(name);
+}
+
+MidiMapper::~MidiMapper()
+{
+ if (_filename) free(_filename);
+ deallocateMaps();
+}
+
+void MidiMapper::deallocateMaps(void)
+{
+ int i;
+ for (i=0;i<16;i++) channelKeymap[i]=NULL;
+ for (i=0;i<128;i++) patchKeymap[i]=NULL;
+ Keymap *km;
+ while (keymaps!=NULL)
+ {
+ km=keymaps->next;
+ delete keymaps;
+ keymaps=km;
+ }
+}
+
+void MidiMapper::getValue(char *s,char *v)
+{
+ char *c=s;
+ while ((*c!=0)&&(*c!='=')) c++;
+ if (*c==0) v[0]=0;
+ else
+ {
+ c++;
+ while (*c!=0)
+ {
+ *v=*c;
+ c++;v++;
+ }
+ *v=0;
+ }
+}
+
+void MidiMapper::removeSpaces(char *s)
+{
+ char *a=s;
+ while ((*a!=0)&&(*a==' ')) a++;
+ if (*a==0) {*s=0;return;};
+ while (*a!=0)
+ {
+ while ((*a!=0)&&(*a!=' ')&&(*a!=10)&&(*a!=13))
+ {
+ *s=*a;
+ s++;
+ a++;
+ }
+ while ((*a!=0)&&((*a==' ')||(*a==10)||(*a==13))) a++;
+ *s=' ';s++;
+ if (*a==0) {*s=0;return;};
+ }
+ *s=0;
+
+}
+
+int MidiMapper::countWords(char *s)
+{
+ int c=0;
+ while (*s!=0)
+ {
+ if (*s==' ') c++;
+ s++;
+ }
+ return c;
+}
+
+void MidiMapper::getWord(char *t,char *s,int w)
+{
+ int i=0;
+ *t=0;
+ while ((*s!=0)&&(i<w))
+ {
+ if (*s==' ') i++;
+ s++;
+ }
+ while ((*s!=0)&&(*s!=' ')&&(*s!=10)&&(*s!=13))
+ {
+ *t=*s;
+ t++;s++;
+ }
+ *t=0;
+}
+
+
+void MidiMapper::loadFile(const char *name)
+{
+ _ok=1;
+ FILE *fh = fopen(name,"rt");
+ if ( fh == NULL ) { _ok = -1; return; };
+ char s[101];
+ s[0] = 0;
+ if ( _filename != NULL ) free(_filename);
+ _filename = strdup(name);
+#ifdef MIDIMAPPERDEBUG
+ printf("Loading mapper ...\n");
+#endif
+ while (!feof(fh))
+ {
+ s[0]=0;
+ while ((!feof(fh))&&((s[0]==0)||(s[0]=='#'))) fgets(s,100,fh);
+ if (strncmp(s,"DEFINE",6)==0)
+ {
+ if (strncmp(&s[7],"PATCHMAP",8)==0) readPatchmap(fh);
+ else
+ if (strncmp(&s[7],"KEYMAP",6)==0) readKeymap(fh,s);
+ else
+ if (strncmp(&s[7],"CHANNELMAP",10)==0) readChannelmap(fh);
+ else
+ {
+ printf("ERROR: Unknown DEFINE line in map file\n");
+ _ok=0;
+ }
+ if (_ok==0)
+ {
+ printf("The midi map file will be ignored\n");
+ fclose(fh);
+ return;
+ }
+ }
+ else if (strncmp(s,"OPTIONS",7)==0) readOptions(fh);
+ }
+ fclose(fh);
+}
+
+MidiMapper::Keymap *MidiMapper::createKeymap(char *name,uchar use_same_note,uchar note)
+{
+ Keymap *km=new Keymap;
+ strncpy(km->name, name, KM_NAME_SIZE);
+ km->name[KM_NAME_SIZE - 1] = 0;
+
+ int i;
+ if (use_same_note==1)
+ {
+ for (i=0;i<128;i++)
+ km->key[i]=note;
+ }
+ else
+ {
+ for (i=0;i<128;i++)
+ km->key[i]=i;
+ }
+ addKeymap(km);
+ return km;
+}
+
+void MidiMapper::addKeymap(Keymap *newkm)
+{
+ Keymap *km=keymaps;
+ if (keymaps==NULL)
+ {
+ keymaps=newkm;
+ newkm->next=NULL;
+ return;
+ }
+ while (km->next!=NULL) km=km->next;
+ km->next=newkm;
+ newkm->next=NULL;
+ return;
+}
+
+MidiMapper::Keymap *MidiMapper::keymap(char *n)
+{
+ Keymap *km=keymaps;
+ while ((km!=NULL)&&(strcmp(km->name,n)!=0)) km=km->next;
+ return km;
+}
+
+void MidiMapper::readOptions(FILE *fh)
+{
+#ifdef MIDIMAPPERDEBUG
+ printf("Loading Options ... \n");
+#endif
+ char s[101];
+ char v[101];
+ char t[101];
+ int fin=0;
+ mapPitchBender=0;
+ while (!fin)
+ {
+ s[0]=0;
+ while ((s[0]==0)||(s[0]=='#')) fgets(s,100,fh);
+ if (strncmp(s,"PitchBenderRatio",16)==0)
+ {
+ getValue(s,v);
+ removeSpaces(v);
+ getWord(t,v,0);
+ mapPitchBender=1;
+ pitchBenderRatio=atoi(t);
+ }
+ else if (strncmp(s,"MapExpressionToVolumeEvents",27)==0) mapExpressionToVolumeEvents=1;
+ else if (strncmp(s,"END",3)==0)
+ {
+ fin=1;
+ }
+ else
+ {
+ printf("ERROR: Invalid option in OPTIONS section of map file : (%s)\n",s);
+ _ok=0;
+ return;
+ }
+ }
+}
+
+void MidiMapper::readPatchmap(FILE *fh)
+{
+ char s[101];
+ char v[101];
+ char t[101];
+ char name[256]; /* Longer than t and 'AllKeysTo' */
+ int i=0;
+ int j,w;
+#ifdef MIDIMAPPERDEBUG
+ printf("Loading Patch map ... \n");
+#endif
+ while (i<128)
+ {
+ s[0]=0;
+ while ((s[0]==0)||(s[0]=='#')) fgets(s,100,fh);
+ getValue(s,v);
+ removeSpaces(v);
+ w=countWords(v);
+ j=0;
+ patchKeymap[i]=NULL;
+ patchmap[i]=i;
+ while (j<w)
+ {
+ getWord(t,v,j);
+ if (strcmp(t,"AllKeysTo")==0)
+ {
+ j++;
+ if (j>=w)
+ {
+ printf("ERROR: Invalid option in PATCHMAP section of map file\n");
+ _ok=0;
+ return;
+ }
+ getWord(t,v,j);
+ sprintf(name,"AllKeysTo%s",t);
+ patchKeymap[i]=createKeymap(name,1,atoi(t));
+ }
+ else
+ {
+ patchmap[i]=atoi(t);
+ }
+ j++;
+ }
+ i++;
+ }
+ s[0]=0;
+ while ((s[0]==0)||(s[0]=='#')||(s[0]==10)||(s[0]==13)) fgets(s,100,fh);
+ if (strncmp(s,"END",3)!=0)
+ {
+ printf("ERROR: End of section not found in map file\n");
+ _ok=0;
+ return;
+ }
+}
+
+void MidiMapper::readKeymap(FILE *fh,char *first_line)
+{
+ char s[101];
+ char v[101];
+#ifdef MIDIMAPPERDEBUG
+ printf("Loading Key map ... %s",first_line);
+#endif
+ removeSpaces(first_line);
+ getWord(v,first_line,2);
+ Keymap *km=new Keymap;
+ strncpy(km->name, v, KM_NAME_SIZE);
+ km->name[KM_NAME_SIZE - 1] = 0;
+
+ int i=0;
+ while (i<128)
+ {
+ s[0]=0;
+ while ((s[0]==0)||(s[0]=='#')) fgets(s,100,fh);
+ getValue(s,v);
+ removeSpaces(v);
+ km->key[i]=atoi(v);
+ i++;
+ }
+ s[0]=0;
+ while ((s[0]==0)||(s[0]=='#')||(s[0]==10)||(s[0]==13)) fgets(s,100,fh);
+ if (strncmp(s,"END",3)!=0)
+ {
+ printf("ERROR: End of section not found in map file\n");
+ _ok=0;
+ return;
+ }
+ addKeymap(km);
+}
+
+void MidiMapper::readChannelmap(FILE *fh)
+{
+ char s[101];
+ char v[101];
+ char t[101];
+ int i=0;
+ int w,j;
+#ifdef MIDIMAPPERDEBUG
+ printf("Loading Channel map ... \n");
+#endif
+ while (i<16)
+ {
+ s[0]=0;
+ while ((s[0]==0)||(s[0]=='#')) fgets(s,100,fh);
+ getValue(s,v);
+ removeSpaces(v);
+ w=countWords(v);
+ j=0;
+ channelKeymap[i]=NULL;
+ channelPatchForced[i]=-1;
+ channelmap[i]=i;
+ while (j<w)
+ {
+ getWord(t,v,j);
+ if (strcmp(t,"Keymap")==0)
+ {
+ j++;
+ if (j>=w)
+ {
+ printf("ERROR: Invalid option in CHANNELMAP section of map file\n");
+ _ok=0;
+ return;
+ }
+ getWord(t,v,j);
+ channelKeymap[i]=keymap(t);
+ }
+ else if (strcmp(t,"ForcePatch")==0)
+ {
+ j++;
+ if (j>=w)
+ {
+ printf("ERROR: Invalid option in CHANNELMAP section of map file\n");
+ _ok=0;
+ return;
+ }
+ getWord(t,v,j);
+ channelPatchForced[i]=atoi(t);
+ }
+ else
+ {
+ channelmap[i]=atoi(t);
+ }
+ j++;
+ }
+ i++;
+ }
+ s[0]=0;
+ while ((s[0]==0)||(s[0]=='#')||(s[0]==10)||(s[0]==13)) fgets(s,100,fh);
+ if (strncmp(s,"END",3)!=0)
+ {
+ printf("END of section not found in map file\n");
+ _ok=0;
+ return;
+ }
+
+}
+
+const char *MidiMapper::filename(void)
+{
+ return (_filename)? _filename : "";
+}
+
+uchar MidiMapper::key(uchar chn,uchar pgm, uchar note)
+{
+ uchar notemapped=note;
+ if (patchKeymap[pgm]!=NULL) notemapped=patchKeymap[pgm]->key[note];
+ if (channelKeymap[chn]!=NULL) notemapped=channelKeymap[chn]->key[note];
+ return notemapped;
+}
+
+uchar MidiMapper::patch(uchar chn,uchar pgm)
+{
+ return (channelPatchForced[chn] == -1) ?
+ patchmap[pgm] : (uchar)channelPatchForced[chn] ;
+}
+
+void MidiMapper::pitchBender(uchar ,uchar &lsb,uchar &msb)
+{
+ if (mapPitchBender)
+ {
+ short pbs=((short)msb<<7) | (lsb & 0x7F);
+ pbs=pbs-0x2000;
+ short pbs2=(((long)pbs*pitchBenderRatio)/4096);
+#ifdef MIDIMAPPERDEBUG
+ printf("Pitch Bender (%d): %d -> %d \n",chn,pbs,pbs2);
+#endif
+ pbs2=pbs2+0x2000;
+ lsb=pbs2 & 0x7F;
+ msb=(pbs2 >> 7)&0x7F;
+ }
+}
+
+void MidiMapper::controller(uchar ,uchar &ctl, uchar &)
+{
+ if ((mapExpressionToVolumeEvents)&&(ctl==11)) ctl=7;
+}
diff --git a/libkmid/midimapper.h b/libkmid/midimapper.h
new file mode 100644
index 000000000..702c104ca
--- /dev/null
+++ b/libkmid/midimapper.h
@@ -0,0 +1,210 @@
+/* midimapper.h - The midi mapper object
+ This file is part of LibKMid 0.9.5
+ Copyright (C) 1997,98,99,2000 Antonio Larrosa Jimenez
+ LibKMid's homepage : http://www.arrakis.es/~rlarrosa/libkmid.html
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ Send comments and bug fixes to Antonio Larrosa <larrosa@kde.org>
+
+***************************************************************************/
+#ifndef _MIDIMAPPER_H
+#define _MIDIMAPPER_H
+
+#include <stdio.h>
+#include <libkmid/dattypes.h>
+#include <kdelibs_export.h>
+
+#define KM_NAME_SIZE 30
+
+/**
+ * A Midi Mapper class which defines the way MIDI events are translated
+ * (or "mapped") to different ones. This way, when two MIDI devices "talk"
+ * in a somehow different way, they can still communicate.
+ *
+ * When the user has an external keyboard that is not compatible with the
+ * General Midi standard, he can use a MIDI mapper file to play files
+ * as if the synthesizer was GM compatible.
+ *
+ * Please see the KMid documentation
+ * ( http://www.arrakis.es/~rlarrosa/kmid.html ) for information on the
+ * format of a MIDI mapper definition file, and how they work.
+ *
+ * I created this class because I had one of those non-GM keyboards,
+ * so it can do everything I needed it to do for my keyboard to work
+ * exactly as a GM synth, and a few more things. Currently, it's the most
+ * featured MIDI mapper available.
+ *
+ * The usage of this class is quite simple, just create an object with
+ * a correct filename in the constructor and then use this object as
+ * parameter for DeviceManager::setMidiMap().
+ *
+ * @short Midi Mapper
+ * @version 0.9.5 17/01/2000
+ * @author Antonio Larrosa Jimenez <larrosa@kde.org>
+ */
+class KMID_EXPORT MidiMapper
+{
+ private:
+ class MidiMapperPrivate;
+ MidiMapperPrivate *d;
+
+ /**
+ * @internal
+ * Internal definition for Keymaps
+ */
+ struct Keymap
+ {
+ char name[KM_NAME_SIZE];
+ uchar key[128];
+ struct Keymap *next;
+ };
+
+ int _ok;
+
+ uchar channelmap[16];
+ /**
+ * @internal
+ * It's a pointer to the Keymap to use for a channel
+ * This variable is used to get faster a given Keymap
+ * The index is the real channel (after mapping it)
+ */
+ Keymap *channelKeymap[16];
+
+ /**
+ * @internal
+ * It's -1 if the channel doesn't have a forced patch,
+ * else indicates the patch to force in the channel.
+ */
+ int channelPatchForced[16];
+
+ uchar patchmap[128];
+
+ /**
+ * @internal
+ * Same as channelKeymap
+ */
+ Keymap *patchKeymap[128];
+
+ /**
+ * @internal
+ * Real linked list of keymaps used around the class.
+ */
+ Keymap *keymaps;
+
+ /**
+ * @internal
+ * Stores the name of the file from which the map was loaded
+ */
+ char *_filename;
+
+ /**
+ * @internal
+ * Simulate expression events with volume events
+ */
+ int mapExpressionToVolumeEvents;
+
+ /**
+ * @internal
+ * Map or not the Pitch Bender using pitchBenderRatio()
+ */
+ int mapPitchBender;
+
+ /**
+ * @internal
+ * Indicates the ratio between the standard and the synthesizer's pitch
+ * bender engine. The number sent to the synth is multiplied by this
+ * and dividied by 4096. Thus if PitchBenderRatio is 4096, the synth's
+ * pitch bender works as the standard one
+ */
+ int pitchBenderRatio;
+
+ void getValue(char *s,char *v);
+ void removeSpaces(char *s);
+ int countWords(char *s);
+ void getWord(char *t,char *s,int w);
+ // get from s the word in position w and store it in t
+
+ void deallocateMaps(void);
+ Keymap *createKeymap(char *name,uchar use_same_note=0,uchar note=0);
+ void readPatchmap(FILE *fh);
+ void readKeymap(FILE *fh,char *first_line);
+ void readChannelmap(FILE *fh);
+ void readOptions(FILE *fh);
+
+ void addKeymap(Keymap *newkm);
+ Keymap *keymap(char *n);
+
+ public:
+ /**
+ * Constructor. Loads a MIDI Mapper definition from a file.
+ * @see filename()
+ */
+ MidiMapper(const char *name);
+
+ /**
+ * Destructor.
+ */
+ ~MidiMapper();
+
+ /**
+ * Loads a MIDI Mapper definition file (you don't need to use this if you
+ * used a correct filename in constructor).
+ */
+ void loadFile(const char *name);
+
+ /**
+ * Returns the status of the object.
+ */
+ int ok(void) { return _ok; }
+
+ /**
+ * Returns the channel which chn should be mapped to.
+ */
+ uchar channel(uchar chn) { return channelmap[chn];}
+
+ /**
+ * Returns the patch which pgm used on channel chn should be mapped to.
+ */
+ uchar patch(uchar chn,uchar pgm);
+
+ /**
+ * Returns the key that key note playing a pgm patch on channel chn should
+ * be mapped to.
+ */
+ uchar key(uchar chn,uchar pgm, uchar note);
+
+ /**
+ * Returns the value which the pitch bender on channel chn should be
+ * mapped to.
+ */
+ void pitchBender(uchar chn,uchar &lsb,uchar &msb);
+
+ /**
+ * Returns the value which a given controller and its value should
+ * be mapped to when played on channel chn.
+ */
+ void controller(uchar chn,uchar &ctl,uchar &v);
+
+ /**
+ * Returns the path and name of the file which the object loaded the
+ * mapper from.
+ */
+ const char *filename(void);
+
+};
+
+#endif
diff --git a/libkmid/midiout.cc b/libkmid/midiout.cc
new file mode 100644
index 000000000..e4a03a405
--- /dev/null
+++ b/libkmid/midiout.cc
@@ -0,0 +1,301 @@
+/**************************************************************************
+
+ midiout.cc - class midiOut which handles external midi devices
+ This file is part of LibKMid 0.9.5
+ Copyright (C) 1997,98,99,2000 Antonio Larrosa Jimenez
+ LibKMid's homepage : http://www.arrakis.es/~rlarrosa/libkmid.html
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ Send comments and bug fixes to Antonio Larrosa <larrosa@kde.org>
+
+***************************************************************************/
+#include "midiout.h"
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include "sndcard.h"
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/param.h>
+#include "midispec.h"
+#include "alsaout.h"
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/ioctl.h>
+
+SEQ_USE_EXTBUF();
+
+MidiOut::MidiOut(int d)
+{
+ seqfd = -1;
+ devicetype=KMID_EXTERNAL_MIDI;
+ device= d;
+ volumepercentage=100;
+ map=new MidiMapper(NULL);
+ if (map==NULL) { printfdebug("ERROR : midiOut : Map is NULL\n"); return; };
+ _ok=1;
+}
+
+MidiOut::~MidiOut()
+{
+ delete map;
+ closeDev();
+}
+
+void MidiOut::openDev (int sqfd)
+{
+#ifdef HAVE_OSS_SUPPORT
+ _ok=1;
+ seqfd=sqfd;
+ if (seqfd==-1)
+ {
+ printfdebug("ERROR: Could not open /dev/sequencer\n");
+ _ok=0;
+ return;
+ }
+#endif
+}
+
+void MidiOut::closeDev (void)
+{
+ if (!ok()) return;
+// if (deviceType()!=KMID_ALSA) allNotesOff();
+ SEQ_STOP_TIMER();
+ SEQ_DUMPBUF();
+ seqfd=-1;
+}
+
+void MidiOut::initDev (void)
+{
+#ifdef HAVE_OSS_SUPPORT
+ int chn;
+ if (!ok()) return;
+ uchar gm_reset[5]={0x7e, 0x7f, 0x09, 0x01, 0xf7};
+ sysex(gm_reset, sizeof(gm_reset));
+ for (chn=0;chn<16;chn++)
+ {
+ chnmute[chn]=0;
+ chnPatchChange(chn,0);
+ chnPressure(chn,127);
+ chnPitchBender(chn, 0x00, 0x40);
+ chnController(chn, CTL_MAIN_VOLUME,110*volumepercentage);
+ chnController(chn, CTL_EXT_EFF_DEPTH, 0);
+ chnController(chn, CTL_CHORUS_DEPTH, 0);
+ chnController(chn, 0x4a, 127);
+ }
+#endif
+}
+
+void MidiOut::setMidiMapper(MidiMapper *_map)
+{
+ delete map;
+ map=_map;
+}
+
+void MidiOut::noteOn (uchar chn, uchar note, uchar vel)
+{
+ if (vel==0)
+ {
+ noteOff(chn,note,vel);
+ }
+ else
+ {
+ SEQ_MIDIOUT(device, MIDI_NOTEON + map->channel(chn));
+ SEQ_MIDIOUT(device, map->key(chn,chnpatch[chn],note));
+ SEQ_MIDIOUT(device, vel);
+ }
+#ifdef MIDIOUTDEBUG
+ printfdebug("Note ON >\t chn : %d\tnote : %d\tvel: %d\n",chn,note,vel);
+#endif
+}
+
+void MidiOut::noteOff (uchar chn, uchar note, uchar vel)
+{
+ SEQ_MIDIOUT(device, MIDI_NOTEOFF + map->channel(chn));
+ SEQ_MIDIOUT(device, map->key(chn,chnpatch[chn],note));
+ SEQ_MIDIOUT(device, vel);
+#ifdef MIDIOUTDEBUG
+ printfdebug("Note OFF >\t chn : %d\tnote : %d\tvel: %d\n",chn,note,vel);
+#endif
+}
+
+void MidiOut::keyPressure (uchar chn, uchar note, uchar vel)
+{
+ SEQ_MIDIOUT(device, MIDI_KEY_PRESSURE + map->channel(chn));
+ SEQ_MIDIOUT(device, map->key(chn,chnpatch[chn],note));
+ SEQ_MIDIOUT(device, vel);
+}
+
+void MidiOut::chnPatchChange (uchar chn, uchar patch)
+{
+#ifdef MIDIOUTDEBUG
+ printfdebug("PATCHCHANGE [%d->%d] %d -> %d\n",
+ chn,map->channel(chn),patch,map->patch(chn,patch));
+#endif
+ SEQ_MIDIOUT(device, MIDI_PGM_CHANGE + map->channel(chn));
+ SEQ_MIDIOUT(device, map->patch(chn,patch));
+ chnpatch[chn]=patch;
+}
+
+void MidiOut::chnPressure (uchar chn, uchar vel)
+{
+ SEQ_MIDIOUT(device, MIDI_CHN_PRESSURE + map->channel(chn));
+ SEQ_MIDIOUT(device, vel);
+
+ chnpressure[chn]=vel;
+}
+
+void MidiOut::chnPitchBender(uchar chn,uchar lsb, uchar msb)
+{
+ SEQ_MIDIOUT(device, MIDI_PITCH_BEND + map->channel(chn));
+ /*
+#ifdef AT_HOME
+ short pbs=((short)msb<<7) | (lsb & 0x7F);
+ pbs=pbs-0x2000;
+ short pbs2=(((long)pbs*672)/4096);
+ printfdebug("Pitch Bender (%d): %d -> %d \n",chn,pbs,pbs2);
+ pbs2=pbs2+0x2000;
+ lsb=pbs2 & 0x7F;
+ msb=(pbs2 >> 7)&0x7F;
+#endif
+ */
+ map->pitchBender(chn,lsb,msb);
+ SEQ_MIDIOUT(device, lsb);
+ SEQ_MIDIOUT(device, msb);
+ chnbender[chn]=(msb << 8) | (lsb & 0xFF);
+}
+
+void MidiOut::chnController (uchar chn, uchar ctl, uchar v)
+{
+ SEQ_MIDIOUT(device, MIDI_CTL_CHANGE + map->channel(chn));
+#ifdef AT_HOME
+ if (ctl==11) ctl=7;
+#endif
+ map->controller(chn,ctl,v);
+ if ((ctl==11)||(ctl==7))
+ {
+ v=(v*volumepercentage)/100;
+ if (v>127) v=127;
+ }
+
+ SEQ_MIDIOUT(device, ctl);
+ SEQ_MIDIOUT(device, v);
+
+ chncontroller[chn][ctl]=v;
+}
+
+void MidiOut::sysex(uchar *data, ulong size)
+{
+ ulong i=0;
+ SEQ_MIDIOUT(device, MIDI_SYSTEM_PREFIX);
+ while (i<size)
+ {
+ SEQ_MIDIOUT(device, *data);
+ data++;
+ i++;
+ }
+#ifdef MIDIOUTDEBUG
+ printfdebug("sysex\n");
+#endif
+}
+
+void MidiOut::allNotesOff (void)
+{
+ for (int i=0; i<16; i++)
+ {
+ chnController(i, 0x78, 0);
+ chnController(i, 0x79, 0);
+ };
+ sync(1);
+}
+
+void MidiOut::channelSilence (uchar chn)
+{
+ uchar i;
+ for ( i=0; i<127; i++)
+ {
+ noteOff(chn,i,0);
+ };
+ sync();
+}
+
+void MidiOut::channelMute(uchar chn, int a)
+{
+ if (a==1)
+ {
+ chnmute[chn]=a;
+ channelSilence(chn);
+ }
+ else if (a==0)
+ {
+ chnmute[chn]=a;
+ }
+ /* else ignore the call to this function */
+}
+
+void MidiOut::seqbuf_dump (void)
+{
+#ifdef HAVE_OSS_SUPPORT
+ if (_seqbufptr && seqfd!=-1 && seqfd!=0)
+ if (write (seqfd, _seqbuf, _seqbufptr) == -1)
+ {
+ printfdebug("Error writing to /dev/sequencer in MidiOut::seq_buf_dump\n");
+ perror ("write /dev/sequencer in seqBufDump\n");
+ exit (-1);
+ }
+ _seqbufptr = 0;
+#endif
+}
+
+void MidiOut::seqbuf_clean(void)
+{
+#ifdef HAVE_OSS_SUPPORT
+ _seqbufptr=0;
+#endif
+}
+
+const char *MidiOut::midiMapFilename(void)
+{
+ return (map!=NULL) ? map->filename() : "";
+}
+
+const char * MidiOut::deviceName(void) const
+{
+ switch (deviceType())
+ {
+ case (KMID_EXTERNAL_MIDI) : return "External Midi";
+ case (KMID_SYNTH) : return "Synth";
+ case (KMID_FM) : return "FM";
+ case (KMID_GUS) : return "GUS";
+ case (KMID_AWE) : return "AWE";
+ case (KMID_ALSA) : return reinterpret_cast<const AlsaOut *>(this)->deviceName();
+ }
+ return "Unknown";
+}
+
+void MidiOut::sync(int i)
+{
+ if (deviceType()==KMID_ALSA) { // XXX : sync should be virtual after next bic
+ reinterpret_cast<AlsaOut *>(this)->sync(i);
+ return;
+ }
+ SEQ_DUMPBUF();
+}
diff --git a/libkmid/midiout.h b/libkmid/midiout.h
new file mode 100644
index 000000000..93bbfff8c
--- /dev/null
+++ b/libkmid/midiout.h
@@ -0,0 +1,251 @@
+/* midiout.h - class midiOut which handles the /dev/sequencer device
+ This file is part of LibKMid 0.9.5
+ Copyright (C) 1997,98,99,2000 Antonio Larrosa Jimenez
+ LibKMid's homepage : http://www.arrakis.es/~rlarrosa/libkmid.html
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ Send comments and bug fixes to Antonio Larrosa <larrosa@kde.org>
+
+***************************************************************************/
+#ifndef _MIDIOUT_H
+#define _MIDIOUT_H
+
+#include <libkmid/dattypes.h>
+#include <libkmid/deviceman.h>
+#include <libkmid/midimapper.h>
+#include <stdio.h>
+
+/**
+ * External MIDI port output class . This class is used to send midi
+ * events to external midi devices.
+ *
+ * MidiOut is inherited by other MIDI devices classes
+ * (like SynthOut or FMOut) to support a common API.
+ *
+ * In general, you don't want to use MidiOut directly, but within a
+ * DeviceManager object, which is the preferred way to generate music.
+ *
+ * If you want to add support for other devices (I don't think
+ * there are any) you just have to create a class that inherits from MidiOut
+ * and create one object of your new class in
+ * DeviceManager::initManager().
+ *
+ * @short Sends MIDI events to external MIDI devices
+ * @version 0.9.5 17/01/2000
+ * @author Antonio Larrosa Jimenez <larrosa@kde.org>
+ */
+class MidiOut
+{
+ private:
+ class MidiOutPrivate;
+ MidiOutPrivate *d;
+
+ protected:
+
+ /**
+ * @internal
+ * This is the /dev/sequencer file handler.
+ * Remember _not_to_close_ it on MidiOut, but just on DeviceManager
+ */
+ int seqfd;
+
+ int device;
+
+ int devicetype;
+
+ int volumepercentage;
+
+ MidiMapper *map;
+
+ uchar chnpatch [16];
+ int chnbender [16];
+ uchar chnpressure [16];
+ uchar chncontroller [16][256];
+ int chnmute [16];
+
+ int _ok;
+
+ void seqbuf_dump (void);
+ void seqbuf_clean(void);
+
+ public:
+
+ /**
+ * Constructor. After constructing a MidiOut device, you must open it
+ * (using openDev() ). Additionally you may want to initialize it
+ * (with initDev() ),
+ */
+ MidiOut(int d=0);
+
+ /**
+ * Destructor. It doesn't matter if you close the device ( closeDev() )
+ * before you destruct the object because in other case, it will be closed
+ * here.
+ */
+ virtual ~MidiOut();
+
+ /**
+ * Opens the device. This is generally called from DeviceManager , so you
+ * shouldn't call this yourself (except if you created the MidiOut object
+ * yourself.
+ * @param sqfd a file descriptor of /dev/sequencer
+ * @see closeDev
+ * @see initDev
+ */
+ virtual void openDev (int sqfd);
+
+ /**
+ * Closes the device. It basically tells the device (the file descriptor)
+ * is going to be closed.
+ * @see openDev
+ */
+ virtual void closeDev ();
+
+ /**
+ * Initializes the device sending generic standard midi events and controllers,
+ * such as changing the patches of each channel to an Acoustic Piano (000),
+ * setting the volume to a normal value, etc.
+ */
+ virtual void initDev ();
+
+ /**
+ * @return the device type of the object. This is to identify the
+ * inherited class that a given object is polymorphed to.
+ * The returned value is one of these :
+ *
+ * @li KMID_EXTERNAL_MIDI if it's a MidiOut object
+ * @li KMID_SYNTH if it's a SynthOut object (as an AWE device)
+ * @li KMID_FM if it's a FMOut object
+ * @li KMID_GUS if it's a GUSOut object
+ *
+ * which are defined in midispec.h
+ *
+ * @see deviceName
+ */
+ int deviceType () const { return devicetype; }
+
+ /**
+ * Returns the name and type of this MIDI device.
+ * @see deviceType
+ */
+ const char * deviceName (void) const;
+
+ /**
+ * Sets a MidiMapper object to be used to modify the midi events before
+ * sending them.
+ *
+ * @param map the MidiMapper to use.
+ *
+ * @see MidiMapper
+ * @see midiMapFilename
+ */
+ void setMidiMapper ( MidiMapper *map );
+
+ /**
+ * See DeviceManager::noteOn()
+ */
+ virtual void noteOn ( uchar chn, uchar note, uchar vel );
+
+ /**
+ * See DeviceManager::noteOff()
+ */
+ virtual void noteOff ( uchar chn, uchar note, uchar vel );
+
+ /**
+ * See DeviceManager::keyPressure()
+ */
+ virtual void keyPressure ( uchar chn, uchar note, uchar vel );
+
+ /**
+ * See DeviceManager::chnPatchChange()
+ */
+ virtual void chnPatchChange ( uchar chn, uchar patch );
+
+ /**
+ * See DeviceManager::chnPressure()
+ */
+ virtual void chnPressure ( uchar chn, uchar vel );
+
+ /**
+ * See DeviceManager::chnPitchBender()
+ */
+ virtual void chnPitchBender ( uchar chn, uchar lsb, uchar msb );
+
+ /**
+ * See DeviceManager::chnController()
+ */
+ virtual void chnController ( uchar chn, uchar ctl , uchar v );
+
+ /**
+ * See DeviceManager::sysex()
+ */
+ virtual void sysex ( uchar *data,ulong size);
+
+ /**
+ * Send a All Notes Off event to every channel
+ */
+ void allNotesOff(void);
+
+ /**
+ * Mutes all notes being played on a given channel.
+ * @param chn the channel
+ */
+ virtual void channelSilence ( uchar chn );
+
+ /**
+ * Mute or "unmute" a given channel .
+ * @param chn channel to work on
+ * @param b if true, the device will ignore subsequent notes played on the chn
+ * channel, and mute all notes being played on it. If b is false, the channel
+ * is back to work.
+ */
+ virtual void channelMute ( uchar chn, int b );
+
+ /**
+ * Change all channel volume events multiplying it by this percentage correction
+ * Instead of forcing a channel to a fixed volume, this method allows to
+ * music to fade out even when it was being played softly.
+ * @param volper is an integer value, where 0 is quiet, 100 is used to send
+ * an unmodified value, 200 play music twice louder than it should, etc.
+ */
+ virtual void setVolumePercentage ( int volper )
+ { volumepercentage = volper; }
+
+ /**
+ * Returns true if everything's ok and false if there has been any problem
+ */
+ int ok (void)
+ { if (seqfd<0) return 0;
+ return (_ok>0);
+ }
+
+ /**
+ * Returns the path to the file where the current used MidiMapper object
+ * reads the configuration from, or an empty string if there's no MidiMapper.
+ */
+ const char *midiMapFilename ();
+
+ /**
+ * Sends the buffer to the device and returns when it's played, so you can
+ * synchronize
+ * XXX: sync should be virtual after next bic release
+ */
+ void sync(int i=0);
+
+};
+
+#endif
diff --git a/libkmid/midispec.h b/libkmid/midispec.h
new file mode 100644
index 000000000..3f60d49f2
--- /dev/null
+++ b/libkmid/midispec.h
@@ -0,0 +1,60 @@
+/* midispec.h - Some definitions to make the code more readable
+ This file is part of LibKMid 0.9.5
+ Copyright (C) 1997,98,99,2000 Antonio Larrosa Jimenez
+ LibKMid's homepage : http://www.arrakis.es/~rlarrosa/libkmid.html
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ Send comments and bug fixes to Antonio Larrosa <larrosa@kde.org>
+
+***************************************************************************/
+
+#ifndef _MIDISPEC_H
+#define _MIDISPEC_H
+
+#define META_EVENT 0xFF
+
+#define ME_TRACK_SEQ_NUMBER 0x00
+#define ME_TEXT 0x01
+#define ME_COPYRIGHT 0x02
+#define ME_SEQ_OR_TRACK_NAME 0x03
+#define ME_TRACK_INSTR_NAME 0x04
+#define ME_LYRIC 0x05
+#define ME_MARKER 0x06
+#define ME_CUE_POINT 0x07
+#define ME_CHANNEL_PREFIX 0x20
+#define ME_MIDI_PORT 0x21
+#define ME_SET_TEMPO 0x51
+#define ME_SMPTE_OFFSET 0x54
+#define ME_TIME_SIGNATURE 0x58
+#define ME_KEY_SIGNATURE 0x59
+/* sf=sharps/flats (-7=7 flats, 0=key of C, 7=7 sharps)
+ mi=major/minor (0=major, 1=minor)
+*/
+
+#define ME_END_OF_TRACK 0x2F
+
+
+#define PERCUSSION_CHANNEL 9
+
+#define KMID_EXTERNAL_MIDI 1
+#define KMID_SYNTH 2
+#define KMID_FM 3
+#define KMID_GUS 4
+#define KMID_AWE 5 //For future class aweOut
+#define KMID_ALSA 6
+
+#endif
diff --git a/libkmid/midistat.cc b/libkmid/midistat.cc
new file mode 100644
index 000000000..29d1436c8
--- /dev/null
+++ b/libkmid/midistat.cc
@@ -0,0 +1,115 @@
+/**************************************************************************
+
+ midistat.cc - class MidiStatus, change it internally and then send it.
+ This file is part of LibKMid 0.9.5
+ Copyright (C) 1997,98,99,2000 Antonio Larrosa Jimenez
+ LibKMid's homepage : http://www.arrakis.es/~rlarrosa/libkmid.html
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ Send comments and bug fixes to Antonio Larrosa <larrosa@kde.org>
+
+***************************************************************************/
+#include "midistat.h"
+#include "deviceman.h"
+#include "sndcard.h"
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+extern int MT32toGM[128];
+
+MidiStatus::MidiStatus()
+{
+ int i;
+ tempo=1000000;
+ for (int chn=0;chn<16;chn++)
+ {
+ chn_patch[chn]=0;
+ chn_bender[chn]=0x4000;
+ chn_pressure[chn]=127;
+ for (i=0;i<256;i++)
+ chn_controller[chn][i]=0;
+ chn_controller[chn][CTL_MAIN_VOLUME]=127;
+ chn_controller[chn][11]=127;
+ chn_controller[chn][0x4a]=127;
+ chn_lastisvolumeev[chn]=1;
+ }
+}
+
+MidiStatus::~MidiStatus()
+{
+}
+
+// void noteOn ( uchar chn, uchar note, uchar vel );
+// void noteOff ( uchar chn, uchar note, uchar vel );
+
+void MidiStatus::chnPatchChange ( uchar chn, uchar patch )
+{
+ chn_patch[chn]=patch;
+}
+
+void MidiStatus::chnPressure ( uchar chn, uchar vel )
+{
+ chn_pressure[chn]=vel;
+}
+
+void MidiStatus::chnPitchBender ( uchar chn, uchar lsb, uchar msb )
+{
+ chn_bender[chn]=((int)msb<<8|lsb);
+}
+
+void MidiStatus::chnController ( uchar chn, uchar ctl , uchar v )
+{
+ if (ctl==7) chn_lastisvolumeev[chn]=1;
+ else if (ctl==11) chn_lastisvolumeev[chn]=0;
+
+ chn_controller[chn][ctl]=v;
+}
+
+void MidiStatus::tmrSetTempo(int v)
+{
+ tempo=v;
+}
+
+void MidiStatus::sendData(DeviceManager *midi,int gm)
+{
+ for (int chn=0;chn<16;chn++)
+ {
+#ifdef MIDISTATDEBUG
+ printf("Restoring channel %d\n",chn);
+#endif
+ midi->chnPatchChange(chn,
+ (gm==1)?(chn_patch[chn]):(MT32toGM[chn_patch[chn]]));
+ midi->chnPitchBender(chn,chn_bender[chn]&0xFF,chn_bender[chn]>>8);
+ midi->chnPressure(chn,chn_pressure[chn]);
+ if (chn_lastisvolumeev[chn])
+ {
+ midi->chnController(chn,11,chn_controller[chn][11]);
+ midi->chnController(chn,CTL_MAIN_VOLUME,chn_controller[chn][CTL_MAIN_VOLUME]);
+ } else {
+ midi->chnController(chn,CTL_MAIN_VOLUME,chn_controller[chn][CTL_MAIN_VOLUME]);
+ midi->chnController(chn,11,chn_controller[chn][11]);
+ }
+ /*
+ for (int i=0;i<256;i++)
+ midi->chnController(chn,i,chn_controller[chn][i]);
+ */
+ }
+ midi->tmrSetTempo(tempo);
+ midi->sync();
+}
diff --git a/libkmid/midistat.h b/libkmid/midistat.h
new file mode 100644
index 000000000..17dda1269
--- /dev/null
+++ b/libkmid/midistat.h
@@ -0,0 +1,143 @@
+/* midistat.h - class midiStat, change it internally and then send it.
+ This file is part of LibKMid 0.9.5
+ Copyright (C) 1997,98,99,2000 Antonio Larrosa Jimenez
+ LibKMid's homepage : http://www.arrakis.es/~rlarrosa/libkmid.html
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ Send comments and bug fixes to Antonio Larrosa <larrosa@kde.org>
+
+ ***************************************************************************/
+#ifndef _MIDISTAT_H
+#define _MIDISTAT_H
+
+#include <libkmid/dattypes.h>
+
+/**
+ * Stores the status of a MIDI device . That is, current patch in each channel,
+ * controller settings, pitch bender value, etc.
+ *
+ * This is used to "play" with all those values and then send them to the
+ * MIDI device just by using sendData()
+ *
+ * @short Stores the MIDI status.
+ * @version 0.9.5 17/01/2000
+ * @author Antonio Larrosa Jimenez <larrosa@kde.org>
+ */
+class MidiStatus
+{
+ private:
+ class MidiStatusPrivate;
+ MidiStatusPrivate *d;
+
+ ulong tempo;
+
+ unsigned char chn_patch [16];
+ int chn_bender [16];
+ unsigned char chn_pressure[16];
+ unsigned char chn_controller[16][256];
+
+ int chn_lastisvolumeev[16];
+
+ public:
+ /**
+ * Constructor.
+ */
+ MidiStatus();
+
+ /**
+ * Destructor.
+ */
+ ~MidiStatus();
+
+
+ // void noteOn ( uchar chn, uchar note, uchar vel );
+ // void noteOff ( uchar chn, uchar note, uchar vel );
+
+ /**
+ * Stores a new value for the key aftertouch.
+ * @see MidiOut::keyPressure()
+ */
+ void keyPressure ( uchar chn, uchar note, uchar vel );
+
+ /**
+ * Stores a new patch in channel @p chn.
+ * @see chnPatch()
+ * @see MidiOut::chnPatchChange()
+ */
+ void chnPatchChange ( uchar chn, uchar patch );
+
+ /**
+ * Returns the patch currently used in channel @p chn.
+ */
+ uchar chnPatch ( uchar chn ) { return chn_patch[chn]; }
+
+ /**
+ * Stores a new channel pressure value in channel @p chn.
+ * @see MidiOut::chnPressure()
+ */
+ void chnPressure ( uchar chn, uchar vel );
+
+ /**
+ * Returns the pressure value currently used in channel @p chn.
+ */
+ uchar chnPressure ( uchar chn ) { return chn_pressure[chn]; }
+
+ /**
+ * Stores a new pitch bender value in channel chn
+ */
+ void chnPitchBender ( uchar chn, uchar lsb, uchar msb );
+
+ /**
+ * Returns the pitch bender value used in channel @p chn
+ */
+ int chnPitchBender ( uchar chn) { return chn_bender[chn]; }
+
+ /**
+ * Stores a new value for controller @p ctl in channel @p chn.
+ */
+ void chnController ( uchar chn, uchar ctl , uchar v );
+
+ /**
+ * Returns the value used for controller @p ctl in channel @p chn
+ */
+ uchar chnController ( uchar chn, uchar ctl )
+ { return chn_controller[chn][ctl]; }
+
+ /**
+ * Stores a sysex message that will be send in the next call to sendData
+ */
+ void sysex ( uchar *data, ulong size);
+
+ /**
+ * Sets the tempo.
+ *
+ * @see DeviceManager::tmrSetTempo()
+ */
+ void tmrSetTempo ( int v );
+
+
+ /**
+ * Sends the current MIDI state to the DeviceManager object used as
+ * parameter (you should have already set the default device to the one you
+ * want to use). The @p gm parameter specifies if the patches used follow
+ * the GM standard (1), or follow the MT32 standard (0), in which case, they
+ * will be converted to GM before being sent.
+ */
+ void sendData ( class DeviceManager *midi, int gm=1 );
+};
+
+#endif
diff --git a/libkmid/mt32togm.cc b/libkmid/mt32togm.cc
new file mode 100644
index 000000000..a59eb959c
--- /dev/null
+++ b/libkmid/mt32togm.cc
@@ -0,0 +1,18 @@
+#include "mt32togm.h"
+
+int MT32toGM[128] =
+{
+ 0, 1, 2, 4, 4, 5, 5, 3, 16, 16,
+ 16, 16, 19, 19, 19, 21, 6, 6, 6, 7,
+ 7, 7, 8, 8, 62, 57, 63, 58, 38, 38,
+ 39, 39, 88, 33, 52, 35, 97, 100, 38, 39,
+ 14, 102, 68, 103, 44, 92, 46, 80, 48, 49,
+ 51, 45, 40, 40, 42, 42, 43, 46, 46, 24,
+ 25, 28, 27, 104, 32, 32, 34, 33, 36, 37,
+ 39, 35, 79, 73, 76, 72, 74, 75, 64, 65,
+ 66, 67, 71, 71, 69, 70, 60, 22, 56, 59,
+ 57, 63, 60, 60, 58, 61, 61, 11, 11, 99,
+ 100, 9, 14, 13, 12, 107, 106, 77, 78, 78,
+ 76, 111, 47, 117, 127, 115, 118, 116, 118, 126,
+ 121, 121, 55, 124, 120, 125, 126, 127
+};
diff --git a/libkmid/mt32togm.h b/libkmid/mt32togm.h
new file mode 100644
index 000000000..1da38a2f0
--- /dev/null
+++ b/libkmid/mt32togm.h
@@ -0,0 +1,31 @@
+/* mt32togm.h - the translation map from MT32 patches to GM patches
+ This file is part of LibKMid 0.9.5
+ Copyright (C) 1997,98,99,2000 Antonio Larrosa Jimenez
+ LibKMid's homepage : http://www.arrakis.es/~rlarrosa/libkmid.html
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ Send comments and bug fixes to Antonio Larrosa <larrosa@kde.org>
+
+***************************************************************************/
+#ifndef _MT32TOGM_H
+#define _MT32TOGM_H
+
+#include <kdelibs_export.h>
+
+extern int KMID_EXPORT MT32toGM[128];
+
+#endif
diff --git a/libkmid/notearray.cc b/libkmid/notearray.cc
new file mode 100644
index 000000000..70652f16e
--- /dev/null
+++ b/libkmid/notearray.cc
@@ -0,0 +1,122 @@
+/**************************************************************************
+
+ notearray.cc - NoteArray class, which holds an array of notes
+ This file is part of LibKMid 0.9.5
+ Copyright (C) 1998,99,2000 Antonio Larrosa Jimenez
+ LibKMid's homepage : http://www.arrakis.es/~rlarrosa/libkmid.html
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ Send comments and bug fixes to Antonio Larrosa <larrosa@kde.org>
+
+***************************************************************************/
+
+#include "notearray.h"
+#include <string.h>
+
+NoteArray::NoteArray(void)
+{
+ totalAllocated=50;
+ data=new noteCmd[totalAllocated];
+ lastAdded=0L;
+}
+
+NoteArray::~NoteArray()
+{
+ delete data;
+ totalAllocated=0;
+}
+
+NoteArray::noteCmd *NoteArray::pointerTo(ulong pos)
+{
+ if (pos<totalAllocated) return &data[pos];
+ while (pos>=totalAllocated)
+ {
+ noteCmd *tmp=new noteCmd[totalAllocated*2];
+ memcpy(tmp,data,sizeof(noteCmd)*totalAllocated);
+ delete data;
+ data=tmp;
+ totalAllocated*=2;
+ }
+ return &data[pos];
+}
+
+void NoteArray::at(ulong pos, ulong ms,int chn,int cmd,int note)
+{
+ noteCmd *tmp=pointerTo(pos);
+ tmp->ms=ms;
+ tmp->chn=chn;
+ tmp->cmd=cmd;
+ tmp->note=note;
+}
+
+void NoteArray::at(ulong pos, noteCmd s)
+{
+ noteCmd *tmp=pointerTo(pos);
+ tmp->ms=s.ms;
+ tmp->chn=s.chn;
+ tmp->cmd=s.cmd;
+ tmp->note=s.note;
+}
+
+NoteArray::noteCmd NoteArray::at(int pos)
+{
+ return *pointerTo(pos);
+}
+
+void NoteArray::add(ulong ms,int chn,int cmd,int note)
+{
+ if (lastAdded==NULL)
+ {
+ lastAdded=data;
+ last=0;
+ }
+ else
+ {
+ last++;
+ if (last==totalAllocated) lastAdded=pointerTo(totalAllocated);
+ else lastAdded++;
+ }
+ lastAdded->ms=ms;
+ lastAdded->chn=chn;
+ lastAdded->cmd=cmd;
+ lastAdded->note=note;
+}
+
+void NoteArray::next(void)
+{
+ if (it==lastAdded) {it=NULL;return;};
+ it++;
+}
+
+void NoteArray::moveIteratorTo(ulong ms,int *pgm)
+{
+ noteCmd *ncmd;
+ iteratorBegin();
+ ncmd=get();
+ int pgm2[16];
+ for (int j=0;j<16;j++) pgm2[j]=0;
+ while ((ncmd!=NULL)&&(ncmd->ms<ms))
+ {
+ if (ncmd->cmd==2) pgm2[ncmd->chn]=ncmd->note;
+ next();
+ ncmd=get();
+ }
+ if (pgm!=NULL)
+ {
+ for (int i=0;i<16;i++) pgm[i]=pgm2[i];
+ }
+}
diff --git a/libkmid/notearray.h b/libkmid/notearray.h
new file mode 100644
index 000000000..d462b06b5
--- /dev/null
+++ b/libkmid/notearray.h
@@ -0,0 +1,145 @@
+/* notearray.h - NoteArray class, which holds an array of notes
+ This file is part of LibKMid 0.9.5
+ Copyright (C) 1998,99,2000 Antonio Larrosa Jimenez
+ LibKMid's homepage : http://www.arrakis.es/~rlarrosa/libkmid.html
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ Send comments and bug fixes to Antonio Larrosa <larrosa@kde.org>
+
+***************************************************************************/
+#ifndef NOTEARRAY_H
+#define NOTEARRAY_H
+
+#include <libkmid/dattypes.h>
+#include <kdelibs_export.h>
+
+/**
+ * Holds a resizeable array of note on/off and patch change events. It can
+ * increase it size, but it doesn't decreases (until destruction :-) )
+ *
+ * @short Stores an array of note on/off events
+ * @version 0.9.5 17/01/2000
+ * @author Antonio Larrosa Jimenez <larrosa@kde.org>
+ */
+class KMID_EXPORT NoteArray
+{
+ private:
+ class NoteArrayPrivate;
+ NoteArrayPrivate *d;
+
+ public:
+ struct noteCmd {
+ /**
+ * ms from beginning of song
+ */
+ ulong ms;
+
+ /**
+ * The channel
+ */
+ int chn;
+
+ /**
+ * 0 note off, 1 note on, 2 change patch
+ */
+ int cmd;
+
+ /**
+ * The note.
+ *
+ * If cmd==2, then the patch is stored in "note"
+ */
+ int note;
+ };
+
+ private:
+ noteCmd *data;
+ ulong totalAllocated;
+
+ ulong last;
+ noteCmd *lastAdded;
+
+ /**
+ * @internal
+ * The iterator
+ */
+ noteCmd *it;
+
+ noteCmd *pointerTo(ulong pos);
+
+ public:
+ /**
+ * Constructor. Initializes internal variables.
+ */
+ NoteArray(void);
+ /**
+ * Destructor.
+ */
+ ~NoteArray();
+
+ /**
+ * Adds (or modifies) an event in the given position .
+ *
+ * Note that this has nothing to do with what is being played, this just
+ * modifies an internal array.
+ */
+ void at(ulong pos, ulong ms,int chn,int cmd,int note);
+
+ /**
+ * A convenience function, which differs from the above in the parameters
+ * it accepts.
+ */
+ void at(ulong pos, noteCmd s);
+
+ /**
+ * Returns the note event at a given position.
+ */
+ noteCmd at(int pos);
+
+ /**
+ * Adds a note/patch event at a given millisecond.
+ *
+ * Note: This method always appends at the end of the list.
+ */
+ void add(ulong ms,int chn,int cmd,int note);
+
+ /**
+ * Initializes the iterator.
+ *
+ * @see get()
+ * @see next()
+ */
+ void iteratorBegin(void) { it=data; }
+
+ /**
+ * Get the command currently pointed to by the iterator.
+ */
+ noteCmd *get(void) { return it; }
+
+ /**
+ * Advances the iterator to the next position.
+ */
+ void next(void);
+
+ /**
+ * Calls next() until the next event is over ms milliseconds
+ * and puts in @p pgm[16] the instruments used at this moment.
+ */
+ void moveIteratorTo(ulong ms,int *pgm=NULL);
+};
+
+#endif
diff --git a/libkmid/player.cc b/libkmid/player.cc
new file mode 100644
index 000000000..a68a9b147
--- /dev/null
+++ b/libkmid/player.cc
@@ -0,0 +1,959 @@
+/**************************************************************************
+
+ player.cc - class MidiPlayer. Plays a set of tracks
+ This file is part of LibKMid 0.9.5
+ Copyright (C) 1997,98,99,2000 Antonio Larrosa Jimenez
+ LibKMid's homepage : http://www.arrakis.es/~rlarrosa/libkmid.html
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ $Id$
+
+ Send comments and bug fixes to Antonio Larrosa <larrosa@kde.org>
+
+***************************************************************************/
+#include "player.h"
+#include "sndcard.h"
+#include "midispec.h"
+#include <string.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include "midistat.h"
+#include "mt32togm.h"
+
+//#define PLAYERDEBUG
+//#define GENERAL_DEBUG_MESSAGES
+
+#define T2MS(ticks) (((double)ticks)*(double)60000L)/((double)tempoToMetronomeTempo(tempo)*(double)info->ticksPerCuarterNote)
+
+#define MS2T(ms) (((ms)*(double)tempoToMetronomeTempo(tempo)*(double)info->ticksPerCuarterNote)/((double)60000L))
+
+#define REMOVEDUPSTRINGS
+
+MidiPlayer::MidiPlayer(DeviceManager *midi_,PlayerController *pctl)
+{
+ midi=midi_;
+ info=NULL;
+ tracks=NULL;
+ songLoaded=0;
+ ctl=pctl;
+ spev=NULL;
+ na=NULL;
+ parsesong=true;
+ generatebeats=false;
+}
+
+MidiPlayer::~MidiPlayer()
+{
+ removeSpecialEvents();
+ removeSong();
+}
+
+void MidiPlayer::removeSong(void)
+{
+ if ((songLoaded)&&(tracks!=NULL))
+ {
+#ifdef PLAYERDEBUG
+ printf("Removing song from memory\n");
+#endif
+ int i=0;
+ while (i<info->ntracks)
+ {
+ if (tracks[i]!=NULL) delete tracks[i];
+ i++;
+ }
+ delete tracks;
+ tracks=NULL;
+ if (info!=NULL)
+ {
+ delete info;
+ info=NULL;
+ }
+ }
+ songLoaded=0;
+}
+
+int MidiPlayer::loadSong(const char *filename)
+{
+ removeSong();
+#ifdef PLAYERDEBUG
+ printf("Loading Song : %s\n",filename);
+#endif
+ info=new MidiFileInfo;
+ int ok;
+ tracks=readMidiFile(filename,info,ok);
+ if (ok<0) return ok;
+ if (tracks==NULL) return -4;
+
+ parseInfoData(info,tracks,ctl->ratioTempo);
+
+ if (parsesong)
+ {
+ parseSpecialEvents();
+ if (generatebeats) generateBeats();
+ }
+
+ songLoaded=1;
+ return 0;
+}
+
+void MidiPlayer::insertBeat(SpecialEvent *ev,ulong ms,int num,int den)
+{
+ SpecialEvent *beat=new SpecialEvent;
+ beat->next=ev->next;
+ ev->next=beat;
+ beat->id=1;
+ beat->type=7;
+ beat->absmilliseconds=ms;
+ beat->num=num;
+ beat->den=den;
+}
+
+
+void MidiPlayer::generateBeats(void)
+{
+#ifdef PLAYERDEBUG
+ printf("player::Generating Beats...\n");
+#endif
+
+ if (spev==NULL) return;
+ SpecialEvent *ev=spev;
+ SpecialEvent *nextev=ev->next;
+ ulong tempo=(ulong)(500000 * ctl->ratioTempo);
+ int i=1;
+ int num=4;
+ int den=4;
+ // ulong beatstep=((double)tempo*4/(den*1000));
+ // ulong beatstep=T2MS(info->ticksPerCuarterNote*(4/den));
+ double ticksleft=(((double)info->ticksPerCuarterNote*4)/den);
+
+ double beatstep=T2MS(ticksleft);
+ double nextbeatms=0;
+ double lastbeatms=0;
+ double measurems=0;
+
+ while (nextev!=NULL)
+ {
+ switch (ev->type)
+ {
+ case (0): // End of list
+ {
+ };break;
+ case (1): // Text
+ case (2): // Lyrics
+ {
+ };break;
+ case (3): // Change Tempo
+ {
+ lastbeatms=ev->absmilliseconds;
+ ticksleft=MS2T(nextbeatms-lastbeatms);
+ tempo=ev->tempo;
+ nextbeatms=lastbeatms+T2MS(ticksleft);
+ // printf("Change at %lu to %d\n",ev->absmilliseconds,ev->tempo);
+ // beatstep=((double)tempo*4/(den*1000));
+ beatstep=T2MS(((static_cast<double>(info->ticksPerCuarterNote)*4)/den));
+ };break;
+ case (6): // Change number of beats per measure
+ {
+ num=ev->num;
+ i=1;
+ den=ev->den;
+ // printf("Change at %lu to %d/%d\n",ev->absmilliseconds,num,den);
+ // beatstep=((double)tempo*4/(den*1000));
+ // beatstep=T2MS(info->ticksPerCuarterNote*(4/den));
+ beatstep=T2MS((((double)info->ticksPerCuarterNote*4)/den));
+ nextbeatms=ev->absmilliseconds;
+ };break;
+ };
+ if (nextev->absmilliseconds>nextbeatms)
+ {
+ //printf("Adding %d,%d\n",num,tot);
+ //printf("beat at %g , %d/%d\n",nextbeatms,i,num);
+ //printf(" %ld %d\n",nextev->absmilliseconds,nextev->type);
+ if (i == 1) {
+ measurems=nextbeatms;
+ }
+ insertBeat(ev, static_cast<unsigned long>(nextbeatms), i++, num);
+ if (i > num) {
+ i=1;
+ }
+ lastbeatms=nextbeatms;
+ nextbeatms+=beatstep;
+ // nextbeatms=measurems+beatstep*i;
+
+ ticksleft = ( (static_cast<double>(info->ticksPerCuarterNote)*4) / den);
+
+ }
+
+ ev=ev->next;
+ nextev=ev->next;
+ }
+
+ /* ev==NULL doesn't indicate the end of the song, so continue generating beats */
+
+ if (ev!=NULL)
+ {
+ if (ev->type==0)
+ {
+ ev=spev;
+ /* Looking if ev->next is NULL is not needed because
+ we are sure that a ev->type == 0 exists, we just have
+ to assure that the first spev is not the only one */
+ if (ev->next!=NULL)
+ while (ev->next->type!=0) ev=ev->next;
+ }
+ while (nextbeatms<info->millisecsTotal)
+ {
+ // printf("beat2 at %g , %d/%d\n",nextbeatms,i,num);
+ if (i==1) measurems=nextbeatms;
+ insertBeat(ev, static_cast<unsigned long>(nextbeatms), i++, num);
+ if (i>num) i=1;
+ nextbeatms+=beatstep;
+ ev=ev->next;
+ }
+ }
+
+ /* Regenerate IDs */
+
+ ev=spev;
+ i=1;
+ while (ev!=NULL)
+ {
+ ev->id=i++;
+ ev=ev->next;
+ }
+
+
+#ifdef PLAYERDEBUG
+ printf("player::Beats Generated\n");
+#endif
+
+}
+
+void MidiPlayer::removeSpecialEvents(void)
+{
+ SpecialEvent * ev=spev;
+ while (spev!=NULL)
+ {
+ ev=spev->next;
+ delete spev;
+ spev=ev;
+ }
+ delete na;
+ na=0;
+}
+
+void MidiPlayer::parseSpecialEvents(void)
+{
+#ifdef PLAYERDEBUG
+ printf("player::Parsing...\n");
+#endif
+ removeSpecialEvents();
+ spev=new SpecialEvent;
+ if (spev==NULL) return;
+ SpecialEvent *pspev=spev;
+ pspev->type=0;
+ pspev->ticks=0;
+ if (na) delete na;
+ na=new NoteArray();
+ if (!na) { delete spev; spev=0L; return; };
+ int trk;
+ int minTrk;
+ double minTime=0;
+ double maxTime;
+ ulong tempo=(ulong)(500000 * (ctl->ratioTempo));
+ ulong firsttempo=0;
+ for (int i=0;i<info->ntracks;i++)
+ {
+ tracks[i]->init();
+ tracks[i]->changeTempo(tempo);
+ }
+ MidiEvent *ev=new MidiEvent;
+ //ulong mspass;
+ double prevms=0;
+ int spev_id=1;
+ int j;
+ int parsing=1;
+#ifdef REMOVEDUPSTRINGS
+ char lasttext[1024];
+ ulong lasttexttime=0;
+ lasttext[0]=0;
+ int lasttexttype=0;
+#endif
+ while (parsing)
+ {
+ prevms=minTime;
+ trk=0;
+ minTrk=0;
+ maxTime=minTime + 2 * 60000L;
+ minTime=maxTime;
+ parsing=0;
+ while (trk<info->ntracks)
+ {
+ if (tracks[trk]->absMsOfNextEvent()<minTime)
+ {
+ minTrk=trk;
+ minTime=tracks[minTrk]->absMsOfNextEvent();
+ parsing=1;
+ }
+ trk++;
+ }
+ // if ((minTime==maxTime))
+ if (parsing==0)
+ {
+ // parsing=0;
+#ifdef PLAYERDEBUG
+ printf("END of parsing\n");
+#endif
+ }
+ else
+ {
+ // mspass=(ulong)(minTime-prevms);
+ trk=0;
+ while (trk<info->ntracks)
+ {
+ tracks[trk]->currentMs(minTime);
+ trk++;
+ }
+ }
+ trk=minTrk;
+ tracks[trk]->readEvent(ev);
+ switch (ev->command)
+ {
+ case (MIDI_NOTEON) :
+ if (ev->vel==0) na->add((ulong)minTime,ev->chn,0, ev->note);
+ else na->add((ulong)minTime,ev->chn,1,ev->note);
+ break;
+ case (MIDI_NOTEOFF) :
+ na->add((ulong)minTime,ev->chn,0, ev->note);
+ break;
+ case (MIDI_PGM_CHANGE) :
+ na->add((ulong)minTime,ev->chn, 2,ev->patch);
+ break;
+ case (MIDI_SYSTEM_PREFIX) :
+ {
+ if ((ev->command|ev->chn)==META_EVENT)
+ {
+ switch (ev->d1)
+ {
+ case (1) :
+ case (5) :
+ {
+ if (pspev!=NULL)
+ {
+ pspev->absmilliseconds=(ulong)minTime;
+ pspev->type=ev->d1;
+ pspev->id=spev_id++;
+#ifdef PLAYERDEBUG
+ printf("ev->length %ld\n",ev->length);
+
+#endif
+ strncpy(pspev->text,(char *)ev->data,
+ (ev->length>= sizeof(lasttext))? sizeof(lasttext)-1 : (ev->length) );
+ pspev->text[(ev->length>= sizeof(lasttext))? sizeof(lasttext)-1:(ev->length)]=0;
+#ifdef PLAYERDEBUG
+ printf("(%s)(%s)\n",pspev->text,lasttext);
+#endif
+#ifdef REMOVEDUPSTRINGS
+ if ((strcmp(pspev->text,lasttext)!=0)||(pspev->absmilliseconds!=lasttexttime)||(pspev->type!=lasttexttype))
+ {
+ lasttexttime=pspev->absmilliseconds;
+ lasttexttype=pspev->type;
+ strncpy(lasttext, pspev->text, 1024);
+ lasttext[sizeof(lasttext)-1] = 0;
+#endif
+ pspev->next=new SpecialEvent;
+#ifdef PLAYERDEBUG
+ if (pspev->next==NULL) printf("pspev->next=NULL\n");
+#endif
+ pspev=pspev->next;
+#ifdef REMOVEDUPSTRINGS
+ }
+#endif
+ }
+ }
+ break;
+ case (ME_SET_TEMPO) :
+ {
+ if (pspev!=NULL)
+ {
+ pspev->absmilliseconds=(ulong)minTime;
+ pspev->type=3;
+ pspev->id=spev_id++;
+ tempo=(ulong)(((ev->data[0]<<16)|(ev->data[1]<<8)|(ev->data[2])) * ctl->ratioTempo);
+ pspev->tempo=tempo;
+ if (firsttempo==0) firsttempo=tempo;
+ for (j=0;j<info->ntracks;j++)
+ {
+ tracks[j]->changeTempo(tempo);
+ }
+ pspev->next=new SpecialEvent;
+ pspev=pspev->next;
+ }
+ }
+ break;
+ case (ME_TIME_SIGNATURE) :
+ {
+ if (pspev!=NULL)
+ {
+ pspev->absmilliseconds=(ulong)minTime;
+ pspev->type=6;
+ pspev->id=spev_id++;
+ pspev->num=ev->d2;
+ pspev->den=ev->d3;
+ pspev->next=new SpecialEvent;
+ pspev=pspev->next;
+ }
+ }
+ break;
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ delete ev;
+ pspev->type=0;
+ pspev->absmilliseconds=(ulong)prevms;
+ pspev->next=NULL;
+ if (firsttempo==0) firsttempo=tempo;
+ ctl->tempo=firsttempo;
+
+ //writeSPEV();
+ for (int i=0;i<info->ntracks;i++)
+ {
+ tracks[i]->init();
+ }
+}
+
+/*
+NoteArray *MidiPlayer::parseNotes(void)
+{
+#ifdef PLAYERDEBUG
+ printf("player::Parsing Notes...\n");
+#endif
+ NoteArray *na=new NoteArray();
+ int trk;
+ int minTrk;
+ double minTime=0;
+ double maxTime;
+ for (int i=0;i<info->ntracks;i++)
+ {
+ tracks[i]->init();
+ };
+ ulong tempo=1000000;
+ ulong tmp;
+ Midi_event *ev=new Midi_event;
+ //ulong mspass;
+ double prevms=0;
+ int j;
+ int parsing=1;
+ while (parsing)
+ {
+ prevms=minTime;
+ trk=0;
+ minTrk=0;
+ maxTime=minTime + 2 * 60000L;
+ minTime=maxTime;
+ while (trk<info->ntracks)
+ {
+ if (tracks[trk]->absMsOfNextEvent()<minTime)
+ {
+ minTrk=trk;
+ minTime=tracks[minTrk]->absMsOfNextEvent();
+ };
+ trk++;
+ };
+ if ((minTime==maxTime))
+ {
+ parsing=0;
+#ifdef PLAYERDEBUG
+ printf("END of parsing\n");
+#endif
+ }
+ else
+ {
+ // mspass=(ulong)(minTime-prevms);
+ trk=0;
+ while (trk<info->ntracks)
+ {
+ tracks[trk]->currentMs(minTime);
+ trk++;
+ };
+ };
+ trk=minTrk;
+ tracks[trk]->readEvent(ev);
+ if (ev->command==MIDI_NOTEON)
+ {
+ if (ev->vel==0) {printf("note off at %g\n",minTime);na->add((ulong)minTime,ev->chn,0, ev->note);}
+ else {printf("note on at %g\n",minTime);na->add((ulong)minTime,ev->chn,1,ev->note);}
+ }
+ else
+ if (ev->command==MIDI_NOTEOFF) na->add((ulong)minTime,ev->chn,0, ev->note);
+ if (ev->command==MIDI_PGM_CHANGE) na->add((ulong)minTime,ev->chn, 2,ev->patch);
+ if (ev->command==MIDI_SYSTEM_PREFIX)
+ {
+ if (((ev->command|ev->chn)==META_EVENT)&&(ev->d1==ME_SET_TEMPO))
+ {
+ tempo=(ev->data[0]<<16)|(ev->data[1]<<8)|(ev->data[2]);
+ for (j=0;j<info->ntracks;j++)
+ {
+ tracks[j]->changeTempo(tempo);
+ };
+ };
+ };
+
+ };
+
+ delete ev;
+ for (int i=0;i<info->ntracks;i++)
+ {
+ tracks[i]->init();
+ };
+ return na;
+};
+*/
+
+void MidiPlayer::play(bool calloutput,void output(void))
+{
+#ifdef PLAYERDEBUG
+ printf("Playing...\n");
+#endif
+
+ if (midi->midiPorts()+midi->synthDevices()==0)
+ {
+ fprintf(stderr,"Player :: There are no midi ports !\n");
+ ctl->error=1;
+ return;
+ }
+
+ midi->openDev();
+ if (midi->ok()==0)
+ {
+ fprintf(stderr,"Player :: Couldn't play !\n");
+ ctl->error=1;
+ return;
+ }
+ midi->setVolumePercentage(ctl->volumepercentage);
+ midi->initDev();
+ // parsePatchesUsed(tracks,info,ctl->gm);
+ midi->setPatchesToUse(info->patchesUsed);
+
+ int trk;
+ int minTrk;
+ double minTime=0;
+ double maxTime;
+ int i;
+ ulong tempo=(ulong)(500000 * ctl->ratioTempo);
+ for (i=0;i<info->ntracks;i++)
+ {
+ tracks[i]->init();
+ tracks[i]->changeTempo(tempo);
+ }
+
+ midi->tmrStart(info->ticksPerCuarterNote);
+ MidiEvent *ev=new MidiEvent;
+ ctl->ev=ev;
+ ctl->ticksTotal=info->ticksTotal;
+ ctl->ticksPlayed=0;
+ //ctl->millisecsPlayed=0;
+ ulong ticksplayed=0;
+ double absTimeAtChangeTempo=0;
+ double absTime=0;
+ double diffTime=0;
+ MidiStatus *midistat;
+ //ulong mspass;
+ double prevms=0;
+ int j;
+ int halt=0;
+ ctl->tempo=tempo;
+ ctl->num=4;
+ ctl->den=4;
+ int playing;
+ ctl->paused=0;
+ if ((ctl->message!=0)&&(ctl->message & PLAYER_SETPOS))
+ {
+ ctl->moving=1;
+ ctl->message&=~PLAYER_SETPOS;
+ midi->sync(1);
+ midi->tmrStop();
+ midi->closeDev();
+ midistat = new MidiStatus();
+ setPos(ctl->gotomsec,midistat);
+ minTime=ctl->gotomsec;
+ prevms=(ulong)minTime;
+ midi->openDev();
+ midi->tmrStart(info->ticksPerCuarterNote);
+ diffTime=ctl->gotomsec;
+ midistat->sendData(midi,ctl->gm);
+ delete midistat;
+ midi->setPatchesToUse(info->patchesUsed);
+ ctl->moving=0;
+ } else
+ for (i=0;i<16;i++)
+ {
+ if (ctl->forcepgm[i])
+ {
+ midi->chnPatchChange(i, ctl->pgm[i]);
+ }
+ }
+
+ timeval begintv;
+ gettimeofday(&begintv, NULL);
+ ctl->beginmillisec=begintv.tv_sec*1000+begintv.tv_usec/1000;
+ ctl->OK=1;
+ ctl->playing=playing=1;
+
+ while (playing)
+ {
+ /*
+ if (ctl->message!=0)
+ {
+ if (ctl->message & PLAYER_DOPAUSE)
+ {
+ diffTime=minTime;
+ ctl->message&=~PLAYER_DOPAUSE;
+ midi->sync(1);
+ midi->tmrStop();
+ ctl->paused=1;
+ midi->closeDev();
+ while ((ctl->paused)&&(!(ctl->message&PLAYER_DOSTOP))
+ &&(!(ctl->message&PLAYER_HALT))) sleep(1);
+ midi->openDev();
+ midi->tmrStart();
+ ctl->OK=1;
+ printf("Continue playing ... \n");
+ };
+ if (ctl->message & PLAYER_DOSTOP)
+ {
+ ctl->message&=~PLAYER_DOSTOP;
+ playing=0;
+ };
+ if (ctl->message & PLAYER_HALT)
+ {
+ ctl->message&=~PLAYER_HALT;
+ playing=0;
+ halt=1;
+ };
+ if (ctl->message & PLAYER_SETPOS)
+ {
+ ctl->moving=1;
+ ctl->message&=~PLAYER_SETPOS;
+ midi->sync(1);
+ midi->tmrStop();
+ midi->closeDev();
+ midistat = new midiStat();
+ SetPos(ctl->gotomsec,midistat);
+ minTime=ctl->gotomsec;
+ prevms=(ulong)minTime;
+ midi->openDev();
+ midi->tmrStart();
+ diffTime=ctl->gotomsec;
+ ctl->moving=0;
+ midistat->sendData(midi,ctl->gm);
+ delete midistat;
+ ctl->OK=1;
+ while (ctl->OK==1) ;
+ ctl->moving=0;
+ };
+ };
+ */
+ prevms=minTime;
+ // ctl->millisecsPlayed=minTime;
+ trk=0;
+ minTrk=0;
+ maxTime=minTime + 120000L /* milliseconds */;
+ minTime=maxTime;
+ playing=0;
+ while (trk<info->ntracks)
+ {
+ if (tracks[trk]->absMsOfNextEvent()<minTime)
+ {
+ minTrk=trk;
+ minTime=tracks[minTrk]->absMsOfNextEvent();
+ playing=1;
+ }
+ trk++;
+ }
+#ifdef PLAYERDEBUG
+ printf("minTime %g\n",minTime);
+#endif
+ // if ((minTime==maxTime)/* || (minTicks> 60000L)*/)
+ if (playing==0)
+ {
+ // playing=0;
+#ifdef PLAYERDEBUG
+ printf("END of playing\n");
+#endif
+ }
+ else
+ {
+ // mspass=(ulong)(minTime-prevms);
+ trk=0;
+ while (trk<info->ntracks)
+ {
+ tracks[trk]->currentMs(minTime);
+ trk++;
+ }
+ midi->wait(minTime-diffTime);
+ }
+ trk=minTrk;
+ tracks[trk]->readEvent(ev);
+ switch (ev->command)
+ {
+ case (MIDI_NOTEON) :
+ midi->noteOn(ev->chn, ev->note, ev->vel);break;
+ case (MIDI_NOTEOFF):
+ midi->noteOff(ev->chn, ev->note, ev->vel);break;
+ case (MIDI_KEY_PRESSURE) :
+ midi->keyPressure(ev->chn, ev->note,ev->vel);break;
+ case (MIDI_PGM_CHANGE) :
+ if (!ctl->forcepgm[ev->chn])
+ midi->chnPatchChange(ev->chn, (ctl->gm==1)?(ev->patch):(MT32toGM[ev->patch]));break;
+ case (MIDI_CHN_PRESSURE) :
+ midi->chnPressure(ev->chn, ev->vel);break;
+ case (MIDI_PITCH_BEND) :
+ midi->chnPitchBender(ev->chn, ev->d1,ev->d2);break;
+ case (MIDI_CTL_CHANGE) :
+ midi->chnController(ev->chn, ev->ctl,ev->d1);break;
+ case (MIDI_SYSTEM_PREFIX) :
+ if ((ev->command|ev->chn)==META_EVENT)
+ {
+ if ((ev->d1==5)||(ev->d1==1))
+ {
+ ctl->SPEVplayed++;
+ }
+ if (ev->d1==ME_SET_TEMPO)
+ {
+ absTimeAtChangeTempo=absTime;
+ ticksplayed=0;
+ ctl->SPEVplayed++;
+ tempo=(ulong)(((ev->data[0]<<16)|(ev->data[1]<<8)|(ev->data[2]))*ctl->ratioTempo);
+#ifdef PLAYERDEBUG
+ printf("Tempo : %ld %g (ratio : %g)\n",tempo,tempoToMetronomeTempo(tempo),ctl->ratioTempo);
+#endif
+ midi->tmrSetTempo((int)tempoToMetronomeTempo(tempo));
+ ctl->tempo=tempo;
+ for (j=0;j<info->ntracks;j++)
+ {
+ tracks[j]->changeTempo(tempo);
+ }
+ }
+ if (ev->d1==ME_TIME_SIGNATURE)
+ {
+ ctl->num=ev->d2;
+ ctl->den=ev->d3;
+ ctl->SPEVplayed++;
+ }
+ }
+ break;
+ }
+ if (calloutput)
+ {
+ midi->sync();
+ output();
+ }
+
+ }
+ ctl->ev=NULL;
+ delete ev;
+#ifdef PLAYERDEBUG
+ printf("Syncronizing ...\n");
+#endif
+ if (halt)
+ midi->sync(1);
+ else
+ midi->sync();
+#ifdef PLAYERDEBUG
+ printf("Closing device ...\n");
+#endif
+ midi->allNotesOff();
+ midi->closeDev();
+ ctl->playing=0;
+#ifdef PLAYERDEBUG
+ printf("Bye...\n");
+#endif
+ ctl->OK=1;
+ ctl->finished=1;
+}
+
+
+void MidiPlayer::setPos(ulong gotomsec,MidiStatus *midistat)
+{
+ int trk,minTrk;
+ ulong tempo=(ulong)(500000 * ctl->ratioTempo);
+ double minTime=0,maxTime,prevms=0;
+ int i,j,likeplaying=1;
+
+ MidiEvent *ev=new MidiEvent;
+ ctl->SPEVplayed=0;
+ for (i=0;i<info->ntracks;i++)
+ {
+ tracks[i]->init();
+ tracks[i]->changeTempo(tempo);
+ }
+
+ for (i=0;i<16;i++)
+ {
+ if (ctl->forcepgm[i]) midistat->chnPatchChange(i, ctl->pgm[i]);
+ }
+
+ while (likeplaying)
+ {
+ trk=0;
+ minTrk=0;
+ maxTime=minTime + 120000L; /*milliseconds (2 minutes)*/
+ minTime=maxTime;
+ while (trk<info->ntracks)
+ {
+ if (tracks[trk]->absMsOfNextEvent()<minTime)
+ {
+ minTrk=trk;
+ minTime=tracks[minTrk]->absMsOfNextEvent();
+ }
+ trk++;
+ }
+ if (minTime==maxTime)
+ {
+ likeplaying=0;
+#ifdef GENERAL_DEBUG_MESSAGES
+ printf("END of likeplaying\n");
+#endif
+ }
+ else
+ {
+ if (minTime>=gotomsec)
+ {
+ prevms=gotomsec;
+ likeplaying=0;
+#ifdef GENERAL_DEBUG_MESSAGES
+ printf("Position reached !! \n");
+#endif
+ minTime=gotomsec;
+ }
+ else
+ {
+ prevms=minTime;
+ }
+ trk=0;
+ while (trk<info->ntracks)
+ {
+ tracks[trk]->currentMs(minTime);
+ trk++;
+ }
+ }
+
+ if (likeplaying)
+ {
+ trk=minTrk;
+ tracks[trk]->readEvent(ev);
+ switch (ev->command)
+ {
+ /* case (MIDI_NOTEON) :
+ midistat->noteOn(ev->chn, ev->note, ev->vel);break;
+ case (MIDI_NOTEOFF):
+ midistat->noteOff(ev->chn, ev->note, ev->vel);break;
+ case (MIDI_KEY_PRESSURE) :
+ midistat->keyPressure(ev->chn, ev->note,ev->vel);break;
+ */
+ case (MIDI_PGM_CHANGE) :
+ if (!ctl->forcepgm[ev->chn]) midistat->chnPatchChange(ev->chn, ev->patch);break;
+ case (MIDI_CHN_PRESSURE) :
+ midistat->chnPressure(ev->chn, ev->vel);break;
+ case (MIDI_PITCH_BEND) :
+ midistat->chnPitchBender(ev->chn, ev->d1,ev->d2);break;
+ case (MIDI_CTL_CHANGE) :
+ midistat->chnController(ev->chn, ev->ctl,ev->d1);break;
+ case (MIDI_SYSTEM_PREFIX) :
+ if ((ev->command|ev->chn)==META_EVENT)
+ {
+ if ((ev->d1==5)||(ev->d1==1))
+ {
+ ctl->SPEVplayed++;
+ }
+ if (ev->d1==ME_SET_TEMPO)
+ {
+ ctl->SPEVplayed++;
+ tempo=(ulong)(((ev->data[0]<<16)|(ev->data[1]<<8)|(ev->data[2]))*ctl->ratioTempo);
+
+ midistat->tmrSetTempo((int)tempoToMetronomeTempo(tempo));
+ for (j=0;j<info->ntracks;j++)
+ {
+ tracks[j]->changeTempo(tempo);
+ }
+ }
+ if (ev->d1==ME_TIME_SIGNATURE)
+ {
+ ctl->num=ev->d2;
+ ctl->den=ev->d3;
+ ctl->SPEVplayed++;
+ }
+ }
+ break;
+ }
+ }
+ }
+ delete ev;
+ ctl->tempo=tempo;
+}
+
+
+void MidiPlayer::debugSpecialEvents(void)
+{
+ SpecialEvent *pspev=spev;
+ printf("**************************************\n");
+ while ((pspev!=NULL)&&(pspev->type!=0))
+ {
+ printf("t:%d ticks:%d diff:%ld abs:%ld s:%s tempo:%ld\n",pspev->type,pspev->ticks,pspev->diffmilliseconds,pspev->absmilliseconds,pspev->text,pspev->tempo);
+ pspev=pspev->next;
+ }
+
+}
+
+void MidiPlayer::setParseSong(bool b)
+{
+ parsesong=b;
+}
+
+void MidiPlayer::setGenerateBeats(bool b)
+{
+ generatebeats=b;
+}
+
+void MidiPlayer::setTempoRatio(double ratio)
+{
+ if (songLoaded)
+ {
+ ctl->ratioTempo=ratio;
+ parseInfoData(info,tracks,ctl->ratioTempo);
+ if (parsesong)
+ {
+ parseSpecialEvents();
+ if (generatebeats) generateBeats();
+
+ }
+ }
+ else
+ {
+ ctl->tempo=(ulong)((ctl->tempo*ctl->ratioTempo)/ratio);
+ ctl->ratioTempo=ratio;
+ }
+
+}
+
+#undef T2MS
+#undef MS2T
diff --git a/libkmid/player.h b/libkmid/player.h
new file mode 100644
index 000000000..6a9210292
--- /dev/null
+++ b/libkmid/player.h
@@ -0,0 +1,396 @@
+/* player.h - class MidiPlayer. Plays a set of tracks
+ This file is part of LibKMid 0.9.5
+ Copyright (C) 1997,98,99,2000 Antonio Larrosa Jimenez
+ LibKMid's homepage : http://www.arrakis.es/~rlarrosa/libkmid.html
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ Send comments and bug fixes to Antonio Larrosa <larrosa@kde.org>
+
+***************************************************************************/
+#ifndef _PLAYER_H
+#define _PLAYER_H
+
+#include <libkmid/dattypes.h>
+#include <libkmid/midfile.h>
+#include <libkmid/deviceman.h>
+#include <libkmid/track.h>
+#include <libkmid/notearray.h>
+#include <kdemacros.h>
+
+/**
+ * This struct stores text, lyrics and change tempo events among others.
+ *
+ * It includes the main information for an event. That is, the absolute
+ * millisecond at which this event is played (from the beginning of the song),
+ * the delta milliseconds from the previous SpecialEvent, an ID, etc.
+ *
+ * This struct is used as nodes for a linked list, which you can get using
+ * MidiPlayer::specialEvents().
+ *
+ * @short Struct used to store certain events
+ * @version 0.9.5 17/01/2000
+ * @author Antonio Larrosa Jimenez <larrosa@kde.org>
+ */
+struct SpecialEvent
+{
+ /**
+ * An integer ID, that is assigned in order to each SpecialEvent.
+ */
+ int id;
+
+ /**
+ * Delta milliseconds from the previous SpecialEvent.
+ *
+ * @see absmilliseconds
+ */
+ ulong diffmilliseconds;
+
+ /**
+ * The absolute millisecond (from the beginning of the song) at which this
+ * SpecialEvent object is played.
+ *
+ * @see diffmilliseconds
+ */
+ ulong absmilliseconds;
+
+ /**
+ * MIDI ticks (from the beginning of the song) at which this event is played.
+ */
+ int ticks;
+
+ /**
+ * Type of event. This currently includes:
+ *
+ * @li 0 - Nothing, end of linked list.
+ * @li 1 - Text Event . See text.
+ * @li 3 - Change Tempo Event . See tempo.
+ * @li 5 - Lyrics Event . See text.
+ * @li 6 - Change number of beats per measure . See num and den.
+ * @li 7 - Beat . See num and den.
+ *
+ * The "Change number of beats per measure" and "beat" events are not really
+ * in the midi file, but they are added to the linked list in case you have
+ * an use for it.
+ */
+ int type;
+
+ /**
+ * Text field . It has a meaning only for Text and Lyrics events.
+ */
+ char text[1024];
+
+ /**
+ * Tempo field . It has a meaning only for Change Tempo events.
+ */
+ ulong tempo;
+ /**
+ * Numerator . It has a meaning only for Change number of beats per measure and
+ * beat events.
+ */
+ int num;
+ /**
+ * Denominator . It has a meaning only for Change number of beats per measure
+ * and beat events.
+ */
+ int den;
+
+ /**
+ * This struct stores text, lyrics and change tempo events among others.
+ *
+ * It includes the main information for an event. That is, the absolute
+ * millisecond at which this event is played (from the beginning of the song),
+ * the delta milliseconds from the previous SpecialEvent, an ID, etc.
+ *
+ * This struct is used as nodes for a linked list, which you can get using
+ * MidiPlayer::specialEvents().
+ */
+ struct SpecialEvent *next;
+
+ /**
+ * Next node in the linked list.
+ */
+
+};
+
+
+/**
+ * PlayerController is a struct that is used by the MidiPlayer object
+ * to tell other parts of the application about the status of the MIDI playing.
+ *
+ * @short Struct used to have control over the player engine
+ * @version 0.9.5 17/01/2000
+ * @author Antonio Larrosa Jimenez <larrosa@kde.org>
+ */
+struct PlayerController
+{
+ volatile ulong ticksTotal;
+ volatile ulong ticksPlayed;
+ volatile double millisecsPlayed;
+ volatile ulong beginmillisec;
+
+ volatile int tempo;
+ volatile int num;
+ volatile int den;
+
+ volatile int SPEVprocessed;
+ volatile int SPEVplayed;
+
+ /**
+ * When pause is released, if the caller must know when the player has
+ * opened the devices and is playing again, then it just has to check
+ * to see when OK changes the value to 1
+ */
+ volatile int OK;
+
+ /**
+ * When the player is playing (or paused), playing is set to 1.
+ */
+ volatile int playing;
+
+ /**
+ * When the player is paused, paused is set to 1.
+ */
+ volatile int paused;
+
+ /**
+ * When the player seeking the position of the song, moving is set to 1.
+ */
+ volatile int moving;
+
+ /**
+ * When the player has finished playing a song, finished is set to 1.
+ */
+ volatile int finished;
+
+ /**
+ * @internal
+ * @deprecated
+ * Not used
+ */
+ volatile int message KDE_DEPRECATED; // set one of the following :
+
+#define PLAYER_DOPAUSE 1
+#define PLAYER_DOSTOP 2
+#define PLAYER_SETPOS 4
+#define PLAYER_HALT 8
+
+ volatile ulong gotomsec; //milliseconds to go to,if player_setpos is set
+
+ /**
+ * When error is 1, an error has ocurred (i.e. it coultn't open the device)
+ */
+ volatile int error;
+
+ /**
+ * If gm is 1, the song follows the General Midi standard, if gm is 0, the song
+ * is in MT 32 format.
+ */
+ volatile int gm;
+
+ /**
+ * 100 means no change, 50 halfs the volume, 200 doubles it (if possible), etc.
+ *
+ * @see DeviceManager::setVolumePercentage()
+ */
+ volatile int volumepercentage ;
+
+ /**
+ * Activate or disactivate the force to use a patch for a given channel.
+ * @see pgm
+ */
+ volatile bool forcepgm[16];
+
+ /**
+ * Force a given patch in each channel at "this" moment, as determined by
+ * forcepgm.
+ */
+ volatile int pgm[16];
+
+ /**
+ * Ratio to multiply the tempo to.
+ */
+ volatile double ratioTempo;
+
+ /**
+ * @internal Used to stop the main pid until the child has finished to
+ * send the all notes off event
+ */
+ volatile bool isSendingAllNotesOff;
+
+ volatile MidiEvent *ev;
+};
+
+
+/**
+ * MIDI file player routines . This class reads a MIDI file and
+ * play it using a DeviceManager object.
+ *
+ * To use it, just call loadSong() with the filename you want to play,
+ * and then play().
+ *
+ * Please have a look at the note in the play() documentation.
+ *
+ * MidiPlayer will write information about the playing process on a
+ * PlayerController() structure that you must supply to the constructor
+ *
+ * Alternatively, if everything you want is to play a midi file in a game or
+ * any other application that doesn't need to fine tune the midi playing, just
+ * use the kmidplay() function.
+ *
+ * @see KMidSimpleAPI
+ *
+ * @short The MIDI file player engine
+ * @version 0.9.5 17/01/2000
+ * @author Antonio Larrosa Jimenez <larrosa@kde.org>
+ */
+class KMID_EXPORT MidiPlayer
+{
+ class MidiPlayerPrivate;
+ MidiPlayerPrivate *d;
+
+ DeviceManager *midi;
+ MidiFileInfo *info;
+ MidiTrack **tracks;
+ SpecialEvent *spev;
+ NoteArray *na;
+
+ int songLoaded;
+
+ PlayerController *ctl;
+
+ bool parsesong;
+ bool generatebeats;
+
+ void removeSpecialEvents(void);
+ void parseSpecialEvents(void);
+ void insertBeat(SpecialEvent *ev,ulong ms,int num,int den);
+ void generateBeats(void);
+
+ //NoteArray *parseNotes(void);
+ void debugSpecialEvents(void);
+ public:
+
+ /**
+ * Constructor . You must construct and pass a DeviceManager object and a
+ * PlayerController structure. None of them will be destroyed by this
+ * object, so you should do it after destroying the MidiPlayer object.
+ */
+ MidiPlayer(DeviceManager *midi_,PlayerController *pctl);
+
+ /**
+ * Destructor.
+ */
+ ~MidiPlayer();
+
+ /**
+ * Loads a Song, and parses it (it the parse wasn't disabled with
+ * setParseSong() ) . It also generates the Beat events (see
+ * SpecialEvent::type() ) if you enabled this by using
+ * setGenerateBeats() .
+ */
+ int loadSong(const char *filename);
+
+ /**
+ * Unloads the current song, so that every internal variable is empty and clean
+ * for further usage.
+ */
+ void removeSong(void);
+
+ /**
+ * Returns true if there's a song already loaded (with a previous call to
+ * loadSong() ) and false if not.
+ */
+ int isSongLoaded(void) { return songLoaded; }
+
+ /**
+ * Returns the linked list of SpecialEvents objects . For this to work,
+ * the parse should be enabled (the default), by using setParseSong().
+ */
+ SpecialEvent *specialEvents() { return spev; }
+
+ /**
+ * Returns and array with the notes playen through the song . MidiPlayer must
+ * parse the song to get the notes, so be sure not to disable the parsing of
+ * the song.
+ *
+ * Returns an array of notes, (just note on and note off events), in the form
+ * of a NoteArray object
+ */
+ NoteArray *noteArray(void) { return na; }
+
+
+ /**
+ * Plays the song using the DeviceManager object supplied in the
+ * constructor. It should be already configured, as play doesn't change the
+ * volume, nor midi mapper, for example.
+ *
+ * Note: Calling this function will block the execution of your application
+ * until the song finishes playing. The solution for this is simple, fork
+ * before calling it, and create the PlayerController object on shared
+ * memory.
+ *
+ * As alternative, if everything you want is playing a midi file in a game or
+ * any other application that doesn't need to fine tune the midi playing, just
+ * use the KMidSimpleAPI::kMidPlay() function.
+ *
+ * @see KMidSimpleAPI::kMidInit
+ * @see KMidSimpleAPI::kMidPlay
+ * @see KMidSimpleAPI::kMidStop
+ * @see KMidSimpleAPI::kMidDestruct
+ */
+ void play(bool calloutput=false,void output(void) = 0);
+
+ /**
+ * Enables or disables the parsing of the song when loading it. This affects
+ * the SpecialEvents ( specialEvents() ) and the NoteArray
+ * ( noteArray() ).
+ */
+ void setParseSong(bool b = true);
+
+ /**
+ * Enables or disables the generation of beats event in a song when loading
+ * it.
+ */
+ void setGenerateBeats(bool b = false);
+
+ /**
+ * Returns information about the current MIDI file.
+ *
+ * @see loadSong
+ */
+ MidiFileInfo *information(void) { return info; }
+
+ /**
+ * Sets the position in a song.
+ * @param gotomsec the number of milliseconds to go to . A subsequent call to
+ * play() will start playing the song from that moment, instead of the
+ * beginning.
+ * @param midistat a MidiStatus object that will contain the status in
+ * which the MIDI device would be if it would have arrived to this situation by
+ * a normal playing of the song.
+ */
+ void setPos(ulong gotomsec, class MidiStatus *midistat);
+
+ /**
+ * Changes the speed at which a song is played. The song's tempo is multiplied
+ * by the specified ratio.
+ */
+ void setTempoRatio(double ratio);
+
+};
+
+#endif
diff --git a/libkmid/sndcard.h b/libkmid/sndcard.h
new file mode 100644
index 000000000..c533451ed
--- /dev/null
+++ b/libkmid/sndcard.h
@@ -0,0 +1,88 @@
+/* sndcard.h - include the OSS' soundcard.h file
+ This file is part of LibKMid 0.9.5
+ Copyright (C) 1997,98,99,2000 Antonio Larrosa Jimenez
+ LibKMid's homepage : http://www.arrakis.es/~rlarrosa/libkmid.html
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ Send comments and bug fixes to Antonio Larrosa <larrosa@kde.org>
+
+***************************************************************************/
+
+#ifndef _SNDCARD_H
+#define _SNDCARD_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_SYS_SOUNDCARD_H
+ #include <sys/soundcard.h>
+#elif defined(HAVE_MACHINE_SOUNDCARD_H)
+ #include <machine/soundcard.h>
+#endif
+
+/* Check for OSS MIDI API */
+#if defined(SNDCTL_SEQ_NRSYNTHS) && defined(CTL_MAIN_VOLUME)
+ #define HAVE_OSS_SUPPORT
+#else
+ #undef HAVE_OSS_SUPPORT
+#endif
+
+#ifdef HAVE_OSS_SUPPORT
+
+#ifndef HZ
+#define HZ 100
+#endif
+
+#ifndef MIDI_TYPE_MPU401
+#define MIDI_TYPE_MPU401 0x401
+#endif
+
+#else
+
+#define MIDI_NOTEON 0x80
+#define MIDI_NOTEOFF 0x90
+#define MIDI_KEY_PRESSURE 0xA0
+#define MIDI_CTL_CHANGE 0xB0
+#define MIDI_PGM_CHANGE 0xC0
+#define MIDI_CHN_PRESSURE 0xD0
+#define MIDI_PITCH_BEND 0xE0
+#define MIDI_SYSTEM_PREFIX 0xF0
+#define CTL_MAIN_VOLUME 7
+
+#define SEQ_DEFINEBUF(a)
+#define SEQ_USE_EXTBUF()
+#define SEQ_MIDIOUT(a,b) {}
+#define SEQ_START_NOTE(a,b,c,d) {}
+#define SEQ_STOP_NOTE(a,b,c,d) {}
+#define SEQ_SET_PATCH(a,b,c) {}
+#define SEQ_CONTROL(a,b,c,d) {}
+#define SEQ_BENDER(a,b,c) {}
+#define SEQ_CHN_PRESSURE(a,b,c) {}
+#define SEQ_KEY_PRESSURE(a,b,c,d) {}
+#define SEQ_DUMPBUF()
+#define SEQ_WAIT_TIME(a)
+#define SEQ_START_TIMER()
+#define SEQ_STOP_TIMER()
+#define SEQ_CONTINUE_TIMER()
+
+
+#endif
+
+
+
+#endif
diff --git a/libkmid/synthout.cc b/libkmid/synthout.cc
new file mode 100644
index 000000000..069bbd32f
--- /dev/null
+++ b/libkmid/synthout.cc
@@ -0,0 +1,211 @@
+/**************************************************************************
+
+ synthout.cc - class synthOut which handles the /dev/sequencer device
+ for synths (as AWE32)
+ This file is part of LibKMid 0.9.5
+ Copyright (C) 1997,98 Antonio Larrosa Jimenez and P.J.Leonard
+ 1999,2000 Antonio Larrosa Jimenez
+ LibKMid's homepage : http://www.arrakis.es/~rlarrosa/libkmid.html
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ Send comments and bug fixes to Antonio Larrosa <larrosa@kde.org>
+
+***************************************************************************/
+#include "synthout.h"
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include "sndcard.h"
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/param.h>
+#include "awe_sup.h"
+#include "midispec.h"
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+SEQ_USE_EXTBUF();
+
+SynthOut::SynthOut(int d)
+{
+ seqfd = -1;
+ devicetype=KMID_SYNTH;
+ device= d;
+ _ok=1;
+}
+
+SynthOut::~SynthOut()
+{
+ closeDev();
+}
+
+void SynthOut::openDev (int sqfd)
+{
+ _ok=1;
+ seqfd = sqfd;
+ if (seqfd==-1)
+ {
+ printfdebug("ERROR: Could not open /dev/sequencer\n");
+ return;
+ }
+#ifdef HAVE_OSS_SUPPORT
+ /*
+ int i=1;
+ ioctl(seqfd,SNDCTL_SEQ_THRESHOLD,i);
+ printfdebug("Threshold : %d\n",i);
+ */
+#ifdef SYNTHOUTDEBUG
+ printfdebug("Number of synth devices : %d\n",ndevs);
+ printfdebug("Number of midi ports : %d\n",nmidiports);
+ printfdebug("Rate : %d\n",m_rate);
+#endif
+
+#ifdef HAVE_AWE32
+
+ struct synth_info info;
+
+ // Should really collect the possible devices and let the user choose ?
+
+ info.device = device;
+
+ if (ioctl (seqfd, SNDCTL_SYNTH_INFO, &info) == -1)
+ printfdebug(" ioctl SNDCTL_SYNTH_INFO FAILED \n");
+
+ if (info.synth_type == SYNTH_TYPE_SAMPLE
+ && info.synth_subtype == SAMPLE_TYPE_AWE32)
+ {
+
+ // Enable layered patches ....
+ AWE_SET_CHANNEL_MODE(device,1);
+#ifdef SYNTHOUTDEBUG
+ printfdebug(" Found AWE32 dev=%d \n",device);
+#endif
+ }
+#endif // HAVE_AWE32
+#endif // HAVE_OSS_SUPPORT
+
+}
+
+void SynthOut::closeDev (void)
+{
+ if (!ok()) return;
+ //if (seqfd>=0) close(seqfd);
+ seqfd=-1;
+}
+
+void SynthOut::initDev (void)
+{
+#ifdef HAVE_OSS_SUPPORT
+ int chn;
+ if (!ok()) return;
+ uchar gm_reset[5]={0x7e, 0x7f, 0x09, 0x01, 0xf7};
+ sysex(gm_reset, sizeof(gm_reset));
+ for (chn=0;chn<16;chn++)
+ {
+ chnmute[chn]=0;
+ chnPatchChange(chn,0);
+ chnPressure(chn,127);
+ chnPitchBender(chn, 0x00, 0x40);
+ chnController(chn, CTL_MAIN_VOLUME,127);
+ chnController(chn, CTL_EXT_EFF_DEPTH, 0);
+ chnController(chn, CTL_CHORUS_DEPTH, 0);
+ chnController(chn, 0x4a, 127);
+ }
+#endif
+}
+
+void SynthOut::noteOn (uchar chn, uchar note, uchar vel)
+{
+ if (vel==0)
+ {
+ noteOff(chn,note,vel);
+ }
+ else
+ {
+ SEQ_START_NOTE(device, map->channel(chn),
+ map->key(chn,chnpatch[chn],note),
+ vel);
+ }
+#ifdef SYNTHOUTDEBUG
+ printfdebug("Note ON >\t chn : %d\tnote : %d\tvel: %d\n",chn,note,vel);
+#endif
+}
+
+void SynthOut::noteOff (uchar chn, uchar note, uchar)
+{
+ SEQ_STOP_NOTE(device, map->channel(chn),
+ map->key(chn,chnpatch[chn],note), 0);
+#ifdef SYNTHOUTDEBUG
+ printfdebug("Note OFF >\t chn : %d\tnote : %d\tvel: %d\n",chn,note,vel);
+#endif
+}
+
+void SynthOut::keyPressure (uchar chn, uchar note, uchar vel)
+{
+ SEQ_KEY_PRESSURE(device, map->channel(chn), map->key(chn,chnpatch[chn],note),vel);
+}
+
+void SynthOut::chnPatchChange (uchar chn, uchar patch)
+{
+ SEQ_SET_PATCH(device,map->channel(chn),map->patch(chn,patch));
+ chnpatch[chn]=patch;
+}
+
+void SynthOut::chnPressure (uchar chn, uchar vel)
+{
+ SEQ_CHN_PRESSURE(device, map->channel(chn) , vel);
+ chnpressure[chn]=vel;
+}
+
+void SynthOut::chnPitchBender(uchar chn,uchar lsb, uchar msb)
+{
+ chnbender[chn]=((int)msb<<7) | (lsb & 0x7F);
+ SEQ_BENDER(device, map->channel(chn), chnbender[chn]);
+}
+
+void SynthOut::chnController (uchar chn, uchar ctl, uchar v)
+{
+ if ((ctl==11)||(ctl==7))
+ {
+ v=(v*volumepercentage)/100;
+ if (v>127) v=127;
+ }
+
+ SEQ_CONTROL(device, map->channel(chn), ctl, v);
+ chncontroller[chn][ctl]=v;
+}
+
+void SynthOut::sysex(uchar *, ulong )
+{
+ // AWE32 doesn't respond to sysex (AFAIK)
+/*
+#ifndef HAVE_AWE32
+ ulong i=0;
+ SEQ_MIDIOUT(device, MIDI_SYSTEM_PREFIX);
+ while (i<size)
+ {
+ SEQ_MIDIOUT(device, *data);
+ data++;
+ i++;
+ };
+ printfdebug("sysex\n");
+#endif
+*/
+}
diff --git a/libkmid/synthout.h b/libkmid/synthout.h
new file mode 100644
index 000000000..4d34336ee
--- /dev/null
+++ b/libkmid/synthout.h
@@ -0,0 +1,118 @@
+/* synthout.h - class synthOut which handles the /dev/sequencer device
+ for synths (as AWE32)
+ This file is part of LibKMid 0.9.5
+ Copyright (C) 1997,98 Antonio Larrosa Jimenez and P.J.Leonard
+ 1999,2000 Antonio Larrosa Jimenez
+ LibKMid's homepage : http://www.arrakis.es/~rlarrosa/libkmid.html
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ Send comments and bug fixes to Antonio Larrosa <larrosa@kde.org>
+
+***************************************************************************/
+#ifndef _SYNTHOUT_H
+#define _SYNTHOUT_H
+
+#include <libkmid/midiout.h>
+
+/**
+ * Synth (AWE) device output class . SynthOut is used to send MIDI events to
+ * a general synthesizer, such as AWE synth.
+ *
+ * SynthOut inherits MidiOut and supports the same simple API.
+ *
+ * The preferred way to use this class is by selecting a synth (or AWE)
+ * device with MidiManager::setDefaultDevice(), and use a
+ * MidiManager object.
+ *
+ * @short Sends MIDI events to AWE synthesizers
+ * @version 0.9.5 17/01/2000
+ * @author Antonio Larrosa Jimenez <larrosa@kde.org>
+ */
+class SynthOut : public MidiOut
+{
+ private:
+ class SynthOutPrivate;
+ SynthOutPrivate *di;
+
+ public:
+ /**
+ * Constructor. See MidiOut::MidiOut() for more information.
+ */
+ SynthOut(int d=0);
+
+ /**
+ * Destructor.
+ */
+ ~SynthOut();
+
+ /**
+ * See MidiOut::openDev()
+ */
+ void openDev (int sqfd);
+
+ /**
+ * See MidiOut::closeDev()
+ */
+ void closeDev(void);
+
+ /**
+ * See MidiOut::initDev()
+ */
+ void initDev (void);
+
+ /**
+ * See MidiOut::noteOn()
+ */
+ void noteOn ( uchar chn, uchar note, uchar vel );
+
+ /**
+ * See MidiOut::noteOff()
+ */
+ void noteOff ( uchar chn, uchar note, uchar vel );
+
+ /**
+ * See MidiOut::keyPressure()
+ */
+ void keyPressure ( uchar chn, uchar note, uchar vel );
+
+ /**
+ * See MidiOut::chnPatchChange()
+ */
+ void chnPatchChange ( uchar chn, uchar patch );
+
+ /**
+ * See MidiOut::chnPressure()
+ */
+ void chnPressure ( uchar chn, uchar vel );
+
+ /**
+ * See MidiOut::chnPitchBender()
+ */
+ void chnPitchBender ( uchar chn, uchar lsb, uchar msb );
+
+ /**
+ * See MidiOut::chnController()
+ */
+ void chnController ( uchar chn, uchar ctl , uchar v );
+
+ /**
+ * It's an empty function, as AWE devices don't support System Exclusive
+ * messages
+ */
+ void sysex ( uchar *data,ulong size);
+};
+
+#endif
diff --git a/libkmid/tests/Kathzy.mid b/libkmid/tests/Kathzy.mid
new file mode 100644
index 000000000..f3a857fba
--- /dev/null
+++ b/libkmid/tests/Kathzy.mid
Binary files differ
diff --git a/libkmid/tests/Makefile.am b/libkmid/tests/Makefile.am
new file mode 100644
index 000000000..58bf5d58b
--- /dev/null
+++ b/libkmid/tests/Makefile.am
@@ -0,0 +1,14 @@
+
+INCLUDES = -I$(srcdir)/.. $(all_includes)
+
+noinst_PROGRAMS = ctest apitest notesoff
+
+ctest_SOURCES = ctest.c
+ctest_LDADD = ../libkmid.la
+
+apitest_SOURCES = apitest.cc
+apitest_LDADD = ../libkmid.la
+
+notesoff_SOURCES = notesoff.cc
+notesoff_LDADD = ../libkmid.la
+
diff --git a/libkmid/tests/apitest.cc b/libkmid/tests/apitest.cc
new file mode 100644
index 000000000..308fe2957
--- /dev/null
+++ b/libkmid/tests/apitest.cc
@@ -0,0 +1,25 @@
+#include <libkmid/libkmid.h>
+#include <unistd.h>
+#include <stdio.h>
+
+int main (int , char **)
+{
+ printf("Libkmid test2 . (C) 2000 Antonio Larrosa Jimenez . Malaga (Spain)\n");
+ printf("Using libkmid from a simple C++ application\n");
+
+ KMidSimpleAPI::kMidInit();
+ KMidSimpleAPI::kMidLoad("Kathzy.mid");
+ KMidSimpleAPI::kMidPlay();
+
+ for (int i=0;i<30;i++)
+ {
+ printf("%d/30 seconds\n",i+1);
+ sleep(1);
+ };
+
+ KMidSimpleAPI::kMidStop();
+ KMidSimpleAPI::kMidDestruct();
+
+ return 0;
+};
+
diff --git a/libkmid/tests/ctest.c b/libkmid/tests/ctest.c
new file mode 100644
index 000000000..6e427a2c7
--- /dev/null
+++ b/libkmid/tests/ctest.c
@@ -0,0 +1,54 @@
+#include <libkmid/libkmid.h>
+#include <unistd.h>
+#include <stdio.h>
+
+int main (int argc, char **argv)
+{
+ int i;
+ char c;
+
+ printf("Test1 %s. Using libkmid from a simple C application\n",kMidVersion());
+ printf("%s\n",kMidCopyright());
+
+ if (kMidInit()!=0)
+ {
+ printf("Error initializing libkmid\n");
+ return 0;
+ }
+
+ if (kMidDevices()!=0)
+ {
+ printf("Available devices :\n");
+ for (i=0;i<kMidDevices();i++)
+ {
+ printf(" %d) %s - %s\n",i,kMidName(i),kMidType(i));
+ }
+ printf("\nSelect one:");
+ c=getc(stdin);
+ }
+ else
+ {
+ printf("There's no available devices. Let's pretend we didn't noticed it\n");
+ printf("and start playing.\n");
+ c='0';
+ }
+
+ kMidSetDevice((int)(c-'0'));
+
+ kMidLoad("Kathzy.mid");
+ kMidPlay();
+
+ for (i=0;i<45;i++)
+ {
+ printf("%d/45 seconds\n",i+1);
+ sleep(1);
+ }
+
+ kMidStop();
+ kMidDestruct();
+
+
+ return 0;
+
+};
+
diff --git a/libkmid/tests/notesoff.cc b/libkmid/tests/notesoff.cc
new file mode 100644
index 000000000..15cbb7f73
--- /dev/null
+++ b/libkmid/tests/notesoff.cc
@@ -0,0 +1,24 @@
+#include <libkmid/libkmid.h>
+#include <libkmid/deviceman.h>
+#include <unistd.h>
+#include <stdio.h>
+
+int main (int , char **)
+{
+ printf("Libkmid Notes Off . (C) 2000 Antonio Larrosa Jimenez . Malaga (Spain)\n");
+ printf("Using libkmid from a simple C++ application\n");
+
+ KMidSimpleAPI::kMidInit();
+
+ kMid.midi->checkInit();
+ kMid.midi->openDev();
+ kMid.midi->initDev();
+
+ kMid.midi->allNotesOff();
+ kMid.midi->closeDev();
+
+ KMidSimpleAPI::kMidDestruct();
+
+ return 0;
+};
+
diff --git a/libkmid/track.cc b/libkmid/track.cc
new file mode 100644
index 000000000..f936906e7
--- /dev/null
+++ b/libkmid/track.cc
@@ -0,0 +1,566 @@
+/**************************************************************************
+
+ track.cc - class track, which has a midi file track and its events
+ This file is part of LibKMid 0.9.5
+ Copyright (C) 1997,98,99,2000 Antonio Larrosa Jimenez
+ LibKMid's homepage : http://www.arrakis.es/~rlarrosa/libkmid.html
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ Send comments and bug fixes to Antonio Larrosa <larrosa@kde.org>
+
+***************************************************************************/
+
+#include "track.h"
+#include <stdlib.h>
+#include "sndcard.h"
+#include "midispec.h"
+#include "midfile.h"
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#define T2MS(ticks) (((double)ticks)*(double)60000L)/((double)tempoToMetronomeTempo(tempo)*(double)tPCN)
+
+#define MS2T(ms) (((ms)*(double)tempoToMetronomeTempo(tempo)*(double)tPCN)/((double)60000L))
+
+#define PEDANTIC_TRACK
+#define CHANGETEMPO_ONLY_IN_TRACK0
+//#define TRACKDEBUG
+//#define TRACKDEBUG2
+
+MidiTrack::MidiTrack(FILE *file,int tpcn,int Id)
+{
+ id=Id;
+ tPCN=tpcn;
+ currentpos=0;
+ size=0;
+ data=0L;
+ tempo=1000000;
+ if (feof(file))
+ {
+ clear();
+ return;
+ };
+ size=readLong(file);
+#ifdef TRACKDEBUG
+ printf("Track %d : Size %ld\n",id,size);
+#endif
+ data=new uchar[size];
+ if (data==NULL)
+ {
+ perror("track: Not enough memory ?");
+ exit(-1);
+ }
+ ulong rsize=0;
+ if ((rsize=fread(data,1,size,file))!=size)
+ {
+ fprintf(stderr,"track (%d): File is corrupt : Couldn't load track (%ld!=%ld) !!\n", id, rsize, size);
+ size=rsize;
+ };
+ /*
+ ptrdata=data;
+ current_ticks=0;
+ delta_ticks=readVariableLengthValue();
+ wait_ticks=delta_ticks;
+ endoftrack=0;
+ */
+ init();
+}
+
+MidiTrack::~MidiTrack()
+{
+ delete data;
+ endoftrack=1;
+ currentpos=0;
+ size=0;
+}
+
+int MidiTrack::power2to(int i)
+{
+ return 1<<i;
+}
+
+ulong MidiTrack::readVariableLengthValue(void)
+{
+ ulong dticks=0;
+
+ while ((*ptrdata) & 0x80)
+ {
+#ifdef PEDANTIC_TRACK
+ if (currentpos>=size)
+ {
+ endoftrack=1;
+ fprintf(stderr, "track (%d) : EndofTrack found by accident !\n",id);
+ delta_ticks = wait_ticks = ~0;
+ time_at_next_event=10000 * 60000L;
+ return 0;
+ }
+ else
+#endif
+ {
+ dticks=(dticks << 7) | (*ptrdata) & 0x7F;
+ ptrdata++;currentpos++;
+ }
+
+ }
+ dticks=((dticks << 7) | (*ptrdata) & 0x7F);
+ ptrdata++;currentpos++;
+
+#ifdef PEDANTIC_TRACK
+
+ if (currentpos>=size)
+ {
+ endoftrack=1;
+ fprintf(stderr,"track (%d): EndofTrack found by accident 2 !\n",id);
+ dticks=0;
+ delta_ticks = wait_ticks = ~0;
+ time_at_next_event=10000 * 60000L;
+ return 0;
+ }
+#endif
+#ifdef TRACKDEBUG
+ printfdebug("track(%d): DTICKS : %ld\n",id,dticks);
+ usleep(10);
+#endif
+ return dticks;
+}
+
+int MidiTrack::ticksPassed (ulong ticks)
+{
+ if (endoftrack==1) return 0;
+ if (ticks>wait_ticks)
+ {
+ printfdebug("track (%d): ERROR : TICKS PASSED > WAIT TICKS\n", id);
+ return 1;
+ }
+ wait_ticks-=ticks;
+ return 0;
+}
+
+int MidiTrack::msPassed (ulong ms)
+{
+ if (endoftrack==1) return 0;
+ current_time+=ms;
+ //fprintf(stderr, "old + %ld = CURR %g ", ms,current_time);
+ if ( current_time>time_at_next_event )
+ {
+ fprintf(stderr, "track (%d): ERROR : MS PASSED > WAIT MS\n", id);
+ return 1;
+ }
+#ifdef TRACKDEBUG
+ if (current_time==time_at_next_event) printfdebug("track(%d): _OK_",id);
+#endif
+ return 0;
+}
+
+int MidiTrack::currentMs(double ms)
+{
+ if (endoftrack==1) return 0;
+ current_time=ms;
+ //printfdebug("CURR %g",current_time);
+#ifdef PEDANTIC_TRACK
+ if (current_time>time_at_next_event)
+ {
+ fprintf(stderr,"track(%d): ERROR : MS PASSED > WAIT MS\n", id);
+ exit(-1);
+ return 1;
+ }
+#endif
+ return 0;
+}
+
+void MidiTrack::readEvent(MidiEvent *ev)
+{
+ int i,j;
+ if (endoftrack==1)
+ {
+ ev->command=0;
+ return;
+ }
+ /*
+ printfdebug("...... %d\n",id);
+ printfdebug("current : %g , tane : %g\n",current_time,time_at_next_event);
+ printfdebug("......\n");
+ */
+ int skip_event=0;
+ current_time=time_at_next_event;
+ if (((*ptrdata)&0x80)!=0)
+ {
+ ev->command=(*ptrdata);
+ ptrdata++;currentpos++;
+ lastcommand=ev->command;
+ }
+ else
+ {
+ ev->command=lastcommand;
+ }
+
+#ifdef PEDANTIC_TRACK
+ if (currentpos>=size)
+ {
+ endoftrack=1;
+ delta_ticks = wait_ticks = ~0;
+ time_at_next_event=10000 * 60000L;
+ ev->command=MIDI_SYSTEM_PREFIX;
+ ev->chn=0xF;
+ ev->d1=ME_END_OF_TRACK;
+ fprintf(stderr, "track (%d): EndofTrack found by accident 3\n",id);
+ return;
+ }
+#endif
+
+ ev->chn=ev->command & 0xF;
+ ev->command=ev->command & 0xF0;
+ switch (ev->command)
+ {
+ case (MIDI_NOTEON) :
+ ev->note = *ptrdata;ptrdata++;currentpos++;
+ ev->vel = *ptrdata;ptrdata++;currentpos++;
+ if (ev->vel==0)
+ note[ev->chn][ev->note]=FALSE;
+ else
+ note[ev->chn][ev->note]=TRUE;
+
+#ifdef TRACKDEBUG2
+ if (ev->chn==6) {
+ if (ev->vel==0) printfdebug("Note Onf\n");
+ else printfdebug("Note On\n");
+ };
+#endif
+ break;
+ case (MIDI_NOTEOFF) :
+#ifdef TRACKDEBUG2
+ if (ev->chn==6) printfdebug("Note Off\n");
+#endif
+ ev->note = *ptrdata;ptrdata++;currentpos++;
+ ev->vel = *ptrdata;ptrdata++;currentpos++;
+ note[ev->chn][ev->note]=FALSE;
+
+ break;
+ case (MIDI_KEY_PRESSURE) :
+#ifdef TRACKDEBUG2
+ if (ev->chn==6) printfdebug ("Key press\n");
+#endif
+ ev->note = *ptrdata;ptrdata++;currentpos++;
+ ev->vel = *ptrdata;ptrdata++;currentpos++;
+ break;
+ case (MIDI_PGM_CHANGE) :
+#ifdef TRACKDEBUG2
+ if (ev->chn==6) printfdebug ("Pgm\n");
+#endif
+ ev->patch = *ptrdata;ptrdata++;currentpos++;
+ break;
+ case (MIDI_CHN_PRESSURE) :
+#ifdef TRACKDEBUG2
+ if (ev->chn==6) printfdebug ("Chn press\n");
+#endif
+ ev->vel = *ptrdata;ptrdata++;currentpos++;
+ break;
+ case (MIDI_PITCH_BEND) :
+#ifdef TRACKDEBUG2
+ if (ev->chn==6) printfdebug ("Pitch\n");
+#endif
+ ev->d1 = *ptrdata;ptrdata++;currentpos++;
+ ev->d2 = *ptrdata;ptrdata++;currentpos++;
+ break;
+ case (MIDI_CTL_CHANGE) :
+#ifdef TRACKDEBUG2
+ if (ev->chn==6) printfdebug (stderr, "Ctl\n");
+#endif
+ ev->ctl = *ptrdata;ptrdata++; currentpos++;
+ ev->d1 = *ptrdata;ptrdata++;currentpos++;
+ /*
+ switch (ev->ctl)
+ {
+ case (96) : printfdebug("RPN Increment\n");break;
+ case (97) : printfdebug("RPN Decrement\n");break;
+ case (98) : printfdebug("nRPN 98 %d\n",ev->d1);break;
+ case (99) : printfdebug("nRPN 99 %d\n",ev->d1);break;
+ case (100) : printfdebug("RPN 100 %d\n",ev->d1);break;
+ case (101) : printfdebug("RPN 101 %d\n",ev->d1);break;
+ };
+ */
+ break;
+
+ case (MIDI_SYSTEM_PREFIX) :
+#ifdef TRACKDEBUG2
+ if (ev->chn==6) printfdebug ("Sys Prefix\n");
+#endif
+ switch ((ev->command|ev->chn))
+ {
+ case (0xF0) :
+ case (0xF7) :
+ ev->length=readVariableLengthValue();
+#ifdef PEDANTIC_TRACK
+ if (endoftrack)
+ {
+ ev->command=MIDI_SYSTEM_PREFIX;
+ ev->chn=0xF;
+ ev->d1=ME_END_OF_TRACK;
+ }
+ else
+#endif
+ {
+ ev->data=ptrdata;
+ ptrdata+=ev->length;currentpos+=ev->length;
+ }
+ break;
+ case (0xFE):
+ case (0xF8):
+ // printfdebug("Active sensing\n");
+ break;
+ case (META_EVENT) :
+ ev->d1=*ptrdata;ptrdata++;currentpos++;
+ switch (ev->d1)
+ {
+ case (ME_END_OF_TRACK) :
+ i=0;
+ j=0;
+ while ((note[i][j]==FALSE)&&(i<16))
+ {
+ j++;
+ if (j==128) { j=0; i++; };
+ }
+ if (i<16) // that is, if there is any key still pressed
+ {
+ ptrdata--;currentpos--;
+ ev->chn=i;
+ ev->command=MIDI_NOTEOFF;
+ ev->note = j;
+ ev->vel = 0;
+ note[ev->chn][ev->note]=FALSE;
+ fprintf(stderr,"Note Off(simulated)\n");
+ return;
+ }
+ else
+ {
+ endoftrack=1;
+ delta_ticks = wait_ticks = ~0;
+ time_at_next_event=10000 * 60000L;
+#ifdef TRACKDEBUG
+ printfdebug("EndofTrack %d event\n",id);
+#endif
+ }
+ break;
+ case (ME_SET_TEMPO):
+ ev->length=readVariableLengthValue();
+#ifdef PEDANTIC_TRACK
+ if (endoftrack)
+ {
+ ev->command=MIDI_SYSTEM_PREFIX;
+ ev->chn=0xF;
+ ev->d1=ME_END_OF_TRACK;
+ }
+ else
+#endif
+ {
+ ev->data=ptrdata;
+ ptrdata+=ev->length;currentpos+=ev->length;
+ // tempo=((ev->data[0]<<16)|(ev->data[1]<<8)|(ev->data[2]));
+ // ticks_from_previous_tempochange=0;
+ // time_at_previous_tempochange=current_time;
+#ifdef TRACKDEBUG
+ printfdebug("Track %d : Set Tempo : %ld\n",id,tempo);
+#endif
+#ifdef CHANGETEMPO_ONLY_IN_TRACK0
+ if (id!=0) skip_event=1;
+#endif
+ }
+ break;
+ case (ME_TIME_SIGNATURE) :
+ ev->length=*ptrdata;ptrdata++;currentpos++;
+ ev->d2=*ptrdata;ptrdata++;currentpos++;
+ ev->d3=power2to(*ptrdata);ptrdata++;currentpos++;
+ ev->d4=*ptrdata;ptrdata++;currentpos++;
+ ev->d5=*ptrdata;ptrdata++;currentpos++;
+#ifdef TRACKDEBUG
+ printfdebug("TIME SIGNATURE :\n");
+ printfdebug("%d\n",ev->d2);
+ printfdebug("---- %d metronome , %d number of 32nd notes per quarter note\n",ev->d4,ev->d5);
+ printfdebug("%d\n",ev->d3);
+#endif
+ break;
+ case (ME_TRACK_SEQ_NUMBER) :
+ case (ME_TEXT) :
+ case (ME_COPYRIGHT) :
+ case (ME_SEQ_OR_TRACK_NAME) :
+ case (ME_TRACK_INSTR_NAME) :
+ case (ME_LYRIC) :
+ case (ME_MARKER) :
+ case (ME_CUE_POINT) :
+ case (ME_CHANNEL_PREFIX) :
+ case (ME_MIDI_PORT) :
+ case (ME_SMPTE_OFFSET) :
+ case (ME_KEY_SIGNATURE) :
+ ev->length=readVariableLengthValue();
+#ifdef PEDANTIC_TRACK
+ if (endoftrack)
+ {
+ ev->command=MIDI_SYSTEM_PREFIX;
+ ev->chn=0xF;
+ ev->d1=ME_END_OF_TRACK;
+ }
+ else
+#endif
+ {
+ ev->data=ptrdata;
+ ptrdata+=ev->length;currentpos+=ev->length;
+ }
+ break;
+ default:
+#ifdef GENERAL_DEBUG_MESSAGES
+ fprintf(stderr,"track (%d) : Default handler for meta event " \
+ "0x%x\n", id, ev->d1);
+#endif
+ ev->length=readVariableLengthValue();
+#ifdef PEDANTIC_TRACK
+ if (endoftrack)
+ {
+ ev->command=MIDI_SYSTEM_PREFIX;
+ ev->chn=0xF;
+ ev->d1=ME_END_OF_TRACK;
+ }
+ else
+#endif
+ {
+ ev->data=ptrdata;
+ ptrdata+=ev->length;currentpos+=ev->length;
+ }
+ break;
+ }
+ break;
+ default :
+ fprintf(stderr,"track (%d): Default handler for system event 0x%x\n",
+ id, (ev->command|ev->chn));
+ break;
+ }
+ break;
+ default :
+ fprintf(stderr,"track (%d): Default handler for event 0x%x\n",
+ id, (ev->command|ev->chn));
+ break;
+ }
+#ifdef PEDANTIC_TRACK
+ if (currentpos>=size)
+ {
+ endoftrack=1;
+ delta_ticks = wait_ticks = ~0;
+ time_at_next_event=10000 * 60000L;
+ printfdebug("track (%d): EndofTrack reached\n",id);
+ }
+#endif
+ if (endoftrack==0)
+ {
+ current_ticks+=delta_ticks;
+ delta_ticks=readVariableLengthValue();
+#ifdef PEDANTIC_TRACK
+ if (endoftrack)
+ {
+ ev->command=MIDI_SYSTEM_PREFIX;
+ ev->chn=0xF;
+ ev->d1=ME_END_OF_TRACK;
+ return;
+ }
+#endif
+ ticks_from_previous_tempochange+=delta_ticks;
+
+ time_at_next_event=T2MS(ticks_from_previous_tempochange)+time_at_previous_tempochange;
+ /*
+ printf("tane2 : %g, ticks : %g, delta_ticks %ld, tempo : %ld\n",
+ time_at_next_event,ticks_from_previous_tempochange,delta_ticks,tempo);
+ printf("timeatprevtc %g , curr %g\n",time_at_previous_tempochange,current_time);
+ */
+ wait_ticks=delta_ticks;
+
+ }
+ if (skip_event) readEvent(ev);
+}
+
+
+void MidiTrack::clear(void)
+{
+ endoftrack=1;
+ ptrdata=data;
+ current_ticks=0;
+ currentpos=0;
+
+ for (int i=0;i<16;i++)
+ for (int j=0;j<128;j++)
+ note[i][j]=FALSE;
+
+ delta_ticks = wait_ticks = ~0;
+ time_at_previous_tempochange=0;
+ current_time=0;
+ ticks_from_previous_tempochange=0;
+ tempo=1000000;
+ time_at_next_event=10000 * 60000L;
+
+}
+
+
+void MidiTrack::init(void)
+{
+ if (data==0L) { clear(); return; };
+ endoftrack=0;
+ ptrdata=data;
+ current_ticks=0;
+ currentpos=0;
+
+ for (int i=0;i<16;i++)
+ for (int j=0;j<128;j++)
+ note[i][j]=FALSE;
+
+ delta_ticks=readVariableLengthValue();
+ if (endoftrack) return;
+ wait_ticks=delta_ticks;
+
+
+ time_at_previous_tempochange=0;
+ current_time=0;
+ ticks_from_previous_tempochange=wait_ticks;
+ tempo=1000000;
+ time_at_next_event=T2MS(delta_ticks);
+ //printf("tane1 : %g\n",time_at_next_event);
+}
+
+void MidiTrack::changeTempo(ulong t)
+{
+ if (endoftrack==1) return;
+ if (tempo==t) return;
+ double ticks;
+ time_at_previous_tempochange=current_time;
+ ticks=MS2T(time_at_next_event-current_time);
+ tempo=t;
+ time_at_next_event=T2MS(ticks)+current_time;
+ ticks_from_previous_tempochange=ticks;
+
+}
+
+/*
+double MidiTrack::absMsOfNextEvent (void)
+{
+ //printf("%d : %g\n",id,time_at_next_event);
+ return time_at_next_event;
+}
+*/
+
+#undef T2MS
+#undef MS2T
diff --git a/libkmid/track.h b/libkmid/track.h
new file mode 100644
index 000000000..073fc9825
--- /dev/null
+++ b/libkmid/track.h
@@ -0,0 +1,237 @@
+/* track.h - class track, which has a midi file track and its events
+ This file is part of LibKMid 0.9.5
+ Copyright (C) 1997,98,99,2000 Antonio Larrosa Jimenez
+ LibKMid's homepage : http://www.arrakis.es/~rlarrosa/libkmid.html
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ Send comments and bug fixes to Antonio Larrosa <larrosa@kde.org>
+
+***************************************************************************/
+#ifndef _TRACK_H
+#define _TRACK_H
+
+#include <stdio.h>
+#include <libkmid/dattypes.h>
+
+/**
+ * An structure that represents a MIDI event.
+ *
+ * @short Represents a MIDI event
+ * @version 0.9.5 17/01/2000
+ * @author Antonio Larrosa Jimenez <larrosa@kde.org>
+ */
+struct MidiEvent
+{
+ /**
+ * MIDI Command
+ *
+ * Caution, if a command doesn't use a variable, it may contain garbage.
+ */
+ uchar command;
+
+ /**
+ * Channel
+ */
+ uchar chn;
+
+ /**
+ * Note
+ */
+ uchar note;
+
+ /**
+ * Velocity
+ */
+ uchar vel;
+
+ /**
+ * Patch (if command was a change patch command)
+ */
+ uchar patch;
+
+ /**
+ * Patch (if command was a controller command)
+ */
+ uchar ctl;
+
+ /**
+ * Data 1
+ */
+ uchar d1;
+
+ /**
+ * Data 2
+ */
+ uchar d2;
+
+ /**
+ * Data 3
+ */
+ uchar d3;
+
+ /**
+ * Data 4
+ */
+ uchar d4;
+
+ /**
+ * Data 5
+ */
+ uchar d5;
+
+ /**
+ * Data 6
+ */
+ uchar d6;
+
+ /**
+ * Length of the generic data variable
+ */
+ ulong length;
+
+ /**
+ * The data for commands like text, sysex, etc.
+ */
+ uchar *data;
+
+};
+
+/**
+ * Stores a MIDI track. This can be thought of as a list of MIDI events.
+ *
+ * The data types used to store the track is similar to how events are
+ * stored on a MIDI file, but used in a way that allows for faster parses.
+ *
+ * This class is used on MidiPlayer::loadSong() to load the song and
+ * later play it with MidiPlayer::play().
+ *
+ * @short Stores a MIDI track with a simple API
+ * @version 0.9.5 17/01/2000
+ * @author Antonio Larrosa Jimenez <larrosa@kde.org>
+ */
+class MidiTrack
+{
+ private:
+ class MidiTrackPrivate;
+ MidiTrackPrivate *d;
+
+ int id;
+
+ ulong size;
+ uchar *data;
+ uchar *ptrdata;
+
+ bool note[16][128]; // Notes that are set on or off by this track
+ ulong current_ticks; // Total number of ticks since beginning of song
+ ulong delta_ticks; // Delta ticks from previous event to next event
+ ulong wait_ticks; // Wait ticks from previous event in other track
+ // to next event in this track
+
+ ulong currentpos; // Some songs don't have a endoftrack event, so
+ // we have to see when currentpos > size
+ int endoftrack;
+
+ ulong readVariableLengthValue(void);
+
+ uchar lastcommand; // This is to run light without overbyte :-)
+
+
+ double current_time; // in ms.
+ double time_at_previous_tempochange; // in ms.
+ double ticks_from_previous_tempochange; // in ticks
+ // double time_to_next_event; // in ms.
+ double time_at_next_event; // in ms.
+ int tPCN;
+ ulong tempo;
+
+ int power2to(int i);
+
+ public:
+ /**
+ * Constructor.
+ * @param file the file to read the track from. It should be ready at the
+ * start of a track. MidiTrack reads just that track and the file is left at
+ * the end of this track).
+ * @param tpcn the ticks per cuarter note used in this file.
+ * @param Id the ID for this track.
+ */
+ MidiTrack(FILE *file,int tpcn,int Id);
+
+ /**
+ * Destructor
+ */
+ ~MidiTrack();
+
+ /**
+ * Makes the iterator advance the given number of ticks.
+ *
+ * @return 0 if OK, and 1 if you didn't handle this track well and you
+ * forgot to take an event (thing that will never happen if you use
+ * MidiPlayer::play() ).
+ */
+ int ticksPassed (ulong ticks);
+
+ /**
+ * Makes the iterator advance the given number of milliseconds.
+ *
+ * @return 0 if OK, and 1 if you didn't handle this track well and you
+ * forgot to take an event (thing that will never happen if you use
+ * MidiPlayer::play() ).
+ */
+ int msPassed (ulong ms);
+
+ /**
+ * Returns the current millisecond which the iterator is at.
+ */
+ int currentMs (double ms);
+
+ /**
+ * Returns the number of ticks left for the next event.
+ */
+ ulong waitTicks (void) { return wait_ticks; }
+
+ // ulong waitMs (void) {return time_to_next_event;};
+
+ /**
+ * Returns the absolute number of milliseconds of the next event.
+ */
+ double absMsOfNextEvent (void) { return time_at_next_event; }
+
+ /**
+ * Change the tempo of the song.
+ */
+ void changeTempo(ulong t);
+
+ /**
+ * Reads the event at the iterator position, and puts it on the structure
+ * pointed to by @p ev.
+ */
+ void readEvent(MidiEvent *ev);
+
+ /**
+ * Initializes the iterator.
+ */
+ void init(void);
+
+ /**
+ * Clears the internal variables.
+ */
+ void clear(void);
+
+};
+
+#endif
diff --git a/libkmid/voiceman.cc b/libkmid/voiceman.cc
new file mode 100644
index 000000000..8e7863720
--- /dev/null
+++ b/libkmid/voiceman.cc
@@ -0,0 +1,279 @@
+/**************************************************************************
+
+ voiceman.cc - The VoiceManager class handles a set of voices for synths
+ This file is part of LibKMid 0.9.5
+ Copyright (C) 1997,98,99,2000 Antonio Larrosa Jimenez
+ LibKMid's homepage : http://www.arrakis.es/~rlarrosa/libkmid.html
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ Send comments and bug fixes to Antonio Larrosa <larrosa@kde.org>
+
+***************************************************************************/
+
+#include "voiceman.h"
+#include <stdio.h>
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+VoiceManager::VoiceManager(int totalvoices)
+{
+ nvoices=totalvoices;
+
+ FirstVoice=new voice;
+ FirstVoice->id=0;
+ FirstVoice->channel=0;
+ FirstVoice->note=0;
+ FirstVoice->used=0;
+ FirstVoice->prev=NULL;
+
+ voice *ptrb=FirstVoice;
+ voice *ptr=NULL;
+ int i;
+ for (i=1;i<nvoices;i++)
+ {
+ ptr=new voice;
+ ptrb->next=ptr;
+ ptr->id=i;
+ ptr->channel=0;
+ ptr->note=0;
+ ptr->used=0;
+ ptr->prev=ptrb;
+ ptrb=ptr;
+ }
+ LastVoice=ptr;
+ LastVoice->next=NULL;
+ LastnotusedVoice=LastVoice;
+
+ VoiceList=new voice *[nvoices];
+ ptr=FirstVoice;
+ for (i=0;i<nvoices;i++)
+ {
+ VoiceList[i]=ptr;
+ ptr=ptr->next;
+ }
+ searcher_aid=new voice;
+}
+
+VoiceManager::~VoiceManager()
+{
+ voice *ptr=FirstVoice;
+ voice *ptr2;
+ while (ptr!=NULL)
+ {
+ ptr2=ptr->next;
+ delete ptr;
+ ptr=ptr2;
+ }
+ FirstVoice=NULL;
+ LastVoice=NULL;
+ LastnotusedVoice=NULL;
+
+ delete [] VoiceList;
+ VoiceList=NULL;
+
+ delete searcher_aid;
+}
+
+void VoiceManager::clearLists(void)
+{
+#ifdef VOICEMANDEBUG
+ printf("voicemanager::cleanLists\n");
+#endif
+ voice *ptr=FirstVoice;
+ voice *ptr2=FirstVoice;
+ while (ptr!=NULL)
+ {
+ ptr->used=0;
+ ptr2=ptr;
+ ptr=ptr->next;
+ }
+ LastVoice=ptr2;
+ LastnotusedVoice=ptr2;
+
+}
+
+int VoiceManager::allocateVoice(int chn,int key)
+{
+ // First, we take the allocated voice out of the first place of the list
+ if ((LastnotusedVoice!=NULL)&&(LastnotusedVoice->id==FirstVoice->id))
+ {
+#ifdef VOICEMANDEBUG
+ printf("Used last voice !\n");
+#endif
+ LastnotusedVoice=NULL;
+ }
+ voice *newvoice=FirstVoice;
+ FirstVoice=FirstVoice->next;
+ FirstVoice->prev=NULL;
+
+#ifdef VOICEMANDEBUG
+ printf("Allocating id :%d\n",newvoice->id);
+#endif
+ // then we put the allocated voice at the end of the list
+ LastVoice->next=newvoice;
+ newvoice->prev=LastVoice;
+ LastVoice=newvoice;
+ LastVoice->next=NULL;
+
+ newvoice->channel=chn;
+ newvoice->note=key;
+
+#ifdef VOICEMANDEBUG
+ if (newvoice->used==1)
+ {
+ printf("Replacing voice : %d\n",newvoice->id);
+ }
+#endif
+ newvoice->used=1;
+
+ //dispStat();
+ return newvoice->id;
+}
+
+void VoiceManager::deallocateVoice(int id)
+{
+ voice *delvoice=VoiceList[id];
+#ifdef VOICEMANDEBUG
+ printf("Deallocating id :%d\n",id);
+#endif
+ if (delvoice->id==LastVoice->id)
+ {
+ LastVoice=delvoice->prev;
+ LastVoice->next=NULL;
+
+ if (LastnotusedVoice==NULL)
+ {
+ delvoice->next=FirstVoice;
+ FirstVoice->prev=delvoice;
+ FirstVoice=delvoice;
+ FirstVoice->prev=NULL;
+ LastnotusedVoice=FirstVoice;
+ }
+ else
+ {
+ if (LastnotusedVoice->next==NULL)
+ {
+ LastnotusedVoice->next=delvoice;
+ delvoice->prev=LastnotusedVoice;
+ delvoice->next=NULL;
+ LastnotusedVoice=delvoice;
+ LastVoice=delvoice;
+ }
+ else
+ {
+ delvoice->next=LastnotusedVoice->next;
+ delvoice->next->prev=delvoice;
+ delvoice->prev=LastnotusedVoice;
+ LastnotusedVoice->next=delvoice;
+ LastnotusedVoice=delvoice;
+ }
+ }
+ }
+ else
+ {
+ if (delvoice->prev!=NULL)
+ {
+ delvoice->prev->next=delvoice->next;
+ delvoice->next->prev=delvoice->prev;
+ if (LastnotusedVoice==NULL)
+ {
+ delvoice->next=FirstVoice;
+ FirstVoice->prev=delvoice;
+ FirstVoice=delvoice;
+ FirstVoice->prev=NULL;
+ LastnotusedVoice=FirstVoice; }
+ else
+ {
+ if (LastnotusedVoice->next==NULL)
+ {
+ LastnotusedVoice->next=delvoice;
+ delvoice->prev=LastnotusedVoice;
+ delvoice->next=NULL;
+ LastnotusedVoice=delvoice;
+ LastVoice=delvoice;
+ }
+ else
+ {
+ delvoice->next=LastnotusedVoice->next;
+ delvoice->next->prev=delvoice;
+ delvoice->prev=LastnotusedVoice;
+ LastnotusedVoice->next=delvoice;
+ LastnotusedVoice=delvoice;
+ }
+ }
+ }
+ }
+ delvoice->used=0;
+
+ // dispStat();
+}
+
+void VoiceManager::initSearch(void)
+{
+ searcher=searcher_aid;
+ searcher_aid->prev=LastVoice;
+}
+
+int VoiceManager::search(int chn)
+{
+ if (searcher==NULL) return -1;
+ searcher=searcher->prev;
+
+ while (searcher!=NULL)
+ {
+ if (searcher->used==0) return -1;
+ if (searcher->channel==chn)
+ {
+ return searcher->id;
+ }
+ searcher=searcher->prev;
+ }
+ return -1;
+}
+
+int VoiceManager::search(int chn,int note)
+{
+ if (searcher==NULL) return -1;
+ searcher=searcher->prev;
+ while ((searcher!=NULL))
+ {
+ if (searcher->used==0) return -1;
+ if ((searcher->channel==chn)&&(searcher->note==note))
+ {
+ return searcher->id;
+ }
+ searcher=searcher->prev;
+ }
+ return -1;
+}
+
+/*
+void VoiceManager::dispStat(void)
+{
+#ifdef VOICEMANDEBUG
+ printf("Stats\n");
+ voice *ptr=FirstVoice;
+ while (ptr!=NULL)
+ {
+ printf("Voice %d is %s\n",ptr->id,(ptr->used==0)?("off"):("on"));
+ ptr=ptr->next;
+ }
+ if (LastnotusedVoice!=NULL) printf("LnuV = %d\n",LastnotusedVoice->id);
+#endif
+}
+*/
diff --git a/libkmid/voiceman.h b/libkmid/voiceman.h
new file mode 100644
index 000000000..79dee9d3d
--- /dev/null
+++ b/libkmid/voiceman.h
@@ -0,0 +1,172 @@
+/* voiceman.h - The VoiceManager class handles a set of voices for synths
+ This file is part of LibKMid 0.9.5
+ Copyright (C) 1997,98,99,2000 Antonio Larrosa Jimenez
+ LibKMid's homepage : http://www.arrakis.es/~rlarrosa/libkmid.html
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ Send comments and bug fixes to Antonio Larrosa <larrosa@kde.org>
+
+***************************************************************************/
+#ifndef _VOICEMAN_H
+#define _VOICEMAN_H
+
+/**
+ * @internal
+ * Manages the voices used by synthesizers.
+ *
+ * @short Manages internally the voices used by synth devices.
+ * @version 0.9.5 17/01/2000
+ * @author Antonio Larrosa Jimenez <larrosa@kde.org>
+ */
+class VoiceManager
+{
+ private:
+ class VoiceManagerPrivate;
+ VoiceManagerPrivate *d;
+
+ /**
+ * Number of voices managed by this object.
+ */
+ int nvoices;
+
+ /**
+ * @internal
+ */
+ struct voice
+ {
+ int id;
+ int channel;
+ int note;
+ int used;
+
+ struct voice *prev;
+ struct voice *next;
+ };
+
+ /**
+ * Points to the beginning of the voice list, that is, to
+ * the older voice which is ready to be used.
+ */
+ voice *FirstVoice;
+
+ /**
+ * Points to the last voice, that is, the latest (most recently) used voice.
+ */
+ voice *LastVoice;
+
+ /**
+ * Points to the latest (list order) not used voice,
+ * that is, to where deallocated voices will be moved.
+ */
+ voice *LastnotusedVoice;
+
+ /**
+ * Array with pointers to the voices, arranged by ID for allow faster searches.
+ */
+ voice **VoiceList;
+
+ /**
+ * @internal
+ * This variable is used to search channels.
+ */
+ voice *searcher;
+
+ /**
+ * @internal
+ * An auxiliary variable for simpler searches.
+ */
+ voice *searcher_aid;
+
+ public:
+ /**
+ * Cronstructor.
+ */
+ VoiceManager(int totalvoices);
+
+ /**
+ * Destructor.
+ */
+ ~VoiceManager();
+
+ /**
+ * Allocates a voice used in channel @p chn, and playing key @p key
+ * @return the voice that should be used.
+ *
+ * @see deallocateVoice
+ */
+
+ int allocateVoice(int chn,int key);
+
+ /**
+ * Deallocates the voice with ID @p id.
+ *
+ * @see allocateVoice
+ */
+ void deallocateVoice(int id);
+
+ /**
+ * initSearch() must be called always before search() to initialize
+ * internal variables.
+ *
+ * @see search
+ */
+ void initSearch(void);
+
+ /**
+ * Returns -1 if channel chn is not currently used, or a voice using
+ * channel @p chn if any.
+ *
+ * Calling search repeteadly, will return all the voices using channel
+ * @p chn, and a -1 after the last one.
+ *
+ * @see initSearch
+ */
+ int search(int chn);
+ //returns -1 if channel chn is not currently used
+ // Continue searching for more voices which
+ // use the channel chn
+
+ /**
+ * This is a convenience function that differs from the above in that it also
+ * looks for a specific note (the second parameter)
+ *
+ * @see initSearch
+ */
+ int search(int chn,int note);
+
+ /**
+ * Returns the channel that voice @p v is using.
+ */
+ int channel(int v) {return VoiceList[v]->channel;}
+
+ /**
+ * Returns the note that voice @p v is playing.
+ */
+ int note(int v) {return VoiceList[v]->note;}
+
+ /**
+ * Returns true or false if the voice @p v is being used or not respectively.
+ */
+ int used(int v) {return VoiceList[v]->used;}
+
+ /**
+ * Clears the lists of used voices.
+ */
+ void clearLists(void);
+};
+
+#endif