From 275bcb52df05f779d4e49355e90b51fe1466521b Mon Sep 17 00:00:00 2001 From: Maurizio Lo Bosco Date: Sun, 9 Aug 2015 09:59:12 +0200 Subject: [PATCH] #1153: added basic apple midi support --- CMakeLists.txt | 3 + include/MidiApple.h | 158 +++++++++ src/CMakeLists.txt | 4 + src/core/CMakeLists.txt | 1 + src/core/Mixer.cpp | 13 + src/core/midi/MidiApple.cpp | 643 ++++++++++++++++++++++++++++++++++++ src/gui/SetupDialog.cpp | 6 + 7 files changed, 828 insertions(+) create mode 100644 include/MidiApple.h create mode 100644 src/core/midi/MidiApple.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 05d39a008..14da3bc97 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,6 +53,7 @@ IF(LMMS_BUILD_APPLE) SET(WANT_VST OFF) SET(STATUS_ALSA "") SET(STATUS_PULSEAUDIO "") + SET(STATUS_APPLEMIDI "OK") # MacPorts: /opt/local/lib LINK_DIRECTORIES(${LINK_DIRECTORIES} /opt/local/lib) ENDIF(LMMS_BUILD_APPLE) @@ -69,6 +70,7 @@ IF(LMMS_BUILD_WIN32) SET(STATUS_JACK "") SET(STATUS_PULSEAUDIO "") SET(STATUS_WINMM "OK") + SET(STATUS_APPLEMIDI "") ELSE(LMMS_BUILD_WIN32) SET(STATUS_WINMM "") ENDIF(LMMS_BUILD_WIN32) @@ -531,6 +533,7 @@ MESSAGE( "* ALSA : ${STATUS_ALSA}\n" "* OSS : ${STATUS_OSS}\n" "* WinMM : ${STATUS_WINMM}\n" +"* AppleMidi : ${STATUS_APPLEMIDI}\n" ) MESSAGE( diff --git a/include/MidiApple.h b/include/MidiApple.h new file mode 100644 index 000000000..d392df110 --- /dev/null +++ b/include/MidiApple.h @@ -0,0 +1,158 @@ +/* + * MidiApple.h - Apple raw MIDI client + * + * Copyright (c) 2005-2014 Tobias Doerffel + * Copyright (c) 2015 Maurizio Lo Bosco (rageboge on github) + * + * This file is part of LMMS - http://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef MIDI_APPLE_H +#define MIDI_APPLE_H + +#include "lmmsconfig.h" + +#ifdef LMMS_BUILD_APPLE + +#include "MidiClient.h" +#include "MidiPort.h" +#include + + +class QLineEdit; + + +class MidiApple : public QObject, public MidiClient +{ + Q_OBJECT +public: + MidiApple(); + virtual ~MidiApple(); + + static QString probeDevice(); + + + inline static QString name() + { + return QT_TRANSLATE_NOOP( "setupWidget", "Apple MIDI" ); + } + + virtual void processOutEvent( const MidiEvent & _me, + const MidiTime & _time, + const MidiPort * _port ); + + virtual void applyPortMode( MidiPort * _port ); + virtual void removePort( MidiPort * _port ); + + + // list devices as ports + virtual QStringList readablePorts() const + { + return m_inputDevices.keys(); + } + + virtual QStringList writablePorts() const + { + return m_outputDevices.keys(); + } + + // return name of port which specified MIDI event came from + virtual QString sourcePortName( const MidiEvent & ) const; + + // (un)subscribe given MidiPort to/from destination-port + virtual void subscribeReadablePort( MidiPort * _port, + const QString & _dest, + bool _subscribe = true ); + + virtual void subscribeWritablePort( MidiPort * _port, + const QString & _dest, + bool _subscribe = true ); + + virtual void connectRPChanged( QObject * _receiver, + const char * _member ) + { + connect( this, SIGNAL( readablePortsChanged() ), + _receiver, _member ); + } + + + virtual void connectWPChanged( QObject * _receiver, + const char * _member ) + { + connect( this, SIGNAL( writablePortsChanged() ), + _receiver, _member ); + } + + + virtual bool isRaw() const + { + return false; + } + + + class setupWidget : public MidiClient::setupWidget + { + public: + setupWidget( QWidget * _parent ); + virtual ~setupWidget(); + + void saveSettings() + { + } + } ; + + +private:// slots: + void updateDeviceList(); + + +private: + void openDevices(); + void closeDevices(); + void openMidiReference( MIDIEndpointRef reference, QString refName,bool isIn ); + MIDIClientRef getMidiClientRef(); + void midiInClose( MIDIEndpointRef reference ); + static void NotifyCallback( const MIDINotification *message, void *refCon ); + static void ReadCallback( const MIDIPacketList *pktlist, void *readProcRefCon, void *srcConnRefCon ); + void HandleReadCallback( const MIDIPacketList *pktlist, void *srcConnRefCon ); + void notifyMidiPortList( MidiPortList portList, MidiEvent midiEvent); + char * getFullName( MIDIEndpointRef &endpoint_ref ); + void sendMidiOut( MIDIEndpointRef & endPointRef, const MidiEvent& event ); + MIDIPacketList createMidiPacketList( const MidiEvent& event ); + + MIDIClientRef mClient = 0; + QMap m_inputDevices; + QMap m_outputDevices; + QMap m_sourcePortRef; + + // subscriptions + typedef QMap SubMap; + SubMap m_inputSubs; + SubMap m_outputSubs; + +signals: + void readablePortsChanged(); + void writablePortsChanged(); + +} ; + +#endif + + +#endif diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 318e3371e..e25c50fbb 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -95,6 +95,10 @@ IF(LMMS_BUILD_WIN32) SET(EXTRA_LIBRARIES "-lwinmm") ENDIF() +IF(LMMS_BUILD_APPLE) + SET(EXTRA_LIBRARIES "-framework CoreMIDI") +ENDIF() + SET(LMMS_REQUIRED_LIBS ${CMAKE_THREAD_LIBS_INIT} ${QT_LIBRARIES} diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 6d6ae4d98..adc6ef3a9 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -81,6 +81,7 @@ set(LMMS_SRCS core/midi/MidiClient.cpp core/midi/MidiController.cpp core/midi/MidiOss.cpp + core/midi/MidiApple.cpp core/midi/MidiPort.cpp core/midi/MidiTime.cpp core/midi/MidiWinMM.cpp diff --git a/src/core/Mixer.cpp b/src/core/Mixer.cpp index fe8443dbc..2766a572a 100644 --- a/src/core/Mixer.cpp +++ b/src/core/Mixer.cpp @@ -50,6 +50,7 @@ #include "MidiAlsaSeq.h" #include "MidiOss.h" #include "MidiWinMM.h" +#include "MidiApple.h" #include "MidiDummy.h" #include "MemoryHelper.h" @@ -894,6 +895,18 @@ MidiClient * Mixer::tryMidiClients() } #endif +#ifdef LMMS_BUILD_APPLE + printf( "trying midi apple...\n" ); + if( client_name == MidiApple::name() || client_name == "" ) + { + MidiApple * mapple = new MidiApple; + m_midiClientName = MidiApple::name(); + printf( "Returning midi apple\n" ); + return mapple; + } + printf( "midi apple didn't work: client_name=%s\n", client_name.toUtf8().constData()); +#endif + printf( "Couldn't create MIDI-client, neither with ALSA nor with " "OSS. Will use dummy-MIDI-client.\n" ); diff --git a/src/core/midi/MidiApple.cpp b/src/core/midi/MidiApple.cpp new file mode 100644 index 000000000..32a635e7a --- /dev/null +++ b/src/core/midi/MidiApple.cpp @@ -0,0 +1,643 @@ +/* + * MidiApple.cpp - Apple MIDI client + * + * Copyright (c) 2005-2014 Tobias Doerffel + * Copyright (c) 2015 Maurizio Lo Bosco (rageboge on github) + * + * This file is part of LMMS - http://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + + +#include "MidiApple.h" + +#include +#include +#include +#include + +#include "ConfigManager.h" +#include "MidiPort.h" +#include "Note.h" + +#ifdef LMMS_BUILD_APPLE + +#include + +const unsigned int SYSEX_LENGTH=1024; + +MidiApple::MidiApple() : + MidiClient(), + m_inputDevices(), + m_outputDevices(), + m_inputSubs(), + m_outputSubs() +{ + openDevices(); +} + + + +MidiApple::~MidiApple() +{ + closeDevices(); +} + + + +void MidiApple::processOutEvent( const MidiEvent& event, const MidiTime& time, const MidiPort* port ) +{ + qDebug("MidiApple:processOutEvent displayName:'%s'",port->displayName().toLatin1().constData()); + + QStringList outDevs; + for( SubMap::ConstIterator it = m_outputSubs.begin(); it != m_outputSubs.end(); ++it ) + { + for( MidiPortList::ConstIterator jt = it.value().begin(); jt != it.value().end(); ++jt ) + { + if( *jt == port ) + { + outDevs += it.key(); + break; + } + } + } + + for( QMap::Iterator it = m_outputDevices.begin(); it != m_outputDevices.end(); ++it ) + { + if( outDevs.contains( it.key() ) ) + { + sendMidiOut( it.value(), event ); + } + } + +} + + + +void MidiApple::sendMidiOut( MIDIEndpointRef &endPointRef, const MidiEvent& event ) +{ + MIDIPacketList packetList=createMidiPacketList(event); + MIDIPortRef port = m_sourcePortRef.value(endPointRef); + MIDISend(port, endPointRef, &packetList); +} + + + +MIDIPacketList MidiApple::createMidiPacketList( const MidiEvent& event ) +{ + MIDIPacketList packetList; + packetList.numPackets = 1; + MIDIPacket* firstPacket = &packetList.packet[0]; + firstPacket->timeStamp = 0; // send immediately + firstPacket->length = 3; + firstPacket->data[0] = ( event.type() + event.channel() ); + firstPacket->data[1] = ( event.param( 0 ) & 0xff ); + firstPacket->data[2] = ( event.param( 1 ) & 0xff ); + return packetList; +} + + + +void MidiApple::applyPortMode( MidiPort* port ) +{ + qDebug("applyPortMode displayName:'%s'",port->displayName().toLatin1().constData()); + // make sure no subscriptions exist which are not possible with + // current port-mode + if( !port->isInputEnabled() ) + { + for( SubMap::Iterator it = m_inputSubs.begin(); it != m_inputSubs.end(); ++it ) + { + it.value().removeAll( port ); + } + } + + if( !port->isOutputEnabled() ) + { + for( SubMap::Iterator it = m_outputSubs.begin(); it != m_outputSubs.end(); ++it ) + { + it.value().removeAll( port ); + } + } +} + + + +void MidiApple::removePort( MidiPort* port ) +{ + qDebug("removePort displayName:'%s'",port->displayName().toLatin1().constData()); + for( SubMap::Iterator it = m_inputSubs.begin(); it != m_inputSubs.end(); ++it ) + { + it.value().removeAll( port ); + } + + for( SubMap::Iterator it = m_outputSubs.begin(); it != m_outputSubs.end(); ++it ) + { + it.value().removeAll( port ); + } + + MidiClient::removePort( port ); +} + + + +QString MidiApple::sourcePortName( const MidiEvent& event ) const +{ + qDebug("sourcePortName return '%s'?\n", event.sourcePort()); + /* + if( event.sourcePort() ) + { + return m_inputDevices.value( *static_cast( event.sourcePort() ) ); + } + */ + return MidiClient::sourcePortName( event ); +} + + + +void MidiApple::subscribeReadablePort( MidiPort* port, const QString& dest, bool subscribe ) +{ + qDebug("subscribeReadablePort %s subscribe=%d",dest.toLatin1().constData(),subscribe); + if( subscribe && port->isInputEnabled() == false ) + { + qWarning( "port %s can't be (un)subscribed!", port->displayName().toLatin1().constData() ); + return; + } + + m_inputSubs[dest].removeAll( port ); + if( subscribe ) + { + qDebug("Subscribing %s",dest.toLatin1().constData()); + m_inputSubs[dest].push_back( port ); + } + else + { + MidiPortList list = m_inputSubs[dest]; + if(list.empty()){ + m_inputSubs.remove(dest); + } + } +} + + + +void MidiApple::subscribeWritablePort( MidiPort* port, const QString& dest, bool subscribe ) +{ + qDebug("subscribeWritablePort %s", port->displayName().toLatin1().constData()); + + if( subscribe && port->isOutputEnabled() == false ) + { + qWarning( "port %s can't be (un)subscribed!", port->displayName().toLatin1().constData() ); + return; + } + + m_outputSubs[dest].removeAll( port ); + if( subscribe ) + { + m_outputSubs[dest].push_back( port ); + } + else + { + MidiPortList list = m_outputSubs[dest]; + if(list.empty()){ + m_outputSubs.remove(dest); + } + } +} + + + +void MidiApple::ReadCallback( const MIDIPacketList *pktlist, void *readProcRefCon, void *srcConnRefCon ) +{ + MidiApple *caller = static_cast(readProcRefCon); + if (!caller) + { + qDebug("Error: !caller: MidiApple::ReadCallback"); + return; + } + caller->HandleReadCallback(pktlist,srcConnRefCon); +} + + + +void MidiApple::HandleReadCallback( const MIDIPacketList *pktlist, void *srcConnRefCon ) +{ + const char * refName = (const char *) srcConnRefCon; + + MIDIEndpointRef endPointRef = m_inputDevices.value(refName); + if( !m_inputSubs.contains( refName ) ) + { +// qDebug("HandleReadCallback '%s' not subscribed",refName); +// printQStringKeys("m_inputDevices", m_inputDevices); + return; + } +// qDebug("HandleReadCallback '%s' subscribed",refName); + bool continueSysEx = false; + unsigned int nBytes; + const MIDIPacket *packet = &pktlist->packet[0]; + unsigned char sysExMessage[SYSEX_LENGTH]; + unsigned int sysExLength = 0; + + for (uint32_t i=0; inumPackets; ++i) + { + nBytes = packet->length; + // Check if this is the end of a continued SysEx message + if (continueSysEx) { + unsigned int lengthToCopy = qMin(nBytes, SYSEX_LENGTH - sysExLength); + // Copy the message into our SysEx message buffer, + // making sure not to overrun the buffer + memcpy(sysExMessage + sysExLength, packet->data, lengthToCopy); + sysExLength += lengthToCopy; + // Check if the last byte is SysEx End. + continueSysEx = (packet->data[nBytes - 1] == 0xF7); + if (!continueSysEx || sysExLength == SYSEX_LENGTH) { + // We would process the SysEx message here, as it is we're just ignoring it + + sysExLength = 0; + } + } + else + { + UInt16 iByte, size; + + iByte = 0; + while (iByte < nBytes) + { + size = 0; + + // First byte should be status + unsigned char status = packet->data[iByte]; + if (status < 0xC0) { + size = 3; + } + else if (status < 0xE0) + { + size = 2; + } + else if (status < 0xF0) + { + size = 3; + } + else if (status == 0xF0) + { + // MIDI SysEx then we copy the rest of the message into the SysEx message buffer + unsigned int lengthLeftInMessage = nBytes - iByte; + unsigned int lengthToCopy = qMin(lengthLeftInMessage, SYSEX_LENGTH); + + memcpy(sysExMessage + sysExLength, packet->data, lengthToCopy); + sysExLength += lengthToCopy; + + size = 0; + iByte = nBytes; + + // Check whether the message at the end is the end of the SysEx + continueSysEx = (packet->data[nBytes - 1] != 0xF7); + } + else if (status < 0xF3) + { + size = 3; + } + else if (status == 0xF3) + { + size = 2; + } + else + { + size = 1; + } + + unsigned char messageChannel = status & 0xF; + const MidiEventTypes cmdtype = static_cast( status & 0xF0 ); + const int par1 = packet->data[iByte + 1]; + const int par2 = packet->data[iByte + 2]; + + switch (cmdtype) + { + case MidiNoteOff: //0x80: + case MidiNoteOn: //0x90: + case MidiKeyPressure: //0xA0: + notifyMidiPortList(m_inputSubs[refName],MidiEvent( cmdtype, messageChannel, par1 - KeysPerOctave, par2 & 0xff, &endPointRef )); + break; + + case MidiControlChange: //0xB0: + case MidiProgramChange: //0xC0: + case MidiChannelPressure: //0xD0: + notifyMidiPortList(m_inputSubs[refName],MidiEvent( cmdtype, messageChannel, par1, par2 & 0xff, &endPointRef )); + break; + + case MidiPitchBend: //0xE0: + notifyMidiPortList(m_inputSubs[refName],MidiEvent( cmdtype, messageChannel, par1 + par2 * 128, 0, &endPointRef )); + break; + case MidiActiveSensing: //0xF0 + case 0xF0: + break; + default: + qDebug("endPointRef name='%s':Some other message %d", refName, cmdtype); + break; + } + iByte += size; + } + } + packet = MIDIPacketNext(packet); + } +} + + + +void MidiApple::updateDeviceList() +{ + closeDevices(); + openDevices(); + + emit readablePortsChanged(); + emit writablePortsChanged(); +} + + + +void MidiApple::closeDevices() +{ + m_inputSubs.clear(); + m_outputSubs.clear(); + + QMapIterator i( m_inputDevices ); + while( i.hasNext() ) + { + midiInClose( i.next().value() ); + } + + QMapIterator o( m_outputDevices ); + while( o.hasNext() ) + { + midiInClose( o.next().value() ); + } + + m_inputDevices.clear(); + m_outputDevices.clear(); +} + + + +void MidiApple::midiInClose( MIDIEndpointRef reference ) +{ + qDebug("midiInClose '%s'", getFullName(reference)); + MIDIPortRef portRef = m_sourcePortRef[reference]; + MIDIPortDisconnectSource(portRef, reference); +} + + + +char *getName( MIDIObjectRef &object ) +{ + // Returns the name of a given MIDIObjectRef as char * + CFStringRef name = nil; + if (noErr != MIDIObjectGetStringProperty(object, kMIDIPropertyName, &name)) + return nil; + int len = CFStringGetLength(name)+1; + char *value = (char *) malloc(len); + + CFStringGetCString(name, value, len, 0); + CFRelease(name); + return value; +} + + + +const void printQStringKeys( char const * mapName, const QMap &inputMap ) +{ + qDebug("%s:", mapName); + QMapIterator i( inputMap ); + while( i.hasNext() ) + { + QString key = i.next().key(); + qDebug(" key='%s'", key.toLatin1().constData()); + } +} + + + +void MidiApple::openDevices() +{ + qDebug("openDevices"); + m_inputDevices.clear(); + // How many MIDI devices do we have? + ItemCount deviceCount = MIDIGetNumberOfDevices(); + + // Iterate through all MIDI devices + for (ItemCount i = 0 ; i < deviceCount ; ++i) + { + // Grab a reference to current device + MIDIDeviceRef device = MIDIGetDevice(i); + char * deviceName = getName(device); + QString qsDeviceName = QString::fromUtf8((char*)(deviceName)); + qDebug("Device name:%s",deviceName); + + // Is this device online? (Currently connected?) + SInt32 isOffline = 0; + MIDIObjectGetIntegerProperty(device, kMIDIPropertyOffline, &isOffline); + qDebug(" is online: %s", (isOffline ? "No" : "Yes")); + // How many entities do we have? + ItemCount entityCount = MIDIDeviceGetNumberOfEntities(device); + + // Iterate through this device's entities + for (ItemCount j = 0 ; j < entityCount ; ++j) + { + // Grab a reference to an entity + MIDIEntityRef entity = MIDIDeviceGetEntity(device, j); + qDebug(" Entity: %s", getName(entity)); + + // Iterate through this device's source endpoints (MIDI In) + ItemCount sourceCount = MIDIEntityGetNumberOfSources(entity); + for ( ItemCount k = 0 ; k < sourceCount ; ++k ) + { + // Grab a reference to a source endpoint + MIDIEndpointRef source = MIDIEntityGetSource(entity, k); + char * name = getName(source); + qDebug(" Source: '%s'", name); + QString sourceName = qsDeviceName + ":" + QString::fromUtf8((char*)(name)); + qDebug(" Source name: '%s'", sourceName.toLatin1().constData() ); + m_inputDevices.insert(sourceName, source); + openMidiReference(source,sourceName,true); + } + + // Iterate through this device's destination endpoints (MIDI Out) + ItemCount destCount = MIDIEntityGetNumberOfDestinations(entity); + for ( ItemCount k = 0 ; k < destCount ; ++k ) + { + // Grab a reference to a destination endpoint + MIDIEndpointRef dest = MIDIEntityGetDestination(entity, k); + char * name = getName(dest); + qDebug(" Destination: '%s'", name); + QString destinationName = qsDeviceName + ":" + QString::fromUtf8((char*)(name)); + qDebug(" Destination name: '%s'", destinationName.toLatin1().constData() ); + m_outputDevices.insert(destinationName, dest); + openMidiReference(dest,destinationName,false); + } + } + qDebug("------"); + } + printQStringKeys("m_inputDevices:",m_inputDevices); + printQStringKeys("m_outputDevices:",m_outputDevices); +} + + + +void MidiApple::openMidiReference( MIDIEndpointRef reference, QString refName, bool isIn ) +{ + char * registeredName = (char*) malloc(refName.length()+1); + sprintf(registeredName, "%s",refName.toLatin1().constData()); + qDebug("openMidiReference refName '%s'",refName.toLatin1().constData()); + + MIDIClientRef mClient = getMidiClientRef(); + MIDIPortRef mPort = 0; + + CFStringRef inName = CFStringCreateWithCString(0, registeredName, kCFStringEncodingASCII); + if(isIn) + { + MIDIInputPortCreate(mClient, inName, &MidiApple::ReadCallback, this, &mPort); + } + else + { + MIDIOutputPortCreate(mClient, inName, &mPort); + } + MIDIPortConnectSource(mPort, reference, (void *)registeredName); + m_sourcePortRef.insert(reference, mPort); + CFRelease(inName); + qDebug("openMidiReference registeredName '%s'",registeredName); +} + + + +MIDIClientRef MidiApple::getMidiClientRef() +{ + if(mClient==0) + { + CFStringRef deviceClientName = CFSTR("MIDI In Device Client"); + MIDIClientCreate(deviceClientName, NotifyCallback, this, &mClient); + CFRelease(deviceClientName); + } + return mClient; +} + + + +void MidiApple::notifyMidiPortList( MidiPortList l, MidiEvent event ) +{ + for( MidiPortList::ConstIterator it = l.begin(); it != l.end(); ++it ) + { + ( *it )->processInEvent( event); + } +} + + + +void MidiApple::NotifyCallback( const MIDINotification *message, void *refCon ) +{ + //refCon is a pointer to MidiApple class + MidiApple *midiApple = (MidiApple *)refCon; + qDebug("MidiApple::NotifyCallback '%d'",message->messageID); + switch (message->messageID) + { + case kMIDIMsgObjectAdded: + { + MIDIObjectAddRemoveNotification* msg = (MIDIObjectAddRemoveNotification*)message ; + MIDIEndpointRef endpoint_ref = (MIDIEndpointRef)msg->child ; + char * fullName = midiApple->getFullName(endpoint_ref); + + if (msg->childType == kMIDIObjectType_Source) { + qDebug("kMIDIMsgObjectAdded source '%s'",fullName); + // Here your code to save the new source ref + // and update your internal "clients" (eg + // update a popup menu, a list, ...) + } + if (msg->childType == kMIDIObjectType_Destination) { + qDebug("kMIDIMsgObjectAdded destination '%s'",fullName); + // Here your code to save the new destination ref + // and update your internal "clients" + } + break; + } + case kMIDIMsgObjectRemoved: + { + MIDIObjectAddRemoveNotification* + msg = (MIDIObjectAddRemoveNotification*)message ; + MIDIEndpointRef endpoint_ref = (MIDIEndpointRef)msg->child ; + char * fullName = midiApple->getFullName(endpoint_ref); + + if (msg->childType == kMIDIObjectType_Source) { + // Here your code to remove the source ref + // and update your internal "clients" + qDebug("kMIDIMsgObjectRemoved source '%s'",fullName); + } + if (msg->childType == kMIDIObjectType_Destination) { + // Here your code to remove the destination ref + // and update your internal "clients" + qDebug("kMIDIMsgObjectRemoved destination '%s'",fullName); } + break; + } + case kMIDIMsgPropertyChanged: + // Currently ignored + qDebug("kMIDIMsgPropertyChanged"); + break; + case kMIDIMsgThruConnectionsChanged: + // Currently ignored + qDebug("kMIDIMsgThruConnectionsChanged"); + break; + case kMIDIMsgSerialPortOwnerChanged: + // Currently ignored + qDebug("kMIDIMsgSerialPortOwnerChanged"); + break; + default: + qDebug("unhandled message type"); + break; + } +} + + + +char * MidiApple::getFullName(MIDIEndpointRef &endpoint_ref) +{ + MIDIEntityRef entity = 0; + MIDIEndpointGetEntity(endpoint_ref, &entity); //get the entity + MIDIDeviceRef device = 0; + MIDIEntityGetDevice(entity, &device); + char * deviceName = getName(device); + char * endPointName = getName(endpoint_ref); + qDebug("device name='%s' endpoint name='%s'",deviceName,endPointName); + char * fullName = (char *)malloc(strlen(deviceName) + strlen(endPointName)+1); + sprintf(fullName, "%s:%s", deviceName,endPointName); + return fullName; +} + + + +MidiApple::setupWidget::setupWidget( QWidget* parent ) : +MidiClient::setupWidget( MidiApple::name(), parent ) +{ +} + + + +MidiApple::setupWidget::~setupWidget() +{ +} + + + +#endif + + diff --git a/src/gui/SetupDialog.cpp b/src/gui/SetupDialog.cpp index 7ca9911ce..e93db546e 100644 --- a/src/gui/SetupDialog.cpp +++ b/src/gui/SetupDialog.cpp @@ -63,6 +63,7 @@ #include "MidiAlsaSeq.h" #include "MidiOss.h" #include "MidiWinMM.h" +#include "MidiApple.h" #include "MidiDummy.h" @@ -830,6 +831,11 @@ SetupDialog::SetupDialog( ConfigTabs _tab_to_open ) : new MidiWinMM::setupWidget( msw ); #endif +#ifdef LMMS_BUILD_APPLE + m_midiIfaceSetupWidgets[MidiApple::name()] = + new MidiApple::setupWidget( msw ); +#endif + m_midiIfaceSetupWidgets[MidiDummy::name()] = new MidiDummy::setupWidget( msw );