diff options
Diffstat (limited to 'attic/muse2-oom/muse2/muse/driver/alsamidi.cpp')
-rw-r--r-- | attic/muse2-oom/muse2/muse/driver/alsamidi.cpp | 917 |
1 files changed, 917 insertions, 0 deletions
diff --git a/attic/muse2-oom/muse2/muse/driver/alsamidi.cpp b/attic/muse2-oom/muse2/muse/driver/alsamidi.cpp new file mode 100644 index 00000000..c7ae07b5 --- /dev/null +++ b/attic/muse2-oom/muse2/muse/driver/alsamidi.cpp @@ -0,0 +1,917 @@ +//========================================================= +// MusE +// Linux Music Editor +// $Id: alsamidi.cpp,v 1.8.2.7 2009/11/19 04:20:33 terminator356 Exp $ +// (C) Copyright 2000-2001 Werner Schweer (ws@seh.de) +//========================================================= + +#include <stdio.h> + +#include "alsamidi.h" +#include "globals.h" +#include "midi.h" +#include "mididev.h" +#include "../midiport.h" +#include "../midiseq.h" +#include "../midictrl.h" +#include "../audio.h" +#include "mpevent.h" +//#include "sync.h" +#include "utils.h" +#include "audiodev.h" +#include "xml.h" + +static int alsaSeqFdi = -1; +static int alsaSeqFdo = -1; + +snd_seq_t* alsaSeq; +static snd_seq_addr_t musePort; + +//--------------------------------------------------------- +// MidiAlsaDevice +//--------------------------------------------------------- + +MidiAlsaDevice::MidiAlsaDevice(const snd_seq_addr_t& a, const QString& n) + : MidiDevice(n) + { + adr = a; + init(); + } + +//--------------------------------------------------------- +// selectWfd +//--------------------------------------------------------- + +int MidiAlsaDevice::selectWfd() + { + return alsaSeqFdo; + } + +//--------------------------------------------------------- +// open +//--------------------------------------------------------- + +QString MidiAlsaDevice::open() +{ + _openFlags &= _rwFlags; // restrict to available bits + snd_seq_port_subscribe_t* subs; + // Allocated on stack, no need to call snd_seq_port_subscribe_free() later. + snd_seq_port_subscribe_alloca(&subs); + + QString estr; + int wer = 0; + int rer = 0; + + // subscribe for writing + if (_openFlags & 1) + { + snd_seq_port_subscribe_set_sender(subs, &musePort); + snd_seq_port_subscribe_set_dest(subs, &adr); + // Not already subscribed (or error)? Then try subscribing. + if(snd_seq_get_port_subscription(alsaSeq, subs) < 0) + { + //int error = snd_seq_subscribe_port(alsaSeq, subs); + wer = snd_seq_subscribe_port(alsaSeq, subs); + //if (error < 0) + if(wer < 0) + //return QString("Play: ")+QString(snd_strerror(error)); + estr += (QString("Play: ") + QString(snd_strerror(wer)) + QString(" ")); + } + if(!wer) + _writeEnable = true; + } + + // subscribe for reading + if (_openFlags & 2) + { + snd_seq_port_subscribe_set_dest(subs, &musePort); + snd_seq_port_subscribe_set_sender(subs, &adr); + // Not already subscribed (or error)? Then try subscribing. + if(snd_seq_get_port_subscription(alsaSeq, subs) < 0) + { + //int error = snd_seq_subscribe_port(alsaSeq, subs); + rer = snd_seq_subscribe_port(alsaSeq, subs); + //if (error < 0) + if(rer < 0) + //return QString("Rec: ") + QString(snd_strerror(error)); + estr += (QString("Rec: ") + QString(snd_strerror(rer))); + } + if(!rer) + _readEnable = true; + } + + + if(wer < 0 || rer < 0) + return estr; + + return QString("OK"); +} + +//--------------------------------------------------------- +// close +//--------------------------------------------------------- + +void MidiAlsaDevice::close() +{ + snd_seq_port_subscribe_t* subs; + // Allocated on stack, no need to call snd_seq_port_subscribe_free() later. + snd_seq_port_subscribe_alloca(&subs); + + // Changed by T356. This function appears to be called only by MidiPort::setMidiDevice(), + // which closes then opens the device. + // Because the open flags are set BEFORE setMidiDevice() is called, we must ignore the flags. + // + // NOTE: Tested: The read unsubscribe works ok but not the write. + // As viewed in say, qjackctl, the connection is clearly lost, + // but strangely the events are still accepted, ie, playback notes + // are still heard etc. Tried an alsa midi device AND external fluidsynth inst. + // + // Also, jack running and with jack midi disabled, we get messages like + // MidiAlsaDevice::0x84512c0 putEvent(): midi write error: No such device + // dst 16:0 + // only sometimes (not when playing notes), but with jack midi turned on, + // we don't get the messages. With jack stopped we get the messages + // no matter if jack midi is turned on or not. + + //if (_openFlags & 1) { + //if (!(_openFlags & 1)) + { + snd_seq_port_subscribe_set_sender(subs, &musePort); + snd_seq_port_subscribe_set_dest(subs, &adr); + + // Already subscribed? Then unsubscribe. + if(!snd_seq_get_port_subscription(alsaSeq, subs)) + { + if(!snd_seq_unsubscribe_port(alsaSeq, subs)) + _writeEnable = false; + else + printf("MidiAlsaDevice::close Error unsubscribing alsa midi port for writing\n"); + } + else + _writeEnable = false; + } + + //if (_openFlags & 2) { + //if (!(_openFlags & 2)) + { + snd_seq_port_subscribe_set_dest(subs, &musePort); + snd_seq_port_subscribe_set_sender(subs, &adr); + + // Already subscribed? Then unsubscribe. + if(!snd_seq_get_port_subscription(alsaSeq, subs)) + { + if(!snd_seq_unsubscribe_port(alsaSeq, subs)) + _readEnable = false; + else + printf("MidiAlsaDevice::close Error unsubscribing alsa midi port for reading\n"); + } + else + _readEnable = false; + } +} + +//--------------------------------------------------------- +// writeRouting +//--------------------------------------------------------- + +void MidiAlsaDevice::writeRouting(int level, Xml& xml) const +{ + // p3.3.45 + // If this device is not actually in use by the song, do not write any routes. + // This prevents bogus routes from being saved and propagated in the med file. + if(midiPort() == -1) + return; + + QString s; + /* + //if(rwFlags() & 2) // Readable + { + //RouteList* rl = _inRoutes; + //for (ciRoute r = rl->begin(); r != rl->end(); ++r) + for (ciRoute r = _inRoutes.begin(); r != _inRoutes.end(); ++r) + { + // Since an ALSA midi device supports read + write, this is the only way we can tell if this route is using the device as input. + if(r->type == Route::TRACK_ROUTE) + continue; + + if(!r->name().isEmpty()) + { + xml.tag(level++, "Route"); + + //xml.strTag(level, "srcNode", r->name()); + xml.tag(level, "source type=\"%d\" name=\"%s\"/", r->type, r->name().toLatin1().constData()); + + //xml.strTag(level, "dstNode", name()); + xml.tag(level, "dest type=\"%d\" name=\"%s\"/", Route::ALSA_MIDI_ROUTE, name().toLatin1().constData()); + + xml.etag(level--, "Route"); + } + } + } + */ + + for (ciRoute r = _outRoutes.begin(); r != _outRoutes.end(); ++r) + { + //if(r->type != Route::TRACK_ROUTE) + //{ + // printf("MidiAlsaDevice::writeRouting Warning out route is not TRACK_ROUTE type\n"); + // continue; + //} + + if(!r->name().isEmpty()) + { + //xml.tag(level++, "Route"); + + s = QT_TRANSLATE_NOOP("@default", "Route"); + if(r->channel != -1) + s += QString(QT_TRANSLATE_NOOP("@default", " channel=\"%1\"")).arg(r->channel); + xml.tag(level++, s.toLatin1().constData()); + + /* + //xml.strTag(level, "srcNode", name()); + if(r->channel != -1) + //xml.tag(level, "source type=\"%d\" channel=\"%d\" name=\"%s\"/", Route::ALSA_MIDI_ROUTE, r->channel, name().toLatin1().constData()); + //xml.tag(level, "source type=\"%d\" channel=\"%d\" name=\"%s\"/", Route::MIDI_DEVICE_ROUTE, r->channel, name().toLatin1().constData()); + xml.tag(level, "source devtype=\"%d\" channel=\"%d\" name=\"%s\"/", MidiDevice::ALSA_MIDI, r->channel, name().toLatin1().constData()); + else + //xml.tag(level, "source type=\"%d\" name=\"%s\"/", Route::ALSA_MIDI_ROUTE, name().toLatin1().constData()); + //xml.tag(level, "source type=\"%d\" name=\"%s\"/", Route::MIDI_DEVICE_ROUTE, name().toLatin1().constData()); + */ + //xml.tag(level, "source devtype=\"%d\" name=\"%s\"/", MidiDevice::ALSA_MIDI, name().toLatin1().constData()); + xml.tag(level, "source devtype=\"%d\" name=\"%s\"/", MidiDevice::ALSA_MIDI, Xml::xmlString(name()).toLatin1().constData()); + + /* + //xml.strTag(level, "dstNode", r->name()); + if(r->channel != -1) + { + if(r->type == Route::MIDI_DEVICE_ROUTE) + xml.tag(level, "dest devtype=\"%d\" channel=\"%d\" name=\"%s\"/", r->device->deviceType(), r->channel, r->name().toLatin1().constData()); + else + xml.tag(level, "dest type=\"%d\" channel=\"%d\" name=\"%s\"/", r->type, r->channel, r->name().toLatin1().constData()); + } + else + { + if(r->type == Route::MIDI_DEVICE_ROUTE) + xml.tag(level, "dest devtype=\"%d\" name=\"%s\"/", r->device->deviceType(), r->name().toLatin1().constData()); + else + xml.tag(level, "dest type=\"%d\" name=\"%s\"/", r->type, r->name().toLatin1().constData()); + } + */ + + s = QT_TRANSLATE_NOOP("@default", "dest"); + if(r->type == Route::MIDI_DEVICE_ROUTE) + s += QString(QT_TRANSLATE_NOOP("@default", " devtype=\"%1\"")).arg(r->device->deviceType()); + else + if(r->type != Route::TRACK_ROUTE) + s += QString(QT_TRANSLATE_NOOP("@default", " type=\"%1\"")).arg(r->type); + //s += QString(QT_TRANSLATE_NOOP("@default", " name=\"%1\"/")).arg(r->name()); + s += QString(QT_TRANSLATE_NOOP("@default", " name=\"%1\"/")).arg(Xml::xmlString(r->name())); + xml.tag(level, s.toLatin1().constData()); + + xml.etag(level--, "Route"); + } + } +} + +//--------------------------------------------------------- +// putEvent +//--------------------------------------------------------- + +bool MidiAlsaDevice::putMidiEvent(const MidiPlayEvent& e) + { + if (midiOutputTrace) { + printf("MidiOut: midiAlsa: "); + e.dump(); + } + int chn = e.channel(); + int a = e.dataA(); + int b = e.dataB(); + + snd_seq_event_t event; + memset(&event, 0, sizeof(event)); + event.queue = SND_SEQ_QUEUE_DIRECT; + event.source = musePort; + event.dest = adr; + + switch(e.type()) { + case ME_NOTEON: + snd_seq_ev_set_noteon(&event, chn, a, b); + break; + case ME_NOTEOFF: + snd_seq_ev_set_noteoff(&event, chn, a, 0); + break; + case ME_PROGRAM: + snd_seq_ev_set_pgmchange(&event, chn, a); + break; + case ME_CONTROLLER: +#if 1 + snd_seq_ev_set_controller(&event, chn, a, b); +#else + { + int a = e.dataA(); + int b = e.dataB(); + int chn = e.channel(); + // p3.3.37 + //if (a < 0x1000) { // 7 Bit Controller + if (a < CTRL_14_OFFSET) { // 7 Bit Controller + snd_seq_ev_set_controller(&event, chn, a, b); + } + //else if (a < 0x20000) { // 14 bit high resolution controller + else if (a < CTRL_RPN_OFFSET) { // 14 bit high resolution controller + int ctrlH = (a >> 8) & 0x7f; + int ctrlL = a & 0x7f; + a = (ctrlH << 7) + ctrlL; + snd_seq_ev_set_controller(&event, chn, a, b); + event.type = SND_SEQ_EVENT_CONTROL14; + } + //else if (a < 0x30000) { // RPN 7-Bit Controller + else if (a < CTRL_NRPN_OFFSET) { // RPN 7-Bit Controller + int ctrlH = (a >> 8) & 0x7f; + int ctrlL = a & 0x7f; + a = (ctrlH << 7) + ctrlL; + b <<= 7; + snd_seq_ev_set_controller(&event, chn, a, b); + event.type = SND_SEQ_EVENT_REGPARAM; + } + //else if (a < 0x40000) { // NRPN 7-Bit Controller + else if (a < CTRL_INTERNAL_OFFSET) { // NRPN 7-Bit Controller + int ctrlH = (a >> 8) & 0x7f; + int ctrlL = a & 0x7f; + a = (ctrlH << 7) + ctrlL; + b <<= 7; + snd_seq_ev_set_controller(&event, chn, a, b); + event.type = SND_SEQ_EVENT_NONREGPARAM; + } + //else if (a < 0x60000) { // RPN14 Controller + else if (a < CTRL_NRPN14_OFFSET) { // RPN14 Controller + int ctrlH = (a >> 8) & 0x7f; + int ctrlL = a & 0x7f; + a = (ctrlH << 7) + ctrlL; + snd_seq_ev_set_controller(&event, chn, a, b); + event.type = SND_SEQ_EVENT_REGPARAM; + } + //else if (a < 0x70000) { // NRPN14 Controller + else if (a < CTRL_NONE_OFFSET) { // NRPN14 Controller + int ctrlH = (a >> 8) & 0x7f; + int ctrlL = a & 0x7f; + a = (ctrlH << 7) + ctrlL; + snd_seq_ev_set_controller(&event, chn, a, b); + event.type = SND_SEQ_EVENT_NONREGPARAM; + } + else { + printf("putEvent: unknown controller type 0x%x\n", a); + } + } +#endif + break; + case ME_PITCHBEND: + snd_seq_ev_set_pitchbend(&event, chn, a); + break; + case ME_POLYAFTER: + // chnEvent2(chn, 0xa0, a, b); + break; + case ME_AFTERTOUCH: + snd_seq_ev_set_chanpress(&event, chn, a); + break; + case ME_SYSEX: + { + const unsigned char* p = e.data(); + int n = e.len(); + int len = n + sizeof(event) + 2; + char buf[len]; + event.type = SND_SEQ_EVENT_SYSEX; + event.flags = SND_SEQ_EVENT_LENGTH_VARIABLE; + event.data.ext.len = n + 2; + event.data.ext.ptr = (void*)(buf + sizeof(event)); + memcpy(buf, &event, sizeof(event)); + char* pp = buf + sizeof(event); + *pp++ = 0xf0; + memcpy(pp, p, n); + pp += n; + *pp = 0xf7; + return putEvent(&event); + } + case ME_SONGPOS: + event.data.control.value = a; + event.type = SND_SEQ_EVENT_SONGPOS; + break; + case ME_CLOCK: + event.type = SND_SEQ_EVENT_CLOCK; + break; + case ME_START: + event.type = SND_SEQ_EVENT_START; + break; + case ME_CONTINUE: + event.type = SND_SEQ_EVENT_CONTINUE; + break; + case ME_STOP: + event.type = SND_SEQ_EVENT_STOP; + break; + default: + printf("MidiAlsaDevice::putEvent(): event type %d not implemented\n", + e.type()); + return true; + } + return putEvent(&event); + } + +//--------------------------------------------------------- +// putEvent +// return false if event is delivered +//--------------------------------------------------------- + +bool MidiAlsaDevice::putEvent(snd_seq_event_t* event) + { + int error; + + do { + error = snd_seq_event_output_direct(alsaSeq, event); + int len = snd_seq_event_length(event); + if (error == len) { +// printf(".");fflush(stdout); + return false; + } + if (error < 0) { + if (error == -12) { +// printf("?");fflush(stdout); + return true; + } + else { + fprintf(stderr, "MidiAlsaDevice::%p putEvent(): midi write error: %s\n", + this, snd_strerror(error)); + fprintf(stderr, " dst %d:%d\n", adr.client, adr.port); + //exit(-1); + } + } + else + fprintf(stderr, "MidiAlsaDevice::putEvent(): midi write returns %d, expected %d: %s\n", + error, len, snd_strerror(error)); + } while (error == -12); + return true; + } + +//--------------------------------------------------------- +// initMidiAlsa +// return true on error +//--------------------------------------------------------- + +bool initMidiAlsa() + { + if (debugMsg) + printf("initMidiAlsa\n"); + int error = snd_seq_open(&alsaSeq, "hw", SND_SEQ_OPEN_DUPLEX, SND_SEQ_NONBLOCK); + if (error < 0) { + fprintf(stderr, "Could not open ALSA sequencer: %s\n", + snd_strerror(error)); + return true; + } + const int inCap = SND_SEQ_PORT_CAP_SUBS_READ; + const int outCap = SND_SEQ_PORT_CAP_SUBS_WRITE; + + snd_seq_client_info_t *cinfo; + snd_seq_client_info_alloca(&cinfo); + snd_seq_client_info_set_client(cinfo, -1); + + while (snd_seq_query_next_client(alsaSeq, cinfo) >= 0) { + snd_seq_port_info_t *pinfo; + snd_seq_port_info_alloca(&pinfo); + snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo)); + snd_seq_port_info_set_port(pinfo, -1); + + while (snd_seq_query_next_port(alsaSeq, pinfo) >= 0) { + unsigned int capability = snd_seq_port_info_get_capability(pinfo); + if ((capability & outCap) == 0) { + const char *name = snd_seq_port_info_get_name(pinfo); + if (strcmp("Timer", name) == 0 || + strcmp("Announce", name) == 0 || + strcmp("Receiver", name) == 0) + continue; + } + snd_seq_addr_t adr = *snd_seq_port_info_get_addr(pinfo); + MidiAlsaDevice* dev = new MidiAlsaDevice(adr, QString(snd_seq_port_info_get_name(pinfo))); + int flags = 0; + if (capability & outCap) + flags |= 1; + if (capability & inCap) + flags |= 2; + dev->setrwFlags(flags); + if (debugMsg) + printf("ALSA port add: <%s>, %d:%d flags %d 0x%0x\n", + snd_seq_port_info_get_name(pinfo), + adr.client, adr.port, + flags, capability); + midiDevices.add(dev); + + /* + // Experimental... Need to list 'sensible' devices first and ignore unwanted ones... + // Add instance last in midi device list. + for(int i = 0; i < MIDI_PORTS; ++i) + { + MidiPort* mp = &midiPorts[i]; + if(mp->device() == 0) + { + // midiSeq might not be initialzed yet! + //midiSeq->msgSetMidiDevice(mp, dev); + mp->setMidiDevice(dev); + + //muse->changeConfig(true); // save configuration file + //update(); + break; + } + } + */ + + } + } + + // p3.3.38 + //snd_seq_set_client_name(alsaSeq, "MusE Sequencer"); + snd_seq_set_client_name(alsaSeq, audioDevice->clientName()); + + int ci = snd_seq_poll_descriptors_count(alsaSeq, POLLIN); + int co = snd_seq_poll_descriptors_count(alsaSeq, POLLOUT); + + if (ci > 1 || co > 1) { + printf("ALSA midi: cannot handle more than one poll fd\n"); + abort(); + } + + struct pollfd pfdi[ci]; + struct pollfd pfdo[co]; + snd_seq_poll_descriptors(alsaSeq, pfdi, ci, POLLIN); + snd_seq_poll_descriptors(alsaSeq, pfdo, co, POLLOUT); + alsaSeqFdo = pfdo[0].fd; + alsaSeqFdi = pfdi[0].fd; + + int port = snd_seq_create_simple_port(alsaSeq, "MusE Port 0", + inCap | outCap | SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_WRITE, + SND_SEQ_PORT_TYPE_APPLICATION); + if (port < 0) { + perror("create port"); + exit(1); + } + musePort.port = port; + musePort.client = snd_seq_client_id(alsaSeq); + + //----------------------------------------- + // subscribe to "Announce" + // this enables callbacks for any + // alsa port changes + //----------------------------------------- + + snd_seq_addr_t aadr; + aadr.client = SND_SEQ_CLIENT_SYSTEM; + aadr.port = SND_SEQ_PORT_SYSTEM_ANNOUNCE; + + snd_seq_port_subscribe_t* subs; + snd_seq_port_subscribe_alloca(&subs); + snd_seq_port_subscribe_set_dest(subs, &musePort); + snd_seq_port_subscribe_set_sender(subs, &aadr); + error = snd_seq_subscribe_port(alsaSeq, subs); + if (error < 0) { + printf("Alsa: Subscribe System failed: %s", snd_strerror(error)); + return true; + } + return false; + } + +struct AlsaPort { + snd_seq_addr_t adr; + char* name; + int flags; + AlsaPort(snd_seq_addr_t a, const char* s, int f) { + adr = a; + name = strdup(s); + flags = f; + } + }; + +static std::list<AlsaPort> portList; + +//--------------------------------------------------------- +// alsaScanMidiPorts +//--------------------------------------------------------- + +void alsaScanMidiPorts() + { +// printf("alsa scan midi ports\n"); + const int inCap = SND_SEQ_PORT_CAP_SUBS_READ; + const int outCap = SND_SEQ_PORT_CAP_SUBS_WRITE; + + portList.clear(); + + snd_seq_client_info_t* cinfo; + snd_seq_client_info_alloca(&cinfo); + snd_seq_client_info_set_client(cinfo, 0); + + while (snd_seq_query_next_client(alsaSeq, cinfo) >= 0) { + snd_seq_port_info_t *pinfo; + snd_seq_port_info_alloca(&pinfo); + snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo)); + snd_seq_port_info_set_port(pinfo, -1); + while (snd_seq_query_next_port(alsaSeq, pinfo) >= 0) { + unsigned int capability = snd_seq_port_info_get_capability(pinfo); + if (((capability & outCap) == 0) + && ((capability & inCap) == 0)) + continue; + snd_seq_addr_t adr; + const char* name; + adr = *snd_seq_port_info_get_addr(pinfo); + name = snd_seq_port_info_get_name(pinfo); + if (adr.client == musePort.client && adr.port == musePort.port) + continue; + int flags = 0; + if (capability & outCap) + flags |= 1; + if (capability & inCap) + flags |= 2; +// printf("ALSA port add: <%s>, flags %d\n", name, flags); + portList.push_back(AlsaPort(adr, name, flags)); + } + } + // + // check for devices to delete + // + for (iMidiDevice i = midiDevices.begin(); i != midiDevices.end();) { + MidiAlsaDevice* d = dynamic_cast<MidiAlsaDevice*>(*i); + if (d == 0) { + ++i; + continue; + } + std::list<AlsaPort>::iterator k = portList.begin(); + for (; k != portList.end(); ++k) { + if (k->adr.client == d->adr.client + && k->adr.port == d->adr.port) { + break; + } + } + if (k == portList.end()) { + if (d->midiPort() != -1) + midiPorts[d->midiPort()].setMidiDevice(0); + iMidiDevice k = i; +// printf("erase device\n"); + ++i; + midiDevices.erase(k); + } + else { + ++i; + } + } + // + // check for devices to add + // + for (std::list<AlsaPort>::iterator k = portList.begin(); k != portList.end(); ++k) { + iMidiDevice i = midiDevices.begin(); +// printf("ALSA port: <%s>\n", k->name); + for (;i != midiDevices.end(); ++i) { + MidiAlsaDevice* d = dynamic_cast<MidiAlsaDevice*>(*i); + if (d == 0) + continue; + if ((k->adr.client == d->adr.client) && (k->adr.port == d->adr.port)) { + break; + } + } + if (i == midiDevices.end()) { + // add device + MidiAlsaDevice* dev = new MidiAlsaDevice(k->adr, + QString(k->name)); + dev->setrwFlags(k->flags); + midiDevices.add(dev); +// printf("add device\n"); + } + } + } + +//--------------------------------------------------------- +// alsaSelectRfd +//--------------------------------------------------------- + +int alsaSelectRfd() + { + return alsaSeqFdi; + } + +//--------------------------------------------------------- +// alsaSelectWfd +//--------------------------------------------------------- + +int alsaSelectWfd() + { + return alsaSeqFdo; + } + +//--------------------------------------------------------- +// processInput +//--------------------------------------------------------- + +void alsaProcessMidiInput() +{ + MidiRecordEvent event; + snd_seq_event_t* ev; + + for (;;) + { + int rv = snd_seq_event_input(alsaSeq, &ev); +// printf("AlsaInput %d\n", rv); + if (rv < 0) { +// printf("AlsaMidi: read error %s\n", snd_strerror(rv)); + return; + } + switch(ev->type) { + case SND_SEQ_EVENT_PORT_SUBSCRIBED: + case SND_SEQ_EVENT_PORT_UNSUBSCRIBED: + return; + case SND_SEQ_EVENT_CLIENT_START: + case SND_SEQ_EVENT_CLIENT_EXIT: + // return; + // on first start of a software synthesizer we only + // get CLIENT_START event and no PORT_START, why? + + case SND_SEQ_EVENT_PORT_START: + case SND_SEQ_EVENT_PORT_EXIT: + alsaScanMidiPorts(); + audio->midiPortsChanged(); // signal gui + snd_seq_free_event(ev); + return; + } + + int curPort = -1; + MidiAlsaDevice* mdev = 0; + // + // find real source device + // + for (iMidiDevice i = midiDevices.begin(); i != midiDevices.end(); ++i) { + MidiAlsaDevice* d = dynamic_cast<MidiAlsaDevice*>(*i); + if (d && d->adr.client == ev->source.client + && d->adr.port == ev->source.port) { + curPort = d->midiPort(); + mdev = d; + } + } + + if (mdev == 0 || curPort == -1) { + if (debugMsg) { + fprintf(stderr, "no port %d:%d found for received alsa event\n", + ev->source.client, ev->source.port); + } + snd_seq_free_event(ev); + return; + } + + /* + if(curPort == -1) + { + if(mdev == 0) + { + if (debugMsg) + { + fprintf(stderr, "no port %d:%d found for received alsa event\n", + ev->source.client, ev->source.port); + } + } + else + { + // Allow the sync detect mechanisms to work, even if device is not assigned to a port. + if(ev->type == SND_SEQ_EVENT_CLOCK) + mdev->syncInfo().trigMCSyncDetect(); + else + if(ev->type == SND_SEQ_EVENT_TICK) + mdev->syncInfo().trigTickDetect(); + } + snd_seq_free_event(ev); + return; + } + */ + + event.setType(0); // mark as unused + event.setPort(curPort); + event.setB(0); + + switch(ev->type) + { + case SND_SEQ_EVENT_NOTEON: + case SND_SEQ_EVENT_KEYPRESS: + event.setChannel(ev->data.note.channel); + event.setType(ME_NOTEON); + event.setA(ev->data.note.note); + event.setB(ev->data.note.velocity); + break; + + case SND_SEQ_EVENT_NOTEOFF: + event.setChannel(ev->data.note.channel); + event.setType(ME_NOTEOFF); + event.setA(ev->data.note.note); + event.setB(ev->data.note.velocity); + break; + + case SND_SEQ_EVENT_CHANPRESS: + event.setChannel(ev->data.control.channel); + event.setType(ME_AFTERTOUCH); + event.setA(ev->data.control.value); + break; + + case SND_SEQ_EVENT_PGMCHANGE: + event.setChannel(ev->data.control.channel); + event.setType(ME_PROGRAM); + event.setA(ev->data.control.value); + break; + + case SND_SEQ_EVENT_PITCHBEND: + event.setChannel(ev->data.control.channel); + event.setType(ME_PITCHBEND); + event.setA(ev->data.control.value); + break; + + case SND_SEQ_EVENT_CONTROLLER: + event.setChannel(ev->data.control.channel); + event.setType(ME_CONTROLLER); + event.setA(ev->data.control.param); + event.setB(ev->data.control.value); + break; + + case SND_SEQ_EVENT_CLOCK: + midiSeq->realtimeSystemInput(curPort, ME_CLOCK); + //mdev->syncInfo().trigMCSyncDetect(); + break; + + case SND_SEQ_EVENT_START: + midiSeq->realtimeSystemInput(curPort, ME_START); + break; + + case SND_SEQ_EVENT_CONTINUE: + midiSeq->realtimeSystemInput(curPort, ME_CONTINUE); + break; + + case SND_SEQ_EVENT_STOP: + midiSeq->realtimeSystemInput(curPort, ME_STOP); + break; + + case SND_SEQ_EVENT_TICK: + midiSeq->realtimeSystemInput(curPort, ME_TICK); + //mdev->syncInfo().trigTickDetect(); + break; + + case SND_SEQ_EVENT_SYSEX: + + // TODO: Deal with large sysex, which are broken up into chunks! + // For now, do not accept if the first byte is not SYSEX or the last byte is not EOX, + // meaning it's a chunk, possibly with more chunks to follow. + if((*((unsigned char*)ev->data.ext.ptr) != ME_SYSEX) || + (*(((unsigned char*)ev->data.ext.ptr) + ev->data.ext.len - 1) != ME_SYSEX_END)) + { + printf("MusE: alsaProcessMidiInput sysex chunks not supported!\n"); + break; + } + + event.setTime(0); // mark as used + event.setType(ME_SYSEX); + event.setData((unsigned char*)(ev->data.ext.ptr)+1, + ev->data.ext.len-2); + break; + case SND_SEQ_EVENT_PORT_SUBSCRIBED: + case SND_SEQ_EVENT_PORT_UNSUBSCRIBED: // write port is released + break; + case SND_SEQ_EVENT_SONGPOS: + midiSeq->setSongPosition(curPort, ev->data.control.value); + break; + case SND_SEQ_EVENT_SENSING: + break; + case SND_SEQ_EVENT_QFRAME: + midiSeq->mtcInputQuarter(curPort, ev->data.control.value); + break; + // case SND_SEQ_EVENT_CLIENT_START: + // case SND_SEQ_EVENT_CLIENT_EXIT: + // case SND_SEQ_EVENT_CLIENT_CHANGE: + // case SND_SEQ_EVENT_PORT_CHANGE: + // case SND_SEQ_EVENT_SONGSEL: + // case SND_SEQ_EVENT_TIMESIGN: + // case SND_SEQ_EVENT_KEYSIGN: + // case SND_SEQ_EVENT_SETPOS_TICK: + // case SND_SEQ_EVENT_SETPOS_TIME: + // case SND_SEQ_EVENT_TEMPO: + // case SND_SEQ_EVENT_TUNE_REQUEST: + // case SND_SEQ_EVENT_RESET: + + // case SND_SEQ_EVENT_NOTE: + // case SND_SEQ_EVENT_CONTROL14: + // case SND_SEQ_EVENT_NONREGPARAM: + // case SND_SEQ_EVENT_REGPARAM: + default: + printf("ALSA Midi input: type %d not handled\n", ev->type); + break; + } + if(event.type()) + { + mdev->recordEvent(event); + // p3.3.26 1/23/10 Moved to MidiDevice now. Anticipating Jack midi support, so don't make it ALSA specific. Tim. + //if(ev->type != SND_SEQ_EVENT_SYSEX) + // Trigger general activity indicator detector. Sysex has no channel, don't trigger. + // midiPorts[curPort].syncInfo().trigActDetect(event.channel()); + } + + snd_seq_free_event(ev); + if (rv == 0) + break; + } +} + |