Mixxx

/home/maxime/Projets/Mixxx/1.10/mixxx/src/midi/midideviceportmidi.cpp

Go to the documentation of this file.
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 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines