drumstick  1.0.2
alsamidioutput.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 <QString>
00021 #include <QStringList>
00022 #include <QMutex>
00023 #include <QMutexLocker>
00024 #include <qmath.h>
00025 #include "alsaclient.h"
00026 #include "alsaport.h"
00027 #include "alsaevent.h"
00028 #include "alsamidioutput.h"
00029 
00030 namespace drumstick {
00031 namespace rt {
00032 
00033     static QString DEFAULT_PUBLIC_NAME(QLatin1String("MIDI Out"));
00034 
00035     class ALSAMIDIOutput::ALSAMIDIOutputPrivate {
00036     public:
00037         ALSAMIDIOutput *m_out;
00038         MidiClient *m_client;
00039         MidiPort *m_port;
00040         int m_portId;
00041         bool m_clientFilter;
00042         int m_runtimeAlsaNum;
00043         QString m_publicName;
00044         QString m_currentOutput;
00045         QStringList m_outputDevices;
00046         QStringList m_excludedNames;
00047         QMutex m_outMutex;
00048 
00049         ALSAMIDIOutputPrivate(ALSAMIDIOutput *q):
00050             m_out(q),
00051             m_client(0),
00052             m_port(0),
00053             m_portId(0),
00054             m_clientFilter(true),
00055             m_runtimeAlsaNum(0),
00056             m_publicName(DEFAULT_PUBLIC_NAME)
00057         {
00058             m_runtimeAlsaNum = getRuntimeALSALibraryNumber();
00059             m_client = new MidiClient(m_out);
00060             m_client->open();
00061             m_client->setClientName(m_publicName);
00062             m_port = m_client->createPort();
00063             m_port->setPortName("out");
00064             m_port->setCapability( SND_SEQ_PORT_CAP_READ  | SND_SEQ_PORT_CAP_SUBS_READ );
00065             m_port->setPortType( SND_SEQ_PORT_TYPE_APPLICATION | SND_SEQ_PORT_TYPE_MIDI_GENERIC );
00066             m_portId = m_port->getPortId();
00067         }
00068 
00069         ~ALSAMIDIOutputPrivate()
00070         {
00071             if (m_client != NULL) {
00072                 clearSubscription();
00073                 if (m_port != NULL)
00074                     m_port->detach();
00075                 m_client->close();
00076                 delete m_client;
00077             }
00078         }
00079 
00080         bool clientIsAdvanced(int clientId)
00081         {
00082             // asking for runtime version instead of SND_LIB_VERSION
00083             if (m_runtimeAlsaNum < 0x01000B)
00084                 // ALSA <= 1.0.10
00085                 return (clientId < 64);
00086             else
00087                 // ALSA >= 1.0.11
00088                 return (clientId < 16);
00089         }
00090 
00091         void reloadDeviceList(bool advanced)
00092         {
00093             m_clientFilter = !advanced;
00094             m_outputDevices.clear();
00095             QListIterator<PortInfo> it(m_client->getAvailableOutputs());
00096             while(it.hasNext()) {
00097                 bool excluded = false;
00098                 PortInfo p = it.next();
00099                 QString name = QString("%1:%2").arg(p.getClientName()).arg(p.getPort());
00100                 if (m_clientFilter && clientIsAdvanced(p.getClient()))
00101                     continue;
00102                 if ( m_clientFilter && name.startsWith(QLatin1String("Virtual Raw MIDI")) )
00103                     continue;
00104                 if ( name.startsWith(m_publicName) )
00105                     continue;
00106                 foreach(const QString& n, m_excludedNames) {
00107                     if ( name.startsWith(n) ) {
00108                         excluded = true;
00109                         break;
00110                     }
00111                 }
00112                 if (!excluded)
00113                     m_outputDevices << name;
00114             }
00115             if (!m_currentOutput.isEmpty() &&
00116                 !m_outputDevices.contains(m_currentOutput)) {
00117                 m_currentOutput.clear();
00118             }
00119         }
00120 
00121         bool setSubscription(const QString &newOutputDevice)
00122         {
00123             //qDebug() << Q_FUNC_INFO << newOutputDevice;
00124             if (m_outputDevices.contains(newOutputDevice)) {
00125                 m_currentOutput = newOutputDevice;
00126                 m_port->unsubscribeAll();
00127                 m_port->subscribeTo(newOutputDevice);
00128                 return true;
00129             }
00130             return false;
00131         }
00132 
00133         void clearSubscription()
00134         {
00135             if (!m_currentOutput.isEmpty()) {
00136                 m_port->unsubscribeAll();
00137                 m_currentOutput.clear();
00138             }
00139         }
00140 
00141         void sendEvent(SequencerEvent *ev)
00142         {
00143             QMutexLocker locker(&m_outMutex);
00144             ev->setSource(m_portId);
00145             ev->setSubscribers();
00146             ev->setDirect();
00147             m_client->outputDirect(ev);
00148         }
00149 
00150         void setPublicName(QString newName)
00151         {
00152             if (newName != m_publicName) {
00153                 m_client->setClientName(newName);
00154                 m_publicName = newName;
00155             }
00156         }
00157 
00158     };
00159 
00160     ALSAMIDIOutput::ALSAMIDIOutput(QObject *parent) : MIDIOutput(parent),
00161         d(new ALSAMIDIOutputPrivate(this))
00162     { }
00163 
00164     ALSAMIDIOutput::~ALSAMIDIOutput()
00165     {
00166         delete d;
00167     }
00168 
00169     void ALSAMIDIOutput::initialize(QSettings* settings)
00170     {
00171         Q_UNUSED(settings)
00172     }
00173 
00174     /* SLOTS */
00175 
00176     void ALSAMIDIOutput::sendNoteOn(int chan, int note, int vel)
00177     {
00178         NoteOnEvent ev(chan, note, vel);
00179         d->sendEvent(&ev);
00180     }
00181 
00182     void ALSAMIDIOutput::sendNoteOff(int chan, int note, int vel)
00183     {
00184         NoteOffEvent ev(chan, note, vel);
00185         d->sendEvent(&ev);
00186     }
00187 
00188     void ALSAMIDIOutput::sendController(int chan, int control, int value)
00189     {
00190         ControllerEvent ev(chan, control, value);
00191         d->sendEvent(&ev);
00192     }
00193 
00194     void ALSAMIDIOutput::sendKeyPressure(int chan, int note, int value)
00195     {
00196         KeyPressEvent ev(chan, note, value);
00197         d->sendEvent(&ev);
00198     }
00199 
00200     void ALSAMIDIOutput::sendProgram(int chan, int program)
00201     {
00202         ProgramChangeEvent ev(chan, program);
00203         d->sendEvent(&ev);
00204     }
00205 
00206     void ALSAMIDIOutput::sendChannelPressure(int chan, int value)
00207     {
00208         ChanPressEvent ev(chan, value);
00209         d->sendEvent(&ev);
00210     }
00211 
00212     void ALSAMIDIOutput::sendPitchBend(int chan, int value)
00213     {
00214         PitchBendEvent ev(chan, value);
00215         d->sendEvent(&ev);
00216     }
00217 
00218     void ALSAMIDIOutput::sendSysex(const QByteArray& data)
00219     {
00220         SysExEvent ev(data);
00221         d->sendEvent(&ev);
00222     }
00223 
00224     void ALSAMIDIOutput::sendSystemMsg(const int status)
00225     {
00226         SystemEvent ev(status);
00227         d->sendEvent(&ev);
00228     }
00229 
00230     QString ALSAMIDIOutput::backendName()
00231     {
00232         return "ALSA";
00233     }
00234 
00235     QString ALSAMIDIOutput::publicName()
00236     {
00237         return d->m_publicName;
00238     }
00239 
00240     void ALSAMIDIOutput::setPublicName(QString name)
00241     {
00242         d->setPublicName(name);
00243     }
00244 
00245     QStringList ALSAMIDIOutput::connections(bool advanced)
00246     {
00247         d->reloadDeviceList(advanced);
00248         return d->m_outputDevices;
00249     }
00250 
00251     void ALSAMIDIOutput::setExcludedConnections(QStringList conns)
00252     {
00253         d->m_excludedNames = conns;
00254     }
00255 
00256     QString ALSAMIDIOutput::currentConnection()
00257     {
00258         return d->m_currentOutput;
00259     }
00260 
00261     void ALSAMIDIOutput::open(QString name)
00262     {
00263         d->setSubscription(name);
00264     }
00265 
00266     void ALSAMIDIOutput::close()
00267     {
00268         d->clearSubscription();
00269     }
00270 
00271 }}
00272