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 <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 }}