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