drumstick  1.0.2
macmidioutput.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 "macmidioutput.h"
00021 #include "maccommon.h"
00022 
00023 #include <QObject>
00024 #include <QDebug>
00025 #include <QString>
00026 #include <QStringList>
00027 #include <QByteArray>
00028 #include <QVarLengthArray>
00029 
00030 #include <CoreFoundation/CoreFoundation.h>
00031 #include <CoreMIDI/MIDIServices.h>
00032 
00033 namespace drumstick {
00034 namespace rt {
00035 
00036     static CFStringRef DEFAULT_PUBLIC_NAME CFSTR("MIDI Out");
00037 
00038     class MacMIDIOutput::MacMIDIOutputPrivate {
00039     public:
00040         MIDIClientRef m_client;
00041         MIDIPortRef m_port;
00042         MIDIEndpointRef m_endpoint;
00043         MIDIEndpointRef m_destination;
00044         bool m_clientFilter;
00045 
00046         QString m_currentOutput;
00047         QString m_publicName;
00048         QStringList m_excludedNames;
00049         QStringList m_outputDevices;
00050 
00051         MacMIDIOutputPrivate():
00052             m_client(0),
00053             m_port(0),
00054             m_endpoint(0),
00055             m_destination(0),
00056             m_clientFilter(true),
00057             m_publicName(QString::fromCFString(DEFAULT_PUBLIC_NAME))
00058         {
00059             internalCreate(DEFAULT_PUBLIC_NAME);
00060         }
00061 
00062         void internalCreate(CFStringRef name)
00063         {
00064             OSStatus result = noErr;
00065             result = MIDIClientCreate( name, NULL, NULL, &m_client );
00066             if ( result != noErr ) {
00067                 qDebug() << "MIDIClientCreate() error:" << result;
00068                 return;
00069             }
00070             result = MIDISourceCreate( m_client, name, &m_endpoint );
00071             if ( result != noErr ) {
00072                 qDebug() << "MIDISourceCreate() error:" << result;
00073                 return;
00074             }
00075             result = MIDIOutputPortCreate( m_client, name, &m_port );
00076             if (result != noErr) {
00077                 qDebug() << "MIDIOutputPortCreate() error:" << result;
00078                 return;
00079             }
00080             reloadDeviceList(true);
00081         }
00082 
00083         virtual ~MacMIDIOutputPrivate()
00084         {
00085             internalDispose();
00086         }
00087 
00088         void internalDispose()
00089         {
00090             OSStatus result = noErr;
00091             if (m_port != 0) {
00092                 result = MIDIPortDispose( m_port );
00093                 if (result != noErr) {
00094                     qDebug() << "MIDIPortDispose() error:" << result;
00095                     m_port = 0;
00096                 }
00097             }
00098             if (m_endpoint != 0) {
00099                 result = MIDIEndpointDispose( m_endpoint );
00100                 if (result != noErr) {
00101                     qDebug() << "MIDIEndpointDispose() err:" << result;
00102                     m_endpoint = 0;
00103                 }
00104             }
00105             if (m_client != 0) {
00106                 result = MIDIClientDispose( m_client );
00107                 if (result != noErr) {
00108                     qDebug() << "MIDIClientDispose() error:" << result;
00109                     m_client = 0;
00110                 }
00111             }
00112         }
00113 
00114         void setPublicName(QString name)
00115         {
00116             if (m_publicName != name) {
00117                 internalDispose();
00118                 internalCreate(name.toCFString());
00119                 m_publicName = name;
00120             }
00121         }
00122 
00123         void reloadDeviceList(bool advanced)
00124         {
00125             int num = MIDIGetNumberOfDestinations();
00126             m_clientFilter = !advanced;
00127             m_outputDevices.clear();
00128             for (int i = 0; i < num; ++i) {
00129                 bool excluded = false;
00130                 MIDIEndpointRef dest = MIDIGetDestination( i );
00131                 if (dest != 0) {
00132                     QString name = getEndpointName(dest);
00133                     if ( m_clientFilter &&
00134                          name.contains(QLatin1String("IAC"), Qt::CaseSensitive) )
00135                         continue;
00136                     if ( name.contains (m_publicName) )
00137                         continue;
00138                     foreach ( const QString& n, m_excludedNames ) {
00139                         if ( name.contains(n) ) {
00140                             excluded = true;
00141                             break;
00142                         }
00143                     }
00144                     if (!excluded)
00145                         m_outputDevices << name;
00146                 }
00147             }
00148             if (!m_currentOutput.isEmpty() && m_destination != 0 &&
00149                 !m_outputDevices.contains(m_currentOutput)) {
00150                 m_currentOutput.clear();
00151                 m_destination = 0;
00152             }
00153         }
00154 
00155         void sendEvents( const MIDIPacketList* events )
00156         {
00157             MIDIReceived(m_endpoint, events);
00158             if (m_destination != 0)
00159                 MIDISend(m_port, m_destination, events);
00160         }
00161 
00162         bool open(const QString &name)
00163         {
00164             int index;
00165             QStringList allOutputDevices;
00166             int num = MIDIGetNumberOfDestinations();
00167             for (int i = 0; i < num; ++i) {
00168                 MIDIEndpointRef dest = MIDIGetDestination( i );
00169                 if (dest != 0)
00170                    allOutputDevices << getEndpointName( dest );
00171             }
00172             index = allOutputDevices.indexOf(name);
00173             if (index < 0)
00174                 return false;
00175             m_destination = MIDIGetDestination( index );
00176             m_currentOutput = name;
00177             return true;
00178         }
00179 
00180         void close()
00181         {
00182             m_destination = 0;
00183             m_currentOutput.clear();
00184         }
00185     };
00186 
00187     MacMIDIOutput::MacMIDIOutput(QObject *parent) :
00188         MIDIOutput(parent), d(new MacMIDIOutputPrivate)
00189     {
00190     }
00191 
00192     MacMIDIOutput::~MacMIDIOutput()
00193     {
00194         delete d;
00195     }
00196 
00197     void MacMIDIOutput::initialize(QSettings *settings)
00198     {
00199         Q_UNUSED(settings)
00200     }
00201 
00202     QString MacMIDIOutput::backendName()
00203     {
00204         return QLatin1Literal("CoreMIDI");
00205     }
00206 
00207     QString MacMIDIOutput::publicName()
00208     {
00209         return d->m_publicName;
00210     }
00211 
00212     void MacMIDIOutput::setPublicName(QString name)
00213     {
00214         d->setPublicName(name);
00215     }
00216 
00217     QStringList MacMIDIOutput::connections(bool advanced)
00218     {
00219         d->reloadDeviceList(advanced);
00220         return d->m_outputDevices;
00221     }
00222 
00223     void MacMIDIOutput::setExcludedConnections(QStringList conns)
00224     {
00225         d->m_excludedNames = conns;
00226     }
00227 
00228     void MacMIDIOutput::open(QString name)
00229     {
00230         d->open(name);
00231     }
00232 
00233     void MacMIDIOutput::close()
00234     {
00235         d->close();
00236     }
00237 
00238     QString MacMIDIOutput::currentConnection()
00239     {
00240         return d->m_currentOutput;
00241     }
00242 
00243     /* Realtime MIDI slots */
00244 
00245     /*void MacMIDIOutput::allNotesOff()
00246     {
00247         quint8 data[3];
00248         quint8 buf[2048];
00249         MIDIPacketList* pktlist = (MIDIPacketList*) &buf;
00250         MIDIPacket* packet = MIDIPacketListInit(pktlist);
00251         for(int chan = 0; chan < MIDI_CHANNELS && packet != NULL; ++chan) {
00252             data[0] = MIDI_STATUS_CONTROLCHANGE | (chan & 0x0f);
00253             data[1] = MIDI_CTL_ALL_NOTES_OFF;
00254             data[2] = 0;
00255             packet = MIDIPacketListAdd(pktlist, sizeof(buf), packet, 0,
00256                       sizeof(data), data);
00257             if (packet != NULL) {
00258                 data[1] = MIDI_CTL_ALL_SOUNDS_OFF;
00259                 packet = MIDIPacketListAdd(pktlist, sizeof(buf), packet, 0,
00260                            sizeof(data), data);
00261             }
00262         }
00263         if (packet != NULL)
00264             d->sendEvents(pktlist);
00265     }*/
00266 
00267     /*void MacMIDIOutput::resetControllers()
00268     {
00269         quint8 data[3];
00270         quint8 buf[2048];
00271         MIDIPacketList* pktlist = (MIDIPacketList*) &buf;
00272         MIDIPacket* packet = MIDIPacketListInit(pktlist);
00273         for(int chan = 0; chan < MIDI_CHANNELS && packet != NULL; ++chan) {
00274             data[0] = MIDI_STATUS_CONTROLCHANGE | (chan & 0x0f);
00275             data[1] = MIDI_CTL_RESET_CONTROLLERS;
00276             data[2] = 0;
00277             packet = MIDIPacketListAdd(pktlist, sizeof(buf), packet, 0,
00278                       sizeof(data), data);
00279             if (packet != NULL) {
00280                 data[1] = MIDI_CTL_MSB_MAIN_VOLUME;
00281                 data[2] = 100;
00282                 packet = MIDIPacketListAdd(pktlist, sizeof(buf), packet, 0,
00283                            sizeof(data), data);
00284             }
00285         }
00286         if (packet != NULL)
00287             d->sendEvents(pktlist);
00288     }*/
00289 
00290     void MacMIDIOutput::sendNoteOn(int chan, int note, int vel)
00291     {
00292         quint8 data[3];
00293         MIDIPacketList pktlist ;
00294         MIDIPacket* packet = MIDIPacketListInit(&pktlist);
00295         data[0] = MIDI_STATUS_NOTEON | (chan & 0x0f);
00296         data[1] = note;
00297         data[2] = vel;
00298         packet = MIDIPacketListAdd(&pktlist, sizeof(pktlist), packet, 0,
00299             sizeof(data), data);
00300         if (packet != NULL)
00301             d->sendEvents(&pktlist);
00302     }
00303 
00304     void MacMIDIOutput::sendNoteOff(int chan, int note, int vel)
00305     {
00306         quint8 data[3];
00307         MIDIPacketList pktlist ;
00308         MIDIPacket* packet = MIDIPacketListInit(&pktlist);
00309         data[0] = MIDI_STATUS_NOTEOFF | (chan & 0x0f);
00310         data[1] = note;
00311         data[2] = vel;
00312         packet = MIDIPacketListAdd(&pktlist, sizeof(pktlist), packet, 0,
00313             sizeof(data), data);
00314         if (packet != NULL)
00315             d->sendEvents(&pktlist);
00316     }
00317 
00318     void MacMIDIOutput::sendController(int chan, int control, int value)
00319     {
00320         quint8 data[3];
00321         MIDIPacketList pktlist ;
00322         MIDIPacket* packet = MIDIPacketListInit(&pktlist);
00323         data[0] = MIDI_STATUS_CONTROLCHANGE | (chan & 0x0f);
00324         data[1] = control;
00325         data[2] = value;
00326         packet = MIDIPacketListAdd(&pktlist, sizeof(pktlist), packet, 0,
00327             sizeof(data), data);
00328         if (packet != NULL)
00329             d->sendEvents(&pktlist);
00330     }
00331 
00332     void MacMIDIOutput::sendKeyPressure(int chan, int note, int value)
00333     {
00334         quint8 data[3];
00335         MIDIPacketList pktlist ;
00336         MIDIPacket* packet = MIDIPacketListInit(&pktlist);
00337         data[0] = MIDI_STATUS_KEYPRESURE | (chan & 0x0f);
00338         data[1] = note;
00339         data[2] = value;
00340         packet = MIDIPacketListAdd(&pktlist, sizeof(pktlist), packet, 0,
00341             sizeof(data), data);
00342         if (packet != NULL)
00343             d->sendEvents(&pktlist);
00344     }
00345 
00346     void MacMIDIOutput::sendProgram(int chan, int program)
00347     {
00348         quint8 data[2];
00349         MIDIPacketList pktlist ;
00350         MIDIPacket* packet = MIDIPacketListInit(&pktlist);
00351         data[0] = MIDI_STATUS_PROGRAMCHANGE | (chan & 0x0f);
00352         data[1] = program;
00353         packet = MIDIPacketListAdd(&pktlist, sizeof(pktlist), packet, 0,
00354             sizeof(data), data);
00355         if (packet != NULL)
00356             d->sendEvents(&pktlist);
00357     }
00358 
00359     void MacMIDIOutput::sendChannelPressure(int chan, int value)
00360     {
00361         quint8 data[2];
00362         MIDIPacketList pktlist ;
00363         MIDIPacket* packet = MIDIPacketListInit(&pktlist);
00364         data[0] = MIDI_STATUS_CHANNELPRESSURE | (chan & 0x0f);
00365         data[1] = value;
00366         packet = MIDIPacketListAdd(&pktlist, sizeof(pktlist), packet, 0,
00367             sizeof(data), data);
00368         if (packet != NULL)
00369             d->sendEvents(&pktlist);
00370     }
00371 
00372     void MacMIDIOutput::sendPitchBend(int chan, int value)
00373     {
00374         quint16 val = value + 8192; // value between -8192 and +8191
00375         quint8 data[3];
00376         MIDIPacketList pktlist ;
00377         MIDIPacket* packet = MIDIPacketListInit(&pktlist);
00378         data[0] = MIDI_STATUS_PITCHBEND | (chan & 0x0f);
00379         data[1] = MIDI_LSB(val); // LSB
00380         data[2] = MIDI_MSB(val); // MSB
00381         packet = MIDIPacketListAdd(&pktlist, sizeof(pktlist), packet, 0,
00382             sizeof(data), data);
00383         if (packet != NULL)
00384             d->sendEvents(&pktlist);
00385     }
00386 
00387     void MacMIDIOutput::sendSysex(const QByteArray& data)
00388     {
00389         quint8 buf[4096];
00390         if (data.size() > 4096)
00391             return;
00392         MIDIPacketList* pktlist = (MIDIPacketList*) &buf;
00393         MIDIPacket* packet = MIDIPacketListInit(pktlist);
00394         packet = MIDIPacketListAdd(pktlist, sizeof(buf), packet, 0,
00395             data.size(), (const Byte*)data.data());
00396         if (packet != NULL)
00397             d->sendEvents(pktlist);
00398     }
00399 
00400     void MacMIDIOutput::sendSystemMsg(const int status)
00401     {
00402         quint8 data;
00403         MIDIPacketList pktlist;
00404         MIDIPacket* packet = MIDIPacketListInit(&pktlist);
00405         data = status;
00406         packet = MIDIPacketListAdd(&pktlist, sizeof(pktlist), packet, 0,
00407             sizeof(data), &data);
00408         if (packet != NULL)
00409             d->sendEvents(&pktlist);
00410     }
00411 
00412 }}