drumstick  1.0.2
alsamidiinput.cpp
00001 /*
00002     Drumstick RT Backend using the ALSA Sequencer
00003     Copyright (C) 2009-2015 Pedro Lopez-Cabanillas <plcl@users.sf.net>
00004 
00005     This program is free software; you can redistribute it and/or modify
00006     it under the terms of the GNU General Public License as published by
00007     the Free Software Foundation; either version 2 of the License, or
00008     (at your option) any later version.
00009 
00010     This program is distributed in the hope that it will be useful,
00011     but WITHOUT ANY WARRANTY; without even the implied warranty of
00012     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013     GNU General Public License for more details.
00014 
00015     You should have received a copy of the GNU General Public License along
00016     with this program; if not, write to the Free Software Foundation, Inc.,
00017     51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00018 */
00019 
00020 #include <QMutexLocker>
00021 #include <cmath>
00022 #include "alsaclient.h"
00023 #include "alsaport.h"
00024 #include "alsaevent.h"
00025 #include "alsamidiinput.h"
00026 #include "rtmidioutput.h"
00027 
00028 namespace drumstick {
00029 namespace rt {
00030 
00031     static QString DEFAULT_PUBLIC_NAME(QLatin1String("MIDI In"));
00032 
00033     class ALSAMIDIInput::ALSAMIDIInputPrivate : public SequencerEventHandler
00034     {
00035     public:
00036         ALSAMIDIInput *m_inp;
00037         MIDIOutput *m_out;
00038         MidiClient *m_client;
00039         MidiPort *m_port;
00040         int m_portId;
00041         int m_clientId;
00042         bool m_thruEnabled;
00043         bool m_clientFilter;
00044         int m_runtimeAlsaNum;
00045         QString m_publicName;
00046         QString m_currentInput;
00047         QStringList m_inputDevices;
00048         QStringList m_excludedNames;
00049         QMutex m_openMutex;
00050 
00051         ALSAMIDIInputPrivate(ALSAMIDIInput *inp) :
00052             m_inp(inp),
00053             m_out(0),
00054             m_client(0),
00055             m_port(0),
00056             m_portId(-1),
00057             m_clientId(-1),
00058             m_thruEnabled(false),
00059             m_publicName(DEFAULT_PUBLIC_NAME)
00060         {
00061             m_runtimeAlsaNum = getRuntimeALSALibraryNumber();
00062             m_client = new MidiClient(m_inp);
00063             m_client->open();
00064             m_client->setClientName(m_publicName);
00065             m_port = m_client->createPort();
00066             m_port->setPortName("in");
00067             m_port->setCapability( SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE );
00068             m_port->setPortType( SND_SEQ_PORT_TYPE_APPLICATION | SND_SEQ_PORT_TYPE_MIDI_GENERIC );
00069             m_clientId = m_client->getClientId();
00070             m_portId = m_port->getPortId();
00071             m_port->setTimestamping(false);
00072             m_port->setTimestampReal(false);
00073             m_client->setHandler(this);
00074         }
00075 
00076         virtual ~ALSAMIDIInputPrivate()
00077         {
00078             if (m_client != NULL) {
00079                 clearSubscription();
00080                 if (m_port != NULL)
00081                     m_port->detach();
00082                 m_client->close();
00083                 delete m_client;
00084             }
00085         }
00086 
00087         bool clientIsAdvanced(int clientId)
00088         {
00089             // asking for runtime version instead of SND_LIB_VERSION
00090             if (m_runtimeAlsaNum < 0x01000B)
00091                 // ALSA <= 1.0.10
00092                 return (clientId < 64);
00093             else
00094                 // ALSA >= 1.0.11
00095                 return (clientId < 16);
00096         }
00097 
00098         void reloadDeviceList(bool advanced)
00099         {
00100             m_clientFilter = !advanced;
00101             m_inputDevices.clear();
00102             QListIterator<PortInfo> it(m_client->getAvailableInputs());
00103             while(it.hasNext()) {
00104                 bool excluded = false;
00105                 PortInfo p = it.next();
00106                 QString name = QString("%1:%2").arg(p.getClientName()).arg(p.getPort());
00107                 if (m_clientFilter && clientIsAdvanced(p.getClient()))
00108                     continue;
00109                 if ( m_clientFilter && name.startsWith(QLatin1String("Virtual Raw MIDI")) )
00110                     continue;
00111                 if ( name.startsWith(m_publicName) )
00112                     continue;
00113                 foreach(const QString& n, m_excludedNames) {
00114                     if (name.startsWith(n)) {
00115                         excluded = true;
00116                         break;
00117                     }
00118                 }
00119                 if (!excluded)
00120                     m_inputDevices << name;
00121             }
00122             if (!m_currentInput.isEmpty() && !m_inputDevices.contains(m_currentInput)) {
00123                 m_currentInput.clear();
00124             }
00125         }
00126 
00127         bool setSubscription(const QString &newDevice)
00128         {
00129             //qDebug() << Q_FUNC_INFO << newDevice;
00130             if (m_inputDevices.contains(newDevice)) {
00131                 m_currentInput = newDevice;
00132                 m_port->unsubscribeAll();
00133                 m_port->subscribeFrom(newDevice);
00134                 m_client->startSequencerInput();
00135                 return true;
00136             }
00137             return false;
00138         }
00139 
00140         void clearSubscription()
00141         {
00142             if (!m_currentInput.isEmpty()) {
00143                 m_client->stopSequencerInput();
00144                 m_port->unsubscribeAll();
00145                 m_currentInput.clear();
00146             }
00147         }
00148 
00149         void setPublicName(QString newName)
00150         {
00151             if (newName != m_publicName) {
00152                 m_client->setClientName(newName);
00153                 m_publicName = newName;
00154             }
00155         }
00156 
00157         void handleSequencerEvent(SequencerEvent* ev)
00158         {
00159             if ( !SequencerEvent::isConnectionChange(ev) )
00160                 switch(ev->getSequencerType()) {
00161                 case SND_SEQ_EVENT_NOTEOFF: {
00162                         const NoteOffEvent* n = static_cast<const NoteOffEvent*>(ev);
00163                         if(m_out != 0 && m_thruEnabled) {
00164                             m_out->sendNoteOff(n->getChannel(), n->getKey(), n->getVelocity());
00165                         }
00166                         emit m_inp->midiNoteOff(n->getChannel(), n->getKey(), n->getVelocity());
00167                     }
00168                     break;
00169                 case SND_SEQ_EVENT_NOTEON: {
00170                         const NoteOnEvent* n = static_cast<const NoteOnEvent*>(ev);
00171                         if(m_out != 0 && m_thruEnabled) {
00172                             m_out->sendNoteOn(n->getChannel(), n->getKey(), n->getVelocity());
00173                         }
00174                         emit m_inp->midiNoteOn(n->getChannel(), n->getKey(), n->getVelocity());
00175                     }
00176                     break;
00177                 case SND_SEQ_EVENT_KEYPRESS: {
00178                         const KeyPressEvent* n = static_cast<const KeyPressEvent*>(ev);
00179                         if(m_out != 0 && m_thruEnabled) {
00180                             m_out->sendKeyPressure(n->getChannel(), n->getKey(), n->getVelocity());
00181                         }
00182                         emit m_inp->midiKeyPressure(n->getChannel(), n->getKey(), n->getVelocity());
00183                     }
00184                     break;
00185                 case SND_SEQ_EVENT_CONTROLLER:
00186                 case SND_SEQ_EVENT_CONTROL14: {
00187                         const ControllerEvent* n = static_cast<const ControllerEvent*>(ev);
00188                         if(m_out != 0 && m_thruEnabled) {
00189                             m_out->sendController(n->getChannel(), n->getParam(), n->getValue());
00190                         }
00191                         emit m_inp->midiController(n->getChannel(), n->getParam(), n->getValue());
00192                     }
00193                     break;
00194                 case SND_SEQ_EVENT_PGMCHANGE: {
00195                         const ProgramChangeEvent* p = static_cast<const ProgramChangeEvent*>(ev);
00196                         if(m_out != 0 && m_thruEnabled) {
00197                             m_out->sendProgram(p->getChannel(), p->getValue());
00198                         }
00199                         emit m_inp->midiProgram(p->getChannel(), p->getValue());
00200                     }
00201                     break;
00202                 case SND_SEQ_EVENT_CHANPRESS: {
00203                         const ChanPressEvent* n = static_cast<const ChanPressEvent*>(ev);
00204                         if(m_out != 0 && m_thruEnabled) {
00205                             m_out->sendChannelPressure(n->getChannel(), n->getValue());
00206                         }
00207                         emit m_inp->midiChannelPressure(n->getChannel(), n->getValue());
00208                     }
00209                     break;
00210                 case SND_SEQ_EVENT_PITCHBEND: {
00211                         const PitchBendEvent* n = static_cast<const PitchBendEvent*>(ev);
00212                         if(m_out != 0 && m_thruEnabled) {
00213                             m_out->sendPitchBend(n->getChannel(), n->getValue());
00214                         }
00215                         emit m_inp->midiPitchBend(n->getChannel(), n->getValue());
00216                     }
00217                     break;
00218                 case SND_SEQ_EVENT_SYSEX: {
00219                         const SysExEvent* n = static_cast<const SysExEvent*>(ev);
00220                         QByteArray data(n->getData(), n->getLength());
00221                         if(m_out != 0 && m_thruEnabled) {
00222                             m_out->sendSysex(data);
00223                         }
00224                         emit m_inp->midiSysex(data);
00225                     }
00226                     break;
00227                 case SND_SEQ_EVENT_SYSTEM: {
00228                         const SystemEvent* n = static_cast<const SystemEvent*>(ev);
00229                         int status = (int) n->getRaw8(0);
00230                         if(m_out != 0 && m_thruEnabled) {
00231                             m_out->sendSystemMsg(status);
00232                         }
00233                         if (status < 0xF7)
00234                             emit m_inp->midiSystemCommon(status);
00235                         else if (status > 0xF7)
00236                             emit m_inp->midiSystemRealtime(status);
00237                     }
00238                     break;
00239                 default:
00240                     break;
00241                 }
00242             delete ev;
00243         }
00244     };
00245 
00246     ALSAMIDIInput::ALSAMIDIInput(QObject *parent) : MIDIInput(parent),
00247         d(new ALSAMIDIInputPrivate(this))
00248     { }
00249 
00250     ALSAMIDIInput::~ALSAMIDIInput()
00251     {
00252         delete d;
00253     }
00254 
00255     void ALSAMIDIInput::initialize(QSettings* settings)
00256     {
00257         Q_UNUSED(settings)
00258     }
00259 
00260     QString ALSAMIDIInput::backendName()
00261     {
00262         return QLatin1String("ALSA");
00263     }
00264 
00265     QString ALSAMIDIInput::publicName()
00266     {
00267         return d->m_publicName;
00268     }
00269 
00270     void ALSAMIDIInput::setPublicName(QString name)
00271     {
00272         d->setPublicName(name);
00273     }
00274 
00275     QStringList ALSAMIDIInput::connections(bool advanced)
00276     {
00277         d->reloadDeviceList(advanced);
00278         return d->m_inputDevices;
00279     }
00280 
00281     void ALSAMIDIInput::setExcludedConnections(QStringList conns)
00282     {
00283         d->m_excludedNames = conns;
00284     }
00285 
00286     void ALSAMIDIInput::open(QString name)
00287     {
00288         d->setSubscription(name);
00289     }
00290 
00291     void ALSAMIDIInput::close()
00292     {
00293         d->clearSubscription();
00294     }
00295 
00296     QString ALSAMIDIInput::currentConnection()
00297     {
00298         return d->m_currentInput;
00299     }
00300 
00301     void ALSAMIDIInput::setMIDIThruDevice(MIDIOutput *device)
00302     {
00303         d->m_out = device;
00304     }
00305 
00306     void ALSAMIDIInput::enableMIDIThru(bool enable)
00307     {
00308         d->m_thruEnabled = enable;
00309     }
00310 
00311     bool ALSAMIDIInput::isEnabledMIDIThru()
00312     {
00313         return d->m_thruEnabled && (d->m_out != 0);
00314     }
00315 
00316 }}