drumstick  1.0.1
winmidiinput.cpp
1 /*
2  Drumstick RT Windows Backend
3  Copyright (C) 2009-2015 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  res = midiInStart(m_handle);
89  if (res != MMSYSERR_NOERROR)
90  qDebug() << "midiInStart() err:" << mmErrorString(res);
91  m_currentInput = name;
92  }
93  }
94  }
95 
96  void close() {
97  MMRESULT res;
98  if (m_handle != 0) {
99  res = midiInStop(m_handle);
100  if (res != MMSYSERR_NOERROR)
101  qDebug() << "midiInStop() err:" << mmErrorString(res);
102  res = midiInReset( m_handle );
103  if (res != MMSYSERR_NOERROR)
104  qDebug() << "midiInReset() err:" << mmErrorString(res);
105  res = midiInClose( m_handle );
106  if (res != MMSYSERR_NOERROR)
107  qDebug() << "midiInClose() err:" << mmErrorString(res);
108  m_handle = 0;
109  }
110  m_currentInput.clear();
111  }
112 
113  void reloadDeviceList(bool advanced)
114  {
115  MMRESULT res;
116  MIDIINCAPS deviceCaps;
117  QString devName;
118  unsigned int dev, max = midiInGetNumDevs();
119  m_inputDevices.clear();
120  m_clientFilter = !advanced;
121 
122  for ( dev = 0; dev < max; ++dev) {
123  bool excluded = false;
124  res = midiInGetDevCaps( dev, &deviceCaps, sizeof(MIDIINCAPS));
125  if (res != MMSYSERR_NOERROR)
126  break;
127 #if defined(UNICODE)
128  devName = QString::fromWCharArray(deviceCaps.szPname);
129 #else
130  devName = QString::fromLocal8Bit(deviceCaps.szPname);
131 #endif
132  foreach(const QString& n, m_excludedNames) {
133  if (devName.startsWith(n)) {
134  excluded = true;
135  break;
136  }
137  }
138  if (!excluded)
139  m_inputDevices[dev] = devName;
140  }
141  }
142 
143  void setPublicName(QString name)
144  {
145  if (m_publicName != name) {
146  m_publicName = name;
147  }
148  }
149 
150  void emitSignals(int status, int channel, int data1, int data2)
151  {
152  switch (status) {
153  case MIDI_STATUS_NOTEOFF:
154  if(m_out != 0 && m_thruEnabled)
155  m_out->sendNoteOff(channel, data1, data2);
156  emit m_inp->midiNoteOff(channel, data1, data2);
157  break;
158  case MIDI_STATUS_NOTEON:
159  if(m_out != 0 && m_thruEnabled)
160  m_out->sendNoteOn(channel, data1, data2);
161  emit m_inp->midiNoteOn(channel, data1, data2);
162  break;
163  case MIDI_STATUS_KEYPRESURE:
164  if(m_out != 0 && m_thruEnabled)
165  m_out->sendKeyPressure(channel, data1, data2);
166  emit m_inp->midiKeyPressure(channel, data1, data2);
167  break;
168  case MIDI_STATUS_CONTROLCHANGE:
169  if(m_out != 0 && m_thruEnabled)
170  m_out->sendController(channel, data1, data2);
171  emit m_inp->midiController(channel, data1, data2);
172  break;
173  case MIDI_STATUS_PROGRAMCHANGE:
174  if(m_out != 0 && m_thruEnabled)
175  m_out->sendProgram(channel, data1);
176  emit m_inp->midiProgram(channel, data1);
177  break;
178  case MIDI_STATUS_CHANNELPRESSURE:
179  if(m_out != 0 && m_thruEnabled)
180  m_out->sendChannelPressure(channel, data1);
181  emit m_inp->midiChannelPressure(channel, data1);
182  break;
183  case MIDI_STATUS_PITCHBEND: {
184  int value = (data1 + data2 * 0x80) - 8192;
185  if(m_out != 0 && m_thruEnabled)
186  m_out->sendPitchBend(channel, value);
187  emit m_inp->midiPitchBend(channel, value);
188  }
189  break;
190  default:
191  qDebug() << "MIDI in status?" << status;
192  }
193  }
194 
195  void emitSysex(QByteArray data)
196  {
197  if(m_out != 0 && m_thruEnabled)
198  m_out->sendSysex(data);
199  emit m_inp->midiSysex(data);
200  }
201 
202  QString mmErrorString(MMRESULT err)
203  {
204  QString errstr;
205  #ifdef UNICODE
206  WCHAR buffer[1024];
207  midiInGetErrorText(err, &buffer[0], sizeof(buffer));
208  errstr = QString::fromUtf16((const ushort*)buffer);
209  #else
210  char buffer[1024];
211  midiOutGetErrorText(err, &buffer[0], sizeof(buffer));
212  errstr = QString::fromLocal8Bit(buffer);
213  #endif
214  return errstr;
215  }
216 
217  };
218 
219  void CALLBACK midiCallback( HMIDIIN hMidiIn,
220  UINT wMsg,
221  DWORD_PTR dwInstance,
222  DWORD_PTR dwParam1,
223  DWORD_PTR dwParam2 )
224  {
225  //Q_UNUSED(hMidiIn)
226  Q_UNUSED(dwParam2)
227  WinMIDIInput::WinMIDIInputPrivate* object = (WinMIDIInput::WinMIDIInputPrivate*) dwInstance;
228  switch( wMsg ) {
229  case MIM_OPEN:
230  qDebug() << "Open input" << hMidiIn;
231  break;
232  case MIM_CLOSE:
233  qDebug() << "Close input" << hMidiIn;
234  break;
235  case MIM_ERROR:
236  case MIM_LONGERROR:
237  qDebug() << "Errors input";
238  break;
239  case MIM_LONGDATA:
240  qDebug() << "Sysex data input";
241  break;
242  case MIM_DATA:
243  case MIM_MOREDATA: {
244  int status = dwParam1 & 0xf0;
245  int channel = dwParam1 & 0x0f;
246  int data1 = (dwParam1 & 0x7f00) >> 8;
247  int data2 = (dwParam1 & 0x7f0000) >> 16;
248  object->emitSignals(status, channel, data1, data2);
249  }
250  break;
251  default:
252  qDebug() << "unknown input message:" << hex << wMsg;
253  break;
254  }
255  }
256 
257  WinMIDIInput::WinMIDIInput(QObject *parent) :
258  MIDIInput(parent), d(new WinMIDIInputPrivate(this))
259  { }
260 
261  WinMIDIInput::~WinMIDIInput()
262  {
263  delete d;
264  }
265 
266  void WinMIDIInput::initialize(QSettings *settings)
267  {
268  Q_UNUSED(settings)
269  }
270 
271  QString WinMIDIInput::backendName()
272  {
273  return QLatin1Literal("Windows MM");
274  }
275 
276  QString WinMIDIInput::publicName()
277  {
278  return d->m_publicName;
279  }
280 
281  void WinMIDIInput::setPublicName(QString name)
282  {
283  d->setPublicName(name);
284  }
285 
286  QStringList WinMIDIInput::connections(bool advanced)
287  {
288  d->reloadDeviceList(advanced);
289  return d->m_inputDevices.values();
290  }
291 
292  void WinMIDIInput::setExcludedConnections(QStringList conns)
293  {
294  d->m_excludedNames = conns;
295  }
296 
297  void WinMIDIInput::open(QString name)
298  {
299  d->open(name);
300  }
301 
302  void WinMIDIInput::close()
303  {
304  d->close();
305  }
306 
307  QString WinMIDIInput::currentConnection()
308  {
309  return d->m_currentInput;
310  }
311 
312  void WinMIDIInput::setMIDIThruDevice(MIDIOutput *device)
313  {
314  d->m_out = device;
315  }
316 
317  void WinMIDIInput::enableMIDIThru(bool enable)
318  {
319  d->m_thruEnabled = enable;
320  }
321 
322  bool WinMIDIInput::isEnabledMIDIThru()
323  {
324  return d->m_thruEnabled && d->m_out != 0;
325  }
326 
327 }} // namespace drumstick::rt
The QObject class is the base class of all Qt objects.