summaryrefslogtreecommitdiffstats
path: root/arts/midi
diff options
context:
space:
mode:
Diffstat (limited to 'arts/midi')
-rw-r--r--arts/midi/Makefile.am53
-rw-r--r--arts/midi/README.midi261
-rw-r--r--arts/midi/alsamidigateway_impl.cc243
-rw-r--r--arts/midi/alsamidiport_impl.cc232
-rw-r--r--arts/midi/alsamidiport_impl.h85
-rw-r--r--arts/midi/artsmidi.idl379
-rw-r--r--arts/midi/audiomiditimer_impl.cc116
-rw-r--r--arts/midi/audiosync_impl.cc203
-rw-r--r--arts/midi/audiosync_impl.h79
-rw-r--r--arts/midi/audiotimer.cc98
-rw-r--r--arts/midi/audiotimer.h73
-rw-r--r--arts/midi/mcopclass/Makefile.am2
-rw-r--r--arts/midi/mcopclass/MidiManager.mcopclass3
-rw-r--r--arts/midi/midiclient_impl.cc274
-rw-r--r--arts/midi/midiclient_impl.h80
-rw-r--r--arts/midi/midimanager_impl.cc156
-rw-r--r--arts/midi/midimanager_impl.h67
-rw-r--r--arts/midi/midimanagerport_impl.cc71
-rw-r--r--arts/midi/midimanagerport_impl.h46
-rw-r--r--arts/midi/midimsg.c177
-rw-r--r--arts/midi/midimsg.h44
-rw-r--r--arts/midi/midisend.cc375
-rw-r--r--arts/midi/midisend.h106
-rw-r--r--arts/midi/midisyncgroup_impl.cc125
-rw-r--r--arts/midi/midisyncgroup_impl.h67
-rw-r--r--arts/midi/midisynctest.cc137
-rw-r--r--arts/midi/miditest_impl.cc57
-rw-r--r--arts/midi/miditimercommon.cc72
-rw-r--r--arts/midi/miditimercommon.h59
-rw-r--r--arts/midi/rawmidiport_impl.cc306
-rw-r--r--arts/midi/systemmiditimer_impl.cc105
-rw-r--r--arts/midi/timestampmath.cc112
-rw-r--r--arts/midi/timestampmath.h62
33 files changed, 4325 insertions, 0 deletions
diff --git a/arts/midi/Makefile.am b/arts/midi/Makefile.am
new file mode 100644
index 00000000..b5719649
--- /dev/null
+++ b/arts/midi/Makefile.am
@@ -0,0 +1,53 @@
+####### Various modules for artsmodules
+
+SUBDIRS = mcopclass
+INCLUDES= -I$(top_builddir)/arts/runtime -I$(srcdir)/freeverb -I$(arts_includes) $(all_includes)
+
+lib_LTLIBRARIES = libartsmidi_idl.la libartsmidi.la
+
+bin_PROGRAMS = midisend
+noinst_PROGRAMS = midisynctest
+
+midisend_SOURCES = midisend.cc midimsg.c
+midisend_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(LIBPTHREAD)
+midisend_LDADD = libartsmidi_idl.la
+midisend_COMPILE_FIRST = artsmidi.h
+
+midisynctest_SOURCES = midisynctest.cc
+midisynctest_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(LIBPTHREAD)
+midisynctest_LDADD = libartsmidi.la -lsoundserver_idl
+midisynctest_COMPILE_FIRST = artsmidi.h
+
+libartsmidi_idl_la_SOURCES = artsmidi.cc
+libartsmidi_idl_la_LIBADD = -lmcop -lartsflow
+libartsmidi_idl_la_LDFLAGS = $(all_libraries) -L$(arts_libraries) \
+ -no-undefined
+
+libartsmidi_la_SOURCES = midimanager_impl.cc midiclient_impl.cc \
+ miditest_impl.cc midimanagerport_impl.cc rawmidiport_impl.cc \
+ systemmiditimer_impl.cc audiomiditimer_impl.cc miditimercommon.cc \
+ audiosync_impl.cc audiotimer.cc alsamidigateway_impl.cc \
+ alsamidiport_impl.cc midisyncgroup_impl.cc timestampmath.cc
+libartsmidi_la_COMPILE_FIRST = artsmidi.h
+
+libartsmidi_la_LIBADD = libartsmidi_idl.la -lartsflow $(ARTS_LIBASOUND)
+libartsmidi_la_LDFLAGS = $(all_libraries) -L$(arts_libraries) \
+ -no-undefined
+
+artsmidi.mcopclass: artsmidi.h
+artsmidi.mcoptype: artsmidi.h
+artsmidi.cc artsmidi.h: $(srcdir)/artsmidi.idl $(MCOPIDL)
+ $(MCOPIDL) -t -I$(arts_includes) $(srcdir)/artsmidi.idl
+
+DISTCLEANFILES = artsmidi.cc artsmidi.h \
+ artsmidi.mcoptype artsmidi.mcopclass
+
+####### install idl files
+
+artsincludedir = $(includedir)/arts
+artsinclude_HEADERS = artsmidi.h artsmidi.idl
+
+mcoptypedir = $(libdir)/mcop
+mcoptype_DATA = artsmidi.mcoptype artsmidi.mcopclass
+
+artsmidi.lo: artsmidi.h
diff --git a/arts/midi/README.midi b/arts/midi/README.midi
new file mode 100644
index 00000000..e341ca01
--- /dev/null
+++ b/arts/midi/README.midi
@@ -0,0 +1,261 @@
+Midi, Audio and Synchronization:
+================================
+
+1. Introduction
+2. The midi manager
+3. Midi synchronization
+4. Audio timestamping and synchronization
+5. Example code
+
+
+ 1. Introduction
+ ---------------
+
+Since aRts-1.0 (as shipped with KDE3.0), aRts provides a lot more
+infrastructure to deal with midi, audio, and their synchronization. The main
+goal is to provide a unified interface between sequencers (or other programs
+that require notes or audio tracks to be played at certain given time stamps)
+and underlying software/hardware that can play notes/audio tracks.
+
+Currently, there exist five distinct destinations that aRts supports, which
+can all be used at the same time or individually, that is:
+
+ * aRts synthetic midi instruments
+ * ALSA-0.5
+ * ALSA-0.9
+ * OSS
+ * other aRts modules (including but not limited to the playback/recording
+ of audio tracks)
+
+ 2. The midi manager
+ -------------------
+
+The midi manager is the basic component that connects between applications
+that supply/record midi data, and devices that process midi data. Devices
+might be both, virtual (as in software synthesis) or real (as in hardware
+devices).
+
+From the view of the midi manager, all event streams correspond to one midi
+client. So, a midi client might be an application (such as a sequencer) that
+provides events, or an ALSA hardware device that consumes events. If there
+are multiple event streams, they correspond to multiple clients. That is,
+if an application wishes to play three different midi tracks, one over ALSA,
+and two over two different synthetic instruments, it needs to register itself
+three times, with three different clients.
+
+The midi managers job is to connect midi clients (as in event streams). It
+maintains a list of connections that the user can modify with an application
+like artscontrol. Applications could also, if they wish so, modify this
+connection list.
+
+As a use case, we'll consider the following: you want to write a sequencer
+application that plays back two different tracks to two different devices.
+You want the user to be able to select these devices in a drop down box for
+each track.
+
+1) getting a list of choices:
+
+First, you will want to obtain a list of choices which the user could possibly
+connect your tracks to. You do so by reading the
+
+interface MidiManager { // SINGLETON: Arts_MidiManager
+ /**
+ * a list of clients
+ */
+ readonly attribute sequence<MidiClientInfo> clients;
+
+ //...
+};
+
+attribute. The three fields of each client that are interesting for you are
+
+struct MidiClientInfo {
+ long ID;
+
+ //...
+
+ MidiClientDirection direction;
+ MidiClientType type;
+ string title;
+};
+
+You would list those devices in the dropdown box that are of the appropriate
+direction, which is mcdRecord, as you would want a client that receives midi
+events (this might be confusing, but you look from the view of the client).
+
+Then, there is the type field, which tells you whether the client is a device-
+like thing (like a synthetic instrument), or another application (like another
+application currently recording a track). While it might not be an impossible
+setup that you send events between two applications, usually users will choose
+such clients that have mctDestination as type.
+
+Finally, you can list the titles in a drop down box, and keep the ID for making
+a connection later.
+
+2) registering clients:
+
+You will need to register one client for each track. Use
+
+ /**
+ * add a client
+ *
+ * this creates a new MidiManagerClient
+ */
+ MidiClient addClient(MidiClientDirection direction, MidiClientType type,
+ string title, string autoRestoreID);
+
+to do so.
+
+3) connecting:
+
+As you probably don't want your sequencer user to use artscontrol to setup
+connections between your tracks and the devices, you will need to connect
+your clients to the hardware devices for playing something.
+
+You can connect clients to their appropriate destinations using
+
+ /**
+ * connect two clients
+ */
+ void connect(long clientID, long destinationID);
+
+and
+
+ /**
+ * disconnect two clients
+ */
+ void disconnect(long clientID, long destinationID);
+
+Keep in mind that a client might be connected to more than one destination
+at the same time, so that you will need to disconnect the old destination
+before connecting the new one.
+
+4) playing events:
+
+You can now play events to the tracks, using each client's
+
+ MidiPort addOutputPort();
+
+function for getting a port where you can send events to. However, you will
+also need to ensure that the events will get synchronized as soon as you are
+playing back events to different devices. Read the next section for details
+on this.
+
+ 3. Midi synchronization
+ -----------------------
+
+As soon as you are writing a real sequencer, you might want to output to more
+than one midi device at a time. For instance, you might want to let some of
+your midi events be played by aRts synthesis, while others should be sent
+over the external midi port.
+
+To support this setup, a new interface called MidiSyncGroup has been added. To
+output midi events synchronized over more than one port, you proceed as follows:
+
+a) you obtain a reference to the midi manager object
+
+ MidiManager midiManager = DynamicCast(Reference("global:Arts_MidiManager"));
+ if(midiManager.isNull()) arts_fatal("midimanager is null");
+
+b) you create a midi synchronization group which will ensure that the
+ timestamps of your midi events will be synchronized
+
+ MidiSyncGroup syncGroup = midiManager.addSyncGroup();
+
+c) you add a client to the midi manager for each port you want to output
+ midi data over
+
+ MidiClient client = midiManager.addClient(mcdPlay, mctApplication, "midisynctest", "midisynctest");
+ MidiClient client2 = midiManager.addClient(mcdPlay, mctApplication, "midisynctest2", "midisynctest2");
+
+d) you insert the clients in the synchronization group
+
+ syncGroup.addClient(client);
+ syncGroup.addClient(client2);
+
+e) you create ports for each client as usual
+
+ MidiPort port = client.addOutputPort();
+ MidiPort port2 = client2.addOutputPort();
+
+f) at this point, you will need to ensure that the midi clients you created
+ are connected, you can either leave the user with artscontrol for doing
+ this, or use the clients and connect methods of the midiManager object
+ yourself (see use case discussed in previous section)
+
+g) you output events over the ports as usual
+
+ /* where t is a suitable TimeStamp */
+ MidiEvent e = MidiEvent(t,MidiCommand(mcsNoteOn|0, notes[np], 100));
+ port.processEvent(e);
+ port2.processEvent(e);
+
+ 4. Audio timestamping and synchronization
+ -----------------------------------------
+
+Audio in aRts is usually handled as structures consisting of small modules
+that do something. While this model allows you to describe anything you want
+to, from playing a sample to playing a synthetic sequence of notes with a
+synthetic instruments, it doesn't give you any notion of time. More so, if
+you build a large graph of objects, you might need quite some time for this,
+and you will want to have them all started at the same time.
+
+To solve this issue, an AudioSync interface has been introduced, that allows
+you to start() and stop() either synchronized at a specific point in time.
+
+Suppose you have two synthesis modules which together play back a sample.
+What can you do to start them at the same time?
+
+ Synth_PLAY_WAV wav = //... create on server
+ Synth_AMAN_PLAY sap //... create on server
+ AudioSync audioSync = //... create on server
+
+ wav.filename("/opt/kde3/share/sounds/pop.wav");
+ sap.title("midisynctest2");
+ sap.autoRestoreID("midisynctest2");
+ connect(wav,sap);
+
+ // this queues back start() to be called atomically later
+ audioSync.queueStart(wav);
+ audioSync.queueStart(sap);
+
+ // this line is a synchronized version of
+ // wav.start();
+ // sap.start();
+ audioSync.execute();
+
+You could also play them back at a specific time in the future and query the
+current time using the time and executeAt methods:
+
+interface AudioSync {
+ /**
+ * the current time
+ */
+ readonly attribute TimeStamp time;
+
+ //...
+
+ /**
+ * atomically executes all queued modifications to the flow system
+ * at a given time
+ */
+ void executeAt(TimeStamp timeStamp);
+};
+
+Finally, to get synchronized midi and audio, you can insert the AudioSync
+object into a midi synchronization group, then their timestamps will be
+synchronized to those of the midi channels.
+
+ 5. Example code
+ ---------------
+
+An example that illustrates most things discussed in this document is
+midisynctest.cc, which plays back two synchronized midi streams and samples.
+Note that you might want to change the source code, as it hardcodes the
+location of the .wav file.
+
+
+Questions and comments are welcome.
+
+Stefan Westerfeld
+stefan@space.twc.de
diff --git a/arts/midi/alsamidigateway_impl.cc b/arts/midi/alsamidigateway_impl.cc
new file mode 100644
index 00000000..91a0f118
--- /dev/null
+++ b/arts/midi/alsamidigateway_impl.cc
@@ -0,0 +1,243 @@
+ /*
+
+ Copyright (C) 2001-2002 Stefan Westerfeld
+ stefan@space.twc.de
+
+ 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., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "artsmidi.h"
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/**
+ * compile real version if we have ALSA support, dummy version otherwise
+ */
+#if defined(HAVE_ARTS_LIBASOUND2) || defined(HAVE_ARTS_LIBASOUND)
+
+#ifdef HAVE_ALSA_ASOUNDLIB_H
+#include <alsa/asoundlib.h>
+#elif defined(HAVE_SYS_ASOUNDLIB_H)
+#include <sys/asoundlib.h>
+#endif
+
+#include "alsamidiport_impl.h"
+#include <arts/debug.h>
+#include <stdio.h>
+
+using namespace Arts;
+using namespace std;
+
+class AlsaMidiGateway_impl : virtual public AlsaMidiGateway_skel {
+protected:
+ snd_seq_t *seq;
+
+ struct PortEntry {
+ int alsaClient, alsaPort;
+ bool keep;
+
+ AlsaMidiPort port;
+ MidiClient client;
+ };
+ list<PortEntry> ports;
+
+#ifdef HAVE_ARTS_LIBASOUND2
+/* ALSA-0.9 specific code */
+ int alsaOpen() {
+ return snd_seq_open(&seq, "hw", SND_SEQ_OPEN_DUPLEX, 0);
+ }
+ bool alsaScan(MidiManager midiManager) {
+ snd_seq_client_info_t *cinfo;
+ snd_seq_port_info_t *pinfo;
+
+ snd_seq_client_info_alloca(&cinfo);
+ snd_seq_client_info_set_client(cinfo, -1);
+
+ while (snd_seq_query_next_client(seq, cinfo) >= 0) {
+ int client = snd_seq_client_info_get_client(cinfo);
+
+ snd_seq_port_info_alloca(&pinfo);
+ snd_seq_port_info_set_client(pinfo, client);
+
+ snd_seq_port_info_set_port(pinfo, -1);
+ while (snd_seq_query_next_port(seq, pinfo) >= 0) {
+ unsigned int cap;
+
+ cap = (SND_SEQ_PORT_CAP_SUBS_WRITE|SND_SEQ_PORT_CAP_WRITE);
+ if ((snd_seq_port_info_get_capability(pinfo) & cap) == cap) {
+ string name = snd_seq_port_info_get_name(pinfo);
+ int client = snd_seq_port_info_get_client(pinfo);
+ int port = snd_seq_port_info_get_port(pinfo);
+
+ createPort(midiManager, name, client, port);
+ }
+ }
+ }
+
+ return true;
+ }
+#else
+/* ALSA-0.5 specific code */
+ int alsaOpen() {
+ return snd_seq_open(&seq, SND_SEQ_OPEN);
+ }
+
+ bool alsaScan(MidiManager midiManager) {
+ snd_seq_system_info_t sysinfo;
+
+ int err = snd_seq_system_info(seq, &sysinfo);
+ if (err < 0)
+ {
+ arts_warning("snd_seq_systeminfo failed: %s", snd_strerror(err));
+ return false;
+ }
+
+ for(int client = 0; client < sysinfo.clients; client++)
+ {
+ snd_seq_client_info_t cinfo;
+ if (snd_seq_get_any_client_info(seq, client, &cinfo) == 0)
+ {
+ for(int port = 0; port < sysinfo.ports; port++)
+ {
+ snd_seq_port_info_t pinfo;
+ if(snd_seq_get_any_port_info(seq, client, port, &pinfo) == 0)
+ {
+ unsigned int cap;
+ cap = (SND_SEQ_PORT_CAP_SUBS_WRITE|SND_SEQ_PORT_CAP_WRITE);
+
+ if ((pinfo.capability & cap) == cap)
+ createPort(midiManager, pinfo.name, client, port);
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+#endif
+
+public:
+ AlsaMidiGateway_impl() : seq(0)
+ {
+ }
+
+ ~AlsaMidiGateway_impl()
+ {
+ if(seq)
+ snd_seq_close(seq);
+ }
+
+ void createPort(MidiManager midiManager, string name, int client, int port)
+ {
+ if(name != "aRts")
+ {
+ char nr[1024];
+
+ sprintf(nr, " (%3d:%-3d)", client, port);
+ name += nr;
+
+ list<PortEntry>::iterator pi = ports.begin();
+ while(pi != ports.end() && (pi->alsaClient != client || pi->alsaPort != port))
+ pi++;
+
+ if(pi != ports.end()) /* we already have this port */
+ pi->keep = true;
+ else /* we need to create it */
+ {
+ PortEntry pe;
+ pe.port = AlsaMidiPort::_from_base(
+ new AlsaMidiPort_impl(seq, client, port));
+
+ if(pe.port.open())
+ {
+ pe.client = midiManager.addClient(mcdRecord,
+ mctDestination,
+ name, name);
+ pe.client.addInputPort(pe.port);
+ pe.alsaClient = client;
+ pe.alsaPort = port;
+ pe.keep = true;
+
+ ports.push_back(pe);
+ }
+ }
+ }
+ }
+
+ bool rescan()
+ {
+ MidiManager midiManager = DynamicCast(Reference("global:Arts_MidiManager"));
+ if(midiManager.isNull())
+ {
+ arts_warning("AlsaMidiGateway: can't find MidiManager");
+ return false;
+ }
+
+ if(!seq)
+ {
+ int err = alsaOpen();
+ if (err < 0)
+ {
+ arts_warning("AlsaMidiGateway: could not open sequencer %s",
+ snd_strerror(err));
+ seq = 0;
+ return false;
+ }
+ }
+
+ list<PortEntry>::iterator pi;
+ for(pi = ports.begin(); pi != ports.end(); pi++)
+ pi->keep = false;
+
+ if(!alsaScan(midiManager))
+ return false;
+
+ /* erase those ports that are no longer needed */
+ pi = ports.begin();
+ while(pi != ports.end())
+ {
+ if(!pi->keep)
+ pi = ports.erase(pi);
+ else
+ pi++;
+ }
+
+ return true;
+ }
+};
+
+#else
+
+using namespace Arts;
+using namespace std;
+
+class AlsaMidiGateway_impl : virtual public AlsaMidiGateway_skel {
+public:
+ bool rescan()
+ {
+ /* dummy version: no ALSA support compiled in */
+ return false;
+ }
+};
+
+#endif
+
+namespace Arts {
+ REGISTER_IMPLEMENTATION(AlsaMidiGateway_impl);
+}
diff --git a/arts/midi/alsamidiport_impl.cc b/arts/midi/alsamidiport_impl.cc
new file mode 100644
index 00000000..e53d7fe7
--- /dev/null
+++ b/arts/midi/alsamidiport_impl.cc
@@ -0,0 +1,232 @@
+ /*
+
+ Copyright (C) 2001-2002 Stefan Westerfeld
+ stefan@space.twc.de
+
+ 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., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "alsamidiport_impl.h"
+
+#if defined(HAVE_ARTS_LIBASOUND2) || defined(HAVE_ARTS_LIBASOUND)
+#include <arts/debug.h>
+
+#ifdef HAVE_ARTS_LIBASOUND
+#define snd_seq_queue_status_alloca(x) \
+ *x = (snd_seq_queue_status_t *)alloca(sizeof(snd_seq_queue_status_t))
+#define snd_seq_queue_status_get_tick_time(x) x->tick
+#define snd_seq_queue_status_get_real_time(x) (&(x->time))
+#endif
+
+using namespace std;
+using namespace Arts;
+
+AlsaMidiPort_impl::AlsaMidiPort_impl(snd_seq_t *seq, long client, long port)
+ : _client(client), _port(port), alsaSeq(seq)
+{
+ opened = false;
+}
+
+/* interface MidiPort */
+Arts::TimeStamp AlsaMidiPort_impl::time()
+{
+ snd_seq_queue_status_t *status;
+ snd_seq_queue_status_alloca(&status);
+
+ snd_seq_get_queue_status(alsaSeq, alsaQueue, status);
+ snd_seq_tick_time_t ttime = snd_seq_queue_status_get_tick_time(status);
+ const snd_seq_real_time_t *rtime =
+ snd_seq_queue_status_get_real_time(status);
+
+ return Arts::TimeStamp(rtime->tv_sec, rtime->tv_nsec / 1000);
+}
+
+Arts::TimeStamp AlsaMidiPort_impl::playTime()
+{
+ return time();
+}
+
+void AlsaMidiPort_impl::fillAlsaEvent(snd_seq_event_t *ev,
+ const MidiCommand& command)
+{
+ ev->source = alsaSourceAddr;
+ ev->dest = alsaDestAddr;
+
+ mcopbyte channel = command.status & mcsChannelMask;
+
+ switch(command.status & mcsCommandMask)
+ {
+ case mcsNoteOn:
+ snd_seq_ev_set_noteon(ev, channel, command.data1, command.data2);
+ break;
+ case mcsNoteOff:
+ snd_seq_ev_set_noteoff(ev, channel, command.data1, command.data2);
+ break;
+ case mcsProgram:
+ snd_seq_ev_set_pgmchange(ev, channel, command.data1);
+ break;
+ case mcsParameter:
+ snd_seq_ev_set_controller(ev, channel, command.data1, command.data2);
+ break;
+ default:
+ /* unhandled */
+ return;
+ }
+}
+
+void AlsaMidiPort_impl::sendAlsaEvent(snd_seq_event_t *ev)
+{
+ int ret = snd_seq_event_output(alsaSeq, ev);
+ if (ret < 0) {
+ arts_warning("AlsaMidiPort: error writing note %s\n",
+ snd_strerror(ret));
+ return;
+ }
+ flushAlsa();
+}
+
+void AlsaMidiPort_impl::processCommand(const MidiCommand& command)
+{
+ snd_seq_event_t ev;
+ snd_seq_ev_clear(&ev);
+
+ fillAlsaEvent(&ev, command);
+ sendAlsaEvent(&ev);
+}
+
+void AlsaMidiPort_impl::processEvent(const MidiEvent& event)
+{
+ snd_seq_event_t ev;
+ snd_seq_real_time_t time;
+
+ time.tv_sec = event.time.sec;
+ time.tv_nsec = event.time.usec * 1000;
+
+ snd_seq_ev_clear(&ev);
+ snd_seq_ev_schedule_real(&ev, alsaQueue, 0, &time);
+
+ fillAlsaEvent(&ev, event.command);
+ sendAlsaEvent(&ev);
+}
+
+/* interface AlsaMidiPort */
+void AlsaMidiPort_impl::client(long newClient)
+{
+ if(newClient != _client)
+ {
+ _client = newClient;
+
+ if(opened)
+ {
+ close();
+ open();
+ }
+
+ client_changed(newClient);
+ }
+}
+
+long AlsaMidiPort_impl::client()
+{
+ return _client;
+}
+
+void AlsaMidiPort_impl::port(long newPort)
+{
+ if(newPort != _port)
+ {
+ _port = newPort;
+
+ if(opened)
+ {
+ close();
+ open();
+ }
+
+ port_changed(newPort);
+ }
+}
+
+long AlsaMidiPort_impl::port()
+{
+ return _port;
+}
+
+bool AlsaMidiPort_impl::open()
+{
+ arts_return_val_if_fail(opened == false, false);
+
+ alsaQueue = snd_seq_alloc_queue(alsaSeq);
+ alsaClientId = snd_seq_client_id(alsaSeq);
+
+ alsaPort = snd_seq_create_simple_port(alsaSeq, "aRts",
+ SND_SEQ_PORT_CAP_WRITE |
+ SND_SEQ_PORT_CAP_SUBS_WRITE |
+ SND_SEQ_PORT_CAP_READ,
+ SND_SEQ_PORT_TYPE_MIDI_GENERIC);
+ if (alsaPort < 0) {
+ arts_warning("AlsaMidiPort: can't creating port %s\n",
+ snd_strerror(alsaPort));
+ return false;
+ }
+
+ alsaSourceAddr.client = alsaClientId;
+ alsaSourceAddr.port = alsaPort;
+
+ alsaDestAddr.client = _client;
+ alsaDestAddr.port = _port;
+
+ int ret;
+ ret = snd_seq_connect_to(alsaSeq, alsaPort,
+ alsaDestAddr.client,
+ alsaDestAddr.port);
+ if (ret < 0) {
+ arts_warning("AlsaMidiPort: error connecting port %s\n",
+ snd_strerror(ret));
+ /* FIXME: destroy port here */
+ return false;
+ }
+
+ snd_seq_start_queue(alsaSeq, alsaQueue, 0);
+ flushAlsa();
+
+ opened = true;
+ return true;
+}
+
+void AlsaMidiPort_impl::close()
+{
+ if(!opened)
+ return;
+
+ opened = false;
+}
+
+void AlsaMidiPort_impl::flushAlsa()
+{
+#ifdef HAVE_ARTS_LIBASOUND2
+ snd_seq_drain_output(alsaSeq);
+#else
+ int err;
+ while((err = snd_seq_flush_output(alsaSeq)) > 0)
+ {
+ arts_debug("alsa flush error %d\n",snd_strerror(err));
+ usleep(2000);
+ }
+#endif
+}
+#endif
diff --git a/arts/midi/alsamidiport_impl.h b/arts/midi/alsamidiport_impl.h
new file mode 100644
index 00000000..251f9d94
--- /dev/null
+++ b/arts/midi/alsamidiport_impl.h
@@ -0,0 +1,85 @@
+ /*
+
+ Copyright (C) 2001-2002 Stefan Westerfeld
+ stefan@space.twc.de
+
+ 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., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef ARTS_ALSAMIDIPORT_IMPL_H
+#define ARTS_ALSAMIDIPORT_IMPL_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/**
+ * compile real version if we have ALSA support, dummy version otherwise
+ */
+#if defined(HAVE_ARTS_LIBASOUND2) || defined(HAVE_ARTS_LIBASOUND)
+
+#ifdef HAVE_ALSA_ASOUNDLIB_H
+#include <alsa/asoundlib.h>
+#elif defined(HAVE_SYS_ASOUNDLIB_H)
+#include <sys/asoundlib.h>
+#endif
+
+#include "artsmidi.h"
+
+namespace Arts {
+
+class AlsaMidiPort_impl : virtual public AlsaMidiPort_skel {
+protected:
+ long _client, _port;
+ bool opened;
+
+ snd_seq_t *alsaSeq;
+
+ int alsaQueue;
+ int alsaClientId;
+ int alsaPort;
+
+ snd_seq_addr_t alsaSourceAddr;
+ snd_seq_addr_t alsaDestAddr;
+
+ void fillAlsaEvent(snd_seq_event_t *ev, const MidiCommand& command);
+ void sendAlsaEvent(snd_seq_event_t *ev);
+ void flushAlsa();
+
+public:
+ AlsaMidiPort_impl(snd_seq_t *seq, long client, long port);
+ void close();
+
+ /* interface MidiPort */
+ Arts::TimeStamp time();
+ Arts::TimeStamp playTime();
+ void processCommand(const MidiCommand& command);
+ void processEvent(const MidiEvent& event);
+
+ /* interface AlsaMidiPort */
+ void client(long newClient);
+ long client();
+
+ void port(long newPort);
+ long port();
+
+ bool open();
+};
+
+}
+#endif /* HAVE_ARTS_LIBASOUND2 */
+#endif /* ARTS_ALSAMIDIPORT_IMPL_H */
diff --git a/arts/midi/artsmidi.idl b/arts/midi/artsmidi.idl
new file mode 100644
index 00000000..63bf868b
--- /dev/null
+++ b/arts/midi/artsmidi.idl
@@ -0,0 +1,379 @@
+ /*
+
+ Copyright (C) 2000 Stefan Westerfeld
+ stefan@space.twc.de
+
+ 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., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+/*
+ * DISCLAIMER: The interfaces in artsmidi.idl (and the derived .cc/.h files)
+ * DO NOT GUARANTEE BINARY COMPATIBILITY YET.
+ *
+ * They are intended for developers. You shouldn't expect that applications in
+ * binary form will be fully compatibile with further releases of these
+ * interfaces.
+ */
+
+#include <artsflow.idl>
+
+module Arts {
+
+/* This is modelled somewhat after
+ - the AudioManager concept
+ - the aRts-0.3.4.1 MidiPort concept
+ - libkmid
+
+ It adds timing as new feature compared to older implementation, and also
+ tries to do the full set of midi operations.
+
+ It's current state is "experimental", and "binary compatibility not kept".
+ */
+
+/**
+ * an absolute timestamp
+ */
+struct TimeStamp {
+ long sec,usec;
+};
+
+/**
+ * different status of a midi command
+ */
+enum MidiCommandStatus {
+// Masks:
+ mcsCommandMask = 0xf0,
+ mcsChannelMask = 0x0f,
+
+// Commands:
+ mcsNoteOff = 0x80,
+ mcsNoteOn = 0x90,
+ mcsKeyPressure = 0xa0,
+ mcsParameter = 0xb0,
+ mcsProgram = 0xc0,
+ mcsChannelPressure = 0xd0,
+ mcsPitchWheel = 0xe0
+};
+
+/**
+ * the following are to be used once status is (mcsParameter|channel):
+ */
+enum MidiCommandParameter {
+ mcpSustain = 0x40,
+ mcpAllNotesOff = 0x7b
+};
+
+/**
+ * a midi command
+ */
+struct MidiCommand {
+ byte status;
+ byte data1;
+ byte data2;
+};
+
+/**
+ * a midi event
+ */
+
+struct MidiEvent {
+ TimeStamp time;
+ MidiCommand command;
+};
+
+/**
+ * a midi port
+ */
+interface MidiPort {
+ /**
+ * the current absolute time (since the existence of the midi device)
+ */
+ readonly attribute TimeStamp time;
+
+ /**
+ * the current play time
+ *
+ * Some midi devices, for instance synthetic audio devices, have a certain
+ * amount of internal buffering. This causes a time difference between
+ * where events are currently being rendered, which is the timestamp
+ * obtained by "time", and the events that the listener is hearing right
+ * now, which is this timestamp, the "playTime".
+ */
+ readonly attribute TimeStamp playTime;
+
+ /**
+ * processes a midi command
+ */
+ oneway void processCommand(MidiCommand command);
+
+ /**
+ * processes a midi event
+ */
+ oneway void processEvent(MidiEvent event);
+};
+
+enum MidiClientDirection { mcdPlay, mcdRecord };
+enum MidiClientType { mctDestination, mctApplication };
+
+/**
+ * information about a midi client
+ */
+struct MidiClientInfo {
+ long ID;
+ sequence<long> connections;
+
+ MidiClientDirection direction;
+ MidiClientType type;
+ string title, autoRestoreID;
+};
+
+/**
+ * a midi manager client
+ */
+interface MidiClient {
+ readonly attribute MidiClientInfo info;
+
+ /**
+ * you can change the title of your client on the fly - everything else
+ * (besides the actual assignment) is static
+ */
+ attribute string title;
+
+ /**
+ * creates a new port through which the client can receive data from
+ * the midi manager
+ */
+ void addInputPort(MidiPort port);
+
+ /**
+ * creates a new port through which the client can send data to the
+ * midi manager
+ */
+ MidiPort addOutputPort();
+
+ /**
+ * removes a port
+ */
+ void removePort(MidiPort port);
+};
+
+interface AudioSync;
+
+/**
+ * this synchronizes multiple midi clients - it also allows synchronization
+ * with audio events
+ */
+interface MidiSyncGroup {
+ /**
+ * adds a midi client to the synchronization group
+ *
+ * hint: during adding the client, the timestamps related to that
+ * client will jump
+ */
+ void addClient(MidiClient client);
+
+ /**
+ * deletes a midi client from the synchronization group
+ */
+ void removeClient(MidiClient client);
+
+ /**
+ * adds an AudioSync object to the synchronization group
+ *
+ * hint: during adding the AudioSync object, the timestamps related to
+ * that object might jump
+ */
+ void addAudioSync(AudioSync audioSync);
+
+ /**
+ * deletes an AudioSync object from the synchronization group
+ */
+ void removeAudioSync(AudioSync audioSync);
+};
+
+/**
+ * Some general notes to the understanding of the midi manager. The midi
+ * manager has the task to intelligently assign applications to destinations.
+ *
+ * It is important to understand what it actually does to understand the
+ * distinction first, which is expressed through the "MidiClientType" of
+ * each client.
+ *
+ * APPLICATIONS: An application is a user visible application, that produces
+ * or records midi data. It is important for the understanding of an
+ * application, that an application actually *wants* to be supplied with
+ * data, or wants to get its data played. Thus, adding an application to
+ * the midi manager is an implicit request: "go and find a place where to
+ * put the events to (or get the events from)".
+ *
+ * Examples for applications would be games or midi players.
+ *
+ * DESTINATIONS: A destination is a system service that plays or supplies
+ * midi data. The characteristic here is that a destination is something
+ * that is there if you need it.
+ *
+ * Examples for destinations might be might be a hardware device or an
+ * emulation of a hardware device (such as a virtual sampler).
+ *
+ * So the process is as follows:
+ * - destinations register themselves at the midi manager, and provide
+ * system services in that way
+ *
+ * - when the user starts an application (such as a midi player), the midi
+ * manager's task is to assign it to a suitable destination
+ *
+ * - the user can interact with the process by changing the way applications
+ * are assigned to destinations - the midi manager will try to learn
+ * what the user wants, and next time do a better job while assigning
+ *
+ * To actually record or play some data, you need to register a client first,
+ * and after that, you can add Input or Output "MidiPort"s to your client,
+ * so that you can actually send or receive events with them.
+ */
+interface MidiManager { // SINGLETON: Arts_MidiManager
+ /**
+ * a list of clients
+ */
+ readonly attribute sequence<MidiClientInfo> clients;
+
+ /**
+ * add a client
+ *
+ * this creates a new MidiManagerClient
+ */
+ MidiClient addClient(MidiClientDirection direction, MidiClientType type,
+ string title, string autoRestoreID);
+
+ /**
+ * connect two clients
+ */
+ void connect(long clientID, long destinationID);
+
+ /**
+ * disconnect two clients
+ */
+ void disconnect(long clientID, long destinationID);
+
+ /**
+ * add a synchronization group
+ *
+ * this creates a new MidiSyncGroup
+ */
+ MidiSyncGroup addSyncGroup();
+};
+
+interface MidiTest : MidiPort {
+};
+
+interface RawMidiPort : MidiPort {
+ attribute string device;
+ attribute boolean input, output;
+ attribute boolean running;
+ boolean open();
+};
+
+interface AlsaMidiGateway {
+ boolean rescan();
+};
+
+interface AlsaMidiPort : MidiPort {
+ attribute long client;
+ attribute long port;
+ boolean open();
+};
+
+/**
+ * EXPERIMENTAL interface for audio synchronization - this allows multiple
+ * objects to be started and stopped at a precisely defined timestamp
+ */
+interface AudioSync {
+ /**
+ * the current time
+ */
+ readonly attribute TimeStamp time;
+
+ /**
+ * the current play time
+ *
+ * Since aRts has internal buffering, there is a time difference between
+ * where events are currently being rendered, which is the timestamp
+ * obtained by "time", and the events that the listener is hearing right
+ * now, which is this timestamp, the "playTime".
+ */
+ readonly attribute TimeStamp playTime;
+
+ /**
+ * queues calling synthModule.start() later
+ *
+ * (will keep a reference on the module until executed)
+ */
+ void queueStart(SynthModule synthModule);
+
+ /**
+ * queues calling synthModule.stop() later
+ *
+ * (will keep a reference on the module until executed)
+ */
+ void queueStop(SynthModule synthModule);
+
+ /**
+ * atomically executes all queued modification to the flow system
+ */
+ void execute();
+
+ /**
+ * atomically executes all queued modifications to the flow system
+ * at a given time
+ */
+ void executeAt(TimeStamp timeStamp);
+};
+
+/**
+ * Midi Timer - can be used to provide timing for midi ports that have
+ * no "native" timing.
+ */
+interface MidiTimer
+{
+ /**
+ * the current time
+ */
+ readonly attribute TimeStamp time;
+
+ /**
+ * this will put the event into an event queue and send it to the port
+ * once the time for the event has been reached
+ */
+ oneway void queueEvent(MidiPort port, MidiEvent event);
+};
+
+/**
+ * Uses the system time (i.e. gettimeofday() and similar) to provide midi
+ * timing
+ */
+interface SystemMidiTimer : MidiTimer
+{
+};
+
+/**
+ * Uses the audio time (i.e. samples rendered to /dev/dsp) to provide midi
+ * timing
+ */
+interface AudioMidiTimer : MidiTimer
+{
+};
+
+};
diff --git a/arts/midi/audiomiditimer_impl.cc b/arts/midi/audiomiditimer_impl.cc
new file mode 100644
index 00000000..1e39e4f8
--- /dev/null
+++ b/arts/midi/audiomiditimer_impl.cc
@@ -0,0 +1,116 @@
+ /*
+
+ Copyright (C) 2001 Stefan Westerfeld
+ stefan@space.twc.de
+
+ 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., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "artsmidi.h"
+#include "artsflow.h"
+#include "stdsynthmodule.h"
+#include "debug.h"
+#include "miditimercommon.h"
+#include "audiotimer.h"
+#include "flowsystem.h"
+
+using namespace std;
+
+namespace Arts {
+
+class AudioMidiTimerCommon : public MidiTimerCommon, public AudioTimerCallback
+{
+protected:
+ AudioMidiTimerCommon();
+ virtual ~AudioMidiTimerCommon();
+
+ AudioTimer *audioTimer;
+
+public:
+ // allocation: share one AudioMidiTimerCommon for everbody who needs one
+ static AudioMidiTimerCommon *subscribe();
+
+ TimeStamp time();
+ void updateTime();
+};
+
+}
+
+using namespace Arts;
+
+static AudioMidiTimerCommon *AudioMidiTimerCommon_the = 0;
+
+AudioMidiTimerCommon::AudioMidiTimerCommon()
+{
+ AudioMidiTimerCommon_the = this;
+
+ audioTimer = AudioTimer::subscribe();
+ audioTimer->addCallback(this);
+}
+
+AudioMidiTimerCommon::~AudioMidiTimerCommon()
+{
+ audioTimer->removeCallback(this);
+ audioTimer->unsubscribe();
+
+ AudioMidiTimerCommon_the = 0;
+}
+
+TimeStamp AudioMidiTimerCommon::time()
+{
+ return audioTimer->time();
+}
+
+void AudioMidiTimerCommon::updateTime()
+{
+ processQueue();
+}
+
+AudioMidiTimerCommon *AudioMidiTimerCommon::subscribe()
+{
+ if(!AudioMidiTimerCommon_the)
+ AudioMidiTimerCommon_the = new AudioMidiTimerCommon();
+ AudioMidiTimerCommon_the->refCount++;
+ return AudioMidiTimerCommon_the;
+}
+
+namespace Arts {
+
+class AudioMidiTimer_impl : public AudioMidiTimer_skel {
+protected:
+ AudioMidiTimerCommon *timer;
+public:
+ AudioMidiTimer_impl()
+ {
+ timer = AudioMidiTimerCommon::subscribe();
+ }
+ ~AudioMidiTimer_impl()
+ {
+ timer->unsubscribe();
+ }
+ TimeStamp time()
+ {
+ return timer->time();
+ }
+ void queueEvent(MidiPort port, const MidiEvent& event)
+ {
+ timer->queueEvent(port, event);
+ }
+};
+
+REGISTER_IMPLEMENTATION(AudioMidiTimer_impl);
+}
diff --git a/arts/midi/audiosync_impl.cc b/arts/midi/audiosync_impl.cc
new file mode 100644
index 00000000..fdba8231
--- /dev/null
+++ b/arts/midi/audiosync_impl.cc
@@ -0,0 +1,203 @@
+ /*
+
+ Copyright (C) 2001-2002 Stefan Westerfeld
+ stefan@space.twc.de
+
+ 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., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "audiosync_impl.h"
+#include "midisyncgroup_impl.h"
+#include "audiotimer.h"
+#include "audiosubsys.h"
+#include "timestampmath.h"
+
+#undef AUDIO_DEBUG_DRIFT
+
+using namespace std;
+using namespace Arts;
+
+namespace Arts {
+ static list<AudioSync_impl *> audioSyncImplList;
+}
+
+void AudioSync_impl::AudioSyncEvent::execute()
+{
+ list<SynthModule>::iterator i;
+
+ for(i = startModules.begin(); i != startModules.end(); i++)
+ i->start();
+
+ for(i = stopModules.begin(); i != stopModules.end(); i++)
+ i->stop();
+}
+
+AudioSync_impl::AudioSync_impl()
+ : newEvent(new AudioSyncEvent), syncGroup(0)
+{
+ syncOffset = TimeStamp(0,0);
+
+ timer = AudioTimer::subscribe();
+ timer->addCallback(this);
+
+ audioSyncImplList.push_back(this);
+}
+
+AudioSync_impl::~AudioSync_impl()
+{
+ delete newEvent;
+
+ while(!events.empty())
+ {
+ delete events.front();
+ events.pop_front();
+ }
+
+ if(syncGroup)
+ {
+ syncGroup->audioSyncDied(this);
+ syncGroup = 0;
+ }
+ audioSyncImplList.remove(this);
+ timer->removeCallback(this);
+ timer->unsubscribe();
+}
+
+TimeStamp AudioSync_impl::time()
+{
+ if(syncGroup)
+ return syncGroup->time();
+ else
+ return audioTime();
+}
+
+TimeStamp AudioSync_impl::playTime()
+{
+ if(syncGroup)
+ return syncGroup->playTime();
+ else
+ return audioPlayTime();
+}
+
+TimeStamp AudioSync_impl::audioTime()
+{
+ return timer->time();
+}
+
+TimeStamp AudioSync_impl::audioPlayTime()
+{
+ double delay = AudioSubSystem::the()->outputDelay();
+
+ TimeStamp time = audioTime();
+ timeStampDec(time,timeStampFromDouble(delay));
+ return time;
+}
+
+TimeStamp AudioSync_impl::clientTime()
+{
+ TimeStamp time = audioTime();
+ timeStampDec(time, syncOffset);
+ return time;
+}
+
+void AudioSync_impl::queueStart(SynthModule synthModule)
+{
+ newEvent->startModules.push_back(synthModule);
+}
+
+void AudioSync_impl::queueStop(SynthModule synthModule)
+{
+ newEvent->stopModules.push_back(synthModule);
+}
+
+void AudioSync_impl::execute()
+{
+ newEvent->execute();
+ newEvent->startModules.clear();
+ newEvent->stopModules.clear();
+}
+
+void AudioSync_impl::executeAt(const TimeStamp& timeStamp)
+{
+ newEvent->time = timeStamp;
+ if(syncGroup)
+ timeStampInc(newEvent->time, syncOffset);
+
+ events.push_back(newEvent);
+
+ newEvent = new AudioSyncEvent;
+}
+
+void AudioSync_impl::updateTime()
+{
+ TimeStamp now = audioTime();
+ list<AudioSyncEvent*>::iterator i;
+
+ i = events.begin();
+ while(i != events.end())
+ {
+ AudioSyncEvent *event = *i;
+ TimeStamp& eventTime = event->time;
+
+ if( now.sec > eventTime.sec
+ || ((now.sec == eventTime.sec) && (now.usec > eventTime.usec)))
+ {
+ event->execute();
+ delete event;
+ i = events.erase(i);
+ }
+ else
+ {
+ i++;
+ }
+ }
+}
+
+void AudioSync_impl::setSyncGroup(MidiSyncGroup_impl *newSyncGroup)
+{
+ syncGroup = newSyncGroup;
+}
+
+void AudioSync_impl::synchronizeTo(const TimeStamp& time)
+{
+#ifdef AUDIO_DEBUG_DRIFT
+ TimeStamp drift = syncOffset; // debug drift
+#endif
+
+ syncOffset = audioPlayTime();
+ timeStampDec(syncOffset, time);
+
+#ifdef AUDIO_DEBUG_DRIFT
+ timeStampDec(drift, syncOffset); // debug drift
+ printf("SYNC DRIFT %30s %30s: %f\n",
+ "AudioSync", "AudioSync", timeStampToDouble(drift));
+#endif
+}
+
+AudioSync_impl *AudioSync_impl::find(AudioSync audioSync)
+{
+ list<AudioSync_impl *>::iterator i;
+
+ for(i = audioSyncImplList.begin(); i != audioSyncImplList.end(); i++)
+ {
+ if((*i)->_isEqual(audioSync._base()))
+ return (*i);
+ }
+ return 0;
+}
+
+namespace Arts { REGISTER_IMPLEMENTATION(AudioSync_impl); }
diff --git a/arts/midi/audiosync_impl.h b/arts/midi/audiosync_impl.h
new file mode 100644
index 00000000..835b8942
--- /dev/null
+++ b/arts/midi/audiosync_impl.h
@@ -0,0 +1,79 @@
+ /*
+
+ Copyright (C) 2001-2002 Stefan Westerfeld
+ stefan@space.twc.de
+
+ 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., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef AUDIOSYNC_IMPL_H
+#define AUDIOSYNC_IMPL_H
+
+#include "artsmidi.h"
+#include "audiotimer.h"
+
+namespace Arts {
+
+class MidiSyncGroup_impl;
+class AudioSync_impl : virtual public AudioSync_skel,
+ virtual public AudioTimerCallback
+{
+ AudioTimer *timer;
+
+ struct AudioSyncEvent
+ {
+ TimeStamp time;
+ std::list<SynthModule> startModules;
+ std::list<SynthModule> stopModules;
+
+ void execute();
+ };
+ std::list<AudioSyncEvent *> events;
+ AudioSyncEvent *newEvent;
+ MidiSyncGroup_impl *syncGroup;
+ TimeStamp syncOffset;
+
+ TimeStamp audioTime();
+ TimeStamp audioPlayTime();
+
+public:
+ AudioSync_impl();
+ ~AudioSync_impl();
+
+ // public interface
+ TimeStamp time();
+ TimeStamp playTime();
+
+ void queueStart(SynthModule synthModule);
+ void queueStop(SynthModule synthModule);
+ void execute();
+ void executeAt(const TimeStamp& timeStamp);
+
+ // interface to AudioTimer
+ void updateTime();
+
+ // interface to MidiSyncGroup
+ static AudioSync_impl *find(AudioSync audioSync);
+
+ void synchronizeTo(const TimeStamp& time);
+ void setSyncGroup(MidiSyncGroup_impl *syncGroup);
+ TimeStamp clientTime();
+};
+
+}
+
+#endif /* AUDIOSYNC_IMPL_H */
diff --git a/arts/midi/audiotimer.cc b/arts/midi/audiotimer.cc
new file mode 100644
index 00000000..354f153b
--- /dev/null
+++ b/arts/midi/audiotimer.cc
@@ -0,0 +1,98 @@
+ /*
+
+ Copyright (C) 2001-2002 Stefan Westerfeld
+ stefan@space.twc.de
+
+ 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., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "artsmidi.h"
+#include "artsflow.h"
+#include "stdsynthmodule.h"
+#include "debug.h"
+#include "miditimercommon.h"
+#include "audiotimer.h"
+#include "flowsystem.h"
+
+using namespace std;
+using namespace Arts;
+
+static AudioTimer *AudioTimer_the = 0;
+
+AudioTimer::AudioTimer()
+{
+ AudioTimer_the = this;
+ samples = seconds = 0;
+}
+
+AudioTimer::~AudioTimer()
+{
+ AudioTimer_the = 0;
+}
+
+TimeStamp AudioTimer::time()
+{
+ return TimeStamp(seconds,
+ (long)((float)samples / samplingRateFloat * 1000000.0));
+}
+
+void AudioTimer::notify(const Notification &)
+{
+ list<AudioTimerCallback *>::iterator i;
+ for(i = callbacks.begin(); i != callbacks.end(); i++)
+ (*i)->updateTime();
+}
+
+void AudioTimer::calculateBlock(unsigned long s)
+{
+ samples += s;
+ while(samples > samplingRate)
+ {
+ samples -= samplingRate;
+ seconds++;
+ }
+ Notification n;
+ n.receiver = this;
+ n.ID = 0;
+ n.data = 0;
+ n.internal = 0;
+ NotificationManager::the()->send(n);
+}
+
+AudioTimer *AudioTimer::subscribe()
+{
+ if(!AudioTimer_the)
+ {
+ new AudioTimer();
+ AudioTimer_the->_node()->start();
+ }
+ else
+ {
+ AudioTimer_the->_copy();
+ }
+ return AudioTimer_the;
+}
+
+void AudioTimer::addCallback(AudioTimerCallback *callback)
+{
+ callbacks.push_back(callback);
+}
+
+void AudioTimer::removeCallback(AudioTimerCallback *callback)
+{
+ callbacks.remove(callback);
+}
diff --git a/arts/midi/audiotimer.h b/arts/midi/audiotimer.h
new file mode 100644
index 00000000..f0299cf3
--- /dev/null
+++ b/arts/midi/audiotimer.h
@@ -0,0 +1,73 @@
+ /*
+
+ Copyright (C) 2002 Stefan Westerfeld
+ stefan@space.twc.de
+
+ 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., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+#ifndef AUDIOTIMER_H
+#define AUDIOTIMER_H
+
+#include "artsmidi.h"
+#include "artsflow.h"
+#include "stdsynthmodule.h"
+#include "miditimercommon.h"
+#include "flowsystem.h"
+
+namespace Arts {
+
+/**
+ * EXPERIMENTAL (this interface will probably not stay this way) way to handle
+ * audio based time stamps. Callback interface.
+ */
+class AudioTimerCallback {
+public:
+ virtual void updateTime() = 0;
+};
+
+/**
+ * EXPERIMENTAL (this interface will probably not stay this way) way to handle
+ * audio based time stamps. Timer class for audio timestamps.
+ */
+class AudioTimer : public SynthModule_skel, public StdSynthModule
+ /* we are a SynthModule to get the timing */
+{
+protected:
+ AudioTimer();
+ virtual ~AudioTimer();
+
+ std::list<AudioTimerCallback *> callbacks;
+
+ long samples; /* at most samplingRate, overflow goes in seconds */
+ long seconds;
+
+public:
+ // allocation: share one AudioTimer for everbody who needs one
+ static AudioTimer *subscribe();
+ void unsubscribe() { _release(); }
+
+ void notify(const Notification& n);
+ TimeStamp time();
+ void calculateBlock(unsigned long samples);
+
+ void addCallback(AudioTimerCallback *callback);
+ void removeCallback(AudioTimerCallback *callback);
+};
+
+}
+
+#endif
diff --git a/arts/midi/mcopclass/Makefile.am b/arts/midi/mcopclass/Makefile.am
new file mode 100644
index 00000000..5fbeafe8
--- /dev/null
+++ b/arts/midi/mcopclass/Makefile.am
@@ -0,0 +1,2 @@
+mcopclassdir = $(libdir)/mcop/Arts
+mcopclass_DATA = MidiManager.mcopclass
diff --git a/arts/midi/mcopclass/MidiManager.mcopclass b/arts/midi/mcopclass/MidiManager.mcopclass
new file mode 100644
index 00000000..a86af4a4
--- /dev/null
+++ b/arts/midi/mcopclass/MidiManager.mcopclass
@@ -0,0 +1,3 @@
+Interface=Arts::MidiManager,Arts::Object
+Language=C++
+Library=libartsmidi.la
diff --git a/arts/midi/midiclient_impl.cc b/arts/midi/midiclient_impl.cc
new file mode 100644
index 00000000..d57e7fd7
--- /dev/null
+++ b/arts/midi/midiclient_impl.cc
@@ -0,0 +1,274 @@
+ /*
+
+ Copyright (C) 2000-2002 Stefan Westerfeld
+ stefan@space.twc.de
+
+ 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., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "midiclient_impl.h"
+#include "midimanager_impl.h"
+#include "midimanagerport_impl.h"
+#include "midisyncgroup_impl.h"
+#include "timestampmath.h"
+
+#undef DEBUG_SYNC_DRIFT
+
+using namespace Arts;
+using namespace std;
+
+MidiClient_impl::MidiClient_impl(const MidiClientInfo& info,
+ MidiManager_impl *manager) :_info(info), manager(manager), syncGroup(0)
+{
+}
+
+MidiClient_impl::~MidiClient_impl()
+{
+ while(!_info.connections.empty())
+ disconnect(manager->findClient(_info.connections[0]));
+
+ if(syncGroup)
+ {
+ syncGroup->clientDied(this);
+ syncGroup = 0;
+ }
+ manager->removeClient(this);
+}
+
+MidiClientInfo MidiClient_impl::info()
+{
+ return _info;
+}
+
+void MidiClient_impl::title(const string &newvalue)
+{
+ _info.title = newvalue;
+}
+
+string MidiClient_impl::title()
+{
+ return _info.title;
+}
+
+void MidiClient_impl::addInputPort(MidiPort port)
+{
+ assert(_info.direction == mcdRecord);
+
+ ports.push_back(port);
+
+ // FIXME: should we synchronize inputPorts at all
+ rebuildConnections();
+}
+
+MidiPort MidiClient_impl::addOutputPort()
+{
+ assert(_info.direction == mcdPlay);
+
+ MidiPort port = MidiPort::_from_base(new MidiManagerPort_impl(this));
+ ports.push_back(port);
+
+ rebuildConnections();
+ return port;
+}
+
+void MidiClient_impl::removePort(MidiPort port)
+{
+ list<MidiPort>::iterator i = ports.begin();
+ while(i != ports.end())
+ {
+ if (i->_isEqual(port))
+ i = ports.erase(i);
+ else
+ i++;
+ }
+
+ rebuildConnections();
+}
+
+void MidiClient_impl::rebuildConnections()
+{
+ _connections.clear();
+
+ vector<long>::iterator li;
+ for(li = _info.connections.begin(); li != _info.connections.end(); li++)
+ {
+ MidiClient_impl *other = manager->findClient(*li);
+ assert(other);
+
+ list<MidiPort>::iterator pi;
+ for(pi = other->ports.begin(); pi != other->ports.end(); pi++)
+ {
+ MidiClientConnection mcc;
+ mcc.offset = TimeStamp(0,0);
+ mcc.port = *pi;
+ _connections.push_back(mcc);
+ }
+ }
+ adjustSync();
+}
+
+list<MidiClientConnection> *MidiClient_impl::connections()
+{
+ return &_connections;
+}
+
+static void removeElement(vector<long>& vec, long el)
+{
+ vector<long> tmp;
+ vec.swap(tmp);
+ vector<long>::iterator i;
+ for(i = tmp.begin(); i != tmp.end(); i++)
+ if(*i != el) vec.push_back(*i);
+}
+
+void MidiClient_impl::connect(MidiClient_impl *dest)
+{
+ assert(_info.direction != dest->_info.direction);
+
+ disconnect(dest);
+
+ _info.connections.push_back(dest->ID());
+ dest->_info.connections.push_back(ID());
+
+ list<MidiPort>::iterator pi;
+
+ /* add the other clients ports to our connection list */
+ for(pi = dest->ports.begin(); pi != dest->ports.end(); pi++)
+ {
+ MidiClientConnection mcc;
+ mcc.offset = TimeStamp(0,0);
+ mcc.port = *pi;
+ _connections.push_back(mcc);
+ }
+
+ /* add our ports to the other clients connection list */
+ for(pi = ports.begin(); pi != ports.end(); pi++)
+ {
+ MidiClientConnection mcc;
+ mcc.offset = TimeStamp(0,0);
+ mcc.port = *pi;
+ dest->_connections.push_back(mcc);
+ }
+ adjustSync();
+}
+
+void MidiClient_impl::disconnect(MidiClient_impl *dest)
+{
+ assert(_info.direction != dest->_info.direction);
+
+ removeElement(_info.connections,dest->ID());
+ removeElement(dest->_info.connections,ID());
+
+ list<MidiPort>::iterator pi;
+
+ /* remove the other clients ports from our connection list */
+ for(pi = dest->ports.begin(); pi != dest->ports.end(); pi++)
+ {
+ list<MidiClientConnection>::iterator ci = _connections.begin();
+
+ while(ci != _connections.end())
+ {
+ if(ci->port._isEqual(*pi))
+ ci = _connections.erase(ci);
+ else
+ ci++;
+ }
+ }
+
+ /* remove our ports from the other clients connection list */
+ for(pi = ports.begin(); pi != ports.end(); pi++)
+ {
+ list<MidiClientConnection>::iterator ci = dest->_connections.begin();
+
+ while(ci != dest->_connections.end())
+ {
+ if(ci->port._isEqual(*pi))
+ ci = dest->_connections.erase(ci);
+ else
+ ci++;
+ }
+ }
+ adjustSync();
+}
+
+void MidiClient_impl::synchronizeTo(const TimeStamp& time)
+{
+ list<MidiClientConnection>::iterator i;
+
+ for(i = _connections.begin(); i != _connections.end(); i++)
+ {
+ MidiClientConnection& mcc = *i;
+
+#ifdef DEBUG_SYNC_DRIFT
+ TimeStamp drift = mcc.offset; // debug drift
+#endif
+
+ mcc.offset = mcc.port.playTime();
+ timeStampDec(mcc.offset, time);
+
+#ifdef DEBUG_SYNC_DRIFT
+ timeStampDec(drift,mcc.offset); // debug drift
+ printf("SYNC DRIFT %30s %30s: %f\n",
+ mcc.port._interfaceName().c_str(), _info.title.c_str(),
+ timeStampToDouble(drift));
+#endif
+ }
+}
+
+void MidiClient_impl::setSyncGroup(MidiSyncGroup_impl *newSyncGroup)
+{
+ syncGroup = newSyncGroup;
+}
+
+void MidiClient_impl::adjustSync()
+{
+ if(syncGroup)
+ syncGroup->clientChanged(this);
+ else
+ synchronizeTo(systemMidiTimer.time());
+}
+
+TimeStamp MidiClient_impl::time()
+{
+ if(syncGroup)
+ return syncGroup->time();
+ else
+ return clientTime();
+}
+
+TimeStamp MidiClient_impl::playTime()
+{
+ if(syncGroup)
+ return syncGroup->playTime();
+ else
+ return systemMidiTimer.time();
+}
+
+TimeStamp MidiClient_impl::clientTime()
+{
+ TimeStamp result = playTime();
+
+ list<MidiClientConnection>::iterator i;
+ for(i = _connections.begin(); i != _connections.end(); i++)
+ {
+ TimeStamp time = i->port.time();
+ timeStampDec(time, i->offset);
+ result = timeStampMax(result, time);
+ }
+
+ return result;
+}
diff --git a/arts/midi/midiclient_impl.h b/arts/midi/midiclient_impl.h
new file mode 100644
index 00000000..b1aeb204
--- /dev/null
+++ b/arts/midi/midiclient_impl.h
@@ -0,0 +1,80 @@
+ /*
+
+ Copyright (C) 2000 Stefan Westerfeld
+ stefan@space.twc.de
+
+ 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., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef ARTS_MIDICLIENT_IMPL_H
+#define ARTS_MIDICLIENT_IMPL_H
+
+#include "artsmidi.h"
+#include "midimanager_impl.h"
+#include "midimanagerport_impl.h"
+
+namespace Arts {
+
+struct MidiClientConnection
+{
+ TimeStamp offset;
+ MidiPort port;
+};
+
+class MidiManager_impl;
+class MidiSyncGroup_impl;
+class MidiClient_impl : virtual public MidiClient_skel
+{
+protected:
+ SystemMidiTimer systemMidiTimer;
+ MidiClientInfo _info;
+ MidiManager_impl *manager;
+ MidiSyncGroup_impl *syncGroup;
+ std::list<MidiPort> ports;
+ std::list<MidiClientConnection> _connections;
+
+public:
+ MidiClient_impl(const MidiClientInfo& info, MidiManager_impl *manager);
+ ~MidiClient_impl();
+
+ // MCOP interface
+ MidiClientInfo info();
+ void title(const std::string &newvalue);
+ std::string title();
+ void addInputPort(MidiPort port);
+ MidiPort addOutputPort();
+ void removePort(MidiPort port);
+
+ // interface to MidiManager/Port
+ inline long ID() { return _info.ID; }
+ std::list<MidiClientConnection> *connections();
+ void connect(MidiClient_impl *dest);
+ void disconnect(MidiClient_impl *dest);
+ void rebuildConnections();
+ void adjustSync();
+
+ TimeStamp time();
+ TimeStamp playTime();
+
+ // interface to MidiSyncGroup
+ void synchronizeTo(const TimeStamp& time);
+ void setSyncGroup(MidiSyncGroup_impl *syncGroup);
+ TimeStamp clientTime();
+};
+
+}
+#endif /* ARTS_MIDICLIENT_IMPL_H */
diff --git a/arts/midi/midimanager_impl.cc b/arts/midi/midimanager_impl.cc
new file mode 100644
index 00000000..1ba07962
--- /dev/null
+++ b/arts/midi/midimanager_impl.cc
@@ -0,0 +1,156 @@
+ /*
+
+ Copyright (C) 2000 Stefan Westerfeld
+ stefan@space.twc.de
+
+ 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., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "midimanager_impl.h"
+#include "midiclient_impl.h"
+#include "midisyncgroup_impl.h"
+#include "debug.h"
+
+using namespace Arts;
+using namespace std;
+
+static int cleanReference(const string& reference)
+{
+ Object test = Reference("global:"+reference);
+ if(test.isNull())
+ {
+ Dispatcher::the()->globalComm().erase(reference);
+ return 1;
+ }
+ else
+ return 0;
+}
+
+MidiManager_impl::MidiManager_impl() : nextID(1)
+{
+ cleanReference("Arts_MidiManager");
+ if(!ObjectManager::the()->addGlobalReference(Object::_from_base(_copy()),
+ "Arts_MidiManager"))
+ {
+ arts_warning("can't register Arts::MidiManager");
+ }
+ else
+ {
+ arts_debug("Arts::MidiManager registered successfully.");
+ }
+ Dispatcher::the()->ioManager()->addTimer(1000, this);
+}
+
+MidiManager_impl::~MidiManager_impl()
+{
+ Dispatcher::the()->ioManager()->removeTimer(this);
+}
+
+vector<MidiClientInfo> *MidiManager_impl::clients()
+{
+ if(!alsaMidiGateway.isNull())
+ {
+ if(!alsaMidiGateway.rescan())
+ alsaMidiGateway = AlsaMidiGateway::null();
+ }
+
+ vector<MidiClientInfo> *result = new vector<MidiClientInfo>;
+
+ list<MidiClient_impl *>::iterator i;
+ for(i = _clients.begin(); i != _clients.end(); i++)
+ result->push_back((*i)->info());
+
+ return result;
+}
+
+MidiClient MidiManager_impl::addClient(MidiClientDirection direction,
+ MidiClientType type, const string& title, const string& autoRestoreID)
+{
+ MidiClientInfo info;
+
+ info.ID = nextID++;
+ info.direction = direction;
+ info.type = type;
+ info.title = title;
+ info.autoRestoreID = autoRestoreID;
+
+ MidiClient_impl *impl = new MidiClient_impl(info, this);
+ _clients.push_back(impl);
+ return MidiClient::_from_base(impl);
+}
+
+void MidiManager_impl::removeClient(MidiClient_impl *client)
+{
+ _clients.remove(client);
+}
+
+MidiClient_impl *MidiManager_impl::findClient(long clientID)
+{
+ list<MidiClient_impl *>::iterator i;
+
+ for(i = _clients.begin(); i != _clients.end(); i++)
+ {
+ if((*i)->ID() == clientID)
+ return (*i);
+ }
+ return 0;
+}
+
+void MidiManager_impl::connect(long clientID, long destinationID)
+{
+ MidiClient_impl *src = findClient(clientID);
+ MidiClient_impl *dest = findClient(destinationID);
+
+ arts_return_if_fail(src);
+ arts_return_if_fail(dest);
+ src->connect(dest);
+}
+
+void MidiManager_impl::disconnect(long clientID, long destinationID)
+{
+ MidiClient_impl *src = findClient(clientID);
+ MidiClient_impl *dest = findClient(destinationID);
+
+ arts_return_if_fail(src);
+ arts_return_if_fail(dest);
+ src->disconnect(dest);
+}
+
+MidiSyncGroup MidiManager_impl::addSyncGroup()
+{
+ MidiSyncGroup_impl *impl = new MidiSyncGroup_impl(this);
+ syncGroups.push_back(impl);
+ return MidiSyncGroup::_from_base(impl);
+}
+
+void MidiManager_impl::removeSyncGroup(MidiSyncGroup_impl *group)
+{
+ syncGroups.remove(group);
+}
+
+void MidiManager_impl::notifyTime()
+{
+ list<MidiClient_impl *>::iterator i;
+ for(i = _clients.begin(); i != _clients.end(); i++)
+ (*i)->adjustSync();
+
+ list<MidiSyncGroup_impl *>::iterator gi;
+ for(gi = syncGroups.begin(); gi != syncGroups.end(); gi++)
+ (*gi)->adjustSync();
+}
+
+namespace Arts { REGISTER_IMPLEMENTATION(MidiManager_impl); }
diff --git a/arts/midi/midimanager_impl.h b/arts/midi/midimanager_impl.h
new file mode 100644
index 00000000..0c9ec5df
--- /dev/null
+++ b/arts/midi/midimanager_impl.h
@@ -0,0 +1,67 @@
+ /*
+
+ Copyright (C) 2000 Stefan Westerfeld
+ stefan@space.twc.de
+
+ 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., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef ARTS_MIDIMANAGER_IMPL_H
+#define ARTS_MIDIMANAGER_IMPL_H
+
+#include "artsmidi.h"
+
+namespace Arts {
+
+class MidiClient_impl;
+class MidiSyncGroup_impl;
+class MidiManager_impl : virtual public MidiManager_skel,
+ virtual public TimeNotify
+{
+protected:
+ std::list<MidiClient_impl *> _clients;
+ std::list<MidiSyncGroup_impl *> syncGroups;
+
+ long nextID;
+ AlsaMidiGateway alsaMidiGateway;
+
+ void notifyTime();
+
+public:
+ MidiManager_impl();
+ ~MidiManager_impl();
+
+ // public interface
+ std::vector<MidiClientInfo> *clients();
+
+ MidiClient addClient(MidiClientDirection direction, MidiClientType type,
+ const std::string& title, const std::string& autoRestoreID);
+
+ void connect(long clientID, long destinationID);
+ void disconnect(long clientID, long destinationID);
+ MidiSyncGroup addSyncGroup();
+
+ // interface to MidiClient_impl
+ void removeClient(MidiClient_impl *client);
+ MidiClient_impl *findClient(long clientID);
+
+ // interface to MidiSyncGroup_impl
+ void removeSyncGroup(MidiSyncGroup_impl *group);
+};
+
+}
+#endif /* ARTS_MIDIMANAGER_IMPL_H */
diff --git a/arts/midi/midimanagerport_impl.cc b/arts/midi/midimanagerport_impl.cc
new file mode 100644
index 00000000..a6973a51
--- /dev/null
+++ b/arts/midi/midimanagerport_impl.cc
@@ -0,0 +1,71 @@
+ /*
+
+ Copyright (C) 2000 Stefan Westerfeld
+ stefan@space.twc.de
+
+ 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., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "midimanagerport_impl.h"
+#include "midimanager_impl.h"
+#include "midiclient_impl.h"
+#include "timestampmath.h"
+
+#include <stdio.h>
+
+using namespace Arts;
+using namespace std;
+
+MidiManagerPort_impl::MidiManagerPort_impl(MidiClient_impl *client)
+ : client(client)
+{
+}
+
+TimeStamp MidiManagerPort_impl::time()
+{
+ return client->time();
+}
+
+TimeStamp MidiManagerPort_impl::playTime()
+{
+ return client->playTime();
+}
+
+
+void MidiManagerPort_impl::processCommand(const MidiCommand& command)
+{
+ list<MidiClientConnection> *connections = client->connections();
+ list<MidiClientConnection>::iterator i;
+
+ for(i = connections->begin(); i != connections->end(); i++)
+ i->port.processCommand(command);
+}
+
+void MidiManagerPort_impl::processEvent(const MidiEvent& event)
+{
+ list<MidiClientConnection> *connections = client->connections();
+ list<MidiClientConnection>::iterator i;
+
+ for(i = connections->begin(); i != connections->end(); i++)
+ {
+ /* relocate the event to the synchronized time */
+ TimeStamp time = event.time;
+ timeStampInc(time, i->offset);
+
+ i->port.processEvent(MidiEvent(time, event.command));
+ }
+}
diff --git a/arts/midi/midimanagerport_impl.h b/arts/midi/midimanagerport_impl.h
new file mode 100644
index 00000000..56998546
--- /dev/null
+++ b/arts/midi/midimanagerport_impl.h
@@ -0,0 +1,46 @@
+ /*
+
+ Copyright (C) 2000 Stefan Westerfeld
+ stefan@space.twc.de
+
+ 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., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef MIDIMANAGERPORT_IMPL_H
+#define MIDIMANAGERPORT_IMPL_H
+
+#include "artsmidi.h"
+
+namespace Arts {
+
+class MidiClient_impl;
+
+class MidiManagerPort_impl : public MidiPort_skel
+{
+ MidiClient_impl *client;
+ SystemMidiTimer timer;
+public:
+ MidiManagerPort_impl(MidiClient_impl *client);
+ TimeStamp time();
+ TimeStamp playTime();
+ void processCommand(const MidiCommand& command);
+ void processEvent(const MidiEvent& event);
+};
+
+}
+
+#endif /* MIDIMANAGERPORT_IMPL_H */
diff --git a/arts/midi/midimsg.c b/arts/midi/midimsg.c
new file mode 100644
index 00000000..7032fba8
--- /dev/null
+++ b/arts/midi/midimsg.c
@@ -0,0 +1,177 @@
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "midimsg.h"
+
+#define STATUS_MASK 0x80
+#define MESSAGE_TYPE_MASK 0xf0
+#define CHANNEL_MASK 0x0f
+
+Byte midiReadByte(int fd)
+{
+ Byte current_byte;
+ /*
+ ** for now ignore all realtime-messages (0xF8 .. 0xFF), which may get
+ ** embedded *inside* every other message
+ */
+ do {
+ /*
+ ** read() is wrapped in a while() in order to handle interruption
+ ** by a signal. In the normal case, read() is only called once.
+ */
+ while (read(/* fd = */ fd, /* buf = */ (void *)(&current_byte),
+ /* count = */ 1) < 1) {}
+
+ } while(current_byte >= 0xf8);
+
+ return current_byte;
+}
+
+int midimsgGetMessageType(Byte *message)
+{
+ return (message[0] & MESSAGE_TYPE_MASK);
+}
+
+void midimsgSetMessageType(Byte *message_inout, int message_type)
+{
+ message_inout[0] = (message_inout[0] & ~MESSAGE_TYPE_MASK) | message_type;
+}
+
+int midimsgGetChannel(Byte *message)
+{
+ return (message[0] & CHANNEL_MASK);
+}
+
+void midimsgSetChannel(Byte *message_inout, int channel)
+{
+ message_inout[0] = (message_inout[0] & ~CHANNEL_MASK) | channel;
+}
+
+int midimsgGetPitch(Byte *message)
+{
+ return (message[1]);
+}
+
+void midimsgSetPitch(Byte *message_inout, int pitch)
+{
+ message_inout[1] = pitch;
+}
+
+int midimsgGetVelocity(Byte *message)
+{
+ return (message[2]);
+}
+
+void midimsgSetVelocity(Byte *message_inout, int velocity)
+{
+ message_inout[2] = velocity;
+}
+
+int midimsgGetParameterNumber(Byte *message)
+{
+ return (message[1]);
+}
+
+void midimsgSetParameterNumber(Byte *message_inout, int number)
+{
+ message_inout[1] = number;
+}
+
+int midimsgGetParameterValue(Byte *message)
+{
+ return (message[2]);
+}
+
+void midimsgSetParameterValue(Byte *message_inout, int value)
+{
+ message_inout[2] = value;
+}
+
+int midimsgGetPitchWheelValue(Byte *message)
+{
+ return (((int)(message[2]) << 7) + message[1]);
+}
+
+void midimsgRead(int fd, Byte *message_return)
+{
+ Byte current_byte;
+ static Byte last_status_byte;
+
+ current_byte = midiReadByte(fd);
+
+ if ((current_byte & STATUS_MASK) == 0)
+ {
+ /*
+ ** must be a running status, unless we're picking up mid-message
+ ** (which would be an unhandled error)
+ */
+
+ message_return[0] = last_status_byte;
+ }
+ else
+ {
+ message_return[0] = current_byte;
+ last_status_byte = current_byte;
+
+ current_byte = midiReadByte(fd);
+ }
+
+ switch (midimsgGetMessageType(/* message = */ message_return))
+ {
+ case MIDIMSG_NOTE_ON:
+ case MIDIMSG_NOTE_OFF:
+ case MIDIMSG_KEY_PRESSURE:
+ case MIDIMSG_PARAMETER:
+ case MIDIMSG_PITCH_WHEEL:
+
+ message_return[1] = current_byte;
+
+ current_byte = midiReadByte(fd);
+ message_return[2] = current_byte;
+
+ if ((midimsgGetMessageType(/* message = */ message_return) ==
+ MIDIMSG_NOTE_ON) && (midimsgGetVelocity(/* message = */
+ message_return) == 0))
+ {
+ /* note-on with velocity of zero is equivalent to note-off */
+
+ midimsgSetMessageType(/* message_inout = */ message_return,
+ /* message_type = */ MIDIMSG_NOTE_OFF);
+ }
+
+ return;
+
+ case MIDIMSG_PROGRAM:
+ case MIDIMSG_CHANNEL_PRESSURE:
+
+ message_return[1] = current_byte;
+
+ return;
+ }
+}
+
+void midimsgWrite(int fd, Byte *message)
+{
+ switch (midimsgGetMessageType(/* message = */ message))
+ {
+ case MIDIMSG_NOTE_ON:
+ case MIDIMSG_NOTE_OFF:
+ case MIDIMSG_KEY_PRESSURE:
+ case MIDIMSG_PARAMETER:
+ case MIDIMSG_PITCH_WHEEL:
+ write(fd, message, 3);
+ break;
+ case MIDIMSG_PROGRAM:
+ case MIDIMSG_CHANNEL_PRESSURE:
+ write(fd, message, 2);
+ break;
+ }
+}
+
diff --git a/arts/midi/midimsg.h b/arts/midi/midimsg.h
new file mode 100644
index 00000000..3bba90d7
--- /dev/null
+++ b/arts/midi/midimsg.h
@@ -0,0 +1,44 @@
+
+#ifndef MIDIMSG_H
+#define MIDIMSG_H
+
+typedef unsigned char Byte;
+
+#define MIDIMSG_NOTE_OFF 0x80
+#define MIDIMSG_NOTE_ON 0x90
+#define MIDIMSG_KEY_PRESSURE 0xa0
+#define MIDIMSG_PARAMETER 0xb0
+#define MIDIMSG_PROGRAM 0xc0
+#define MIDIMSG_CHANNEL_PRESSURE 0xd0
+#define MIDIMSG_PITCH_WHEEL 0xe0
+
+#ifndef True
+#define True 1
+#define False 0
+#endif
+
+int midimsgGetMessageType(Byte *message);
+void midimsgSetMessageType(Byte *message_inout, int message_type);
+
+int midimsgGetChannel(Byte *message);
+void midimsgSetChannel(Byte *message_inout, int channel);
+
+int midimsgGetPitch(Byte *message);
+void midimsgSetPitch(Byte *message_inout, int pitch);
+
+int midimsgGetVelocity(Byte *message);
+void midimsgSetVelocity(Byte *message_inout, int velocity);
+
+int midimsgGetParameterNumber(Byte *message);
+void midimsgSetParameterNumber(Byte *message_inout, int number);
+
+int midimsgGetParameterValue(Byte *message);
+void midimsgSetParameterValue(Byte *message_inout, int value);
+
+int midimsgGetPitchWheelValue(Byte *message);
+
+void midimsgRead(int fd, Byte *message_return);
+void midimsgWrite(int fd, Byte *message_return);
+
+#endif
+
diff --git a/arts/midi/midisend.cc b/arts/midi/midisend.cc
new file mode 100644
index 00000000..2c1878ef
--- /dev/null
+++ b/arts/midi/midisend.cc
@@ -0,0 +1,375 @@
+/*
+
+Copyright (C) 1999 Emmeran Seehuber
+ the_emmy@gmx.de
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+/*
+ Changes:
+ 16.09.1999 Emmeran "Emmy" Seehuber <the_emmy@gmx.de>
+ - Implementeted mapping of channels and pitches.
+ - Reworked option parsing, now using getopt().
+ Note: The parameters of the programms have changed !
+*/
+
+/*
+** This program was in original by David G. Slomin.
+** It was released to the Public Domain on 1/25/99.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "midisend.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+using namespace std;
+
+int input_fd = -1, test = 0, verbose = 0;
+char cFileName[1025];
+int optch;
+CMidiMap Map;
+
+void usage(char *prog)
+{
+ fprintf(stderr,"\n");
+ fprintf(stderr,"Usage: %s [ -f <mididevice> ] [ -m <mapfile> ] [ -v ] [ -t <loop> ]\n",prog);
+ fprintf(stderr," -f the mididevice to read the input from.\n");
+ fprintf(stderr," Default is /dev/midi. If you specify a dash, it is stdin\n");
+ fprintf(stderr," -m the mapfile to load.\n");
+ fprintf(stderr," -v verbose output.\n");
+ fprintf(stderr," -t test mode. Generates a testoutput on the midibus\n");
+ fprintf(stderr," -l long test mode. Generates a testoutput on the midibus\n");
+ exit(1);
+}
+
+void parseArgs(int argc, char** argv)
+{
+ // Setup default
+ strcpy(cFileName,"/dev/midi");
+
+ while((optch = getopt(argc,argv,"m:f:vtl")) > 0)
+ {
+ switch(optch)
+ {
+ case 'm': if( !Map.readMap(optarg) )
+ fprintf(stderr,"%s: can't read file %s!\n",argv[0],optarg);
+ break;
+ case 't': test = 1;
+ break;
+ case 'l': test = 2;
+ break;
+#ifdef VERSION
+ case 'v': verbose = 1; printf("MidiSend %s\n", VERSION );
+ break;
+#endif
+ case 'f': strncpy(cFileName,optarg,1024);
+ break;
+ default: usage(argv[0]);
+ break;
+ }
+ }
+}
+
+
+#ifdef COMMON_BINARY
+int midisend_main(int argc, char *argv[])
+#else
+int main(int argc, char *argv[])
+#endif
+{
+ Arts::Dispatcher dispatcher;
+ Arts::MidiManager manager = Arts::Reference("global:Arts_MidiManager");
+
+ if (manager.isNull())
+ {
+ fprintf(stderr, "%s trouble: No midimanager object found; please start "
+ "artsd.\n",argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ /*
+ ** MIDI input initialization.
+ */
+
+ parseArgs(argc,argv);
+
+ string title = string("midisend (") + cFileName +")";
+ Arts::MidiClient client
+ = manager.addClient(Arts::mcdPlay,Arts::mctApplication,title,"midisend");
+ Arts::MidiPort port = client.addOutputPort();
+
+ if(test)
+ {
+ if( verbose )
+ printf("performing test ...\n");
+ unsigned long i,max=5000;
+ if(test==2) max = 20000;
+ for(i=0;i<max;i++)
+ {
+ port.processCommand(
+ Arts::MidiCommand(Arts::mcsNoteOn, 60+(i%12), 100));
+ port.processCommand(
+ Arts::MidiCommand(Arts::mcsNoteOff,60+(i%12), 0));
+ }
+ exit(0);
+ }
+
+ if( verbose )
+ printf("trying to open %s ...", cFileName );
+
+ input_fd = open(cFileName,O_RDONLY);
+
+ if(input_fd == -1)
+ {
+ fprintf(stderr,"\n%s trouble: can't open input device!\n",argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ else if( verbose )
+ printf(" ok!\n");
+
+
+ /*
+ ** Main loop.
+ */
+
+ if(verbose)
+ printf("beginning loop ...\n");
+
+ unsigned char msg[3];
+
+ while (1)
+ {
+ midimsgRead(input_fd, msg);
+ switch (midimsgGetMessageType(msg))
+ {
+ case MIDIMSG_NOTE_OFF:
+ Map.mapMsg(msg);
+ port.processCommand(
+ Arts::MidiCommand(Arts::mcsNoteOff|midimsgGetChannel(msg),
+ midimsgGetPitch(msg), midimsgGetVelocity(msg)));
+ if( verbose )
+ printf("NoteOff: Channel %d, Pitch %3d\n", midimsgGetChannel(msg),midimsgGetPitch(msg));
+ break;
+ case MIDIMSG_NOTE_ON:
+ Map.mapMsg(msg);
+ port.processCommand(
+ Arts::MidiCommand(Arts::mcsNoteOn|midimsgGetChannel(msg),
+ midimsgGetPitch(msg), midimsgGetVelocity(msg)));
+ if( verbose )
+ printf("NoteOn : Channel %d, Pitch %3d, Velocity %2d\n", midimsgGetChannel(msg),
+ midimsgGetPitch(msg),midimsgGetVelocity(msg));
+ break;
+ case MIDIMSG_PITCH_WHEEL:
+ Map.mapMsg(msg);
+ port.processCommand(
+ Arts::MidiCommand(Arts::mcsPitchWheel|midimsgGetChannel(msg),
+ midimsgGetPitch(msg), midimsgGetVelocity(msg)));
+ if( verbose )
+ printf("PitchWheel : Channel %d, LSB %3d MSB %3d\n", midimsgGetChannel(msg),
+ midimsgGetPitch(msg),midimsgGetVelocity(msg));
+ break;
+ }
+ }
+}
+
+//--------------------------------------------
+// The mapping stuff
+//--------------------------------------------
+
+bool CMidiMap::readMap(const char* pszFileName)
+{
+ if( verbose )
+ printf("reading mapfile %s ...\n", pszFileName);
+
+ FILE *file = fopen(pszFileName,"r");
+ if( !file )
+ return false;
+
+ char cBuffer[1024+1];
+ char* pszLine;
+ int nLine = 0;
+ while( (pszLine = fgets(cBuffer,1024,file)) ) {
+ nLine++;
+ parseLine(pszLine,pszFileName,nLine);
+ }
+ fclose(file);
+
+ return true;
+}
+
+bool CMidiMap::getNextWord(char*& pszLine, char*& pszWord)
+{
+ // First skip all leading blanks, etc.
+ bool bCont = true;
+ while(bCont) {
+ char cHelp = *pszLine;
+ switch( cHelp )
+ {
+ case 0: return false; // Out of line
+ case ' ':
+ case '\n':
+ case '\r':
+ case '\t': pszLine++; break; // Goto next char
+ default: bCont = false; break; // NonSpace character -> Word begins here.
+ }
+ }
+
+ // The word starts here
+ pszWord = pszLine;
+
+ // And now, goto the end of the word.
+ bCont = true;
+ while(bCont) {
+ char cHelp = *pszLine;
+ switch( cHelp )
+ {
+ case 0:
+ case ',':
+ case ';':
+ case ' ':
+ case '\n':
+ case '\r':
+ case '\t': *pszLine++ = 0; bCont = false; break; // Goto next char
+ default: pszLine++; break;
+ }
+ }
+
+ return true;
+}
+
+void CMidiMap::parseLine(char* pszLine, const char* pszConfigFile, int nConfigLine )
+{
+ char* pszWord = 0;
+ bool bOk = true;
+
+ // Get first word of the line
+ bOk = bOk && getNextWord(pszLine,pszWord);
+ if( !bOk )
+ return;
+
+ // Skip comments
+ if( *pszWord == '#' )
+ return;
+
+ if( strcmp(pszWord,"PRC") == 0 ) {
+ bOk = bOk && getNextWord(pszLine,pszWord);
+ int nOrigChannel = atol(pszWord);
+ bOk = bOk && getNextWord(pszLine,pszWord);
+ int nStart = atol(pszWord);
+ bOk = bOk && getNextWord(pszLine,pszWord);
+ int nEnd = atol(pszWord);
+ bOk = bOk && getNextWord(pszLine,pszWord);
+ int nChannel = atol(pszWord);
+ if( bOk ) {
+ channelMaps[nOrigChannel].nChannel = nOrigChannel;
+ for( int i = nStart; i <= nEnd; i++ ) {
+ channelMaps[nOrigChannel].channelRemaps[i].nPitch = i;
+ channelMaps[nOrigChannel].channelRemaps[i].nChannel = nChannel;
+ }
+ }
+ else {
+ printf("midisend: (PRC) missing parameters at %s:%d\n", pszConfigFile,nConfigLine);
+ }
+ return;
+ }
+
+ if( strcmp(pszWord,"PRD") == 0 ) {
+ bOk = bOk && getNextWord(pszLine,pszWord);
+ int nOrigChannel = atol(pszWord);
+ bOk = bOk && getNextWord(pszLine,pszWord);
+ int nStart = atol(pszWord);
+ bOk = bOk && getNextWord(pszLine,pszWord);
+ int nEnd = atol(pszWord);
+ bOk = bOk && getNextWord(pszLine,pszWord);
+ int nPitchDiff = atol(pszWord);
+ if( bOk ) {
+ channelMaps[nOrigChannel].nChannel = nOrigChannel;
+ for( int i = nStart; i <= nEnd; i++ ) {
+ channelMaps[nOrigChannel].pitchRemaps[i].nPitch = i;
+ channelMaps[nOrigChannel].pitchRemaps[i].nToPitch = i + nPitchDiff;
+ }
+ }
+ else {
+ printf("midisend: (PRD) missing parameters at %s:%d\n", pszConfigFile,nConfigLine);
+ }
+ return;
+ }
+
+ if( strcmp(pszWord,"PTC") == 0 ) {
+ bOk = bOk && getNextWord(pszLine,pszWord);
+ int nOrigChannel = atol(pszWord);
+ bOk = bOk && getNextWord(pszLine,pszWord);
+ int nPitch = atol(pszWord);
+ bOk = bOk && getNextWord(pszLine,pszWord);
+ int nToChannel = atol(pszWord);
+ if( bOk ) {
+ channelMaps[nOrigChannel].nChannel = nOrigChannel;
+ channelMaps[nOrigChannel].channelRemaps[nPitch].nPitch = nPitch;
+ channelMaps[nOrigChannel].channelRemaps[nPitch].nChannel = nToChannel;
+ }
+ else {
+ printf("midisend: (PTC) missing parameters at %s:%d\n", pszConfigFile,nConfigLine);
+ }
+ return;
+ }
+
+ if( strcmp(pszWord,"PTP") == 0 ) {
+ bOk = bOk && getNextWord(pszLine,pszWord);
+ int nOrigChannel = atol(pszWord);
+ bOk = bOk && getNextWord(pszLine,pszWord);
+ int nPitch = atol(pszWord);
+ bOk = bOk && getNextWord(pszLine,pszWord);
+ int nToPitch = atol(pszWord);
+ if( bOk ) {
+ channelMaps[nOrigChannel].nChannel = nOrigChannel;
+ channelMaps[nOrigChannel].pitchRemaps[nPitch].nPitch = nPitch;
+ channelMaps[nOrigChannel].pitchRemaps[nPitch].nToPitch = nToPitch;
+ }
+ else {
+ printf("midisend: (PTP) missing parameters at %s:%d\n", pszConfigFile,nConfigLine);
+ }
+ return;
+ }
+
+ printf("midisend: Unknown command at %s:%d\n", pszConfigFile,nConfigLine);
+}
+
+void CMidiMap::mapMsg(Byte* msg)
+{
+ // Get out the data for mapping
+ int nChannel = midimsgGetChannel(msg);
+ int nPitch = midimsgGetPitch(msg);
+
+ // Is there something to map for this channel ?
+ if( channelMaps.find(nChannel) != channelMaps.end() ) {
+ // => Yes, than do it.
+
+ // C/P => C
+ if( channelMaps[nChannel].channelRemaps.find(nPitch) != channelMaps[nChannel].channelRemaps.end() )
+ midimsgSetChannel(msg,channelMaps[nChannel].channelRemaps[nPitch].nChannel);
+
+ // C/P => P
+ if( channelMaps[nChannel].pitchRemaps.find(nPitch) != channelMaps[nChannel].pitchRemaps.end() ) {
+ midimsgSetPitch(msg,channelMaps[nChannel].pitchRemaps[nPitch].nToPitch);
+ }
+ }
+}
diff --git a/arts/midi/midisend.h b/arts/midi/midisend.h
new file mode 100644
index 00000000..817ebfa2
--- /dev/null
+++ b/arts/midi/midisend.h
@@ -0,0 +1,106 @@
+/*
+
+Copyright (C) 1999 Emmeran Seehuber
+ the_emmy@gmx.de
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+#ifndef MIDISEND_H
+#define MIDISEND_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "artsmidi.h"
+#include <vector>
+#include <iostream>
+#include <string>
+
+extern "C"
+{
+#include "midimsg.h"
+}
+
+/*
+ This class does the mapping of the
+ channels and pitches
+*/
+class CMidiMap {
+public:
+ /*
+ Reads in the mapfile pszFileName.
+ Returns TRUE, if successful.
+ */
+ bool readMap(const char* pszFileName);
+ /*
+ Maps the given message according to
+ the actual read configuration.
+ */
+ void mapMsg(Byte* msg);
+
+private:
+ /*
+ Parses a configuration line.
+ */
+ void parseLine(char* pszLine, const char* pszConfigFile, int nConfigLine );
+
+ /*
+ Gets the next word out of the line. throws
+ the exception CEOutOfLine, if there is no
+ more word in the line.
+
+ A word consists of all but \0, spacecharacter, ';' and
+ ','
+
+ pszLine is the pointer to the start of the
+ line. This function modifies the pointer.
+ pszWord is the pointer to the found word.
+ */
+ bool getNextWord(char*& pszLine, char*& pszWord);
+private:
+
+ /*
+ For each channel one instance of this structure
+ exists in the channelMaps. It holds the mapping information
+ for nChannel.
+ */
+ struct ChannelMaps {
+ int nChannel;
+
+ struct ChannelRemap {
+ int nPitch;
+ int nChannel;
+ };
+
+ struct PitchRemap {
+ int nPitch;
+ int nToPitch;
+ };
+
+ typedef std::map<int,ChannelRemap> ChannelRemapMap;
+ ChannelRemapMap channelRemaps;
+ typedef std::map<int,PitchRemap> PitchRemapMap;
+ PitchRemapMap pitchRemaps;
+ };
+ typedef std::map<int,ChannelMaps> ChannelMapsMap;
+ ChannelMapsMap channelMaps;
+};
+
+
+#endif
diff --git a/arts/midi/midisyncgroup_impl.cc b/arts/midi/midisyncgroup_impl.cc
new file mode 100644
index 00000000..57ebca2c
--- /dev/null
+++ b/arts/midi/midisyncgroup_impl.cc
@@ -0,0 +1,125 @@
+ /*
+
+ Copyright (C) 2002 Stefan Westerfeld
+ stefan@space.twc.de
+
+ 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., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "midisyncgroup_impl.h"
+#include "audiosync_impl.h"
+#include "midiclient_impl.h"
+#include "midimanager_impl.h"
+#include "timestampmath.h"
+
+using namespace Arts;
+using namespace std;
+
+MidiSyncGroup_impl::MidiSyncGroup_impl(MidiManager_impl *manager)
+ : manager(manager)
+{
+}
+
+MidiSyncGroup_impl::~MidiSyncGroup_impl()
+{
+ /* tell clients we're dead */
+ list<MidiClient_impl *>::iterator i;
+ for(i = clients.begin(); i != clients.end(); i++)
+ (*i)->setSyncGroup(0);
+
+ list<AudioSync_impl *>::iterator ai;
+ for(ai = audioSyncs.begin(); ai != audioSyncs.end(); ai++)
+ (*ai)->setSyncGroup(0);
+
+ manager->removeSyncGroup(this);
+}
+
+void MidiSyncGroup_impl::addClient(MidiClient client)
+{
+ /* add client to list */
+ MidiClient_impl *impl = manager->findClient(client.info().ID);
+ impl->setSyncGroup(this);
+ clients.push_back(impl);
+
+ impl->synchronizeTo(masterTimer.time());
+}
+
+void MidiSyncGroup_impl::removeClient(MidiClient client)
+{
+ /* remove client from the list */
+ MidiClient_impl *impl = manager->findClient(client.info().ID);
+ impl->setSyncGroup(0);
+ clients.remove(impl);
+}
+
+void MidiSyncGroup_impl::addAudioSync(AudioSync audioSync)
+{
+ AudioSync_impl *impl = AudioSync_impl::find(audioSync);
+ impl->setSyncGroup(this);
+ audioSyncs.push_back(impl);
+
+ impl->synchronizeTo(masterTimer.time());
+}
+
+void MidiSyncGroup_impl::removeAudioSync(AudioSync audioSync)
+{
+ AudioSync_impl *impl = AudioSync_impl::find(audioSync);
+ impl->setSyncGroup(0);
+ audioSyncs.remove(impl);
+}
+
+void MidiSyncGroup_impl::clientChanged(MidiClient_impl *client)
+{
+ client->synchronizeTo(masterTimer.time());
+}
+
+void MidiSyncGroup_impl::clientDied(MidiClient_impl *client)
+{
+ clients.remove(client);
+}
+
+void MidiSyncGroup_impl::audioSyncDied(AudioSync_impl *audioSync)
+{
+ audioSyncs.remove(audioSync);
+}
+
+TimeStamp MidiSyncGroup_impl::time()
+{
+ TimeStamp result = masterTimer.time();
+
+ list<MidiClient_impl *>::iterator i;
+ for(i = clients.begin(); i != clients.end(); i++)
+ result = timeStampMax(result, (*i)->clientTime());
+
+ list<AudioSync_impl *>::iterator ai;
+ for(ai = audioSyncs.begin(); ai != audioSyncs.end(); ai++)
+ result = timeStampMax(result, (*ai)->clientTime());
+
+ return result;
+}
+
+TimeStamp MidiSyncGroup_impl::playTime()
+{
+ return masterTimer.time();
+}
+
+void MidiSyncGroup_impl::adjustSync()
+{
+ list<AudioSync_impl *>::iterator ai;
+ for(ai = audioSyncs.begin(); ai != audioSyncs.end(); ai++)
+ (*ai)->synchronizeTo(masterTimer.time());
+}
diff --git a/arts/midi/midisyncgroup_impl.h b/arts/midi/midisyncgroup_impl.h
new file mode 100644
index 00000000..2c2191ac
--- /dev/null
+++ b/arts/midi/midisyncgroup_impl.h
@@ -0,0 +1,67 @@
+ /*
+
+ Copyright (C) 2002 Stefan Westerfeld
+ stefan@space.twc.de
+
+ 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., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef ARTS_MIDISYNCGROUP_IMPL_H
+#define ARTS_MIDISYNCGROUP_IMPL_H
+
+#include "artsmidi.h"
+
+namespace Arts {
+
+class MidiClient_impl;
+class MidiManager_impl;
+class AudioSync_impl;
+
+class MidiSyncGroup_impl : virtual public MidiSyncGroup_skel {
+protected:
+ SystemMidiTimer masterTimer;
+
+ MidiManager_impl *manager;
+ std::list<MidiClient_impl *> clients;
+ std::list<AudioSync_impl *> audioSyncs;
+
+public:
+ MidiSyncGroup_impl(MidiManager_impl *manager);
+ ~MidiSyncGroup_impl();
+
+ // public interface
+ void addClient(MidiClient client);
+ void removeClient(MidiClient client);
+
+ void addAudioSync(AudioSync audioSync);
+ void removeAudioSync(AudioSync audioSync);
+
+ // interface to MidiClient (AudioSync)
+ void clientChanged(MidiClient_impl *client);
+ void clientDied(MidiClient_impl *client);
+ void audioSyncDied(AudioSync_impl *audioSync);
+
+ TimeStamp time();
+ TimeStamp playTime();
+
+ // interface to MidiManager
+ void adjustSync();
+};
+
+}
+
+#endif /* ARTS_MIDISYNCGROUP_IMPL_H */
diff --git a/arts/midi/midisynctest.cc b/arts/midi/midisynctest.cc
new file mode 100644
index 00000000..4faa5279
--- /dev/null
+++ b/arts/midi/midisynctest.cc
@@ -0,0 +1,137 @@
+ /*
+
+ Copyright (C) 2002 Stefan Westerfeld
+ stefan@space.twc.de
+
+ 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., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "config.h"
+#include "artsmidi.h"
+#include "soundserver.h"
+#include "timestampmath.h"
+#include "debug.h"
+#include <stdio.h>
+#include <math.h>
+
+using namespace Arts;
+using namespace std;
+
+int main()
+{
+ Dispatcher dispatcher;
+
+ MidiManager midiManager = DynamicCast(Reference("global:Arts_MidiManager"));
+ if(midiManager.isNull())
+ arts_fatal("midimanager is null");
+
+ SoundServer soundServer = DynamicCast(Reference("global:Arts_SoundServer"));
+ if(soundServer.isNull())
+ arts_fatal("soundServer is null");
+
+ MidiSyncGroup syncGroup = midiManager.addSyncGroup();
+ MidiClient client = midiManager.addClient(mcdPlay, mctApplication, "midisynctest", "midisynctest");
+ syncGroup.addClient(client);
+
+ MidiPort port = client.addOutputPort();
+
+ MidiClient client2 = midiManager.addClient(mcdPlay, mctApplication, "midisynctest2", "midisynctest2");
+
+ syncGroup.addClient(client2);
+
+ MidiPort port2 = client2.addOutputPort();
+
+ /* setup audio synchronization */
+ AudioSync audioSync;
+ audioSync = DynamicCast(soundServer.createObject("Arts::AudioSync"));
+ if(audioSync.isNull())
+ arts_fatal("audioSync is null");
+
+ syncGroup.addAudioSync(audioSync);
+
+ const int C = 60;
+ const int D = 62;
+ const int E = 64;
+ const int F = 65, f = F - 12;
+ const int G = 67, g = G - 12;
+ const int A = 69, a = A - 12;
+ const int H = 71, h = H - 12;
+ int np = 0;
+ int notes[] = { C,E,G,E,a,C,E,C,f,a,C,a,g,h,D,h,0 };
+
+ printf("connect port1 and port2 to two different ports in the artscontrol midi manager,\n"
+ "hit return");
+ getchar();
+
+ TimeStamp t = port.time();
+ timeStampInc(t,TimeStamp(0,100000));
+ for(;;)
+ {
+ Synth_PLAY_WAV wav;
+ Synth_AMAN_PLAY sap;
+
+ MidiEvent e;
+
+ e = MidiEvent(t,MidiCommand(mcsNoteOn|0, notes[np], 100));
+
+ port.processEvent(e);
+ port2.processEvent(e);
+
+ if((np & 1) == 0)
+ {
+ /* setup wave player */
+ wav = DynamicCast(soundServer.createObject("Arts::Synth_PLAY_WAV"));
+ if(wav.isNull())
+ arts_fatal("can't create Arts::Synth_PLAY_WAV");
+
+ sap = DynamicCast(soundServer.createObject("Arts::Synth_AMAN_PLAY"));
+ if(sap.isNull())
+ arts_fatal("can't create Arts::Synth_AMAN_PLAY");
+
+ wav.filename("/opt/kde3/share/sounds/pop.wav");
+ sap.title("midisynctest2");
+ sap.autoRestoreID("midisynctest2");
+ connect(wav,sap);
+
+ audioSync.queueStart(wav);
+ audioSync.queueStart(sap);
+ audioSync.executeAt(t);
+ }
+
+ timeStampInc(t,TimeStamp(0,100000));
+
+ e = MidiEvent(t,MidiCommand(mcsNoteOff|0, notes[np], 100));
+
+ port.processEvent(e);
+ port2.processEvent(e);
+
+ if((np & 1) == 0)
+ {
+ audioSync.queueStop(wav);
+ audioSync.queueStop(sap);
+ audioSync.executeAt(t);
+ }
+
+ timeStampInc(t,TimeStamp(0,400000));
+
+ while(port.time().sec < (t.sec - 2))
+ usleep(100000);
+
+ np++;
+ if(notes[np] == 0) np = 0;
+ }
+}
diff --git a/arts/midi/miditest_impl.cc b/arts/midi/miditest_impl.cc
new file mode 100644
index 00000000..fe61e4c7
--- /dev/null
+++ b/arts/midi/miditest_impl.cc
@@ -0,0 +1,57 @@
+ /*
+
+ Copyright (C) 2000 Stefan Westerfeld
+ stefan@space.twc.de
+
+ 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., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "midimanager_impl.h"
+#include <stdio.h>
+
+namespace Arts {
+
+class MidiTest_impl : virtual public MidiTest_skel {
+public:
+ Arts::TimeStamp time()
+ {
+ return TimeStamp(0,0);
+ }
+ Arts::TimeStamp playTime()
+ {
+ return time();
+ }
+ void processCommand(const MidiCommand& command)
+ {
+ if((command.status & mcsCommandMask) == mcsNoteOn)
+ {
+ mcopbyte ch = command.status & mcsChannelMask;
+
+ printf("noteon ch = %d, note = %d, vel = %d\n",
+ ch,command.data1,command.data2);
+ }
+ }
+ void processEvent(const MidiEvent& event)
+ {
+ printf("At %ld.%06ld: ",event.time.sec,event.time.usec);
+ processCommand(event.command);
+ }
+};
+
+REGISTER_IMPLEMENTATION(MidiTest_impl);
+}
+
diff --git a/arts/midi/miditimercommon.cc b/arts/midi/miditimercommon.cc
new file mode 100644
index 00000000..205b72e5
--- /dev/null
+++ b/arts/midi/miditimercommon.cc
@@ -0,0 +1,72 @@
+ /*
+
+ Copyright (C) 2001 Stefan Westerfeld
+ stefan@space.twc.de
+
+ 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., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "artsmidi.h"
+#include "debug.h"
+#include "miditimercommon.h"
+#include "math.h"
+
+#undef DEBUG_JITTER
+
+using namespace std;
+using namespace Arts;
+
+MidiTimerCommon::MidiTimerCommon() :refCount(0)
+{
+ refCount = 0;
+}
+
+MidiTimerCommon::~MidiTimerCommon()
+{
+ arts_assert(refCount == 0);
+}
+
+void MidiTimerCommon::processQueue()
+{
+ TimeStamp now = time();
+
+ list<TSNote>::iterator n = noteQueue.begin();
+ while(n != noteQueue.end())
+ {
+ TSNote& note = *n;
+ TimeStamp& noteTime = note.event.time;
+
+ if( now.sec > noteTime.sec
+ || ((now.sec == noteTime.sec) && (now.usec > noteTime.usec)))
+ {
+#ifdef DEBUG_JITTER
+ float jitter = (now.sec-noteTime.sec) * 1000.0;
+ jitter += (float)(now.usec-noteTime.usec) / 1000.0;
+ arts_debug("midi jitter: %f",jitter);
+#endif
+
+ note.port.processCommand(note.event.command);
+ n = noteQueue.erase(n);
+ }
+ else n++;
+ }
+}
+
+void MidiTimerCommon::queueEvent(MidiPort port,const MidiEvent& event)
+{
+ noteQueue.push_back(TSNote(port, event));
+}
diff --git a/arts/midi/miditimercommon.h b/arts/midi/miditimercommon.h
new file mode 100644
index 00000000..5f4bb250
--- /dev/null
+++ b/arts/midi/miditimercommon.h
@@ -0,0 +1,59 @@
+ /*
+
+ Copyright (C) 2001 Stefan Westerfeld
+ stefan@space.twc.de
+
+ 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., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef ARTS_MIDI_MIDITIMERCOMMON_H
+#define ARTS_MIDI_MIDITIMERCOMMON_H
+
+#include "artsmidi.h"
+
+namespace Arts {
+
+class MidiTimerCommon {
+protected:
+ struct TSNote {
+ MidiPort port;
+ MidiEvent event;
+
+ TSNote() { /* for some stl impls */ }
+
+ TSNote(MidiPort port, const MidiEvent& event) :
+ port(port), event(event)
+ {
+ }
+ };
+ std::list<TSNote> noteQueue;
+
+ int refCount;
+ void processQueue();
+
+ MidiTimerCommon();
+ virtual ~MidiTimerCommon();
+
+public:
+ void unsubscribe() { if(--refCount == 0) delete this; }
+
+ void queueEvent(MidiPort port, const MidiEvent& event);
+ virtual TimeStamp time() = 0;
+};
+
+}
+#endif /* ARTS_MIDI_MIDITIMERCOMMON_H */
diff --git a/arts/midi/rawmidiport_impl.cc b/arts/midi/rawmidiport_impl.cc
new file mode 100644
index 00000000..0c9fd340
--- /dev/null
+++ b/arts/midi/rawmidiport_impl.cc
@@ -0,0 +1,306 @@
+ /*
+
+ Copyright (C) 2000 Stefan Westerfeld
+ stefan@space.twc.de
+
+ 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., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "artsmidi.h"
+#include <iomanager.h>
+#include <debug.h>
+#include <fcntl.h>
+
+using namespace std;
+
+namespace Arts {
+
+class RawMidiPort_impl : virtual public RawMidiPort_skel,
+ virtual public IONotify
+{
+protected:
+ int fd;
+
+ string _device;
+ bool _input, _output;
+ bool _running;
+ mcopbyte laststatus;
+ queue<mcopbyte> inq;
+ MidiClient clientRecord, clientPlay;
+ MidiPort outputPort;
+ MidiManager manager;
+
+ RawMidiPort self() {
+ return RawMidiPort::_from_base(_copy());
+ }
+ SystemMidiTimer timer;
+
+public:
+ RawMidiPort_impl()
+ :_device("/dev/midi"), _input(true), _output(true), _running(false),
+ clientRecord(MidiClient::null()), clientPlay(MidiClient::null()),
+ outputPort(MidiPort::null()),
+ manager(Reference("global:Arts_MidiManager"))
+ {
+ }
+ Arts::TimeStamp time()
+ {
+ return timer.time();
+ }
+ Arts::TimeStamp playTime()
+ {
+ return timer.time();
+ }
+
+ // attribute string running;
+ bool running() { return _running; }
+ void running(bool newrunning) {
+ if(_running == newrunning) return;
+
+ if(newrunning)
+ open();
+ else
+ close();
+
+ running_changed(_running);
+ }
+
+ // attribute string device;
+ void device(const string& newdevice)
+ {
+ if(newdevice == _device) return;
+
+ if(_running)
+ {
+ close();
+ _device = newdevice;
+ open();
+ }
+ else _device = newdevice;
+
+ device_changed(newdevice);
+ }
+ string device() { return _device; }
+
+ // attribute boolean input;
+ void input(bool newinput)
+ {
+ if(newinput == _input) return;
+
+ if(_running)
+ {
+ close();
+ _input = newinput;
+ open();
+ }
+ else _input = newinput;
+
+ input_changed(newinput);
+ }
+ bool input() { return _input; }
+
+ // attribute boolean output;
+ void output(bool newoutput)
+ {
+ if(newoutput == _output) return;
+
+ if(_running)
+ {
+ close();
+ _output = newoutput;
+ open();
+ }
+ else _output = newoutput;
+
+ output_changed(newoutput);
+ }
+ bool output() { return _output; }
+
+ bool open() {
+ arts_return_val_if_fail(_running == false, true);
+ arts_return_val_if_fail(_output || _input, false);
+ arts_return_val_if_fail(manager.isNull() == false, false);
+ laststatus = 0;
+
+ int mode = O_NDELAY;
+ if(_input)
+ {
+ if(_output)
+ mode |= O_RDWR;
+ else
+ mode |= O_RDONLY;
+ }
+ else mode |= O_WRONLY;
+
+ fd = ::open(_device.c_str(),mode);
+ if(fd != -1)
+ {
+ IOManager *iom = Dispatcher::the()->ioManager();
+ if(_output)
+ iom->watchFD(fd,IOType::read,this);
+
+ string name = "OSS Midi Port ("+_device+")";
+ if(_input)
+ {
+ clientRecord =
+ manager.addClient(mcdRecord,mctDestination,name,name);
+ clientRecord.addInputPort(self());
+ }
+ if(_output)
+ {
+ clientPlay =
+ manager.addClient(mcdPlay,mctDestination,name,name);
+ outputPort = clientPlay.addOutputPort();
+ }
+
+ _running = true;
+ running_changed(true);
+ }
+ return _running;
+ }
+
+ void close()
+ {
+ arts_return_if_fail(_running == true);
+
+ if(_input)
+ {
+ clientRecord.removePort(self());
+ clientRecord = MidiClient::null();
+ }
+ if(_output)
+ {
+ clientPlay.removePort(outputPort);
+ clientPlay = MidiClient::null();
+ }
+
+ Dispatcher::the()->ioManager()->remove(this,IOType::all);
+ ::close(fd);
+ }
+
+ int midiMsgLen(mcopbyte status)
+ {
+ switch(status & mcsCommandMask)
+ {
+ case mcsNoteOn:
+ case mcsNoteOff:
+ case mcsKeyPressure:
+ case mcsParameter:
+ case mcsPitchWheel:
+ return 3;
+ break;
+ case mcsProgram:
+ case mcsChannelPressure:
+ return 2;
+ break;
+ }
+ return 0;
+ }
+ void notifyIO(int fd, int type)
+ {
+ arts_return_if_fail(_running);
+ assert(fd == this->fd);
+
+ // convert iomanager notification types to audiosubsys notification
+ if(type & IOType::read)
+ {
+ mcopbyte buffer[1024];
+ int count = read(fd,buffer,1024);
+ for(int i=0; i<count; i++)
+ {
+ /*
+ * for now ignore all realtime-messages (0xF8 .. 0xFF),
+ * which may get * embedded *inside* every other message
+ */
+ if(buffer[i] < 0xf8)
+ inq.push(buffer[i]);
+ }
+ }
+ processMidi();
+ }
+
+ void processMidi()
+ {
+ for(;;)
+ {
+ // if we get a status byte, this is our new status
+ if(!inq.empty())
+ {
+ if(inq.front() & 0x80)
+ {
+ laststatus = inq.front();
+ inq.pop();
+ }
+ }
+
+ // try to read a midi message with our current status
+ // (this supports running status as well as normal messages)
+ int len = midiMsgLen(laststatus);
+ if(len)
+ {
+ if(len == 2)
+ {
+ if(inq.empty()) return; // need more input
+
+ MidiCommand command;
+ command.status = laststatus;
+ command.data1 = inq.front(); inq.pop();
+ command.data2 = 0;
+ outputPort.processCommand(command);
+ }
+ else if(len == 3)
+ {
+ if(inq.size() < 2) return; // need more input
+
+ MidiCommand command;
+ command.status = laststatus;
+ command.data1 = inq.front(); inq.pop();
+ command.data2 = inq.front(); inq.pop();
+ outputPort.processCommand(command);
+ }
+ else
+ {
+ arts_assert(false);
+ }
+ }
+ else
+ {
+ if(inq.empty()) return; // need more input
+
+ /* we are somewhat out of sync it seems -> read something
+ * away and hope we'll find a status byte */
+ inq.pop();
+ }
+ }
+ }
+ void processCommand(const MidiCommand& command)
+ {
+ char message[3] = { command.status, command.data1, command.data2 };
+
+ int len = midiMsgLen(command.status);
+ if(midiMsgLen(command.status))
+ write(fd, message, len);
+ }
+ void processEvent(const MidiEvent& event)
+ {
+ timer.queueEvent(self(), event);
+ }
+};
+
+REGISTER_IMPLEMENTATION(RawMidiPort_impl);
+}
+
diff --git a/arts/midi/systemmiditimer_impl.cc b/arts/midi/systemmiditimer_impl.cc
new file mode 100644
index 00000000..bda27a8c
--- /dev/null
+++ b/arts/midi/systemmiditimer_impl.cc
@@ -0,0 +1,105 @@
+ /*
+
+ Copyright (C) 2001 Stefan Westerfeld
+ stefan@space.twc.de
+
+ 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., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "artsmidi.h"
+#include "debug.h"
+#include "miditimercommon.h"
+
+using namespace std;
+using namespace Arts;
+
+namespace Arts {
+
+class SystemMidiTimerCommon : public MidiTimerCommon,
+ public TimeNotify {
+protected:
+ SystemMidiTimerCommon();
+ virtual ~SystemMidiTimerCommon();
+
+public:
+ // allocation: share one SystemMidiTimerCommon for everbody who needs one
+ static SystemMidiTimerCommon *subscribe();
+
+ void notifyTime();
+ TimeStamp time();
+};
+
+}
+
+static SystemMidiTimerCommon *SystemMidiTimerCommon_the = 0;
+
+SystemMidiTimerCommon::SystemMidiTimerCommon()
+{
+ SystemMidiTimerCommon_the = this;
+ Dispatcher::the()->ioManager()->addTimer(10, this);
+}
+
+SystemMidiTimerCommon::~SystemMidiTimerCommon()
+{
+ Dispatcher::the()->ioManager()->removeTimer(this);
+ SystemMidiTimerCommon_the = 0;
+}
+
+TimeStamp SystemMidiTimerCommon::time()
+{
+ timeval tv;
+ gettimeofday(&tv,0);
+ return TimeStamp(tv.tv_sec, tv.tv_usec);
+}
+
+void SystemMidiTimerCommon::notifyTime()
+{
+ processQueue();
+}
+
+SystemMidiTimerCommon *SystemMidiTimerCommon::subscribe()
+{
+ if(!SystemMidiTimerCommon_the) new SystemMidiTimerCommon();
+ SystemMidiTimerCommon_the->refCount++;
+ return SystemMidiTimerCommon_the;
+}
+
+class SystemMidiTimer_impl : public SystemMidiTimer_skel {
+protected:
+ SystemMidiTimerCommon *timer;
+public:
+ SystemMidiTimer_impl()
+ {
+ timer = SystemMidiTimerCommon::subscribe();
+ }
+ ~SystemMidiTimer_impl()
+ {
+ timer->unsubscribe();
+ }
+ TimeStamp time()
+ {
+ return timer->time();
+ }
+ void queueEvent(MidiPort port, const MidiEvent& event)
+ {
+ timer->queueEvent(port, event);
+ }
+};
+
+namespace Arts {
+ REGISTER_IMPLEMENTATION(SystemMidiTimer_impl);
+}
diff --git a/arts/midi/timestampmath.cc b/arts/midi/timestampmath.cc
new file mode 100644
index 00000000..ff33c280
--- /dev/null
+++ b/arts/midi/timestampmath.cc
@@ -0,0 +1,112 @@
+ /*
+
+ Copyright (C) 2002 Stefan Westerfeld
+ stefan@space.twc.de
+
+ 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., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "timestampmath.h"
+#include <arts/debug.h>
+#include <stdio.h>
+
+using namespace std;
+
+namespace Arts {
+void timeStampInc(TimeStamp& t, const TimeStamp& delta)
+{
+ /* expect a normalized t, delta */
+ arts_return_if_fail(t.usec >= 0 && t.usec < 1000000);
+ arts_return_if_fail(delta.usec >= 0 && delta.usec < 1000000);
+
+ t.sec += delta.sec;
+ t.usec += delta.usec;
+
+ if (t.usec >= 1000000)
+ {
+ t.usec -= 1000000;
+ t.sec += 1;
+ }
+
+ arts_assert (t.usec >= 0 && t.usec < 1000000);
+}
+
+void timeStampDec(TimeStamp& t, const TimeStamp& delta)
+{
+ /* expect a normalized t, delta */
+ arts_return_if_fail(t.usec >= 0 && t.usec < 1000000);
+ arts_return_if_fail(delta.usec >= 0 && delta.usec < 1000000);
+
+ t.sec -= delta.sec;
+ t.usec -= delta.usec;
+
+ if(t.usec < 0)
+ {
+ t.usec += 1000000;
+ t.sec -= 1;
+ }
+
+ arts_assert(t.usec >= 0 && t.usec < 1000000);
+}
+
+string timeStampToString(const TimeStamp& t)
+{
+ arts_return_val_if_fail(t.usec >= 0 && t.usec < 1000000, "");
+
+ char buffer[1024];
+ if(t.sec < 0 && t.usec != 0)
+ {
+ sprintf(buffer, "-%d.%06d", -t.sec-1, 1000000-t.usec);
+ }
+ else
+ {
+ sprintf(buffer, "%d.%06d", t.sec, t.usec);
+ }
+ return buffer;
+}
+
+double timeStampToDouble(const TimeStamp& t)
+{
+ arts_return_val_if_fail(t.usec >= 0 && t.usec < 1000000, 0.0);
+
+ return double(t.sec) + double(t.usec)/1000000.0;
+}
+
+TimeStamp timeStampFromDouble(double d)
+{
+ TimeStamp t;
+
+ arts_return_val_if_fail(d >= 0, t);
+
+ t.sec = int(d);
+ d -= t.sec;
+ t.usec = int(d * 1000000.0);
+
+ return t;
+}
+
+TimeStamp timeStampMax(const TimeStamp& t1, const TimeStamp& t2)
+{
+ if(t1.sec > t2.sec)
+ return t1;
+ else if((t1.sec == t2.sec) && (t1.usec > t2.usec))
+ return t1;
+ else
+ return t2;
+}
+
+}
diff --git a/arts/midi/timestampmath.h b/arts/midi/timestampmath.h
new file mode 100644
index 00000000..934df716
--- /dev/null
+++ b/arts/midi/timestampmath.h
@@ -0,0 +1,62 @@
+ /*
+
+ Copyright (C) 2002 Stefan Westerfeld
+ stefan@space.twc.de
+
+ 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., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef ARTS_TIMESTAMPMATH_H
+#define ARTS_TIMESTAMPMATH_H
+
+#include "artsmidi.h"
+#include <kdelibs_export.h>
+namespace Arts {
+
+/**
+ * increments the timestamp by delta
+ */
+KDE_EXPORT void timeStampInc(TimeStamp& t, const TimeStamp& delta);
+
+/**
+ * decrements the timestamp by delta
+ */
+void timeStampDec(TimeStamp& t, const TimeStamp& delta);
+
+/**
+ * stringifies a timestamp
+ */
+std::string timeStampToString(const TimeStamp& t);
+
+/**
+ * converts a timestamp to a double of seconds
+ */
+double timeStampToDouble(const TimeStamp& t);
+
+/**
+ * converts a double of seconds to a timestamp
+ */
+TimeStamp timeStampFromDouble(double d);
+
+/**
+ * returns the maximum of two timestamps
+ */
+TimeStamp timeStampMax(const TimeStamp& t1, const TimeStamp& t2);
+
+}
+
+#endif /* ARTS_TIMESTAMPMATH_H */