drumstick  1.0.2
macmidiinput.cpp
00001 /*
00002     Drumstick RT Backend
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 "macmidiinput.h"
00021 #include "rtmidioutput.h"
00022 #include "maccommon.h"
00023 
00024 #include <QDebug>
00025 #include <QStringList>
00026 #include <QMutex>
00027 #include <QTextCodec>
00028 #include <QObject>
00029 
00030 #include <CoreFoundation/CoreFoundation.h>
00031 #include <CoreMIDI/CoreMIDI.h>
00032 
00033 namespace drumstick {
00034 namespace rt {
00035 
00036     static CFStringRef DEFAULT_PUBLIC_NAME CFSTR("MIDI In");
00037 
00038     void MacMIDIReadProc( const MIDIPacketList *pktlist,
00039                           void *refCon, void *connRefCon );
00040 
00041     class MacMIDIInputPrivate {
00042     public:
00043         MacMIDIInput *m_inp;
00044         MIDIOutput *m_out;
00045         MIDIClientRef m_client;
00046         MIDIPortRef m_port;
00047         MIDIEndpointRef m_endpoint;
00048         MIDIEndpointRef m_source;
00049         bool m_thruEnabled;
00050         bool m_clientFilter;
00051         QString m_publicName;
00052         QString m_currentInput;
00053         QStringList m_excludedNames;
00054         QStringList m_inputDevices;
00055 
00056         MacMIDIInputPrivate(MacMIDIInput *inp) :
00057             m_inp(inp),
00058             m_out(0),
00059             m_client(0),
00060             m_port(0),
00061             m_endpoint(0),
00062             m_source(0),
00063             m_thruEnabled(false),
00064             m_clientFilter(true),
00065             m_publicName(QString::fromCFString(DEFAULT_PUBLIC_NAME))
00066         {
00067             internalCreate( DEFAULT_PUBLIC_NAME );
00068         }
00069 
00070         void internalCreate(CFStringRef name)
00071         {
00072             OSStatus result = noErr;
00073             result = MIDIClientCreate( name , NULL, NULL, &m_client );
00074             if (result != noErr) {
00075                 qDebug() << "MIDIClientCreate() err:" << result;
00076                 return;
00077             }
00078             result = MIDIDestinationCreate ( m_client, name, MacMIDIReadProc, (void*) this, &m_endpoint );
00079             if (result != noErr) {
00080                 qDebug() << "MIDIDestinationCreate() err:" << result;
00081                 return;
00082             }
00083             result = MIDIInputPortCreate( m_client, name, MacMIDIReadProc, (void *) this,  &m_port );
00084             if (result != noErr) {
00085                 qDebug() << "MIDIInputPortCreate() error:" << result;
00086                 return;
00087             }
00088             reloadDeviceList(true);
00089         }
00090 
00091         virtual ~MacMIDIInputPrivate()
00092         {
00093             internalDispose();
00094         }
00095 
00096         void internalDispose()
00097         {
00098             OSStatus result = noErr;
00099             if (m_port != 0) {
00100                 result = MIDIPortDispose(m_port);
00101                 if (result != noErr) {
00102                     qDebug() << "MIDIPortDispose() error:" << result;
00103                     m_port = 0;
00104                 }
00105             }
00106             if (m_endpoint != 0) {
00107                 result = MIDIEndpointDispose(m_endpoint);
00108                 if (result != noErr) {
00109                     qDebug() << "MIDIEndpointDispose() err:" << result;
00110                     m_endpoint = 0;
00111                 }
00112             }
00113             if (m_client != 0) {
00114                 result = MIDIClientDispose(m_client);
00115                 if (result != noErr) {
00116                     qDebug() << "MIDIClientDispose() err:" << result;
00117                     m_client = 0;
00118                 }
00119             }
00120         }
00121 
00122         void reloadDeviceList(bool advanced)
00123         {
00124             int num = MIDIGetNumberOfSources();
00125             m_clientFilter = !advanced;
00126             m_inputDevices.clear();
00127             for (int i = 0; i < num; ++i) {
00128                 bool excluded = false;
00129                 MIDIEndpointRef dest = MIDIGetSource( i );
00130                 if (dest != 0) {
00131                     QString name = getEndpointName(dest);
00132                     if ( m_clientFilter &&
00133                          name.contains(QLatin1String("IAC"), Qt::CaseSensitive) )
00134                         continue;
00135                     if ( name.contains(m_publicName))
00136                         continue;
00137                     foreach( const QString& n, m_excludedNames) {
00138                         if (name.contains(n)) {
00139                             excluded = true;
00140                             break;
00141                         }
00142                     }
00143                     if (!excluded)
00144                         m_inputDevices << name;
00145                 }
00146             }
00147             if (!m_currentInput.isEmpty() && m_source != 0 &&
00148                 !m_inputDevices.contains(m_currentInput)) {
00149                 m_currentInput.clear();
00150                 m_source = 0;
00151             }
00152         }
00153 
00154         void setPublicName(QString name)
00155         {
00156             if (m_publicName != name) {
00157                 internalDispose();
00158                 internalCreate(name.toCFString());
00159                 m_publicName = name;
00160             }
00161         }
00162 
00163         void open(QString name)
00164         {
00165             int index = -1;
00166             OSStatus result = noErr;
00167             QStringList allInputDevices;
00168             int num = MIDIGetNumberOfSources();
00169             for (int i = 0; i < num; ++i) {
00170                 MIDIEndpointRef dest = MIDIGetSource( i );
00171                 if (dest != 0)
00172                    allInputDevices << getEndpointName( dest );
00173             }
00174             index = allInputDevices.indexOf(name);
00175             if (index < 0)
00176                 return;
00177             m_source = MIDIGetSource( index );
00178             result = MIDIPortConnectSource( m_port, m_source, NULL );
00179             if (result != noErr) {
00180                 qDebug() << "MIDIPortConnectSource() error:" << result;
00181                 return;
00182             }
00183             m_currentInput = name;
00184             return;
00185         }
00186 
00187         void close()
00188         {
00189             OSStatus result = noErr;
00190             if (m_source != 0) {
00191                 result = MIDIPortDisconnectSource(m_port, m_source);
00192                 if (result != noErr)
00193                     qDebug() << "MIDIPortDisconnectSource() error:" << result;
00194                 m_source = 0;
00195                 m_currentInput.clear();
00196             }
00197         }
00198 
00199         void emitSignals(MIDIPacket* packet)
00200         {
00201             int value = 0;
00202             int status = packet->data[0] & 0xf0;
00203             int channel = packet->data[0] & 0x0f;
00204             QByteArray data;
00205             switch (status) {
00206             case MIDI_STATUS_NOTEOFF:
00207                 if(m_out != 0 && m_thruEnabled)
00208                     m_out->sendNoteOff(channel, packet->data[1], packet->data[2]);
00209                 emit m_inp->midiNoteOff(channel, packet->data[1], packet->data[2]);
00210                 break;
00211             case MIDI_STATUS_NOTEON:
00212                 if(m_out != 0 && m_thruEnabled)
00213                     m_out->sendNoteOn(channel, packet->data[1], packet->data[2]);
00214                 emit m_inp->midiNoteOn(channel, packet->data[1], packet->data[2]);
00215                 break;
00216             case MIDI_STATUS_KEYPRESURE:
00217                 if(m_out != 0 && m_thruEnabled)
00218                     m_out->sendKeyPressure(channel, packet->data[1], packet->data[2]);
00219                 emit m_inp->midiKeyPressure(channel, packet->data[1], packet->data[2]);
00220                 break;
00221             case MIDI_STATUS_CONTROLCHANGE:
00222                 if(m_out != 0 && m_thruEnabled)
00223                     m_out->sendController(channel, packet->data[1], packet->data[2]);
00224                 emit m_inp->midiController(channel, packet->data[1], packet->data[2]);
00225                 break;
00226             case MIDI_STATUS_PROGRAMCHANGE:
00227                 if(m_out != 0 && m_thruEnabled)
00228                     m_out->sendProgram(channel, packet->data[1]);
00229                 emit m_inp->midiProgram(channel, packet->data[1]);
00230                 break;
00231             case MIDI_STATUS_CHANNELPRESSURE:
00232                 if(m_out != 0 && m_thruEnabled)
00233                     m_out->sendChannelPressure(channel, packet->data[1]);
00234                 emit m_inp->midiChannelPressure(channel, packet->data[1]);
00235                 break;
00236             case MIDI_STATUS_PITCHBEND:
00237                 value = (packet->data[1] + packet->data[2] * 0x80) - 8192;
00238                 if(m_out != 0 && m_thruEnabled)
00239                     m_out->sendPitchBend(channel, value);
00240                 emit m_inp->midiPitchBend(channel, value);
00241                 break;
00242             case MIDI_STATUS_SYSEX:
00243                 data = QByteArray((const char *)packet->data, packet->length);
00244                 if(m_out != 0 && m_thruEnabled)
00245                     m_out->sendSysex(data);
00246                 emit m_inp->midiSysex(data);
00247                 break;
00248             default:
00249                 qDebug() << "status?" << status;
00250             }
00251         }
00252 
00253     };
00254 
00255     void MacMIDIReadProc( const MIDIPacketList *pktlist,
00256                           void *refCon, void *connRefCon )
00257     {
00258         Q_UNUSED(connRefCon)
00259         MacMIDIInputPrivate  *obj = NULL;
00260         if (refCon != NULL)
00261             obj = static_cast<MacMIDIInputPrivate*>(refCon);
00262 
00263         MIDIPacket *packet = (MIDIPacket *)pktlist->packet;
00264         for (unsigned int i = 0; i < pktlist->numPackets; ++i) {
00265             if (obj != NULL)
00266                obj->emitSignals(packet);
00267             packet = MIDIPacketNext(packet);
00268         }
00269     }
00270 
00271     MacMIDIInput::MacMIDIInput(QObject *parent) :
00272             MIDIInput(parent), d(new MacMIDIInputPrivate(this))
00273     {
00274     }
00275 
00276     MacMIDIInput::~MacMIDIInput()
00277     {
00278         delete d;
00279     }
00280 
00281     void MacMIDIInput::initialize(QSettings *settings)
00282     {
00283         Q_UNUSED(settings)
00284     }
00285 
00286     QString MacMIDIInput::backendName()
00287     {
00288         return QLatin1Literal("CoreMIDI");
00289     }
00290 
00291     QString MacMIDIInput::publicName()
00292     {
00293         return d->m_publicName;
00294     }
00295 
00296     void MacMIDIInput::setPublicName(QString name)
00297     {
00298         d->setPublicName(name);
00299     }
00300 
00301     QStringList MacMIDIInput::connections(bool advanced)
00302     {
00303         d->reloadDeviceList(advanced);
00304         return d->m_inputDevices;
00305     }
00306 
00307     void MacMIDIInput::setExcludedConnections(QStringList conns)
00308     {
00309         d->m_excludedNames = conns;
00310     }
00311 
00312     void MacMIDIInput::open(QString name)
00313     {
00314         d->open(name);
00315     }
00316 
00317     void MacMIDIInput::close()
00318     {
00319         d->close();
00320     }
00321 
00322     QString MacMIDIInput::currentConnection()
00323     {
00324         return d->m_currentInput;
00325     }
00326 
00327     void MacMIDIInput::setMIDIThruDevice(MIDIOutput *device)
00328     {
00329         d->m_out = device;
00330     }
00331 
00332     void MacMIDIInput::enableMIDIThru(bool enable)
00333     {
00334         d->m_thruEnabled = enable;
00335     }
00336 
00337     bool MacMIDIInput::isEnabledMIDIThru()
00338     {
00339         return d->m_thruEnabled && d->m_out != 0;
00340     }
00341 
00342 }} // namespace drumstick::rt
00343