![]() |
Mixxx
|
00001 00018 /*************************************************************************** 00019 * * 00020 * This program is free software; you can redistribute it and/or modify * 00021 * it under the terms of the GNU General Public License as published by * 00022 * the Free Software Foundation; either version 2 of the License, or * 00023 * (at your option) any later version. * 00024 * * 00025 ***************************************************************************/ 00026 00027 #include <QtCore> 00028 #include "configobject.h" 00029 #include "midimapping.h" 00030 #include "midideviceportmidi.h" 00031 00032 QMutex MidiDevicePortMidi::m_sPMLock; // PortMidi is not thread-safe 00033 00034 MidiDevicePortMidi::MidiDevicePortMidi(MidiMapping* mapping, 00035 const PmDeviceInfo* inputDeviceInfo, 00036 const PmDeviceInfo* outputDeviceInfo, 00037 int inputDeviceIndex, 00038 int outputDeviceIndex) 00039 : MidiDevice(mapping) 00040 { 00041 m_pInputStream = NULL; 00042 m_pOutputStream = NULL; 00043 m_bStopRequested = false; 00044 m_pInputDeviceInfo = inputDeviceInfo; 00045 m_pOutputDeviceInfo = outputDeviceInfo; 00046 m_iInputDeviceIndex = inputDeviceIndex; 00047 m_iOutputDeviceIndex = outputDeviceIndex; 00048 00049 //Note: We prepend the input stream's index to the device's name to prevent duplicate devices from causing mayhem. 00050 m_strDeviceName = QString("%1. %2").arg(QString::number(m_iInputDeviceIndex)).arg(inputDeviceInfo->name); 00051 00052 if (inputDeviceInfo) { 00053 m_bIsInputDevice = m_pInputDeviceInfo->input; 00054 } 00055 if (outputDeviceInfo) { 00056 m_bIsOutputDevice = m_pOutputDeviceInfo->output; 00057 } 00058 00059 m_pMidiMapping->setName(m_strDeviceName); 00060 } 00061 00062 MidiDevicePortMidi::~MidiDevicePortMidi() 00063 { 00064 int success = close(); 00065 00066 // Wait until run() has actually finished before letting the 00067 // destructor finish up 00068 if (success == 0) this->wait(); 00069 //Otherwise the stream was already closed, so we don't have to wait. 00070 } 00071 00072 int MidiDevicePortMidi::open() 00073 { 00074 QMutexLocker Locker(&m_mutex); //Make this function thread safe. 00075 00076 if (m_bIsOpen) { 00077 qDebug() << "PortMIDI device" << m_strDeviceName << "already open"; 00078 return -1; 00079 } 00080 00081 setReceiveInhibit(false); 00082 00083 startup(); 00084 00085 m_bStopRequested = false; 00086 00087 if (m_strDeviceName == MIXXX_PORTMIDI_NO_DEVICE_STRING) 00088 return -1; 00089 00090 m_sPMLock.lock(); 00091 PmError err = Pm_Initialize(); 00092 m_sPMLock.unlock(); 00093 if( err != pmNoError ) 00094 { 00095 qDebug() << "PortMidi error:" << Pm_GetErrorText(err); 00096 return -1; 00097 } 00098 00099 if (m_pInputDeviceInfo) 00100 { 00101 if (m_bIsInputDevice) 00102 { 00103 if (midiDebugging()) qDebug() << "MidiDevicePortMidi: Opening" << m_pInputDeviceInfo->name << "index" << m_iInputDeviceIndex << "for input"; 00104 00105 m_sPMLock.lock(); 00106 err = Pm_OpenInput( &m_pInputStream, 00107 m_iInputDeviceIndex, 00108 NULL, //No drive hacks 00109 MIXXX_PORTMIDI_BUFFER_LEN, 00110 NULL, 00111 NULL); 00112 m_sPMLock.unlock(); 00113 00114 if( err != pmNoError ) 00115 { 00116 qDebug() << "PortMidi error:" << Pm_GetErrorText(err); 00117 return -2; 00118 } 00119 } 00120 } 00121 if (m_pOutputDeviceInfo) 00122 { 00123 if (m_bIsOutputDevice) 00124 { 00125 if (midiDebugging()) qDebug() << "MidiDevicePortMidi: Opening" << m_pOutputDeviceInfo->name << "index" << m_iOutputDeviceIndex << "for output"; 00126 00127 m_sPMLock.lock(); 00128 err = Pm_OpenOutput( &m_pOutputStream, 00129 m_iOutputDeviceIndex, 00130 NULL, // No driver hacks 00131 0, // No buffering 00132 NULL, // Use PortTime for timing 00133 NULL, // No time info 00134 0); // No latency compensation. 00135 m_sPMLock.unlock(); 00136 00137 if( err != pmNoError ) 00138 { 00139 qDebug() << "PortMidi error:" << Pm_GetErrorText(err); 00140 return -2; 00141 } 00142 } 00143 } 00144 00145 m_bIsOpen = true; 00146 start(); 00147 00148 return 0; 00149 00150 } 00151 00152 int MidiDevicePortMidi::close() 00153 { 00154 setReceiveInhibit(true); // Prevent deadlock 00155 00156 if (!m_bIsOpen) { 00157 qDebug() << "PortMIDI device" << m_strDeviceName << "already closed"; 00158 return -1; 00159 } 00160 00161 shutdown(); 00162 00163 //shutdown() locks so we must lock after it. 00164 QMutexLocker Locker(&m_mutex); 00165 00166 m_bStopRequested = true; 00167 00168 if (m_pInputStream) 00169 { 00170 m_sPMLock.lock(); 00171 PmError err = Pm_Close(m_pInputStream); 00172 if( err != pmNoError ) 00173 { 00174 qDebug() << "PortMidi error:" << Pm_GetErrorText(err); 00175 m_sPMLock.unlock(); 00176 return -1; 00177 } 00178 m_sPMLock.unlock(); 00179 } 00180 00181 if (m_pOutputStream) 00182 { 00183 m_sPMLock.lock(); 00184 PmError err = Pm_Close(m_pOutputStream); 00185 if( err != pmNoError ) 00186 { 00187 qDebug() << "PortMidi error:" << Pm_GetErrorText(err); 00188 m_sPMLock.unlock(); 00189 return -1; 00190 } 00191 m_sPMLock.unlock(); 00192 } 00193 00194 m_bIsOpen = false; 00195 00196 return 0; 00197 } 00198 00199 void MidiDevicePortMidi::run() 00200 { 00201 QThread::currentThread()->setObjectName(QString("PM %1").arg(m_strDeviceName)); 00202 int numEvents = 0; 00203 bool stopRunning = false; 00204 // Storage for SysEx messages 00205 unsigned char receive_msg[1024]; 00206 int receive_msg_index = 0; 00207 bool inSysex = false; 00208 bool endSysex = false; 00209 00210 do 00211 { 00212 if (m_pInputStream) 00213 { 00214 // TODO: Inhibit receiving of MIDI messages to prevent race condition? 00215 m_sPMLock.lock(); 00216 numEvents = Pm_Read(m_pInputStream, m_midiBuffer, MIXXX_PORTMIDI_BUFFER_LEN); 00217 m_sPMLock.unlock(); 00218 00219 if (numEvents < 0) { 00220 qDebug() << "PortMidi error:" << Pm_GetErrorText((PmError)numEvents); 00221 00222 // Don't process anything, continue to loop 00223 numEvents = 0; 00224 } 00225 00226 for (int i = 0; i < numEvents; i++) 00227 { 00228 unsigned char status = Pm_MessageStatus(m_midiBuffer[i].message); 00229 00230 if ((status & 0xF8) == 0xF8) { 00231 // Handle real-time MIDI messages at any time 00232 MidiDevice::receive((MidiStatusByte)status, 0, 0, 0); 00233 } 00234 00235 if (!inSysex) { 00236 if (status == 0xF0) { 00237 inSysex=true; 00238 status = 0; 00239 } 00240 else { 00241 unsigned char channel = status & 0x0F; 00242 unsigned char note = Pm_MessageData1(m_midiBuffer[i].message); 00243 unsigned char velocity = Pm_MessageData2(m_midiBuffer[i].message); 00244 00245 // mini-rant: this is WRONG. The MidiStatusByte enum does not 00246 // contain the full range of possible MIDI status bytes (for 00247 // example, 0x91) but the local 'status' could very well be 00248 // '0x91', making the MidiStatusByte type meaningless (at least 00249 // C enums aren't anything more than glorified constants, otherwise 00250 // this would be bloody murder) -bkgood XXX FIXME TODO(bkgood) 00251 MidiDevice::receive((MidiStatusByte)status, channel, note, velocity); 00252 } 00253 } 00254 00255 if (inSysex) { 00256 int data = 0; 00257 // Collect bytes from PmMessage 00258 for (int shift = 0; shift < 32 && (data != MIDI_STATUS_EOX); shift += 8) { 00259 receive_msg[receive_msg_index++] = data = 00260 (m_midiBuffer[i].message >> shift) & 0xFF; 00261 } 00262 // End System Exclusive message if the EOX byte or 00263 // a non-realtime status byte was received 00264 if (data == MIDI_STATUS_EOX || status > 0x7F) endSysex=true; 00265 } 00266 } 00267 00268 if (inSysex && endSysex) { 00269 MidiDevice::receive(receive_msg, receive_msg_index); 00270 inSysex=false; 00271 endSysex=false; 00272 receive_msg_index = 0; 00273 } 00274 00275 // Check if new events came while we were processing, if not, sleep 00276 m_sPMLock.lock(); 00277 PmError gotEvents = Pm_Poll(m_pInputStream); 00278 m_sPMLock.unlock(); 00279 00280 if (gotEvents == FALSE) { 00281 // Sleep this thread for 5 milliseconds between checking for new 00282 // MIDI event. 00283 usleep(5000); 00284 } else if (gotEvents < 0) { 00285 qDebug() << "PortMidi error:" << Pm_GetErrorText(gotEvents); 00286 } 00287 } 00288 00289 m_mutex.lock(); 00290 stopRunning = m_bStopRequested; //Cache locally for thread-safety. 00291 //Have to unlock inside the loop to give the other thread a chance to lock. 00292 m_mutex.unlock(); 00293 00294 } while (!stopRunning); 00295 } 00296 00297 void MidiDevicePortMidi::sendShortMsg(unsigned int word) 00298 { 00299 QMutexLocker Locker(&m_mutex); 00300 00301 if (m_pOutputStream) 00302 { 00303 m_sPMLock.lock(); 00304 PmError err = Pm_WriteShort(m_pOutputStream, 0, word); 00305 if( err != pmNoError ) qDebug() << "PortMidi sendShortMsg error:" << Pm_GetErrorText(err); 00306 m_sPMLock.unlock(); 00307 } 00308 00309 } 00310 00311 // The sysex data must already contain the start byte 0xf0 and the end byte 0xf7. 00312 void MidiDevicePortMidi::sendSysexMsg(unsigned char data[], unsigned int length) 00313 { 00314 Q_UNUSED(length); // We have to accept it even if we don't use it 00315 // to be consistent with other MIDI APIs that do need it 00316 QMutexLocker Locker(&m_mutex); 00317 00318 if (m_pOutputStream) 00319 { 00320 m_sPMLock.lock(); 00321 PmError err = Pm_WriteSysEx(m_pOutputStream, 0, data); 00322 if( err != pmNoError ) qDebug() << "PortMidi sendSysexMsg error:" << Pm_GetErrorText(err); 00323 m_sPMLock.unlock(); 00324 } 00325 }