drumstick  1.0.2
winmidiinput.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 <QString>
00021 #include <QMap>
00022 #include <QDebug>
00023 #include <windows.h>
00024 #include <mmsystem.h>
00025 
00026 #include "winmidiinput.h"
00027 
00028 namespace drumstick {
00029 namespace rt {
00030 
00031     static QLatin1Literal DEFAULT_PUBLIC_NAME = QLatin1Literal("MIDI In");
00032 
00033     void CALLBACK midiCallback( HMIDIIN hMidiIn,
00034                                 UINT wMsg,
00035                                 DWORD_PTR dwInstance,
00036                                 DWORD_PTR dwParam1,
00037                                 DWORD_PTR dwParam2 );
00038 
00039 
00040     class WinMIDIInput::WinMIDIInputPrivate {
00041     public:
00042         WinMIDIInput *m_inp;
00043         MIDIOutput *m_out;
00044         bool m_thruEnabled;
00045         bool m_clientFilter;
00046         HMIDIIN m_handle;
00047         QString m_publicName;
00048         QString m_currentInput;
00049         QStringList m_excludedNames;
00050         QMap<int,QString> m_inputDevices;
00051 
00052         WinMIDIInputPrivate(WinMIDIInput *inp):
00053             m_inp(inp),
00054             m_out(0),
00055             m_thruEnabled(false),
00056             m_clientFilter(true),
00057             m_handle(0),
00058             m_publicName(DEFAULT_PUBLIC_NAME)
00059         {
00060             reloadDeviceList(true);
00061         }
00062 
00063         int deviceIndex( const QString& newDevice )
00064         {
00065             int index = -1;
00066             QMap<int,QString>::ConstIterator it;
00067             for( it = m_inputDevices.constBegin();
00068                  it != m_inputDevices.constEnd(); ++it ) {
00069                 if (it.value() == newDevice) {
00070                     index = it.key();
00071                     break;
00072                 }
00073             }
00074             return index;
00075         }
00076 
00077         void open(QString name) {
00078             MMRESULT res;
00079             if (name != m_currentInput) {
00080                 if (m_handle != 0)
00081                     close();
00082                 reloadDeviceList(!m_clientFilter);
00083                 int dev = deviceIndex(name);
00084                 if (dev > -1) {
00085                     res = midiInOpen(&m_handle, dev, (DWORD_PTR) midiCallback, (DWORD_PTR) this, CALLBACK_FUNCTION | MIDI_IO_STATUS );
00086                     if (res != MMSYSERR_NOERROR)
00087                         qDebug() << "midiInOpen() err:" << mmErrorString(res);
00088                     res = midiInStart(m_handle);
00089                     if (res != MMSYSERR_NOERROR)
00090                         qDebug() << "midiInStart() err:" << mmErrorString(res);
00091                     m_currentInput = name;
00092                 }
00093             }
00094         }
00095 
00096         void close() {
00097             MMRESULT res;
00098             if (m_handle != 0) {
00099                 res = midiInStop(m_handle);
00100                 if (res != MMSYSERR_NOERROR)
00101                     qDebug() << "midiInStop() err:" << mmErrorString(res);
00102                 res = midiInReset( m_handle );
00103                 if (res != MMSYSERR_NOERROR)
00104                     qDebug() << "midiInReset() err:" << mmErrorString(res);
00105                 res = midiInClose( m_handle );
00106                 if (res != MMSYSERR_NOERROR)
00107                     qDebug() << "midiInClose() err:" << mmErrorString(res);
00108                 m_handle = 0;
00109             }
00110             m_currentInput.clear();
00111         }
00112 
00113         void reloadDeviceList(bool advanced)
00114         {
00115             MMRESULT res;
00116             MIDIINCAPS deviceCaps;
00117             QString devName;
00118             unsigned int dev, max = midiInGetNumDevs();
00119             m_inputDevices.clear();
00120             m_clientFilter = !advanced;
00121 
00122             for ( dev = 0; dev < max; ++dev) {
00123                 bool excluded = false;
00124                 res = midiInGetDevCaps( dev, &deviceCaps, sizeof(MIDIINCAPS));
00125                 if (res != MMSYSERR_NOERROR)
00126                     break;
00127 #if defined(UNICODE)
00128                 devName = QString::fromWCharArray(deviceCaps.szPname);
00129 #else
00130                 devName = QString::fromLocal8Bit(deviceCaps.szPname);
00131 #endif
00132                 foreach(const QString& n, m_excludedNames) {
00133                     if (devName.startsWith(n)) {
00134                         excluded = true;
00135                         break;
00136                     }
00137                 }
00138                 if (!excluded)
00139                     m_inputDevices[dev] = devName;
00140             }
00141         }
00142 
00143         void setPublicName(QString name)
00144         {
00145             if (m_publicName != name) {
00146                 m_publicName = name;
00147             }
00148         }
00149 
00150         void emitSignals(int status, int channel, int data1, int data2)
00151         {
00152             switch (status) {
00153             case MIDI_STATUS_NOTEOFF:
00154                 if(m_out != 0 && m_thruEnabled)
00155                     m_out->sendNoteOff(channel, data1, data2);
00156                 emit m_inp->midiNoteOff(channel, data1, data2);
00157                 break;
00158             case MIDI_STATUS_NOTEON:
00159                 if(m_out != 0 && m_thruEnabled)
00160                     m_out->sendNoteOn(channel, data1, data2);
00161                 emit m_inp->midiNoteOn(channel, data1, data2);
00162                 break;
00163             case MIDI_STATUS_KEYPRESURE:
00164                 if(m_out != 0 && m_thruEnabled)
00165                     m_out->sendKeyPressure(channel, data1, data2);
00166                 emit m_inp->midiKeyPressure(channel, data1, data2);
00167                 break;
00168             case MIDI_STATUS_CONTROLCHANGE:
00169                 if(m_out != 0 && m_thruEnabled)
00170                     m_out->sendController(channel, data1, data2);
00171                 emit m_inp->midiController(channel, data1, data2);
00172                 break;
00173             case MIDI_STATUS_PROGRAMCHANGE:
00174                 if(m_out != 0 && m_thruEnabled)
00175                     m_out->sendProgram(channel, data1);
00176                 emit m_inp->midiProgram(channel, data1);
00177                 break;
00178             case MIDI_STATUS_CHANNELPRESSURE:
00179                 if(m_out != 0 && m_thruEnabled)
00180                     m_out->sendChannelPressure(channel, data1);
00181                 emit m_inp->midiChannelPressure(channel, data1);
00182                 break;
00183             case MIDI_STATUS_PITCHBEND: {
00184                     int value = (data1 + data2 * 0x80) - 8192;
00185                     if(m_out != 0 && m_thruEnabled)
00186                         m_out->sendPitchBend(channel, value);
00187                     emit m_inp->midiPitchBend(channel, value);
00188                 }
00189                 break;
00190             default:
00191                 qDebug() << "MIDI in status?" << status;
00192             }
00193         }
00194 
00195         void emitSysex(QByteArray data)
00196         {
00197             if(m_out != 0 && m_thruEnabled)
00198                 m_out->sendSysex(data);
00199             emit m_inp->midiSysex(data);
00200         }
00201 
00202         QString mmErrorString(MMRESULT err)
00203         {
00204             QString errstr;
00205     #ifdef UNICODE
00206             WCHAR buffer[1024];
00207             midiInGetErrorText(err, &buffer[0], sizeof(buffer));
00208             errstr = QString::fromUtf16((const ushort*)buffer);
00209     #else
00210             char buffer[1024];
00211             midiOutGetErrorText(err, &buffer[0], sizeof(buffer));
00212             errstr = QString::fromLocal8Bit(buffer);
00213     #endif
00214             return errstr;
00215         }
00216 
00217     };
00218 
00219     void CALLBACK midiCallback( HMIDIIN hMidiIn,
00220                                 UINT wMsg,
00221                                 DWORD_PTR dwInstance,
00222                                 DWORD_PTR dwParam1,
00223                                 DWORD_PTR dwParam2 )
00224     {
00225         //Q_UNUSED(hMidiIn)
00226         Q_UNUSED(dwParam2)
00227         WinMIDIInput::WinMIDIInputPrivate* object = (WinMIDIInput::WinMIDIInputPrivate*) dwInstance;
00228         switch( wMsg ) {
00229         case MIM_OPEN:
00230             qDebug() << "Open input" << hMidiIn;
00231             break;
00232         case MIM_CLOSE:
00233             qDebug() << "Close input" << hMidiIn;
00234             break;
00235         case MIM_ERROR:
00236         case MIM_LONGERROR:
00237             qDebug() << "Errors input";
00238             break;
00239         case MIM_LONGDATA:
00240             qDebug() << "Sysex data input";
00241             break;
00242         case MIM_DATA:
00243         case MIM_MOREDATA: {
00244                 int status = dwParam1 & 0xf0;
00245                 int channel = dwParam1 & 0x0f;
00246                 int data1 = (dwParam1 & 0x7f00) >> 8;
00247                 int data2 = (dwParam1 & 0x7f0000) >> 16;
00248                 object->emitSignals(status, channel, data1, data2);
00249             }
00250             break;
00251         default:
00252             qDebug() << "unknown input message:" << hex << wMsg;
00253             break;
00254         }
00255     }
00256 
00257     WinMIDIInput::WinMIDIInput(QObject *parent) :
00258         MIDIInput(parent), d(new WinMIDIInputPrivate(this))
00259     { }
00260 
00261     WinMIDIInput::~WinMIDIInput()
00262     {
00263         delete d;
00264     }
00265 
00266     void WinMIDIInput::initialize(QSettings *settings)
00267     {
00268         Q_UNUSED(settings)
00269     }
00270 
00271     QString WinMIDIInput::backendName()
00272     {
00273         return QLatin1Literal("Windows MM");
00274     }
00275 
00276     QString WinMIDIInput::publicName()
00277     {
00278         return d->m_publicName;
00279     }
00280 
00281     void WinMIDIInput::setPublicName(QString name)
00282     {
00283         d->setPublicName(name);
00284     }
00285 
00286     QStringList WinMIDIInput::connections(bool advanced)
00287     {
00288         d->reloadDeviceList(advanced);
00289         return d->m_inputDevices.values();
00290     }
00291 
00292     void WinMIDIInput::setExcludedConnections(QStringList conns)
00293     {
00294         d->m_excludedNames = conns;
00295     }
00296 
00297     void WinMIDIInput::open(QString name)
00298     {
00299         d->open(name);
00300     }
00301 
00302     void WinMIDIInput::close()
00303     {
00304         d->close();
00305     }
00306 
00307     QString WinMIDIInput::currentConnection()
00308     {
00309         return d->m_currentInput;
00310     }
00311 
00312     void WinMIDIInput::setMIDIThruDevice(MIDIOutput *device)
00313     {
00314         d->m_out = device;
00315     }
00316 
00317     void WinMIDIInput::enableMIDIThru(bool enable)
00318     {
00319         d->m_thruEnabled = enable;
00320     }
00321 
00322     bool WinMIDIInput::isEnabledMIDIThru()
00323     {
00324         return d->m_thruEnabled && d->m_out != 0;
00325     }
00326 
00327 }} // namespace drumstick::rt