drumstick  1.0.2
winmidioutput.cpp
00001 /*
00002     Drumstick RT Windows 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 <QDebug>
00021 #include <QStringList>
00022 #include <QByteArray>
00023 #include <QVarLengthArray>
00024 #include <qmath.h>
00025 #include <windows.h>
00026 #include <mmsystem.h>
00027 #include "winmidioutput.h"
00028 
00029 namespace drumstick {
00030 namespace rt {
00031 
00032     union WinMIDIPacket {
00033         WinMIDIPacket() : dwPacket(0) {}
00034         DWORD dwPacket;
00035         quint8 data[sizeof(DWORD)];
00036     };
00037 
00038     static QLatin1Literal DEFAULT_PUBLIC_NAME = QLatin1Literal("MIDI Out");
00039 
00040     void CALLBACK midiCallback( HMIDIOUT hmo,
00041                                 UINT wMsg,
00042                                 DWORD_PTR dwInstance,
00043                                 DWORD_PTR dwParam1,
00044                                 DWORD_PTR dwParam2);
00045 
00046     class WinMIDIOutput::WinMIDIOutputPrivate {
00047     public:
00048         HMIDIOUT m_handle;
00049         bool m_clientFilter;
00050         QString m_publicName;
00051         QString m_currentOutput;
00052         QMap<int,QString> m_outputDevices;
00053         MIDIHDR m_midiSysexHdr;
00054         QByteArray m_sysexBuffer;
00055         QStringList m_excludedNames;
00056 
00057         WinMIDIOutputPrivate():
00058             m_handle(0),
00059             m_clientFilter(true),
00060             m_publicName(DEFAULT_PUBLIC_NAME)
00061         {
00062             reloadDeviceList(true);
00063         }
00064 
00065         void reloadDeviceList(bool advanced)
00066         {
00067             MMRESULT res;
00068             MIDIOUTCAPS deviceCaps;
00069             QString devName;
00070             unsigned int dev, max = midiOutGetNumDevs();
00071             m_outputDevices.clear();
00072             m_clientFilter = !advanced;
00073 
00074             for ( dev = 0; dev < max; ++dev) {
00075                 res = midiOutGetDevCaps( dev, &deviceCaps, sizeof(MIDIOUTCAPS));
00076                 if (res != MMSYSERR_NOERROR)
00077                     break;
00078                 if (m_clientFilter && (deviceCaps.wTechnology == MOD_MAPPER))
00079                     continue;
00080     #if defined(UNICODE)
00081                 devName = QString::fromWCharArray(deviceCaps.szPname);
00082     #else
00083                 devName = QString::fromLocal8Bit(deviceCaps.szPname);
00084     #endif
00085                 m_outputDevices[dev] = devName;
00086             }
00087             if (!m_clientFilter) {
00088                 dev = MIDI_MAPPER;
00089                 res = midiOutGetDevCaps( dev, &deviceCaps, sizeof(MIDIOUTCAPS));
00090                 if (res == MMSYSERR_NOERROR) {
00091     #if defined(UNICODE)
00092                     devName = QString::fromWCharArray(deviceCaps.szPname);
00093     #else
00094                     devName = QString::fromLocal8Bit(deviceCaps.szPname);
00095     #endif
00096                     m_outputDevices[dev] = devName;
00097                 }
00098             }
00099         }
00100 
00101         void setPublicName(QString name)
00102         {
00103             if (m_publicName != name) {
00104                 m_publicName = name;
00105             }
00106         }
00107 
00108         int deviceIndex( const QString& newOutputDevice )
00109         {
00110             int index = -1;
00111             QMap<int,QString>::ConstIterator it;
00112             for( it = m_outputDevices.constBegin();
00113                  it != m_outputDevices.constEnd(); ++it ) {
00114                 if (it.value() == newOutputDevice) {
00115                     index = it.key();
00116                     break;
00117                 }
00118             }
00119             return index;
00120         }
00121 
00122         void open(QString name)
00123         {
00124             MMRESULT res;
00125             int dev = -1;
00126 
00127             if (m_handle != 0)
00128                 close();
00129             reloadDeviceList(!m_clientFilter);
00130             dev = deviceIndex(name);
00131             if (dev > -1) {
00132                 res = midiOutOpen( &m_handle, dev, (DWORD_PTR) midiCallback, (DWORD_PTR) this, CALLBACK_FUNCTION);
00133                 if (res == MMSYSERR_NOERROR)
00134                     m_currentOutput = name;
00135                 else
00136                     qDebug() << "midiStreamOpen() err:" << mmErrorString(res);
00137             }
00138         }
00139 
00140         void close()
00141         {
00142             MMRESULT res;
00143             if (m_handle != 0) {
00144                 res = midiOutReset( m_handle );
00145                 if (res != MMSYSERR_NOERROR)
00146                     qDebug() << "midiOutReset() err:" << mmErrorString(res);
00147                 res = midiOutClose( m_handle );
00148                 if (res == MMSYSERR_NOERROR)
00149                     m_currentOutput.clear();
00150                 else
00151                     qDebug() << "midiStreamClose() err:" << mmErrorString(res);
00152                 m_handle = 0;
00153             }
00154         }
00155 
00156         void doneHeader( LPMIDIHDR lpMidiHdr )
00157         {
00158             MMRESULT res;
00159             res = midiOutUnprepareHeader( m_handle, lpMidiHdr, sizeof(MIDIHDR) );
00160             if (res != MMSYSERR_NOERROR)
00161                 qDebug() << "midiOutUnprepareHeader() err:" << mmErrorString(res);
00162             if ((lpMidiHdr->dwFlags & MHDR_ISSTRM) == 0)
00163                 return; // sysex header?
00164         }
00165 
00166         void sendShortMessage(WinMIDIPacket &packet)
00167         {
00168             MMRESULT res;
00169             res = midiOutShortMsg( m_handle, packet.dwPacket );
00170             if ( res != MMSYSERR_NOERROR )
00171                 qDebug() << "midiOutShortMsg() err:" << mmErrorString(res);
00172         }
00173 
00174         void sendSysexEvent(const QByteArray& data)
00175         {
00176             MMRESULT res;
00177             m_sysexBuffer = data;
00178             m_midiSysexHdr.lpData = (LPSTR) m_sysexBuffer.data();
00179             m_midiSysexHdr.dwBufferLength = m_sysexBuffer.size();
00180             m_midiSysexHdr.dwBytesRecorded = m_sysexBuffer.size();
00181             m_midiSysexHdr.dwFlags = 0;
00182             m_midiSysexHdr.dwUser = 0;
00183             res = midiOutPrepareHeader( m_handle, &m_midiSysexHdr, sizeof(MIDIHDR) );
00184             if (res != MMSYSERR_NOERROR)
00185                 qDebug() << "midiOutPrepareHeader() err:" << mmErrorString(res);
00186             else {
00187                 res = midiOutLongMsg( m_handle, &m_midiSysexHdr, sizeof(MIDIHDR) );
00188                 if (res != MMSYSERR_NOERROR)
00189                     qDebug() << "midiOutLongMsg() err:" << mmErrorString(res);
00190             }
00191         }
00192 
00193         QString mmErrorString(MMRESULT err)
00194         {
00195             QString errstr;
00196     #ifdef UNICODE
00197             WCHAR buffer[1024];
00198             midiOutGetErrorText(err, &buffer[0], sizeof(buffer));
00199             errstr = QString::fromUtf16((const ushort*)buffer);
00200     #else
00201             char buffer[1024];
00202             midiOutGetErrorText(err, &buffer[0], sizeof(buffer));
00203             errstr = QString::fromLocal8Bit(buffer);
00204     #endif
00205             return errstr;
00206         }
00207     };
00208 
00209 
00210     void CALLBACK midiCallback( HMIDIOUT hmo,
00211                                 UINT wMsg,
00212                                 DWORD_PTR dwInstance,
00213                                 DWORD_PTR dwParam1,
00214                                 DWORD_PTR dwParam2)
00215     {
00216         //Q_UNUSED(hmo)
00217         Q_UNUSED(dwParam2)
00218 
00219         WinMIDIOutput::WinMIDIOutputPrivate* obj = (WinMIDIOutput::WinMIDIOutputPrivate*) dwInstance;
00220         switch( wMsg ) {
00221         case MOM_DONE:
00222             obj->doneHeader( (LPMIDIHDR) dwParam1 );
00223             break;
00224         case MOM_OPEN:
00225             qDebug() << "Open output" << hmo;
00226             break;
00227         case MOM_CLOSE:
00228             qDebug() << "Close output" << hmo;
00229             break;
00230         default:
00231             qDebug() << "unknown output message:" << hex << wMsg;
00232             break;
00233         }
00234     }
00235 
00236     WinMIDIOutput::WinMIDIOutput(QObject *parent) :
00237         MIDIOutput(parent), d(new WinMIDIOutputPrivate)
00238     { }
00239 
00240     WinMIDIOutput::~WinMIDIOutput()
00241     {
00242         delete d;
00243     }
00244 
00245     void WinMIDIOutput::initialize(QSettings *settings)
00246     {
00247         Q_UNUSED(settings)
00248     }
00249 
00250     QString WinMIDIOutput::backendName()
00251     {
00252         return "Windows MM";
00253     }
00254 
00255     QString WinMIDIOutput::publicName()
00256     {
00257         return d->m_publicName;
00258     }
00259 
00260     void WinMIDIOutput::setPublicName(QString name)
00261     {
00262         d->setPublicName(name);
00263     }
00264 
00265     QStringList WinMIDIOutput::connections(bool advanced)
00266     {
00267         d->reloadDeviceList(advanced);
00268         return d->m_outputDevices.values();
00269     }
00270 
00271     void WinMIDIOutput::setExcludedConnections(QStringList conns)
00272     {
00273         d->m_excludedNames = conns;
00274     }
00275 
00276     void WinMIDIOutput::open(QString name)
00277     {
00278         d->open(name);
00279     }
00280 
00281     void WinMIDIOutput::close()
00282     {
00283         d->close();
00284     }
00285 
00286     QString WinMIDIOutput::currentConnection()
00287     {
00288         return d->m_currentOutput;
00289     }
00290 
00291     void WinMIDIOutput::sendNoteOn(int chan, int note, int vel)
00292     {
00293         WinMIDIPacket packet;
00294         packet.data[0] = MIDI_STATUS_NOTEON | (chan & MIDI_CHANNEL_MASK);
00295         packet.data[1] = note;
00296         packet.data[2] = vel;
00297         d->sendShortMessage(packet);
00298     }
00299 
00300     void WinMIDIOutput::sendNoteOff(int chan, int note, int vel)
00301     {
00302         WinMIDIPacket packet;
00303         packet.data[0] = MIDI_STATUS_NOTEOFF | (chan & MIDI_CHANNEL_MASK);
00304         packet.data[1] = note;
00305         packet.data[2] = vel;
00306         d->sendShortMessage(packet);
00307     }
00308 
00309     void WinMIDIOutput::sendController(int chan, int control, int value)
00310     {
00311         WinMIDIPacket packet;
00312         packet.data[0] = MIDI_STATUS_CONTROLCHANGE | (chan & MIDI_CHANNEL_MASK);
00313         packet.data[1] = control;
00314         packet.data[2] = value;
00315         d->sendShortMessage(packet);
00316     }
00317 
00318     void WinMIDIOutput::sendKeyPressure(int chan, int note, int value)
00319     {
00320         WinMIDIPacket packet;
00321         packet.data[0] = MIDI_STATUS_KEYPRESURE | (chan & MIDI_CHANNEL_MASK);
00322         packet.data[1] = note;
00323         packet.data[2] = value;
00324         d->sendShortMessage(packet);
00325     }
00326 
00327     void WinMIDIOutput::sendProgram(int chan, int program)
00328     {
00329         WinMIDIPacket packet;
00330         packet.data[0] = MIDI_STATUS_PROGRAMCHANGE | (chan & MIDI_CHANNEL_MASK);
00331         packet.data[1] = program;
00332         d->sendShortMessage(packet);
00333     }
00334 
00335     void WinMIDIOutput::sendChannelPressure(int chan, int value)
00336     {
00337         WinMIDIPacket packet;
00338         packet.data[0] = MIDI_STATUS_CHANNELPRESSURE | (chan & MIDI_CHANNEL_MASK);
00339         packet.data[1] = value;
00340         d->sendShortMessage(packet);
00341     }
00342 
00343     void WinMIDIOutput::sendPitchBend(int chan, int value)
00344     {
00345         WinMIDIPacket packet;
00346         packet.data[0] = MIDI_STATUS_PITCHBEND | (chan & MIDI_CHANNEL_MASK);
00347         packet.data[1] = MIDI_LSB(value);
00348         packet.data[2] = MIDI_MSB(value);
00349         d->sendShortMessage(packet);
00350     }
00351 
00352     void WinMIDIOutput::sendSystemMsg(const int status)
00353     {
00354         WinMIDIPacket packet;
00355         packet.data[0] = status;
00356         d->sendShortMessage(packet);
00357     }
00358 
00359     void WinMIDIOutput::sendSysex(const QByteArray &data)
00360     {
00361         d->sendSysexEvent(data);
00362     }
00363 
00364 }}