drumstick  1.0.0
alsamidiinput.cpp
1 /*
2  Drumstick RT Backend using the ALSA Sequencer
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 <QMutexLocker>
21 #include <cmath>
22 #include "alsaclient.h"
23 #include "alsaport.h"
24 #include "alsaevent.h"
25 #include "alsamidiinput.h"
26 #include "rtmidioutput.h"
27 
28 namespace drumstick {
29 namespace rt {
30 
31  static QString DEFAULT_PUBLIC_NAME(QLatin1String("MIDI In"));
32 
33  class ALSAMIDIInput::ALSAMIDIInputPrivate : public SequencerEventHandler
34  {
35  public:
36  ALSAMIDIInput *m_inp;
37  MIDIOutput *m_out;
38  MidiClient *m_client;
39  MidiPort *m_port;
40  int m_portId;
41  int m_clientId;
42  bool m_thruEnabled;
43  bool m_clientFilter;
44  int m_runtimeAlsaNum;
45  QString m_publicName;
46  QString m_currentInput;
47  QStringList m_inputDevices;
48  QStringList m_excludedNames;
49  QMutex m_openMutex;
50 
51  ALSAMIDIInputPrivate(ALSAMIDIInput *inp) :
52  m_inp(inp),
53  m_out(0),
54  m_client(0),
55  m_port(0),
56  m_portId(-1),
57  m_clientId(-1),
58  m_thruEnabled(false),
59  m_publicName(DEFAULT_PUBLIC_NAME)
60  {
61  m_runtimeAlsaNum = getRuntimeALSALibraryNumber();
62  m_client = new MidiClient(m_inp);
63  m_client->open();
64  m_client->setClientName(m_publicName);
65  m_port = m_client->createPort();
66  m_port->setPortName("in");
67  m_port->setCapability( SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE );
68  m_port->setPortType( SND_SEQ_PORT_TYPE_APPLICATION | SND_SEQ_PORT_TYPE_MIDI_GENERIC );
69  m_clientId = m_client->getClientId();
70  m_portId = m_port->getPortId();
71  m_port->setTimestamping(false);
72  m_port->setTimestampReal(false);
73  m_client->setHandler(this);
74  }
75 
76  virtual ~ALSAMIDIInputPrivate()
77  {
78  if (m_client != NULL) {
79  clearSubscription();
80  if (m_port != NULL)
81  m_port->detach();
82  m_client->close();
83  delete m_client;
84  }
85  }
86 
87  bool clientIsAdvanced(int clientId)
88  {
89  // asking for runtime version instead of SND_LIB_VERSION
90  if (m_runtimeAlsaNum < 0x01000B)
91  // ALSA <= 1.0.10
92  return (clientId < 64);
93  else
94  // ALSA >= 1.0.11
95  return (clientId < 16);
96  }
97 
98  void reloadDeviceList(bool advanced)
99  {
100  m_clientFilter = !advanced;
101  m_inputDevices.clear();
102  QListIterator<PortInfo> it(m_client->getAvailableInputs());
103  while(it.hasNext()) {
104  bool excluded = false;
105  PortInfo p = it.next();
106  QString name = QString("%1:%2").arg(p.getClientName()).arg(p.getPort());
107  if (m_clientFilter && clientIsAdvanced(p.getClient()))
108  continue;
109  if ( m_clientFilter && name.startsWith(QLatin1String("Virtual Raw MIDI")) )
110  continue;
111  if ( name.startsWith(m_publicName) )
112  continue;
113  foreach(const QString& n, m_excludedNames) {
114  if (name.startsWith(n)) {
115  excluded = true;
116  break;
117  }
118  }
119  if (!excluded)
120  m_inputDevices << name;
121  }
122  if (!m_currentInput.isEmpty() && !m_inputDevices.contains(m_currentInput)) {
123  m_currentInput.clear();
124  }
125  }
126 
127  bool setSubscription(const QString &newDevice)
128  {
129  //qDebug() << Q_FUNC_INFO << newDevice;
130  if (m_inputDevices.contains(newDevice)) {
131  m_currentInput = newDevice;
132  m_port->unsubscribeAll();
133  m_port->subscribeTo(newDevice);
134  m_client->startSequencerInput();
135  return true;
136  }
137  return false;
138  }
139 
140  void clearSubscription()
141  {
142  if (!m_currentInput.isEmpty()) {
143  m_client->stopSequencerInput();
144  m_port->unsubscribeAll();
145  m_currentInput.clear();
146  }
147  }
148 
149  void setPublicName(QString newName)
150  {
151  if (newName != m_publicName) {
152  m_client->setClientName(newName);
153  m_publicName = newName;
154  }
155  }
156 
157  void handleSequencerEvent(SequencerEvent* ev)
158  {
160  switch(ev->getSequencerType()) {
161  case SND_SEQ_EVENT_NOTEOFF: {
162  const NoteOffEvent* n = static_cast<const NoteOffEvent*>(ev);
163  if(m_out != 0 && m_thruEnabled) {
164  m_out->sendNoteOff(n->getChannel(), n->getKey(), n->getVelocity());
165  }
166  emit m_inp->midiNoteOff(n->getChannel(), n->getKey(), n->getVelocity());
167  }
168  break;
169  case SND_SEQ_EVENT_NOTEON: {
170  const NoteOnEvent* n = static_cast<const NoteOnEvent*>(ev);
171  if(m_out != 0 && m_thruEnabled) {
172  m_out->sendNoteOn(n->getChannel(), n->getKey(), n->getVelocity());
173  }
174  emit m_inp->midiNoteOn(n->getChannel(), n->getKey(), n->getVelocity());
175  }
176  break;
177  case SND_SEQ_EVENT_KEYPRESS: {
178  const KeyPressEvent* n = static_cast<const KeyPressEvent*>(ev);
179  if(m_out != 0 && m_thruEnabled) {
180  m_out->sendKeyPressure(n->getChannel(), n->getKey(), n->getVelocity());
181  }
182  emit m_inp->midiKeyPressure(n->getChannel(), n->getKey(), n->getVelocity());
183  }
184  break;
185  case SND_SEQ_EVENT_CONTROLLER:
186  case SND_SEQ_EVENT_CONTROL14: {
187  const ControllerEvent* n = static_cast<const ControllerEvent*>(ev);
188  if(m_out != 0 && m_thruEnabled) {
189  m_out->sendController(n->getChannel(), n->getParam(), n->getValue());
190  }
191  emit m_inp->midiController(n->getChannel(), n->getParam(), n->getValue());
192  }
193  break;
194  case SND_SEQ_EVENT_PGMCHANGE: {
195  const ProgramChangeEvent* p = static_cast<const ProgramChangeEvent*>(ev);
196  if(m_out != 0 && m_thruEnabled) {
197  m_out->sendProgram(p->getChannel(), p->getValue());
198  }
199  emit m_inp->midiProgram(p->getChannel(), p->getValue());
200  }
201  break;
202  case SND_SEQ_EVENT_CHANPRESS: {
203  const ChanPressEvent* n = static_cast<const ChanPressEvent*>(ev);
204  if(m_out != 0 && m_thruEnabled) {
205  m_out->sendChannelPressure(n->getChannel(), n->getValue());
206  }
207  emit m_inp->midiChannelPressure(n->getChannel(), n->getValue());
208  }
209  break;
210  case SND_SEQ_EVENT_PITCHBEND: {
211  const PitchBendEvent* n = static_cast<const PitchBendEvent*>(ev);
212  if(m_out != 0 && m_thruEnabled) {
213  m_out->sendPitchBend(n->getChannel(), n->getValue());
214  }
215  emit m_inp->midiPitchBend(n->getChannel(), n->getValue());
216  }
217  break;
218  case SND_SEQ_EVENT_SYSEX: {
219  const SysExEvent* n = static_cast<const SysExEvent*>(ev);
220  QByteArray data(n->getData(), n->getLength());
221  if(m_out != 0 && m_thruEnabled) {
222  m_out->sendSysex(data);
223  }
224  emit m_inp->midiSysex(data);
225  }
226  break;
227  case SND_SEQ_EVENT_SYSTEM: {
228  const SystemEvent* n = static_cast<const SystemEvent*>(ev);
229  int status = (int) n->getRaw8(0);
230  if(m_out != 0 && m_thruEnabled) {
231  m_out->sendSystemMsg(status);
232  }
233  if (status < 0xF7)
234  emit m_inp->midiSystemCommon(status);
235  else if (status > 0xF7)
236  emit m_inp->midiSystemRealtime(status);
237  }
238  break;
239  default:
240  break;
241  }
242  delete ev;
243  }
244  };
245 
246  ALSAMIDIInput::ALSAMIDIInput(QObject *parent) : MIDIInput(parent),
247  d(new ALSAMIDIInputPrivate(this))
248  { }
249 
250  ALSAMIDIInput::~ALSAMIDIInput()
251  {
252  delete d;
253  }
254 
255  void ALSAMIDIInput::initialize(QSettings* settings)
256  {
257  Q_UNUSED(settings)
258  }
259 
260  QString ALSAMIDIInput::backendName()
261  {
262  return QLatin1String("ALSA");
263  }
264 
265  QString ALSAMIDIInput::publicName()
266  {
267  return d->m_publicName;
268  }
269 
270  void ALSAMIDIInput::setPublicName(QString name)
271  {
272  d->setPublicName(name);
273  }
274 
275  QStringList ALSAMIDIInput::connections(bool advanced)
276  {
277  d->reloadDeviceList(advanced);
278  return d->m_inputDevices;
279  }
280 
281  void ALSAMIDIInput::setExcludedConnections(QStringList conns)
282  {
283  d->m_excludedNames = conns;
284  }
285 
286  void ALSAMIDIInput::open(QString name)
287  {
288  d->setSubscription(name);
289  }
290 
291  void ALSAMIDIInput::close()
292  {
293  d->clearSubscription();
294  }
295 
296  QString ALSAMIDIInput::currentConnection()
297  {
298  return d->m_currentInput;
299  }
300 
301  void ALSAMIDIInput::setMIDIThruDevice(MIDIOutput *device)
302  {
303  d->m_out = device;
304  }
305 
306  void ALSAMIDIInput::enableMIDIThru(bool enable)
307  {
308  d->m_thruEnabled = enable;
309  }
310 
311  bool ALSAMIDIInput::isEnabledMIDIThru()
312  {
313  return d->m_thruEnabled && (d->m_out != 0);
314  }
315 
316 }}
Classes managing ALSA Sequencer clients.
static bool isConnectionChange(const SequencerEvent *event)
Checks if the event's type is of type connection change.
Definition: alsaevent.cpp:184
The QObject class is the base class of all Qt objects.
Classes managing ALSA Sequencer ports.
Classes managing ALSA Sequencer events.