You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdemultimedia/arts/midi/alsamidiport_impl.cc

233 lines
5.1 KiB

/*
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., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, 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