drumstick  1.0.0
winmidioutput.cpp
1 /*
2  Drumstick RT Windows Backend
3  Copyright (C) 2009-2014 Pedro Lopez-Cabanillas <plcl@users.sf.net>
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9 
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  GNU General Public License for more details.
14 
15  You should have received a copy of the GNU General Public License along
16  with this program; if not, write to the Free Software Foundation, Inc.,
17  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 */
19 
20 #include <QDebug>
21 #include <QStringList>
22 #include <QByteArray>
23 #include <QVarLengthArray>
24 #include <qmath.h>
25 #include <windows.h>
26 #include <mmsystem.h>
27 #include "winmidioutput.h"
28 
29 namespace drumstick {
30 namespace rt {
31 
32  union WinMIDIPacket {
33  WinMIDIPacket() : dwPacket(0) {}
34  DWORD dwPacket;
35  quint8 data[sizeof(DWORD)];
36  };
37 
38  static QLatin1Literal DEFAULT_PUBLIC_NAME = QLatin1Literal("MIDI Out");
39 
40  void CALLBACK midiCallback( HMIDIOUT hmo,
41  UINT wMsg,
42  DWORD_PTR dwInstance,
43  DWORD_PTR dwParam1,
44  DWORD_PTR dwParam2);
45 
46  class WinMIDIOutput::WinMIDIOutputPrivate {
47  public:
48  HMIDIOUT m_handle;
49  bool m_clientFilter;
50  QString m_publicName;
51  QString m_currentOutput;
52  QMap<int,QString> m_outputDevices;
53  MIDIHDR m_midiSysexHdr;
54  QByteArray m_sysexBuffer;
55  QStringList m_excludedNames;
56 
57  WinMIDIOutputPrivate():
58  m_handle(0),
59  m_clientFilter(true),
60  m_publicName(DEFAULT_PUBLIC_NAME)
61  {
62  reloadDeviceList(true);
63  }
64 
65  void reloadDeviceList(bool advanced)
66  {
67  MMRESULT res;
68  MIDIOUTCAPS deviceCaps;
69  QString devName;
70  unsigned int dev, max = midiOutGetNumDevs();
71  m_outputDevices.clear();
72  m_clientFilter = !advanced;
73 
74  for ( dev = 0; dev < max; ++dev) {
75  res = midiOutGetDevCaps( dev, &deviceCaps, sizeof(MIDIOUTCAPS));
76  if (res != MMSYSERR_NOERROR)
77  break;
78  if (m_clientFilter && (deviceCaps.wTechnology == MOD_MAPPER))
79  continue;
80  #if defined(UNICODE)
81  devName = QString::fromWCharArray(deviceCaps.szPname);
82  #else
83  devName = QString::fromLocal8Bit(deviceCaps.szPname);
84  #endif
85  m_outputDevices[dev] = devName;
86  }
87  if (!m_clientFilter) {
88  dev = MIDI_MAPPER;
89  res = midiOutGetDevCaps( dev, &deviceCaps, sizeof(MIDIOUTCAPS));
90  if (res == MMSYSERR_NOERROR) {
91  #if defined(UNICODE)
92  devName = QString::fromWCharArray(deviceCaps.szPname);
93  #else
94  devName = QString::fromLocal8Bit(deviceCaps.szPname);
95  #endif
96  m_outputDevices[dev] = devName;
97  }
98  }
99  }
100 
101  void setPublicName(QString name)
102  {
103  if (m_publicName != name) {
104  m_publicName = name;
105  }
106  }
107 
108  int deviceIndex( const QString& newOutputDevice )
109  {
110  int index = -1;
111  QMap<int,QString>::ConstIterator it;
112  for( it = m_outputDevices.constBegin();
113  it != m_outputDevices.constEnd(); ++it ) {
114  if (it.value() == newOutputDevice) {
115  index = it.key();
116  break;
117  }
118  }
119  return index;
120  }
121 
122  void open(QString name)
123  {
124  MMRESULT res;
125  int dev = -1;
126 
127  if (m_handle != 0)
128  close();
129  reloadDeviceList(!m_clientFilter);
130  dev = deviceIndex(name);
131  if (dev > -1) {
132  res = midiOutOpen( &m_handle, dev, (DWORD_PTR) midiCallback, (DWORD_PTR) this, CALLBACK_FUNCTION);
133  if (res != MMSYSERR_NOERROR)
134  qDebug() << "midiStreamOpen() err:" << mmErrorString(res);
135  }
136  }
137 
138  void close()
139  {
140  MMRESULT res;
141  if (m_handle != 0) {
142  res = midiOutReset( m_handle );
143  if (res != MMSYSERR_NOERROR)
144  qDebug() << "midiOutReset() err:" << mmErrorString(res);
145  res = midiOutClose( m_handle );
146  if (res != MMSYSERR_NOERROR)
147  qDebug() << "midiStreamClose() err:" << mmErrorString(res);
148  m_handle = 0;
149  }
150  }
151 
152  void doneHeader( LPMIDIHDR lpMidiHdr )
153  {
154  MMRESULT res;
155  res = midiOutUnprepareHeader( m_handle, lpMidiHdr, sizeof(MIDIHDR) );
156  if (res != MMSYSERR_NOERROR)
157  qDebug() << "midiOutUnprepareHeader() err:" << mmErrorString(res);
158  if ((lpMidiHdr->dwFlags & MHDR_ISSTRM) == 0)
159  return; // sysex header?
160  }
161 
162  void sendShortMessage(WinMIDIPacket &packet)
163  {
164  MMRESULT res;
165  res = midiOutShortMsg( m_handle, packet.dwPacket );
166  if ( res != MMSYSERR_NOERROR )
167  qDebug() << "midiOutShortMsg() err:" << mmErrorString(res);
168  }
169 
170  void sendSysexEvent(const QByteArray& data)
171  {
172  MMRESULT res;
173  m_sysexBuffer = data;
174  m_midiSysexHdr.lpData = (LPSTR) m_sysexBuffer.data();
175  m_midiSysexHdr.dwBufferLength = m_sysexBuffer.size();
176  m_midiSysexHdr.dwBytesRecorded = m_sysexBuffer.size();
177  m_midiSysexHdr.dwFlags = 0;
178  m_midiSysexHdr.dwUser = 0;
179  res = midiOutPrepareHeader( m_handle, &m_midiSysexHdr, sizeof(MIDIHDR) );
180  if (res != MMSYSERR_NOERROR)
181  qDebug() << "midiOutPrepareHeader() err:" << mmErrorString(res);
182  else {
183  res = midiOutLongMsg( m_handle, &m_midiSysexHdr, sizeof(MIDIHDR) );
184  if (res != MMSYSERR_NOERROR)
185  qDebug() << "midiOutLongMsg() err:" << mmErrorString(res);
186  }
187  }
188 
189  QString mmErrorString(MMRESULT err)
190  {
191  QString errstr;
192  #ifdef UNICODE
193  WCHAR buffer[1024];
194  midiOutGetErrorText(err, &buffer[0], sizeof(buffer));
195  errstr = QString::fromUtf16((const ushort*)buffer);
196  #else
197  char buffer[1024];
198  midiOutGetErrorText(err, &buffer[0], sizeof(buffer));
199  errstr = QString::fromLocal8Bit(buffer);
200  #endif
201  return errstr;
202  }
203  };
204 
205 
206  void CALLBACK midiCallback( HMIDIOUT hmo,
207  UINT wMsg,
208  DWORD_PTR dwInstance,
209  DWORD_PTR dwParam1,
210  DWORD_PTR dwParam2)
211  {
212  Q_UNUSED(hmo)
213  Q_UNUSED(dwParam2)
214 
215  WinMIDIOutput::WinMIDIOutputPrivate* obj = (WinMIDIOutput::WinMIDIOutputPrivate*) dwInstance;
216  switch( wMsg ) {
217  case MOM_DONE:
218  obj->doneHeader( (LPMIDIHDR) dwParam1 );
219  break;
220  case MOM_OPEN:
221  qDebug() << "Open";
222  break;
223  case MOM_CLOSE:
224  qDebug() << "Close";
225  break;
226  default:
227  qDebug() << "unknown:" << hex << wMsg;
228  break;
229  }
230  }
231 
232  WinMIDIOutput::WinMIDIOutput(QObject *parent) :
233  MIDIOutput(parent), d(new WinMIDIOutputPrivate)
234  { }
235 
236  WinMIDIOutput::~WinMIDIOutput()
237  {
238  delete d;
239  }
240 
241  void WinMIDIOutput::initialize(QSettings *settings)
242  {
243  Q_UNUSED(settings)
244  }
245 
246  QString WinMIDIOutput::backendName()
247  {
248  return "Windows MM";
249  }
250 
251  QString WinMIDIOutput::publicName()
252  {
253  return d->m_publicName;
254  }
255 
256  void WinMIDIOutput::setPublicName(QString name)
257  {
258  d->setPublicName(name);
259  }
260 
261  QStringList WinMIDIOutput::connections(bool advanced)
262  {
263  d->reloadDeviceList(advanced);
264  return d->m_outputDevices.values();
265  }
266 
267  void WinMIDIOutput::setExcludedConnections(QStringList conns)
268  {
269  d->m_excludedNames = conns;
270  }
271 
272  void WinMIDIOutput::open(QString name)
273  {
274  d->open(name);
275  }
276 
277  void WinMIDIOutput::close()
278  {
279  d->close();
280  }
281 
282  QString WinMIDIOutput::currentConnection()
283  {
284  return d->m_currentOutput;
285  }
286 
287  void WinMIDIOutput::sendNoteOn(int chan, int note, int vel)
288  {
289  WinMIDIPacket packet;
290  packet.data[0] = MIDI_STATUS_NOTEON | (chan & MIDI_CHANNEL_MASK);
291  packet.data[1] = note;
292  packet.data[2] = vel;
293  d->sendShortMessage(packet);
294  }
295 
296  void WinMIDIOutput::sendNoteOff(int chan, int note, int vel)
297  {
298  WinMIDIPacket packet;
299  packet.data[0] = MIDI_STATUS_NOTEOFF | (chan & MIDI_CHANNEL_MASK);
300  packet.data[1] = note;
301  packet.data[2] = vel;
302  d->sendShortMessage(packet);
303  }
304 
305  void WinMIDIOutput::sendController(int chan, int control, int value)
306  {
307  WinMIDIPacket packet;
308  packet.data[0] = MIDI_STATUS_CONTROLCHANGE | (chan & MIDI_CHANNEL_MASK);
309  packet.data[1] = control;
310  packet.data[2] = value;
311  d->sendShortMessage(packet);
312  }
313 
314  void WinMIDIOutput::sendKeyPressure(int chan, int note, int value)
315  {
316  WinMIDIPacket packet;
317  packet.data[0] = MIDI_STATUS_KEYPRESURE | (chan & MIDI_CHANNEL_MASK);
318  packet.data[1] = note;
319  packet.data[2] = value;
320  d->sendShortMessage(packet);
321  }
322 
323  void WinMIDIOutput::sendProgram(int chan, int program)
324  {
325  WinMIDIPacket packet;
326  packet.data[0] = MIDI_STATUS_PROGRAMCHANGE | (chan & MIDI_CHANNEL_MASK);
327  packet.data[1] = program;
328  d->sendShortMessage(packet);
329  }
330 
331  void WinMIDIOutput::sendChannelPressure(int chan, int value)
332  {
333  WinMIDIPacket packet;
334  packet.data[0] = MIDI_STATUS_CHANNELPRESSURE | (chan & MIDI_CHANNEL_MASK);
335  packet.data[1] = value;
336  d->sendShortMessage(packet);
337  }
338 
339  void WinMIDIOutput::sendPitchBend(int chan, int value)
340  {
341  WinMIDIPacket packet;
342  packet.data[0] = MIDI_STATUS_PITCHBEND | (chan & MIDI_CHANNEL_MASK);
343  packet.data[1] = MIDI_LSB(value);
344  packet.data[2] = MIDI_MSB(value);
345  d->sendShortMessage(packet);
346  }
347 
348  void WinMIDIOutput::sendSystemMsg(const int status)
349  {
350  WinMIDIPacket packet;
351  packet.data[0] = status;
352  d->sendShortMessage(packet);
353  }
354 
355  void WinMIDIOutput::sendSysex(const QByteArray &data)
356  {
357  d->sendSysexEvent(data);
358  }
359 
360 }}
The QObject class is the base class of all Qt objects.