Merge pull request #2228 from rageboge/midi_apple
#1153 (Apple) MIDI Support
This commit is contained in:
@@ -53,6 +53,7 @@ IF(LMMS_BUILD_APPLE)
|
||||
SET(WANT_VST OFF)
|
||||
SET(STATUS_ALSA "<not supported on this platform>")
|
||||
SET(STATUS_PULSEAUDIO "<not supported on this platform>")
|
||||
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 "<not supported on this platform>")
|
||||
SET(STATUS_PULSEAUDIO "<not supported on this platform>")
|
||||
SET(STATUS_WINMM "OK")
|
||||
SET(STATUS_APPLEMIDI "<not supported on this platform>")
|
||||
ELSE(LMMS_BUILD_WIN32)
|
||||
SET(STATUS_WINMM "<not supported on this platform>")
|
||||
ENDIF(LMMS_BUILD_WIN32)
|
||||
@@ -527,6 +529,7 @@ MESSAGE(
|
||||
"* ALSA : ${STATUS_ALSA}\n"
|
||||
"* OSS : ${STATUS_OSS}\n"
|
||||
"* WinMM : ${STATUS_WINMM}\n"
|
||||
"* AppleMidi : ${STATUS_APPLEMIDI}\n"
|
||||
)
|
||||
|
||||
MESSAGE(
|
||||
|
||||
158
include/MidiApple.h
Normal file
158
include/MidiApple.h
Normal file
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* MidiApple.h - Apple raw MIDI client
|
||||
*
|
||||
* Copyright (c) 2005-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
* 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 <CoreMIDI/CoreMIDI.h>
|
||||
|
||||
|
||||
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<QString, MIDIEndpointRef> m_inputDevices;
|
||||
QMap<QString, MIDIEndpointRef> m_outputDevices;
|
||||
QMap<MIDIEndpointRef, MIDIPortRef> m_sourcePortRef;
|
||||
|
||||
// subscriptions
|
||||
typedef QMap<QString, MidiPortList> SubMap;
|
||||
SubMap m_inputSubs;
|
||||
SubMap m_outputSubs;
|
||||
|
||||
signals:
|
||||
void readablePortsChanged();
|
||||
void writablePortsChanged();
|
||||
|
||||
} ;
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
@@ -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}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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" );
|
||||
|
||||
|
||||
643
src/core/midi/MidiApple.cpp
Normal file
643
src/core/midi/MidiApple.cpp
Normal file
@@ -0,0 +1,643 @@
|
||||
/*
|
||||
* MidiApple.cpp - Apple MIDI client
|
||||
*
|
||||
* Copyright (c) 2005-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
* 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 <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QtAlgorithms>
|
||||
#include <algorithm>
|
||||
|
||||
#include "ConfigManager.h"
|
||||
#include "MidiPort.h"
|
||||
#include "Note.h"
|
||||
|
||||
#ifdef LMMS_BUILD_APPLE
|
||||
|
||||
#include <CoreMIDI/CoreMIDI.h>
|
||||
|
||||
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<QString, MIDIEndpointRef>::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<const HMIDIIN *>( 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<MidiApple*>(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; i<pktlist->numPackets; ++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<MidiEventTypes>( 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<QString, MIDIEndpointRef> i( m_inputDevices );
|
||||
while( i.hasNext() )
|
||||
{
|
||||
midiInClose( i.next().value() );
|
||||
}
|
||||
|
||||
QMapIterator<QString, MIDIEndpointRef> 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<QString,MIDIEndpointRef> &inputMap )
|
||||
{
|
||||
qDebug("%s:", mapName);
|
||||
QMapIterator<QString, MIDIEndpointRef> 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
|
||||
|
||||
|
||||
@@ -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 );
|
||||
|
||||
|
||||
Reference in New Issue
Block a user