drumstick
1.0.2
|
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