summaryrefslogtreecommitdiffstats
path: root/arts/midi/alsamidiport_impl.cc
diff options
context:
space:
mode:
Diffstat (limited to 'arts/midi/alsamidiport_impl.cc')
-rw-r--r--arts/midi/alsamidiport_impl.cc232
1 files changed, 232 insertions, 0 deletions
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