drumstick  1.0.1
macmidioutput.cpp
1 /*
2  Drumstick RT 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 "macmidioutput.h"
21 #include "maccommon.h"
22 
23 #include <QObject>
24 #include <QDebug>
25 #include <QString>
26 #include <QStringList>
27 #include <QByteArray>
28 #include <QVarLengthArray>
29 
30 #include <CoreFoundation/CoreFoundation.h>
31 #include <CoreMIDI/MIDIServices.h>
32 
33 namespace drumstick {
34 namespace rt {
35 
36  static CFStringRef DEFAULT_PUBLIC_NAME CFSTR("MIDI Out");
37 
38  class MacMIDIOutput::MacMIDIOutputPrivate {
39  public:
40  MIDIClientRef m_client;
41  MIDIPortRef m_port;
42  MIDIEndpointRef m_endpoint;
43  MIDIEndpointRef m_destination;
44  bool m_clientFilter;
45 
46  QString m_currentOutput;
47  QString m_publicName;
48  QStringList m_excludedNames;
49  QStringList m_outputDevices;
50 
51  MacMIDIOutputPrivate():
52  m_client(0),
53  m_port(0),
54  m_endpoint(0),
55  m_destination(0),
56  m_clientFilter(true),
57  m_publicName(QString::fromCFString(DEFAULT_PUBLIC_NAME))
58  {
59  internalCreate(DEFAULT_PUBLIC_NAME);
60  }
61 
62  void internalCreate(CFStringRef name)
63  {
64  OSStatus result = noErr;
65  result = MIDIClientCreate( name, NULL, NULL, &m_client );
66  if ( result != noErr ) {
67  qDebug() << "MIDIClientCreate() error:" << result;
68  return;
69  }
70  result = MIDISourceCreate( m_client, name, &m_endpoint );
71  if ( result != noErr ) {
72  qDebug() << "MIDISourceCreate() error:" << result;
73  return;
74  }
75  result = MIDIOutputPortCreate( m_client, name, &m_port );
76  if (result != noErr) {
77  qDebug() << "MIDIOutputPortCreate() error:" << result;
78  return;
79  }
80  reloadDeviceList(true);
81  }
82 
83  virtual ~MacMIDIOutputPrivate()
84  {
85  internalDispose();
86  }
87 
88  void internalDispose()
89  {
90  OSStatus result = noErr;
91  if (m_port != 0) {
92  result = MIDIPortDispose( m_port );
93  if (result != noErr) {
94  qDebug() << "MIDIPortDispose() error:" << result;
95  m_port = 0;
96  }
97  }
98  if (m_endpoint != 0) {
99  result = MIDIEndpointDispose( m_endpoint );
100  if (result != noErr) {
101  qDebug() << "MIDIEndpointDispose() err:" << result;
102  m_endpoint = 0;
103  }
104  }
105  if (m_client != 0) {
106  result = MIDIClientDispose( m_client );
107  if (result != noErr) {
108  qDebug() << "MIDIClientDispose() error:" << result;
109  m_client = 0;
110  }
111  }
112  }
113 
114  void setPublicName(QString name)
115  {
116  if (m_publicName != name) {
117  internalDispose();
118  internalCreate(name.toCFString());
119  m_publicName = name;
120  }
121  }
122 
123  void reloadDeviceList(bool advanced)
124  {
125  int num = MIDIGetNumberOfDestinations();
126  m_clientFilter = !advanced;
127  m_outputDevices.clear();
128  for (int i = 0; i < num; ++i) {
129  bool excluded = false;
130  MIDIEndpointRef dest = MIDIGetDestination( i );
131  if (dest != 0) {
132  QString name = getEndpointName(dest);
133  if ( m_clientFilter &&
134  name.contains(QLatin1String("IAC"), Qt::CaseSensitive) )
135  continue;
136  if ( name.contains (m_publicName) )
137  continue;
138  foreach ( const QString& n, m_excludedNames ) {
139  if ( name.contains(n) ) {
140  excluded = true;
141  break;
142  }
143  }
144  if (!excluded)
145  m_outputDevices << name;
146  }
147  }
148  if (!m_currentOutput.isEmpty() && m_destination != 0 &&
149  !m_outputDevices.contains(m_currentOutput)) {
150  m_currentOutput.clear();
151  m_destination = 0;
152  }
153  }
154 
155  void sendEvents( const MIDIPacketList* events )
156  {
157  MIDIReceived(m_endpoint, events);
158  if (m_destination != 0)
159  MIDISend(m_port, m_destination, events);
160  }
161 
162  bool open(const QString &name)
163  {
164  int index;
165  QStringList allOutputDevices;
166  int num = MIDIGetNumberOfDestinations();
167  for (int i = 0; i < num; ++i) {
168  MIDIEndpointRef dest = MIDIGetDestination( i );
169  if (dest != 0)
170  allOutputDevices << getEndpointName( dest );
171  }
172  index = allOutputDevices.indexOf(name);
173  if (index < 0)
174  return false;
175  m_destination = MIDIGetDestination( index );
176  m_currentOutput = name;
177  return true;
178  }
179 
180  void close()
181  {
182  m_destination = 0;
183  m_currentOutput.clear();
184  }
185  };
186 
187  MacMIDIOutput::MacMIDIOutput(QObject *parent) :
188  MIDIOutput(parent), d(new MacMIDIOutputPrivate)
189  {
190  }
191 
192  MacMIDIOutput::~MacMIDIOutput()
193  {
194  delete d;
195  }
196 
197  void MacMIDIOutput::initialize(QSettings *settings)
198  {
199  Q_UNUSED(settings)
200  }
201 
202  QString MacMIDIOutput::backendName()
203  {
204  return QLatin1Literal("CoreMIDI");
205  }
206 
207  QString MacMIDIOutput::publicName()
208  {
209  return d->m_publicName;
210  }
211 
212  void MacMIDIOutput::setPublicName(QString name)
213  {
214  d->setPublicName(name);
215  }
216 
217  QStringList MacMIDIOutput::connections(bool advanced)
218  {
219  d->reloadDeviceList(advanced);
220  return d->m_outputDevices;
221  }
222 
223  void MacMIDIOutput::setExcludedConnections(QStringList conns)
224  {
225  d->m_excludedNames = conns;
226  }
227 
228  void MacMIDIOutput::open(QString name)
229  {
230  d->open(name);
231  }
232 
233  void MacMIDIOutput::close()
234  {
235  d->close();
236  }
237 
238  QString MacMIDIOutput::currentConnection()
239  {
240  return d->m_currentOutput;
241  }
242 
243  /* Realtime MIDI slots */
244 
245  /*void MacMIDIOutput::allNotesOff()
246  {
247  quint8 data[3];
248  quint8 buf[2048];
249  MIDIPacketList* pktlist = (MIDIPacketList*) &buf;
250  MIDIPacket* packet = MIDIPacketListInit(pktlist);
251  for(int chan = 0; chan < MIDI_CHANNELS && packet != NULL; ++chan) {
252  data[0] = MIDI_STATUS_CONTROLCHANGE | (chan & 0x0f);
253  data[1] = MIDI_CTL_ALL_NOTES_OFF;
254  data[2] = 0;
255  packet = MIDIPacketListAdd(pktlist, sizeof(buf), packet, 0,
256  sizeof(data), data);
257  if (packet != NULL) {
258  data[1] = MIDI_CTL_ALL_SOUNDS_OFF;
259  packet = MIDIPacketListAdd(pktlist, sizeof(buf), packet, 0,
260  sizeof(data), data);
261  }
262  }
263  if (packet != NULL)
264  d->sendEvents(pktlist);
265  }*/
266 
267  /*void MacMIDIOutput::resetControllers()
268  {
269  quint8 data[3];
270  quint8 buf[2048];
271  MIDIPacketList* pktlist = (MIDIPacketList*) &buf;
272  MIDIPacket* packet = MIDIPacketListInit(pktlist);
273  for(int chan = 0; chan < MIDI_CHANNELS && packet != NULL; ++chan) {
274  data[0] = MIDI_STATUS_CONTROLCHANGE | (chan & 0x0f);
275  data[1] = MIDI_CTL_RESET_CONTROLLERS;
276  data[2] = 0;
277  packet = MIDIPacketListAdd(pktlist, sizeof(buf), packet, 0,
278  sizeof(data), data);
279  if (packet != NULL) {
280  data[1] = MIDI_CTL_MSB_MAIN_VOLUME;
281  data[2] = 100;
282  packet = MIDIPacketListAdd(pktlist, sizeof(buf), packet, 0,
283  sizeof(data), data);
284  }
285  }
286  if (packet != NULL)
287  d->sendEvents(pktlist);
288  }*/
289 
290  void MacMIDIOutput::sendNoteOn(int chan, int note, int vel)
291  {
292  quint8 data[3];
293  MIDIPacketList pktlist ;
294  MIDIPacket* packet = MIDIPacketListInit(&pktlist);
295  data[0] = MIDI_STATUS_NOTEON | (chan & 0x0f);
296  data[1] = note;
297  data[2] = vel;
298  packet = MIDIPacketListAdd(&pktlist, sizeof(pktlist), packet, 0,
299  sizeof(data), data);
300  if (packet != NULL)
301  d->sendEvents(&pktlist);
302  }
303 
304  void MacMIDIOutput::sendNoteOff(int chan, int note, int vel)
305  {
306  quint8 data[3];
307  MIDIPacketList pktlist ;
308  MIDIPacket* packet = MIDIPacketListInit(&pktlist);
309  data[0] = MIDI_STATUS_NOTEOFF | (chan & 0x0f);
310  data[1] = note;
311  data[2] = vel;
312  packet = MIDIPacketListAdd(&pktlist, sizeof(pktlist), packet, 0,
313  sizeof(data), data);
314  if (packet != NULL)
315  d->sendEvents(&pktlist);
316  }
317 
318  void MacMIDIOutput::sendController(int chan, int control, int value)
319  {
320  quint8 data[3];
321  MIDIPacketList pktlist ;
322  MIDIPacket* packet = MIDIPacketListInit(&pktlist);
323  data[0] = MIDI_STATUS_CONTROLCHANGE | (chan & 0x0f);
324  data[1] = control;
325  data[2] = value;
326  packet = MIDIPacketListAdd(&pktlist, sizeof(pktlist), packet, 0,
327  sizeof(data), data);
328  if (packet != NULL)
329  d->sendEvents(&pktlist);
330  }
331 
332  void MacMIDIOutput::sendKeyPressure(int chan, int note, int value)
333  {
334  quint8 data[3];
335  MIDIPacketList pktlist ;
336  MIDIPacket* packet = MIDIPacketListInit(&pktlist);
337  data[0] = MIDI_STATUS_KEYPRESURE | (chan & 0x0f);
338  data[1] = note;
339  data[2] = value;
340  packet = MIDIPacketListAdd(&pktlist, sizeof(pktlist), packet, 0,
341  sizeof(data), data);
342  if (packet != NULL)
343  d->sendEvents(&pktlist);
344  }
345 
346  void MacMIDIOutput::sendProgram(int chan, int program)
347  {
348  quint8 data[2];
349  MIDIPacketList pktlist ;
350  MIDIPacket* packet = MIDIPacketListInit(&pktlist);
351  data[0] = MIDI_STATUS_PROGRAMCHANGE | (chan & 0x0f);
352  data[1] = program;
353  packet = MIDIPacketListAdd(&pktlist, sizeof(pktlist), packet, 0,
354  sizeof(data), data);
355  if (packet != NULL)
356  d->sendEvents(&pktlist);
357  }
358 
359  void MacMIDIOutput::sendChannelPressure(int chan, int value)
360  {
361  quint8 data[2];
362  MIDIPacketList pktlist ;
363  MIDIPacket* packet = MIDIPacketListInit(&pktlist);
364  data[0] = MIDI_STATUS_CHANNELPRESSURE | (chan & 0x0f);
365  data[1] = value;
366  packet = MIDIPacketListAdd(&pktlist, sizeof(pktlist), packet, 0,
367  sizeof(data), data);
368  if (packet != NULL)
369  d->sendEvents(&pktlist);
370  }
371 
372  void MacMIDIOutput::sendPitchBend(int chan, int value)
373  {
374  quint16 val = value + 8192; // value between -8192 and +8191
375  quint8 data[3];
376  MIDIPacketList pktlist ;
377  MIDIPacket* packet = MIDIPacketListInit(&pktlist);
378  data[0] = MIDI_STATUS_PITCHBEND | (chan & 0x0f);
379  data[1] = MIDI_LSB(val); // LSB
380  data[2] = MIDI_MSB(val); // MSB
381  packet = MIDIPacketListAdd(&pktlist, sizeof(pktlist), packet, 0,
382  sizeof(data), data);
383  if (packet != NULL)
384  d->sendEvents(&pktlist);
385  }
386 
387  void MacMIDIOutput::sendSysex(const QByteArray& data)
388  {
389  quint8 buf[4096];
390  if (data.size() > 4096)
391  return;
392  MIDIPacketList* pktlist = (MIDIPacketList*) &buf;
393  MIDIPacket* packet = MIDIPacketListInit(pktlist);
394  packet = MIDIPacketListAdd(pktlist, sizeof(buf), packet, 0,
395  data.size(), (const Byte*)data.data());
396  if (packet != NULL)
397  d->sendEvents(pktlist);
398  }
399 
400  void MacMIDIOutput::sendSystemMsg(const int status)
401  {
402  quint8 data;
403  MIDIPacketList pktlist;
404  MIDIPacket* packet = MIDIPacketListInit(&pktlist);
405  data = status;
406  packet = MIDIPacketListAdd(&pktlist, sizeof(pktlist), packet, 0,
407  sizeof(data), &data);
408  if (packet != NULL)
409  d->sendEvents(&pktlist);
410  }
411 
412 }}
The QObject class is the base class of all Qt objects.