drumstick  1.0.0
winmidiinput.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 <QString>
21 #include <QMap>
22 #include <QDebug>
23 #include <windows.h>
24 #include <mmsystem.h>
25 
26 #include "winmidiinput.h"
27 
28 namespace drumstick {
29 namespace rt {
30 
31  static QLatin1Literal DEFAULT_PUBLIC_NAME = QLatin1Literal("MIDI In");
32 
33  void CALLBACK midiCallback( HMIDIIN hMidiIn,
34  UINT wMsg,
35  DWORD_PTR dwInstance,
36  DWORD_PTR dwParam1,
37  DWORD_PTR dwParam2 );
38 
39 
40  class WinMIDIInput::WinMIDIInputPrivate {
41  public:
42  WinMIDIInput *m_inp;
43  MIDIOutput *m_out;
44  bool m_thruEnabled;
45  bool m_clientFilter;
46  HMIDIIN m_handle;
47  QString m_publicName;
48  QString m_currentInput;
49  QStringList m_excludedNames;
50  QMap<int,QString> m_inputDevices;
51 
52  WinMIDIInputPrivate(WinMIDIInput *inp):
53  m_inp(inp),
54  m_out(0),
55  m_thruEnabled(false),
56  m_clientFilter(true),
57  m_handle(0),
58  m_publicName(DEFAULT_PUBLIC_NAME)
59  {
60  reloadDeviceList(true);
61  }
62 
63  int deviceIndex( const QString& newDevice )
64  {
65  int index = -1;
66  QMap<int,QString>::ConstIterator it;
67  for( it = m_inputDevices.constBegin();
68  it != m_inputDevices.constEnd(); ++it ) {
69  if (it.value() == newDevice) {
70  index = it.key();
71  break;
72  }
73  }
74  return index;
75  }
76 
77  void open(QString name) {
78  MMRESULT res;
79  if (name != m_currentInput) {
80  if (m_handle != 0)
81  close();
82  reloadDeviceList(!m_clientFilter);
83  int dev = deviceIndex(name);
84  if (dev > -1) {
85  res = midiInOpen(&m_handle, dev, (DWORD_PTR) midiCallback, (DWORD_PTR) this, CALLBACK_FUNCTION | MIDI_IO_STATUS );
86  if (res != MMSYSERR_NOERROR)
87  qDebug() << "midiInOpen() err:" << mmErrorString(res);
88  m_currentInput = name;
89  }
90  }
91  }
92 
93  void close() {
94  MMRESULT res;
95  if (m_handle != 0) {
96  res = midiInReset( m_handle );
97  if (res != MMSYSERR_NOERROR)
98  qDebug() << "midiInReset() err:" << mmErrorString(res);
99  res = midiInClose( m_handle );
100  if (res != MMSYSERR_NOERROR)
101  qDebug() << "midiInClose() err:" << mmErrorString(res);
102  m_handle = 0;
103  }
104  m_currentInput.clear();
105  }
106 
107  void reloadDeviceList(bool advanced)
108  {
109  MMRESULT res;
110  MIDIINCAPS deviceCaps;
111  QString devName;
112  unsigned int dev, max = midiInGetNumDevs();
113  m_inputDevices.clear();
114  m_clientFilter = !advanced;
115 
116  for ( dev = 0; dev < max; ++dev) {
117  bool excluded = false;
118  res = midiInGetDevCaps( dev, &deviceCaps, sizeof(MIDIINCAPS));
119  if (res != MMSYSERR_NOERROR)
120  break;
121 #if defined(UNICODE)
122  devName = QString::fromWCharArray(deviceCaps.szPname);
123 #else
124  devName = QString::fromLocal8Bit(deviceCaps.szPname);
125 #endif
126  foreach(const QString& n, m_excludedNames) {
127  if (devName.startsWith(n)) {
128  excluded = true;
129  break;
130  }
131  }
132  if (!excluded)
133  m_inputDevices[dev] = devName;
134  }
135  }
136 
137  void setPublicName(QString name)
138  {
139  if (m_publicName != name) {
140  m_publicName = name;
141  }
142  }
143 
144  void emitSignals(int status, int channel, int data1, int data2)
145  {
146  switch (status) {
147  case MIDI_STATUS_NOTEOFF:
148  if(m_out != 0 && m_thruEnabled)
149  m_out->sendNoteOff(channel, data1, data2);
150  emit m_inp->midiNoteOff(channel, data1, data2);
151  break;
152  case MIDI_STATUS_NOTEON:
153  if(m_out != 0 && m_thruEnabled)
154  m_out->sendNoteOn(channel, data1, data2);
155  emit m_inp->midiNoteOn(channel, data1, data2);
156  break;
157  case MIDI_STATUS_KEYPRESURE:
158  if(m_out != 0 && m_thruEnabled)
159  m_out->sendKeyPressure(channel, data1, data2);
160  emit m_inp->midiKeyPressure(channel, data1, data2);
161  break;
162  case MIDI_STATUS_CONTROLCHANGE:
163  if(m_out != 0 && m_thruEnabled)
164  m_out->sendController(channel, data1, data2);
165  emit m_inp->midiController(channel, data1, data2);
166  break;
167  case MIDI_STATUS_PROGRAMCHANGE:
168  if(m_out != 0 && m_thruEnabled)
169  m_out->sendProgram(channel, data1);
170  emit m_inp->midiProgram(channel, data1);
171  break;
172  case MIDI_STATUS_CHANNELPRESSURE:
173  if(m_out != 0 && m_thruEnabled)
174  m_out->sendChannelPressure(channel, data1);
175  emit m_inp->midiChannelPressure(channel, data1);
176  break;
177  case MIDI_STATUS_PITCHBEND: {
178  int value = (data1 + data2 * 0x80) - 8192;
179  if(m_out != 0 && m_thruEnabled)
180  m_out->sendPitchBend(channel, value);
181  emit m_inp->midiPitchBend(channel, value);
182  }
183  break;
184  default:
185  qDebug() << "status?" << status;
186  }
187  }
188 
189  void emitSysex(QByteArray data)
190  {
191  if(m_out != 0 && m_thruEnabled)
192  m_out->sendSysex(data);
193  emit m_inp->midiSysex(data);
194  }
195 
196  QString mmErrorString(MMRESULT err)
197  {
198  QString errstr;
199  #ifdef UNICODE
200  WCHAR buffer[1024];
201  midiInGetErrorText(err, &buffer[0], sizeof(buffer));
202  errstr = QString::fromUtf16((const ushort*)buffer);
203  #else
204  char buffer[1024];
205  midiOutGetErrorText(err, &buffer[0], sizeof(buffer));
206  errstr = QString::fromLocal8Bit(buffer);
207  #endif
208  return errstr;
209  }
210 
211  };
212 
213  void CALLBACK midiCallback( HMIDIIN hMidiIn,
214  UINT wMsg,
215  DWORD_PTR dwInstance,
216  DWORD_PTR dwParam1,
217  DWORD_PTR dwParam2 )
218  {
219  Q_UNUSED(hMidiIn)
220  Q_UNUSED(dwParam2)
221  WinMIDIInput::WinMIDIInputPrivate* object = (WinMIDIInput::WinMIDIInputPrivate*) dwInstance;
222  switch( wMsg ) {
223  case MIM_OPEN:
224  qDebug() << "Open";
225  break;
226  case MIM_CLOSE:
227  qDebug() << "Close";
228  break;
229  case MIM_ERROR:
230  case MIM_LONGERROR:
231  qDebug() << "Errors";
232  break;
233  case MIM_LONGDATA:
234  qDebug() << "Sysex data";
235  break;
236  case MIM_DATA:
237  case MIM_MOREDATA: {
238  int status = dwParam1 & 0xf0;
239  int channel = dwParam1 & 0x0f;
240  int data1 = (dwParam1 & 0x7f00) >> 8;
241  int data2 = (dwParam1 & 0x7f0000) >> 16;
242  object->emitSignals(status, channel, data1, data2);
243  }
244  break;
245  default:
246  qDebug() << "unknown:" << hex << wMsg;
247  break;
248  }
249  }
250 
251  WinMIDIInput::WinMIDIInput(QObject *parent) :
252  MIDIInput(parent), d(new WinMIDIInputPrivate(this))
253  { }
254 
255  WinMIDIInput::~WinMIDIInput()
256  {
257  delete d;
258  }
259 
260  void WinMIDIInput::initialize(QSettings *settings)
261  {
262  Q_UNUSED(settings)
263  }
264 
265  QString WinMIDIInput::backendName()
266  {
267  return QLatin1Literal("Windows MM");
268  }
269 
270  QString WinMIDIInput::publicName()
271  {
272  return d->m_publicName;
273  }
274 
275  void WinMIDIInput::setPublicName(QString name)
276  {
277  d->setPublicName(name);
278  }
279 
280  QStringList WinMIDIInput::connections(bool advanced)
281  {
282  d->reloadDeviceList(advanced);
283  return d->m_inputDevices.values();
284  }
285 
286  void WinMIDIInput::setExcludedConnections(QStringList conns)
287  {
288  d->m_excludedNames = conns;
289  }
290 
291  void WinMIDIInput::open(QString name)
292  {
293  d->open(name);
294  }
295 
296  void WinMIDIInput::close()
297  {
298  d->close();
299  }
300 
301  QString WinMIDIInput::currentConnection()
302  {
303  return d->m_currentInput;
304  }
305 
306  void WinMIDIInput::setMIDIThruDevice(MIDIOutput *device)
307  {
308  d->m_out = device;
309  }
310 
311  void WinMIDIInput::enableMIDIThru(bool enable)
312  {
313  d->m_thruEnabled = enable;
314  }
315 
316  bool WinMIDIInput::isEnabledMIDIThru()
317  {
318  return d->m_thruEnabled && d->m_out != 0;
319  }
320 
321 }} // namespace drumstick::rt
The QObject class is the base class of all Qt objects.