drumstick  1.0.1
alsamidioutput.cpp
1 /*
2  Drumstick RT Backend using the ALSA Sequencer
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 <QStringList>
22 #include <QMutex>
23 #include <QMutexLocker>
24 #include <qmath.h>
25 #include "alsaclient.h"
26 #include "alsaport.h"
27 #include "alsaevent.h"
28 #include "alsamidioutput.h"
29 
30 namespace drumstick {
31 namespace rt {
32 
33  static QString DEFAULT_PUBLIC_NAME(QLatin1String("MIDI Out"));
34 
35  class ALSAMIDIOutput::ALSAMIDIOutputPrivate {
36  public:
37  ALSAMIDIOutput *m_out;
38  MidiClient *m_client;
39  MidiPort *m_port;
40  int m_portId;
41  bool m_clientFilter;
42  int m_runtimeAlsaNum;
43  QString m_publicName;
44  QString m_currentOutput;
45  QStringList m_outputDevices;
46  QStringList m_excludedNames;
47  QMutex m_outMutex;
48 
49  ALSAMIDIOutputPrivate(ALSAMIDIOutput *q):
50  m_out(q),
51  m_client(0),
52  m_port(0),
53  m_portId(0),
54  m_clientFilter(true),
55  m_runtimeAlsaNum(0),
56  m_publicName(DEFAULT_PUBLIC_NAME)
57  {
58  m_runtimeAlsaNum = getRuntimeALSALibraryNumber();
59  m_client = new MidiClient(m_out);
60  m_client->open();
61  m_client->setClientName(m_publicName);
62  m_port = m_client->createPort();
63  m_port->setPortName("out");
64  m_port->setCapability( SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ );
65  m_port->setPortType( SND_SEQ_PORT_TYPE_APPLICATION | SND_SEQ_PORT_TYPE_MIDI_GENERIC );
66  m_portId = m_port->getPortId();
67  }
68 
69  ~ALSAMIDIOutputPrivate()
70  {
71  if (m_client != NULL) {
72  clearSubscription();
73  if (m_port != NULL)
74  m_port->detach();
75  m_client->close();
76  delete m_client;
77  }
78  }
79 
80  bool clientIsAdvanced(int clientId)
81  {
82  // asking for runtime version instead of SND_LIB_VERSION
83  if (m_runtimeAlsaNum < 0x01000B)
84  // ALSA <= 1.0.10
85  return (clientId < 64);
86  else
87  // ALSA >= 1.0.11
88  return (clientId < 16);
89  }
90 
91  void reloadDeviceList(bool advanced)
92  {
93  m_clientFilter = !advanced;
94  m_outputDevices.clear();
95  QListIterator<PortInfo> it(m_client->getAvailableOutputs());
96  while(it.hasNext()) {
97  bool excluded = false;
98  PortInfo p = it.next();
99  QString name = QString("%1:%2").arg(p.getClientName()).arg(p.getPort());
100  if (m_clientFilter && clientIsAdvanced(p.getClient()))
101  continue;
102  if ( m_clientFilter && name.startsWith(QLatin1String("Virtual Raw MIDI")) )
103  continue;
104  if ( name.startsWith(m_publicName) )
105  continue;
106  foreach(const QString& n, m_excludedNames) {
107  if ( name.startsWith(n) ) {
108  excluded = true;
109  break;
110  }
111  }
112  if (!excluded)
113  m_outputDevices << name;
114  }
115  if (!m_currentOutput.isEmpty() &&
116  !m_outputDevices.contains(m_currentOutput)) {
117  m_currentOutput.clear();
118  }
119  }
120 
121  bool setSubscription(const QString &newOutputDevice)
122  {
123  //qDebug() << Q_FUNC_INFO << newOutputDevice;
124  if (m_outputDevices.contains(newOutputDevice)) {
125  m_currentOutput = newOutputDevice;
126  m_port->unsubscribeAll();
127  m_port->subscribeTo(newOutputDevice);
128  return true;
129  }
130  return false;
131  }
132 
133  void clearSubscription()
134  {
135  if (!m_currentOutput.isEmpty()) {
136  m_port->unsubscribeAll();
137  m_currentOutput.clear();
138  }
139  }
140 
141  void sendEvent(SequencerEvent *ev)
142  {
143  QMutexLocker locker(&m_outMutex);
144  ev->setSource(m_portId);
145  ev->setSubscribers();
146  ev->setDirect();
147  m_client->outputDirect(ev);
148  }
149 
150  void setPublicName(QString newName)
151  {
152  if (newName != m_publicName) {
153  m_client->setClientName(newName);
154  m_publicName = newName;
155  }
156  }
157 
158  };
159 
160  ALSAMIDIOutput::ALSAMIDIOutput(QObject *parent) : MIDIOutput(parent),
161  d(new ALSAMIDIOutputPrivate(this))
162  { }
163 
164  ALSAMIDIOutput::~ALSAMIDIOutput()
165  {
166  delete d;
167  }
168 
169  void ALSAMIDIOutput::initialize(QSettings* settings)
170  {
171  Q_UNUSED(settings)
172  }
173 
174  /* SLOTS */
175 
176  void ALSAMIDIOutput::sendNoteOn(int chan, int note, int vel)
177  {
178  NoteOnEvent ev(chan, note, vel);
179  d->sendEvent(&ev);
180  }
181 
182  void ALSAMIDIOutput::sendNoteOff(int chan, int note, int vel)
183  {
184  NoteOffEvent ev(chan, note, vel);
185  d->sendEvent(&ev);
186  }
187 
188  void ALSAMIDIOutput::sendController(int chan, int control, int value)
189  {
190  ControllerEvent ev(chan, control, value);
191  d->sendEvent(&ev);
192  }
193 
194  void ALSAMIDIOutput::sendKeyPressure(int chan, int note, int value)
195  {
196  KeyPressEvent ev(chan, note, value);
197  d->sendEvent(&ev);
198  }
199 
200  void ALSAMIDIOutput::sendProgram(int chan, int program)
201  {
202  ProgramChangeEvent ev(chan, program);
203  d->sendEvent(&ev);
204  }
205 
206  void ALSAMIDIOutput::sendChannelPressure(int chan, int value)
207  {
208  ChanPressEvent ev(chan, value);
209  d->sendEvent(&ev);
210  }
211 
212  void ALSAMIDIOutput::sendPitchBend(int chan, int value)
213  {
214  PitchBendEvent ev(chan, value);
215  d->sendEvent(&ev);
216  }
217 
218  void ALSAMIDIOutput::sendSysex(const QByteArray& data)
219  {
220  SysExEvent ev(data);
221  d->sendEvent(&ev);
222  }
223 
224  void ALSAMIDIOutput::sendSystemMsg(const int status)
225  {
226  SystemEvent ev(status);
227  d->sendEvent(&ev);
228  }
229 
230  QString ALSAMIDIOutput::backendName()
231  {
232  return "ALSA";
233  }
234 
235  QString ALSAMIDIOutput::publicName()
236  {
237  return d->m_publicName;
238  }
239 
240  void ALSAMIDIOutput::setPublicName(QString name)
241  {
242  d->setPublicName(name);
243  }
244 
245  QStringList ALSAMIDIOutput::connections(bool advanced)
246  {
247  d->reloadDeviceList(advanced);
248  return d->m_outputDevices;
249  }
250 
251  void ALSAMIDIOutput::setExcludedConnections(QStringList conns)
252  {
253  d->m_excludedNames = conns;
254  }
255 
256  QString ALSAMIDIOutput::currentConnection()
257  {
258  return d->m_currentOutput;
259  }
260 
261  void ALSAMIDIOutput::open(QString name)
262  {
263  d->setSubscription(name);
264  }
265 
266  void ALSAMIDIOutput::close()
267  {
268  d->clearSubscription();
269  }
270 
271 }}
272 
Classes managing ALSA Sequencer clients.
The QObject class is the base class of all Qt objects.
Classes managing ALSA Sequencer ports.
Classes managing ALSA Sequencer events.