1 /*
2     MIDI Sequencer C++ library
3     Copyright (C) 2006-2021, Pedro Lopez-Cabanillas <plcl@users.sf.net>
4 
5     This library 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 3 of the License, or
8     (at your option) any later version.
9 
10     This library 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
16     along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18 
19 #include "errorcheck.h"
20 #include <QCoreApplication>
21 #include <QFile>
22 #include <QReadLocker>
23 #include <QRegularExpression>
24 #include <QThread>
25 #include <QWriteLocker>
26 #include <drumstick/alsaclient.h>
27 #include <drumstick/alsaevent.h>
28 #include <drumstick/alsaqueue.h>
29 
30 #if defined(RTKIT_SUPPORT)
31 #include <QDBusConnection>
32 #include <QDBusInterface>
33 #include <sys/resource.h>
34 #include <sys/syscall.h>
35 #include <sys/types.h>
36 #endif
37 #include <pthread.h>
38 
39 #ifndef RLIMIT_RTTIME
40 #define RLIMIT_RTTIME 15
41 #endif
42 
43 #ifndef SCHED_RESET_ON_FORK
44 #define SCHED_RESET_ON_FORK 0x40000000
45 #endif
46 
47 #ifndef DEFAULT_INPUT_TIMEOUT
48 #define DEFAULT_INPUT_TIMEOUT 500
49 #endif
50 
51 /**
52  * @file alsaclient.cpp
53  * Implementation of classes managing ALSA Sequencer clients
54  */
55 
56 /**
57  * @class QObject
58  * The QObject class is the base class of all Qt objects.
59  * @see https://doc.qt.io/qt-5/qobject.html
60  */
61 
62 /**
63  * @class QThread
64  * The QThread class provides platform-independent threads.
65  * @see https://doc.qt.io/qt-5/qthread.html
66  */
67 
68 namespace drumstick { namespace ALSA {
69 
70 /**
71  * @addtogroup ALSAClient
72  * @{
73  *
74  * ALSA clients are any entities using ALSA sequencer services. A client
75  * may be an application or a device driver for an external MIDI port, like
76  * USB MIDI devices or the MIDI/game ports of some sound cards. This library
77  * allows to easily create applications managing ALSA clients.
78  *
79  * ALSA clients are also file descriptors representing a sequencer device,
80  * that must be opened before reading or writing MIDI events. When the client
81  * is opened, it is given some handle and a number identifying it to other
82  * clients in the system. You can also provide a name for it.
83  *
84  * Each ALSA sequencer client can have several ports attached. The ports can be
85  * readable or writable, and can be subscribed in pairs: one readable port to
86  * one writable port. The subscriptions can be made and queried by external
87  * applications, like "aconnect" or "qjackctl".
88  *
89  * SystemInfo is an auxiliary class to query several system capabilities.
90  *
91  * The PoolInfo class represents a container to query and change some values
92  * for the kernel memory pool assigned to an ALSA client.
93  *
94  * The ClientInfo class is another container to query and change properties of
95  * the MidiClient itself.
96  *
97  * The SequencerEventHandler abstract class is used to define an interface
98  * that other class can implement to receive sequencer events. It is one of the
99  * three methods of delivering input events offered by the library.
100  *
101  * @section EventInput Input
102  * MidiClient uses a separate thread to receive events from the ALSA sequencer.
103  * The input thread can be started and stopped using the methods
104  * MidiClient::startSequencerInput() and MidiClient::stopSequencerInput().
105  * It is necessary to have this thread in mind when using this library to read
106  * events. There are three delivering methods of input events:
107  * <ul>
108  * <li>A Callback method. To use this method, you must derive a class from
109  * SequencerEventHandler, overriding the method
110  * SequencerEventHandler::handleSequencerEvent() to provide your own event
111  * processing code. You must give a handler instance pointer to
112  * the client using MidiClient::setHandler().</li>
113  * <li>Using QEvent listeners. To use this method, you must have one or more
114  * classes derived from QObject overriding the method QObject::customEvent().
115  * You must also use the method MidiClient::addListener() to add such objects
116  * to the client's listeners list, and MidiClient::setEventsEnabled().</li>
117  * <li>The third method involves signals and slots. Whenever a sequencer event
118  * is received, a signal MidiClient::eventReceived() is emitted, that can be
119  * connected to your own supplied slot(s) to process it.
120  * </ul>
121  * The selected method depends only on your requirements and your preferences.
122  * <ul>
123  * <li>The Callback method is preferred for real-time usage because the handler
124  * receives the events without any delay, but at the same time you must
125  * avoid calling methods of any GUI widgets within the handler. Instead,
126  * you can create QEvents and call QObject::postEvent() to notify the GUI.</li>
127  * <li>Inside QObject::eventReceiver() you can collect QEvents and call
128  * any method you want, but the events are not delivered in real-time. Instead,
129  * they are enqueued and dispatched by the main application's event loop.</li>
130  * <li>The signals/slots method can be real-time or queued, depending on the
131  * last parameter of QObject::connect(). If it is Qt::DirectConnection, the signal
132  * is delivered in real-time, and the same rule about avoiding calls to any
133  * GUI widgets methods apply. If it is Qt::QueuedConnection, then the signal is
134  * enqueued using the application's event loop, and it is safe to call any GUI
135  * methods in this case.</li>
136  * </ul>
137  * Whichever method you select, it excludes the other methods for the same
138  * program. A callback takes precedence over the others. If it is not set, then
139  * the events are sent if MidiClient::setEventsEnabled() is called.
140  * If neither a callback handler is set nor events are enabled, then the signal
141  * is emitted. In any case, the event pointer must be deleted by the receiver
142  * method.
143  *
144  * @see https://doc.qt.io/qt-5/threads-reentrancy.html
145  *
146  * @section EventOutput Output
147  *
148  * The methods to send a single event to the ALSA sequencer are:
149  * <ul>
150  * <li>MidiClient::output() using the library buffer, automatically flushed.</li>
151  * <li>MidiClient::outputBuffer() using the library buffer. Not flushed automatically.</li>
152  * <li>MidiClient::outputDirect() not using the library buffer.</li>
153  * </ul>
154  * The two first methods usually require a call to MidiClient::drainOutput() to
155  * flush the ALSA library output buffer. The third one bypasses the buffer, and
156  * doesn't require the call to MidiClient::drainOutput(). Note that the buffer
157  * can be automatically drained by the first method when it becomes full.
158  *
159  * After being dispatched to the ALSA Sequencer, the events can be scheduled at
160  * some time in the future, or immediately. This depends on the following
161  * methods of the SequencerEvent class:
162  * <ul>
163  * <li>SequencerEvent::setDirect() not scheduled</li>
164  * <li>SequencerEvent::scheduleTick() scheduled in musical time (ticks)</li>
165  * <li>SequencerEvent::scheduleReal() scheduled in clock time (seconds)</li>
166  * </ul>
167  *
168  * When you need to schedule a lot of events, for instance reproducing
169  * a Standard MIDI File (SMF) or a MIDI sequence, you may want to use the
170  * abstract class SequencerOutputThread.
171  *
172  * @section Memory
173  *
174  * There are two memory issues: the memory pool belongs to the kernel sequencer,
175  * and can be managed by the class PoolInfo and the methods
176  * MidiClient::getPoolInfo() and MidiClient::setPoolInfo(). The library buffer
177  * can be controlled using the methods MidiClient::getOutputBufferSize() and
178  * MidiClient::setOutputBufferSize() as well as MidiClient::getInputBufferSize()
179  * and MidiClient::setInputBufferSize().
180  *
181  * @see https://www.alsa-project.org/alsa-doc/alsa-lib/group___seq_client.html
182  */
183 
184 /**
185  * This class manages event input from the ALSA sequencer.
186  */
187 class MidiClient::SequencerInputThread: public QThread
188 {
189 public:
SequencerInputThread(MidiClient * seq,int timeout)190     SequencerInputThread(MidiClient *seq, int timeout)
191         : QThread(),
192         m_MidiClient(seq),
193         m_Wait(timeout),
194         m_Stopped(false),
195         m_RealTime(true) {}
196     virtual ~SequencerInputThread() = default;
197     void run() override;
198     bool stopped();
199     void stop();
200     void setRealtimePriority();
201 
202     MidiClient *m_MidiClient;
203     int m_Wait;
204     bool m_Stopped;
205     bool m_RealTime;
206     QReadWriteLock m_mutex;
207 };
208 
209 class MidiClient::MidiClientPrivate
210 {
211 public:
MidiClientPrivate()212     MidiClientPrivate() :
213         m_eventsEnabled(false),
214         m_BlockMode(false),
215         m_NeedRefreshClientList(true),
216         m_OpenMode(SND_SEQ_OPEN_DUPLEX),
217         m_DeviceName("default"),
218         m_SeqHandle(nullptr),
219         m_Thread(nullptr),
220         m_Queue(nullptr),
221         m_handler(nullptr)
222     { }
223 
224     bool m_eventsEnabled;
225     bool m_BlockMode;
226     bool m_NeedRefreshClientList;
227     int  m_OpenMode;
228     QString m_DeviceName;
229     snd_seq_t* m_SeqHandle;
230     QPointer<SequencerInputThread> m_Thread;
231     QPointer<MidiQueue> m_Queue;
232     SequencerEventHandler* m_handler;
233 
234     ClientInfo m_Info;
235     ClientInfoList m_ClientList;
236     MidiPortList m_Ports;
237     PortInfoList m_OutputsAvail;
238     PortInfoList m_InputsAvail;
239     QObjectList m_listeners;
240     SystemInfo m_sysInfo;
241     PoolInfo m_poolInfo;
242 };
243 
244 /**
245  * Constructor.
246  *
247  * This constructor optionally gets a QObject parent. When you create a
248  * MidiClient with another object as parent, the MidiClient object will
249  * automatically add itself to the parent's children() list. The parent takes
250  * ownership of the object i.e. it will automatically delete its children in
251  * its destructor.
252  *
253  * It is necessary to invoke open() later to get the sequencer client handler
254  * from the ALSA sequencer subsystem.
255  *
256  * @param parent The optional parent object
257  * @return a MidiClient instance
258  */
MidiClient(QObject * parent)259 MidiClient::MidiClient( QObject* parent ) :
260     QObject(parent),
261     d(new MidiClientPrivate)
262 { }
263 
264 /**
265  * Destructor.
266  *
267  * The ports and queue associated to this client are automatically released.
268  */
~MidiClient()269 MidiClient::~MidiClient()
270 {
271     stopSequencerInput();
272     detachAllPorts();
273     delete d->m_Queue;
274     close();
275     freeClients();
276     delete d->m_Thread;
277 }
278 
279 /**
280  * Returns the sequencer handler managed by ALSA
281  * @return the sequencer handler
282  */
283 snd_seq_t*
getHandle()284 MidiClient::getHandle()
285 {
286     return d->m_SeqHandle;
287 }
288 
289 /**
290  * Returns true if the sequencer is opened
291  * @return wheter the sequencer is opened
292  */
isOpened()293 bool MidiClient::isOpened()
294 {
295     return !d.isNull() && (d->m_SeqHandle != nullptr);
296 }
297 
298 /**
299  * Returns the name of the sequencer device
300  * @return the device name
301  */
getDeviceName()302 QString MidiClient::getDeviceName()
303 {
304     return d->m_DeviceName;
305 }
306 
307 /**
308  * Returns the last open mode used in open()
309  * @return the last open mode
310  */
getOpenMode()311 int MidiClient::getOpenMode()
312 {
313     return d->m_OpenMode;
314 }
315 
316 /**
317  * Returns the last block mode used in open()
318  * @return the last block mode
319  */
getBlockMode()320 bool MidiClient::getBlockMode()
321 {
322     return d->m_BlockMode;
323 }
324 
325 /**
326  * Returns true if the events mode of delivery has been enabled
327  * @return whether the events mode of delivery is enabled
328  */
getEventsEnabled() const329 bool MidiClient::getEventsEnabled() const
330 {
331     return d->m_eventsEnabled;
332 }
333 
334 /**
335  * Sets a sequencer event handler enabling the callback delivery mode
336  * @param handler the sequencer event handler
337  */
setHandler(SequencerEventHandler * handler)338 void MidiClient::setHandler(SequencerEventHandler* handler)
339 {
340     d->m_handler = handler;
341 }
342 
343 
344 /**
345  * Enables real-time priority for the MIDI input thread. The system needs either
346  * RLIMIT_RTPRIO or RealtimeKit. First RLIMIT_RTPRIO is tried, and if this
347  * method fails, RealtimeKit is used.
348  *
349  * @param enable real-time priority enabled
350  * @since 0.5.0
351  */
setRealTimeInput(bool enable)352 void MidiClient::setRealTimeInput(bool enable)
353 {
354     if (d->m_Thread == nullptr) {
355         d->m_Thread = new SequencerInputThread(this, DEFAULT_INPUT_TIMEOUT);
356         d->m_Thread->m_RealTime = enable;
357     }
358 }
359 
360 /**
361  * Return the real-time priority setting for the MIDI input thread.
362  * @return true if the real-time priority is enabled
363  * @since 0.5.0
364  */
realTimeInputEnabled()365 bool MidiClient::realTimeInputEnabled()
366 {
367     if (d->m_Thread == nullptr)
368         return true;
369     return d->m_Thread->m_RealTime;
370 }
371 
372 /**
373  * Open the sequencer device.
374  *
375  * When opening the MidiClient instance, several properties may optionally
376  * be set as the device name, the open mode and block mode. Default values
377  * are provided for them. After a successful open, an event with
378  * SND_SEQ_EVENT_CLIENT_START is broadcast to the announce port.
379  *
380  * @param deviceName the sequencer device name, default value = &quot;default&quot;.
381  * This is not a name you make up for your own purposes; it has special
382  * significance to the ALSA library. Usually you need to pass &quot;default&quot; here.
383  * @param openMode the open mode, default value = SND_SEQ_OPEN_DUPLEX.
384  * The read/write mode of the sequencer. Can be one of these three values:
385  * <ul>
386  * <li>SND_SEQ_OPEN_OUTPUT - open the sequencer for output only</li>
387  * <li>SND_SEQ_OPEN_INPUT - open the sequencer for input only</li>
388  * <li>SND_SEQ_OPEN_DUPLEX - open the sequencer for output and input</li>
389  * </ul>
390  * @param blockMode open in blocking mode, default value = false.
391  */
392 void
open(const QString deviceName,const int openMode,const bool blockMode)393 MidiClient::open( const QString deviceName,
394                   const int openMode,
395                   const bool blockMode)
396 {
397     DRUMSTICK_ALSA_CHECK_ERROR( snd_seq_open( &d->m_SeqHandle, deviceName.toLocal8Bit().data(),
398                               openMode, blockMode ? 0 : SND_SEQ_NONBLOCK ) );
399     DRUMSTICK_ALSA_CHECK_WARNING( snd_seq_get_client_info( d->m_SeqHandle, d->m_Info.m_Info ) );
400     d->m_DeviceName = deviceName;
401     d->m_OpenMode = openMode;
402     d->m_BlockMode = blockMode;
403 }
404 
405 /**
406  * Open the sequencer device, providing a configuration object pointer.
407  *
408  * This method is like open() except that a configuration is explicitly
409  * provided. After a successful open, an event with SND_SEQ_EVENT_CLIENT_START
410  * type is broadcasted from the announce port.
411  *
412  * @param conf a configuration object pointer.
413  * @param deviceName the sequencer device name, default value = &quot;default&quot;.
414  * This is not a name you make up for your own purposes; it has special
415  * significance to the ALSA library. Usually you need to pass &quot;default&quot; here.
416  * @param openMode the open mode, default value = SND_SEQ_OPEN_DUPLEX.
417  * The read/write mode of the sequencer. Can be one of these three values:
418  * <ul>
419  * <li>SND_SEQ_OPEN_OUTPUT - open the sequencer for output only</li>
420  * <li>SND_SEQ_OPEN_INPUT - open the sequencer for input only</li>
421  * <li>SND_SEQ_OPEN_DUPLEX - open the sequencer for output and input</li>
422  * </ul>
423  * @param blockMode open in blocking mode, default value = false.
424  */
425 void
open(snd_config_t * conf,const QString deviceName,const int openMode,const bool blockMode)426 MidiClient::open( snd_config_t* conf,
427                   const QString deviceName,
428                   const int openMode,
429                   const bool blockMode )
430 {
431     DRUMSTICK_ALSA_CHECK_ERROR( snd_seq_open_lconf( &d->m_SeqHandle,
432                                      deviceName.toLocal8Bit().data(),
433                                      openMode,
434                                      blockMode ? 0 : SND_SEQ_NONBLOCK,
435                                      conf ));
436     DRUMSTICK_ALSA_CHECK_WARNING( snd_seq_get_client_info(d->m_SeqHandle, d->m_Info.m_Info));
437     d->m_DeviceName = deviceName;
438     d->m_OpenMode = openMode;
439     d->m_BlockMode = blockMode;
440 }
441 
442 /**
443  * Close the sequencer device.
444  *
445  * After a client is closed, an event with SND_SEQ_EVENT_CLIENT_EXIT is
446  * broadcast to the announce port. The connection between other clients are
447  * disconnected. Call this just before exiting your program.
448  */
449 void
close()450 MidiClient::close()
451 {
452     if (d->m_SeqHandle != nullptr) {
453         stopSequencerInput();
454         DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_close(d->m_SeqHandle));
455         d->m_SeqHandle = nullptr;
456     }
457 }
458 
459 /**
460  * Gets the size of the library output buffer for the ALSA client.
461  *
462  * This buffer is used to store the decoded byte-stream of output events before
463  * transferring to the sequencer.
464  *
465  * @return the size of the library output buffer
466  */
467 size_t
getOutputBufferSize()468 MidiClient::getOutputBufferSize()
469 {
470     return snd_seq_get_output_buffer_size(d->m_SeqHandle);
471 }
472 
473 /**
474  * Sets the size of the library output buffer for the ALSA client.
475  *
476  * This buffer is used to store the decoded byte-stream of output events before
477  * transferring to the sequencer.
478  *
479  * @param newSize the size of the library output buffer
480  */
481 void
setOutputBufferSize(size_t newSize)482 MidiClient::setOutputBufferSize(size_t newSize)
483 {
484     if (getOutputBufferSize() != newSize) {
485         DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_set_output_buffer_size(d->m_SeqHandle, newSize));
486     }
487 }
488 
489 /**
490  * Gets the size of the library input buffer for the ALSA client.
491  *
492  * This buffer is used to read a byte-stream of input events before
493  * transferring from the sequencer.
494  *
495  * @return the size of the library input buffer
496  */
497 size_t
getInputBufferSize()498 MidiClient::getInputBufferSize()
499 {
500     return snd_seq_get_input_buffer_size(d->m_SeqHandle);
501 }
502 
503 /**
504  * Sets the size of the library input buffer for the ALSA client.
505  *
506  * This buffer is used to read a byte-stream of input events before
507  * transferring from the sequencer.
508  *
509  * @param newSize the size of the library input buffer
510  */
511 void
setInputBufferSize(size_t newSize)512 MidiClient::setInputBufferSize(size_t newSize)
513 {
514     if (getInputBufferSize() != newSize) {
515         DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_set_input_buffer_size(d->m_SeqHandle, newSize));
516     }
517 }
518 
519 /**
520  * Change the blocking mode of the client.
521  *
522  * In block mode, the client falls into sleep when it fills the output memory
523  * pool with full events. The client will be woken up after a certain amount
524  * of free space becomes available.
525  *
526  * @param newValue the blocking mode
527  */
528 void
setBlockMode(bool newValue)529 MidiClient::setBlockMode(bool newValue)
530 {
531     if (d->m_BlockMode != newValue)
532     {
533         d->m_BlockMode = newValue;
534         if (d->m_SeqHandle != nullptr)
535         {
536             DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_nonblock(d->m_SeqHandle, d->m_BlockMode ? 0 : 1));
537         }
538     }
539 }
540 
541 /**
542  * Gets the client ID.
543  *
544  * Returns the ID of the client. A client ID is necessary to inquiry or to set
545  * the client information. A user client ID is assigned from 128 to 191.
546  *
547  * @return the client ID.
548  */
549 int
getClientId()550 MidiClient::getClientId()
551 {
552     return DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_client_id(d->m_SeqHandle));
553 }
554 
555 /**
556  * Returns the type snd_seq_type_t of the given sequencer handle.
557  * @return the type snd_seq_type_t of the given sequencer handle.
558  */
559 snd_seq_type_t
getSequencerType()560 MidiClient::getSequencerType()
561 {
562     return snd_seq_type(d->m_SeqHandle);
563 }
564 
565 /**
566  * Dispatch the events received from the Sequencer.
567  *
568  * There are three methods of events delivering:
569  * <ul>
570  * <li>A Callback method. To use this method, you must derive a class from
571  * SequencerEventHandler, overriding the method
572  * SequencerEventHandler::handleSequencerEvent() to
573  * provide your own event processing. You must provide the handler instance to
574  * the client using setHandler().</li>
575  * <li>Using QEvent listeners. To use this method, you must use one or more
576  * classes derived from QObject overriding the method QObject::customEvent().
577  * You must also use the method addListener() to add such objects to the
578  * client's listeners list.</li>
579  * <li>The third method involves signals and slots. Whenever a sequencer event
580  * is received, a signal eventReceived() is emitted, that can be connected to
581  * your own supplied slot(s) to process it.
582  * </ul>
583  * @see ALSAClient
584  */
585 void
doEvents()586 MidiClient::doEvents()
587 {
588     do {
589         int err = 0;
590         snd_seq_event_t* evp = nullptr;
591         SequencerEvent* event = nullptr;
592         err = snd_seq_event_input(d->m_SeqHandle, &evp);
593         if ((err >= 0) && (evp != nullptr)) {
594             switch (evp->type) {
595 
596             case SND_SEQ_EVENT_NOTE:
597                 event = new NoteEvent(evp);
598                 break;
599 
600             case SND_SEQ_EVENT_NOTEON:
601                 event = new NoteOnEvent(evp);
602                 break;
603 
604             case SND_SEQ_EVENT_NOTEOFF:
605                 event = new NoteOffEvent(evp);
606                 break;
607 
608             case SND_SEQ_EVENT_KEYPRESS:
609                 event = new KeyPressEvent(evp);
610                 break;
611 
612             case SND_SEQ_EVENT_CONTROLLER:
613             case SND_SEQ_EVENT_CONTROL14:
614             case SND_SEQ_EVENT_REGPARAM:
615             case SND_SEQ_EVENT_NONREGPARAM:
616                 event = new ControllerEvent(evp);
617                 break;
618 
619             case SND_SEQ_EVENT_PGMCHANGE:
620                 event = new ProgramChangeEvent(evp);
621                 break;
622 
623             case SND_SEQ_EVENT_CHANPRESS:
624                 event = new ChanPressEvent(evp);
625                 break;
626 
627             case SND_SEQ_EVENT_PITCHBEND:
628                 event = new PitchBendEvent(evp);
629                 break;
630 
631             case SND_SEQ_EVENT_SYSEX:
632                 event = new SysExEvent(evp);
633                 break;
634 
635             case SND_SEQ_EVENT_PORT_SUBSCRIBED:
636             case SND_SEQ_EVENT_PORT_UNSUBSCRIBED:
637                 event = new SubscriptionEvent(evp);
638                 break;
639 
640             case SND_SEQ_EVENT_PORT_CHANGE:
641             case SND_SEQ_EVENT_PORT_EXIT:
642             case SND_SEQ_EVENT_PORT_START:
643                 event = new PortEvent(evp);
644                 d->m_NeedRefreshClientList = true;
645                 break;
646 
647             case SND_SEQ_EVENT_CLIENT_CHANGE:
648             case SND_SEQ_EVENT_CLIENT_EXIT:
649             case SND_SEQ_EVENT_CLIENT_START:
650                 event = new ClientEvent(evp);
651                 d->m_NeedRefreshClientList = true;
652                 break;
653 
654             case SND_SEQ_EVENT_SONGPOS:
655             case SND_SEQ_EVENT_SONGSEL:
656             case SND_SEQ_EVENT_QFRAME:
657             case SND_SEQ_EVENT_TIMESIGN:
658             case SND_SEQ_EVENT_KEYSIGN:
659                 event = new ValueEvent(evp);
660                 break;
661 
662             case SND_SEQ_EVENT_SETPOS_TICK:
663             case SND_SEQ_EVENT_SETPOS_TIME:
664             case SND_SEQ_EVENT_QUEUE_SKEW:
665                 event = new QueueControlEvent(evp);
666                 break;
667 
668             case SND_SEQ_EVENT_TEMPO:
669                 event = new TempoEvent(evp);
670                 break;
671 
672             default:
673                 event = new SequencerEvent(evp);
674                 break;
675             }
676             // first, process the callback (if any)
677             if (d->m_handler != nullptr) {
678                 d->m_handler->handleSequencerEvent(event->clone());
679             } else {
680                 // second, process the event listeners
681                 if (d->m_eventsEnabled) {
682                    QObjectList::Iterator it;
683                     for(it=d->m_listeners.begin(); it!=d->m_listeners.end(); ++it) {
684                         QObject* sub = (*it);
685                         QCoreApplication::postEvent(sub, event->clone());
686                     }
687                 } else {
688                     // finally, process signals
689                     emit eventReceived(event->clone());
690                 }
691             }
692             delete event;
693         }
694     }
695     while (snd_seq_event_input_pending(d->m_SeqHandle, 0) > 0);
696 }
697 
698 /**
699  * Starts reading events from the ALSA sequencer.
700  */
701 void
startSequencerInput()702 MidiClient::startSequencerInput()
703 {
704     if (d->m_Thread == nullptr) {
705         d->m_Thread = new SequencerInputThread(this, DEFAULT_INPUT_TIMEOUT);
706     }
707     d->m_Thread->start( d->m_Thread->m_RealTime ?
708             QThread::TimeCriticalPriority : QThread::InheritPriority );
709 }
710 
711 /**
712  * Stops reading events from the ALSA sequencer.
713  */
714 void
stopSequencerInput()715 MidiClient::stopSequencerInput()
716 {
717     int counter = 0;
718     if (d->m_Thread != nullptr) {
719         if (d->m_Thread->isRunning()) {
720             d->m_Thread->stop();
721             while (!d->m_Thread->wait(500) && (counter < 10)) {
722                 counter++;
723             }
724             if (!d->m_Thread->isFinished()) {
725                 d->m_Thread->terminate();
726             }
727         }
728         delete d->m_Thread;
729     }
730 }
731 
732 /**
733  * Reads the ALSA sequencer's clients list.
734  */
735 void
readClients()736 MidiClient::readClients()
737 {
738     ClientInfo cInfo;
739     freeClients();
740     cInfo.setClient(-1);
741     while (snd_seq_query_next_client(d->m_SeqHandle, cInfo.m_Info) >= 0) {
742         cInfo.readPorts(this);
743         d->m_ClientList.append(cInfo);
744     }
745     d->m_NeedRefreshClientList = false;
746 }
747 
748 /**
749  * Releases the list of ALSA sequencer's clients.
750  */
751 void
freeClients()752 MidiClient::freeClients()
753 {
754     d->m_ClientList.clear();
755 }
756 
757 /**
758  * Gets the list of clients from the ALSA sequencer.
759  * @return the list of clients.
760  */
761 ClientInfoList
getAvailableClients()762 MidiClient::getAvailableClients()
763 {
764     if (d->m_NeedRefreshClientList)
765         readClients();
766     ClientInfoList lst = d->m_ClientList; // copy
767     return lst;
768 }
769 
770 /**
771  * Gets the ClientInfo object holding data about this client.
772  * @return the ClientInfo object representing this client.
773  */
774 ClientInfo&
getThisClientInfo()775 MidiClient::getThisClientInfo()
776 {
777     snd_seq_get_client_info(d->m_SeqHandle, d->m_Info.m_Info);
778     return d->m_Info;
779 }
780 
781 /**
782  * Sets the data supplied by the ClientInfo object into the ALSA sequencer
783  * client. This allows to change the name, capabilities, type and other data
784  * in a single step.
785  *
786  * @param val a ClientInfo object reference
787  */
788 void
setThisClientInfo(const ClientInfo & val)789 MidiClient::setThisClientInfo(const ClientInfo& val)
790 {
791     d->m_Info = val;
792     snd_seq_set_client_info(d->m_SeqHandle, d->m_Info.m_Info);
793 }
794 
795 /**
796  * This internal method applies the ClientInfo data to the ALSA sequencer client
797  */
798 void
applyClientInfo()799 MidiClient::applyClientInfo()
800 {
801     if (d->m_SeqHandle != nullptr) {
802         snd_seq_set_client_info(d->m_SeqHandle, d->m_Info.m_Info);
803     }
804 }
805 
806 /**
807  * Gets the client's public name
808  * @return The client's name
809  */
810 QString
getClientName()811 MidiClient::getClientName()
812 {
813     return d->m_Info.getName();
814 }
815 
816 /**
817  * Gets the public name corresponding to the given Client ID.
818  * @param clientId The ID of any existing sequencer client
819  * @return The client's name
820  */
821 QString
getClientName(const int clientId)822 MidiClient::getClientName(const int clientId)
823 {
824     ClientInfoList::Iterator it;
825     if (d->m_NeedRefreshClientList)
826         readClients();
827     for (it = d->m_ClientList.begin(); it != d->m_ClientList.end(); ++it) {
828         if ((*it).getClientId() == clientId) {
829             return (*it).getName();
830         }
831     }
832     return QString();
833 }
834 
835 /**
836  * Changes the public name of the ALSA sequencer client.
837  * @param newName A new public name
838  */
839 void
setClientName(QString const & newName)840 MidiClient::setClientName(QString const& newName)
841 {
842     if (newName != d->m_Info.getName()) {
843         d->m_Info.setName(newName);
844         applyClientInfo();
845     }
846 }
847 
848 /**
849  * Gets the list of MidiPort instances belonging to this client.
850  * @return The list of MidiPort instances.
851  */
852 MidiPortList
getMidiPorts() const853 MidiClient::getMidiPorts() const
854 {
855     return d->m_Ports;
856 }
857 
858 /**
859  * Create and attach a new MidiPort instance to this client.
860  * @return The pointer to the new MidiPort instance.
861  */
862 MidiPort*
createPort()863 MidiClient::createPort()
864 {
865     MidiPort* port = new MidiPort(this);
866     port->attach(this);
867     return port;
868 }
869 
870 /**
871  * Attach a MidiPort instance to this client
872  * @param port The MidiPort to be attached
873  */
874 void
portAttach(MidiPort * port)875 MidiClient::portAttach(MidiPort* port)
876 {
877     if (d->m_SeqHandle != nullptr) {
878         DRUMSTICK_ALSA_CHECK_ERROR(snd_seq_create_port(d->m_SeqHandle, port->m_Info.m_Info));
879         d->m_Ports.push_back(port);
880     }
881 }
882 
883 /**
884  * Detach a MidiPort instance from this client
885  * @param port The MidiPort to be detached
886  */
887 void
portDetach(MidiPort * port)888 MidiClient::portDetach(MidiPort* port)
889 {
890     if (d->m_SeqHandle != nullptr) {
891         if(port->getPortInfo()->getClient() == getClientId())
892         {
893             return;
894         }
895         DRUMSTICK_ALSA_CHECK_ERROR(snd_seq_delete_port(d->m_SeqHandle, port->getPortInfo()->getPort()));
896         port->setMidiClient(nullptr);
897 
898         MidiPortList::iterator it;
899         for(it = d->m_Ports.begin(); it != d->m_Ports.end(); ++it)
900         {
901             if ((*it)->getPortInfo()->getPort() == port->getPortInfo()->getPort())
902             {
903                 d->m_Ports.erase(it);
904                 break;
905             }
906         }
907     }
908 }
909 
910 /**
911  * Detach all the ports belonging to this client.
912  */
detachAllPorts()913 void MidiClient::detachAllPorts()
914 {
915     if (d->m_SeqHandle != nullptr) {
916         QMutableListIterator<MidiPort*> it(d->m_Ports);
917         while (it.hasNext()) {
918             MidiPort* p = it.next();
919             DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_delete_port(d->m_SeqHandle, p->getPortInfo()->getPort()));
920             p->setMidiClient(nullptr);
921             it.remove();
922         }
923     }
924 }
925 
926 /**
927  * Add an event filter to the client.
928  * @param evtype An event filter to be added.
929  */
930 void
addEventFilter(int evtype)931 MidiClient::addEventFilter(int evtype)
932 {
933     snd_seq_set_client_event_filter(d->m_SeqHandle, evtype);
934 }
935 
936 /**
937  * Gets the broadcast filter usage of the client.
938  *
939  * @return The broadcast filter.
940  */
941 bool
getBroadcastFilter()942 MidiClient::getBroadcastFilter()
943 {
944     return d->m_Info.getBroadcastFilter();
945 }
946 
947 /**
948  * Sets the broadcast filter usage of the client.
949  *
950  * @param newValue The broadcast filter.
951  */
952 void
setBroadcastFilter(bool newValue)953 MidiClient::setBroadcastFilter(bool newValue)
954 {
955     d->m_Info.setBroadcastFilter(newValue);
956     applyClientInfo();
957 }
958 
959 /**
960  * Get the error-bounce usage of the client.
961  *
962  * @return The error-bounce usage.
963  */
964 bool
getErrorBounce()965 MidiClient::getErrorBounce()
966 {
967     return d->m_Info.getErrorBounce();
968 }
969 
970 /**
971  * Sets the error-bounce usage of the client.
972  *
973  * @param newValue The error-bounce usage.
974  */
975 void
setErrorBounce(bool newValue)976 MidiClient::setErrorBounce(bool newValue)
977 {
978     d->m_Info.setErrorBounce(newValue);
979     applyClientInfo();
980 }
981 
982 /**
983  * Output an event using the library output buffer.
984  *
985  * An event is once expanded on the output buffer. The output buffer will be
986  * drained automatically if it becomes full.
987  *
988  * @param ev The event to be sent.
989  * @param async Use asynchronous mode. If false, this call will block until the
990  * event can be delivered.
991  * @param timeout The maximum time to wait in synchronous mode.
992  */
993 void
output(SequencerEvent * ev,bool async,int timeout)994 MidiClient::output(SequencerEvent* ev, bool async, int timeout)
995 {
996     pollfd* pfds = nullptr;
997     if (async) {
998         DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_event_output(d->m_SeqHandle, ev->getHandle()));
999     } else {
1000         int npfds = snd_seq_poll_descriptors_count(d->m_SeqHandle, POLLOUT);
1001         pfds = (pollfd*) calloc(npfds, sizeof(pollfd));
1002         snd_seq_poll_descriptors(d->m_SeqHandle, pfds, npfds, POLLOUT);
1003         while (snd_seq_event_output(d->m_SeqHandle, ev->getHandle()) < 0)
1004         {
1005             poll(pfds, npfds, timeout);
1006         }
1007         free(pfds);
1008     }
1009 }
1010 
1011 /**
1012  * Output an event directly to the sequencer
1013  *
1014  * This function sends an event to the sequencer directly not using the library
1015  * output buffer.
1016  *
1017  * @param ev The event to be sent.
1018  * @param async Use asynchronous mode. If false, this call will block until the
1019  * event is delivered to the sequencer.
1020  * @param timeout The maximum time to wait in synchronous mode.
1021  */
outputDirect(SequencerEvent * ev,bool async,int timeout)1022 void MidiClient::outputDirect(SequencerEvent* ev, bool async, int timeout)
1023 {
1024     if (async) {
1025         DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_event_output_direct(d->m_SeqHandle, ev->getHandle()));
1026     } else {
1027         int npfds = snd_seq_poll_descriptors_count(d->m_SeqHandle, POLLOUT);
1028         pollfd* pfds = (pollfd*) calloc(npfds, sizeof(pollfd));
1029         snd_seq_poll_descriptors(d->m_SeqHandle, pfds, npfds, POLLOUT);
1030         while (snd_seq_event_output_direct(d->m_SeqHandle, ev->getHandle()) < 0)
1031         {
1032             poll(pfds, npfds, timeout);
1033         }
1034         free(pfds);
1035     }
1036 }
1037 
1038 /**
1039  * Output an event using the library output buffer, without draining the buffer.
1040  *
1041  * An event is once expanded on the output buffer. The output buffer will NOT be
1042  * drained automatically if it becomes full.
1043  *
1044  * @param ev The event to be sent.
1045  */
1046 void
outputBuffer(SequencerEvent * ev)1047 MidiClient::outputBuffer(SequencerEvent* ev)
1048 {
1049     DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_event_output_buffer(d->m_SeqHandle, ev->getHandle()));
1050 }
1051 
1052 /**
1053  * Drain the library output buffer.
1054  *
1055  * This function drains all pending events on the output buffer. The function
1056  * returns immediately after the events are sent to the queues regardless
1057  * whether the events are processed or not.
1058  *
1059  * @param async Use asynchronous mode. If false, this call will block until the
1060  * buffer can be flushed.
1061  * @param timeout The maximum time to wait in synchronous mode.
1062  */
drainOutput(bool async,int timeout)1063 void MidiClient::drainOutput(bool async, int timeout)
1064 {
1065     if (async) {
1066         DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_drain_output(d->m_SeqHandle));
1067     } else {
1068         int npfds = snd_seq_poll_descriptors_count(d->m_SeqHandle, POLLOUT);
1069         pollfd* pfds = (pollfd*) calloc(npfds, sizeof(pollfd));
1070         snd_seq_poll_descriptors(d->m_SeqHandle, pfds, npfds, POLLOUT);
1071         while (snd_seq_drain_output(d->m_SeqHandle) < 0)
1072         {
1073             poll(pfds, npfds, timeout);
1074         }
1075         free(pfds);
1076     }
1077 }
1078 
1079 /**
1080  * Wait until all sent events are processed.
1081  *
1082  * This function waits until all events of this client are processed.
1083  */
1084 void
synchronizeOutput()1085 MidiClient::synchronizeOutput()
1086 {
1087     snd_seq_sync_output_queue(d->m_SeqHandle);
1088 }
1089 
1090 /**
1091  * Get the MidiQueue instance associated to this client.
1092  * If the client is not associated to a MidiQueue, one is created.
1093  * @return A MidiQueue instance pointer
1094  */
1095 MidiQueue*
getQueue()1096 MidiClient::getQueue()
1097 {
1098     if (d->m_Queue == nullptr) {
1099         createQueue();
1100     }
1101     return d->m_Queue;
1102 }
1103 
1104 /**
1105  * Create and return a new MidiQueue associated to this client.
1106  * @return A new MidiQueue instance.
1107  */
1108 MidiQueue*
createQueue()1109 MidiClient::createQueue()
1110 {
1111     if (d->m_Queue != nullptr) {
1112         delete d->m_Queue;
1113     }
1114     d->m_Queue = new MidiQueue(this, this);
1115     return d->m_Queue;
1116 }
1117 
1118 /**
1119  * Create and return a new MidiQueue with the given name, associated to this
1120  * client.
1121  * @param queueName The name for the new queue.
1122  * @return A new MidiQueue instance.
1123  */
1124 MidiQueue*
createQueue(QString const & queueName)1125 MidiClient::createQueue(QString const& queueName )
1126 {
1127     if (d->m_Queue != nullptr) {
1128         delete d->m_Queue;
1129     }
1130     d->m_Queue = new MidiQueue(this, queueName, this);
1131     return d->m_Queue;
1132 }
1133 
1134 /**
1135  * Create a new MidiQueue instance using a queue already existing in the
1136  * system, associating it to the client.
1137  *
1138  * @param queue_id An existing queue identifier.
1139  * @return A new MidiQueue instance.
1140  */
1141 MidiQueue*
useQueue(int queue_id)1142 MidiClient::useQueue(int queue_id)
1143 {
1144     if (d->m_Queue != nullptr) {
1145         delete d->m_Queue;
1146     }
1147     d->m_Queue = new MidiQueue(this, queue_id, this);
1148     return d->m_Queue;
1149 }
1150 
1151 /**
1152  * Create a new MidiQueue instance using a queue already existing in the
1153  * system, associating it to the client.
1154  *
1155  * @param name An existing queue name.
1156  * @return A new MidiQueue instance.
1157  */
1158 MidiQueue*
useQueue(const QString & name)1159 MidiClient::useQueue(const QString& name)
1160 {
1161     if (d->m_Queue != nullptr) {
1162         delete d->m_Queue;
1163     }
1164     int queue_id = getQueueId(name);
1165     if ( queue_id >= 0) {
1166        d->m_Queue = new MidiQueue(this, queue_id, this);
1167     }
1168     return d->m_Queue;
1169 }
1170 
1171 /**
1172  * Associate an existing MidiQueue instance to the client.
1173  *
1174  * @param queue An existing MidiQueue.
1175  * @return The provided MidiQueue instance.
1176  */
1177 MidiQueue*
useQueue(MidiQueue * queue)1178 MidiClient::useQueue(MidiQueue* queue)
1179 {
1180     if (d->m_Queue != nullptr) {
1181         delete d->m_Queue;
1182     }
1183     queue->setParent(this);
1184     d->m_Queue = queue;
1185     return d->m_Queue;
1186 }
1187 
1188 /**
1189  * Get a list of the existing queues
1190  * @return a list of existing queues
1191  */
1192 QList<int>
getAvailableQueues()1193 MidiClient::getAvailableQueues()
1194 {
1195     int q, err, max;
1196     QList<int> queues;
1197     snd_seq_queue_info_t* qinfo;
1198     snd_seq_queue_info_alloca(&qinfo);
1199     max = getSystemInfo().getMaxQueues();
1200     for ( q = 0; q < max; ++q ) {
1201         err = snd_seq_get_queue_info(d->m_SeqHandle, q, qinfo);
1202         if (err == 0) {
1203             queues.append(q);
1204         }
1205     }
1206     return queues;
1207 }
1208 
1209 /**
1210  * Gets a list of the available user ports in the system, filtered by the given
1211  * bitmap of desired capabilities.
1212  *
1213  * @param filter A bitmap of capabilities.
1214  * @return A filtered list of the available ports in the system.
1215  */
1216 PortInfoList
filterPorts(unsigned int filter)1217 MidiClient::filterPorts(unsigned int filter)
1218 {
1219     PortInfoList result;
1220     ClientInfoList::ConstIterator itc;
1221     PortInfoList::ConstIterator itp;
1222 
1223     if (d->m_NeedRefreshClientList)
1224         readClients();
1225 
1226     for (itc = d->m_ClientList.constBegin(); itc != d->m_ClientList.constEnd(); ++itc) {
1227         ClientInfo ci = (*itc);
1228         if ((ci.getClientId() == SND_SEQ_CLIENT_SYSTEM) ||
1229             (ci.getClientId() == d->m_Info.getClientId()))
1230             continue;
1231         PortInfoList lstPorts = ci.getPorts();
1232         for(itp = lstPorts.constBegin(); itp != lstPorts.constEnd(); ++itp) {
1233             PortInfo pi = (*itp);
1234             unsigned int cap = pi.getCapability();
1235             if ( ((filter & cap) != 0) &&
1236                  ((SND_SEQ_PORT_CAP_NO_EXPORT & cap) == 0) ) {
1237                 result.append(pi);
1238             }
1239         }
1240     }
1241     return result;
1242 }
1243 
1244 /**
1245  * Update the internal lists of user ports.
1246  */
1247 void
updateAvailablePorts()1248 MidiClient::updateAvailablePorts()
1249 {
1250     d->m_InputsAvail.clear();
1251     d->m_OutputsAvail.clear();
1252     d->m_InputsAvail = filterPorts( SND_SEQ_PORT_CAP_READ |
1253                                  SND_SEQ_PORT_CAP_SUBS_READ );
1254     d->m_OutputsAvail = filterPorts( SND_SEQ_PORT_CAP_WRITE |
1255                                   SND_SEQ_PORT_CAP_SUBS_WRITE );
1256 }
1257 
1258 /**
1259  * Gets the available user input ports in the system.
1260  * @return The list of available input ports.
1261  */
1262 PortInfoList
getAvailableInputs()1263 MidiClient::getAvailableInputs()
1264 {
1265     d->m_NeedRefreshClientList = true;
1266     updateAvailablePorts();
1267     return d->m_InputsAvail;
1268 }
1269 
1270 /**
1271  * Gets the available user output ports in the system.
1272  * @return The list of available output ports.
1273  */
1274 PortInfoList
getAvailableOutputs()1275 MidiClient::getAvailableOutputs()
1276 {
1277     d->m_NeedRefreshClientList = true;
1278     updateAvailablePorts();
1279     return d->m_OutputsAvail;
1280 }
1281 
1282 /**
1283  * Adds a QObject to the listeners list. This object should override the method
1284  * QObject::customEvent() to receive SequencerEvent instances.
1285  * @param listener A QObject listener to be notified of received events.
1286  * @see removeListener(), setEventsEnabled()
1287  */
1288 void
addListener(QObject * listener)1289 MidiClient::addListener(QObject* listener)
1290 {
1291     d->m_listeners.append(listener);
1292 }
1293 
1294 /**
1295  * Removes a QObject listener from the listeners list.
1296  * @param listener listener A QObject listener to be removed of received events.
1297  * @see addListener(), setEventsEnabled()
1298  */
1299 void
removeListener(QObject * listener)1300 MidiClient::removeListener(QObject* listener)
1301 {
1302     d->m_listeners.removeAll(listener);
1303 }
1304 
1305 /**
1306  * Enables the notification of received SequencerEvent instances to the listeners
1307  * registered with addListener()
1308  * @param bEnabled The new state of the events delivering mode.
1309  * @see addListener(), removeListener(), setEventsEnabled()
1310  */
1311 void
setEventsEnabled(bool bEnabled)1312 MidiClient::setEventsEnabled(bool bEnabled)
1313 {
1314     if (bEnabled != d->m_eventsEnabled) {
1315         d->m_eventsEnabled = bEnabled;
1316     }
1317 }
1318 
1319 /**
1320  * Gets a SystemInfo instance with the updated state of the system.
1321  * @return The updated system info.
1322  */
1323 SystemInfo&
getSystemInfo()1324 MidiClient::getSystemInfo()
1325 {
1326     snd_seq_system_info(d->m_SeqHandle, d->m_sysInfo.m_Info);
1327     return d->m_sysInfo;
1328 }
1329 
1330 /**
1331  * Gets a PoolInfo instance with an updated state of the client memory pool
1332  * @return The updated memory pool state.
1333  */
1334 PoolInfo&
getPoolInfo()1335 MidiClient::getPoolInfo()
1336 {
1337     snd_seq_get_client_pool(d->m_SeqHandle, d->m_poolInfo.m_Info);
1338     return d->m_poolInfo;
1339 }
1340 
1341 /**
1342  * Applies (updates) the client's PoolInfo data into the system.
1343  * @param info The PoolInfo reference to be applied to the client.
1344  */
1345 void
setPoolInfo(const PoolInfo & info)1346 MidiClient::setPoolInfo(const PoolInfo& info)
1347 {
1348     d->m_poolInfo = info;
1349     DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_set_client_pool(d->m_SeqHandle, d->m_poolInfo.m_Info));
1350 }
1351 
1352 /**
1353  * Resets the client input pool.
1354  * @see dropInput()
1355  */
1356 void
resetPoolInput()1357 MidiClient::resetPoolInput()
1358 {
1359     DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_reset_pool_input(d->m_SeqHandle));
1360 }
1361 
1362 /**
1363  * Resets the client output pool.
1364  * @see dropOutput()
1365  */
1366 void
resetPoolOutput()1367 MidiClient::resetPoolOutput()
1368 {
1369     DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_reset_pool_output(d->m_SeqHandle));
1370 }
1371 
1372 /**
1373  * Sets the size of the client's input pool.
1374  * @param size The new size
1375  */
1376 void
setPoolInput(int size)1377 MidiClient::setPoolInput(int size)
1378 {
1379     DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_set_client_pool_input(d->m_SeqHandle, size));
1380 }
1381 
1382 /**
1383  * Sets the size of the client's output pool.
1384  * @param size The new size
1385  */
1386 void
setPoolOutput(int size)1387 MidiClient::setPoolOutput(int size)
1388 {
1389     DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_set_client_pool_output(d->m_SeqHandle, size));
1390 }
1391 
1392 /**
1393  * Sets the room size of the client's output pool.
1394  * @param size The new size
1395  */
1396 void
setPoolOutputRoom(int size)1397 MidiClient::setPoolOutputRoom(int size)
1398 {
1399     DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_set_client_pool_output_room(d->m_SeqHandle, size));
1400 }
1401 
1402 /**
1403  * Clears the client's input buffer and and remove events in sequencer queue.
1404  * @see resetPoolInput()
1405  */
1406 void
dropInput()1407 MidiClient::dropInput()
1408 {
1409     DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_drop_input(d->m_SeqHandle));
1410 }
1411 
1412 /**
1413  * Remove all events on user-space input buffer.
1414  * @see dropInput()
1415  */
1416 void
dropInputBuffer()1417 MidiClient::dropInputBuffer()
1418 {
1419     DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_drop_input_buffer(d->m_SeqHandle));
1420 }
1421 
1422 /**
1423  * Clears the client's output buffer and and remove events in sequencer queue.
1424  *
1425  * This method removes all events on both user-space output buffer and output
1426  * memory pool on kernel.
1427  * @see resetPoolOutput()
1428  */
1429 void
dropOutput()1430 MidiClient::dropOutput()
1431 {
1432     DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_drop_output(d->m_SeqHandle));
1433 }
1434 
1435 /**
1436  * Removes all events on the library output buffer.
1437  *
1438  * Removes all events on the user-space output buffer. Unlike dropOutput(), this
1439  * method doesn't remove events on the client's output memory pool.
1440  * @see dropOutput()
1441  */
1442 void
dropOutputBuffer()1443 MidiClient::dropOutputBuffer()
1444 {
1445     DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_drop_output_buffer(d->m_SeqHandle));
1446 }
1447 
1448 /**
1449  * Removes events on input/output buffers and pools.
1450  * Removes matching events with the given condition from input/output buffers
1451  * and pools. The removal condition is specified in the spec argument.
1452  * @param spec A RemoveEvents instance specifying the removal condition.
1453  */
1454 void
removeEvents(const RemoveEvents * spec)1455 MidiClient::removeEvents(const RemoveEvents* spec)
1456 {
1457     DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_remove_events(d->m_SeqHandle, spec->m_Info));
1458 }
1459 
1460 /**
1461  * Extracts (and removes) the first event in the output buffer.
1462  * @return The extracted event.
1463  */
1464 SequencerEvent*
extractOutput()1465 MidiClient::extractOutput()
1466 {
1467     snd_seq_event_t* ev;
1468     if (DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_extract_output(d->m_SeqHandle, &ev) == 0)) {
1469         return new SequencerEvent(ev);
1470     }
1471     return nullptr;
1472 }
1473 
1474 /**
1475  * Returns the size of pending events on the output buffer.
1476  *
1477  * @return The size of pending events.
1478  */
1479 int
outputPending()1480 MidiClient::outputPending()
1481 {
1482     return snd_seq_event_output_pending(d->m_SeqHandle);
1483 }
1484 
1485 /**
1486  * Gets the size of the events on the input buffer.
1487  *
1488  * If there are events remaining on the user-space input buffer, this method
1489  * returns the total size of events on it. If the argument is true, this method
1490  * checks the presence of events on the sequencer FIFO, and when events exist
1491  * they are transferred to the input buffer, and the number of received events
1492  * are returned. If the argument is false and no events remain on the input
1493  * buffer, this method simply returns zero.
1494  *
1495  * @param fetch Check and fetch the sequencer input pool.
1496  * @return The size in bytes of the remaining input events on the buffer.
1497  */
1498 int
inputPending(bool fetch)1499 MidiClient::inputPending(bool fetch)
1500 {
1501     return snd_seq_event_input_pending(d->m_SeqHandle, fetch ? 1 : 0);
1502 }
1503 
1504 /**
1505  * Gets the queue's numeric identifier corresponding to the provided name.
1506  *
1507  * @param name The name string to query.
1508  * @return The number of the matching queue.
1509  */
1510 int
getQueueId(const QString & name)1511 MidiClient::getQueueId(const QString& name)
1512 {
1513     return snd_seq_query_named_queue(d->m_SeqHandle, name.toLocal8Bit().data());
1514 }
1515 
1516 /**
1517  * Returns the number of poll descriptors.
1518  * @param events Poll events to be checked (POLLIN and POLLOUT).
1519  * @return The number of poll descriptors.
1520  */
1521 int
getPollDescriptorsCount(short events)1522 MidiClient::getPollDescriptorsCount(short events)
1523 {
1524     return snd_seq_poll_descriptors_count(d->m_SeqHandle, events);
1525 }
1526 
1527 /**
1528  * Get poll descriptors.
1529  *
1530  * Get poll descriptors assigned to the sequencer handle. Since a sequencer
1531  * handle can duplex streams, you need to set which direction(s) is/are polled
1532  * in events argument. When POLLIN bit is specified, the incoming events to the
1533  * ports are checked.
1534  *
1535  * @param pfds  Array of poll descriptors
1536  * @param space Space in the poll descriptor array
1537  * @param events Polling events to be checked (POLLIN and POLLOUT)
1538  * @return Count of filled descriptors
1539  */
1540 int
pollDescriptors(struct pollfd * pfds,unsigned int space,short events)1541 MidiClient::pollDescriptors( struct pollfd *pfds, unsigned int space,
1542                              short events )
1543 {
1544     return snd_seq_poll_descriptors(d->m_SeqHandle, pfds, space, events);
1545 }
1546 
1547 /**
1548  * Gets the number of returned events from poll descriptors
1549  * @param pfds Array of poll descriptors.
1550  * @param nfds Count of poll descriptors.
1551  * @return Number of returned events.
1552  */
1553 unsigned short
pollDescriptorsRevents(struct pollfd * pfds,unsigned int nfds)1554 MidiClient::pollDescriptorsRevents(struct pollfd *pfds, unsigned int nfds)
1555 {
1556     unsigned short revents;
1557     DRUMSTICK_ALSA_CHECK_WARNING( snd_seq_poll_descriptors_revents( d->m_SeqHandle,
1558                                                      pfds, nfds,
1559                                                      &revents ));
1560     return revents;
1561 }
1562 
1563 /**
1564  * Gets the internal sequencer device name
1565  * @return The device name.
1566  */
1567 const char *
_getDeviceName()1568 MidiClient::_getDeviceName()
1569 {
1570     return snd_seq_name(d->m_SeqHandle);
1571 }
1572 
1573 /**
1574  * Sets the client name
1575  * @param name The new client name.
1576  */
1577 void
_setClientName(const char * name)1578 MidiClient::_setClientName(const char *name)
1579 {
1580     DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_set_client_name(d->m_SeqHandle, name));
1581 }
1582 
1583 /**
1584  * Create an ALSA sequencer port, without using MidiPort.
1585  * @param name The name of the new port.
1586  * @param caps The new port capabilities.
1587  * @param type The type of the new port.
1588  * @return The port numeric identifier.
1589  */
1590 int
createSimplePort(const char * name,unsigned int caps,unsigned int type)1591 MidiClient::createSimplePort( const char *name,
1592                               unsigned int caps,
1593                               unsigned int type )
1594 {
1595     return DRUMSTICK_ALSA_CHECK_WARNING( snd_seq_create_simple_port( d->m_SeqHandle,
1596                                                       name, caps, type ));
1597 }
1598 
1599 /**
1600  * Remove an ALSA sequencer port.
1601  * @param port The numeric identifier of the port.
1602  */
1603 void
deleteSimplePort(int port)1604 MidiClient::deleteSimplePort(int port)
1605 {
1606     DRUMSTICK_ALSA_CHECK_WARNING( snd_seq_delete_simple_port( d->m_SeqHandle, port ));
1607 }
1608 
1609 /**
1610  * Subscribe one port from another arbitrary sequencer client:port.
1611  * @param myport The number of the internal port.
1612  * @param client The external client's identifier.
1613  * @param port The external port's identifier.
1614  */
1615 void
connectFrom(int myport,int client,int port)1616 MidiClient::connectFrom(int myport, int client, int port)
1617 {
1618     DRUMSTICK_ALSA_CHECK_WARNING( snd_seq_connect_from(d->m_SeqHandle, myport, client, port ));
1619 }
1620 
1621 /**
1622  * Subscribe one port to another arbitrary sequencer client:port.
1623  * @param myport The number of the internal port.
1624  * @param client The external client's identifier.
1625  * @param port The external port's identifier.
1626  */
1627 void
connectTo(int myport,int client,int port)1628 MidiClient::connectTo(int myport, int client, int port)
1629 {
1630     DRUMSTICK_ALSA_CHECK_WARNING( snd_seq_connect_to(d->m_SeqHandle, myport, client, port ));
1631 }
1632 
1633 /**
1634  * Unsubscribe one port from another arbitrary sequencer client:port.
1635  * @param myport The number of the internal port.
1636  * @param client The external client's identifier.
1637  * @param port The external port's identifier.
1638  */
1639 void
disconnectFrom(int myport,int client,int port)1640 MidiClient::disconnectFrom(int myport, int client, int port)
1641 {
1642     DRUMSTICK_ALSA_CHECK_WARNING( snd_seq_disconnect_from(d->m_SeqHandle, myport, client, port ));
1643 }
1644 
1645 /**
1646  * Unsubscribe one port to another arbitrary sequencer client:port.
1647  * @param myport The number of the internal port.
1648  * @param client The external client's identifier.
1649  * @param port The external port's identifier.
1650  */
1651 void
disconnectTo(int myport,int client,int port)1652 MidiClient::disconnectTo(int myport, int client, int port)
1653 {
1654     DRUMSTICK_ALSA_CHECK_WARNING( snd_seq_disconnect_to(d->m_SeqHandle, myport, client, port ));
1655 }
1656 
1657 /**
1658  * Parse a text address representation, returning an ALSA address record.
1659  *
1660  * This function can be used as a replacement of the standard ALSA function
1661  * snd_seq_parse_address().
1662  *
1663  * @param straddr source text address representation
1664  * @param addr returned ALSA address record
1665  * @return true if the text address was successfully parsed
1666  * @since 0.3.1
1667  */
1668 bool
parseAddress(const QString & straddr,snd_seq_addr & addr)1669 MidiClient::parseAddress( const QString& straddr, snd_seq_addr& addr )
1670 {
1671     bool ok(false);
1672     QString testClient, testPort;
1673     ClientInfoList::ConstIterator cit;
1674     int pos = straddr.indexOf(':');
1675     if (pos > -1) {
1676         testClient = straddr.left(pos);
1677         testPort = straddr.mid(pos+1);
1678     } else {
1679         testClient = straddr;
1680         testPort = '0';
1681     }
1682     addr.client = testClient.toInt(&ok);
1683     if (ok)
1684         addr.port = testPort.toInt(&ok);
1685     if (!ok) {
1686         if (d->m_NeedRefreshClientList)
1687             readClients();
1688         for ( cit = d->m_ClientList.constBegin();
1689               cit != d->m_ClientList.constEnd(); ++cit ) {
1690             ClientInfo ci = *cit;
1691             if (testClient.compare(ci.getName(), Qt::CaseInsensitive) == 0) {
1692                 addr.client = ci.getClientId();
1693                 addr.port = testPort.toInt(&ok);
1694                 return ok;
1695             }
1696         }
1697     }
1698     return ok;
1699 }
1700 
1701 /**
1702  * Returns true or false depending on the input thread state.
1703  * @return true if the input thread is stopped.
1704  */
1705 bool
stopped()1706 MidiClient::SequencerInputThread::stopped()
1707 {
1708     QReadLocker locker(&m_mutex);
1709     return m_Stopped;
1710 }
1711 
1712 /**
1713  * Stops the input thread.
1714  */
1715 void
stop()1716 MidiClient::SequencerInputThread::stop()
1717 {
1718     QWriteLocker locker(&m_mutex);
1719     m_Stopped = true;
1720 }
1721 
1722 #if defined(RTKIT_SUPPORT)
_gettid()1723 static pid_t _gettid() {
1724     return (pid_t) ::syscall(SYS_gettid);
1725 }
1726 #endif
1727 
1728 void
setRealtimePriority()1729 MidiClient::SequencerInputThread::setRealtimePriority()
1730 {
1731     struct sched_param p;
1732     int rt, policy = SCHED_RR | SCHED_RESET_ON_FORK;
1733     quint32 priority = 6;
1734 #if defined(RTKIT_SUPPORT)
1735     bool ok;
1736     quint32 max_prio;
1737     quint64 thread;
1738     struct rlimit old_limit, new_limit;
1739     long long max_rttime;
1740 #endif
1741 
1742     ::memset(&p, 0, sizeof(p));
1743     p.sched_priority = priority;
1744     rt = ::pthread_setschedparam(::pthread_self(), policy, &p);
1745     if (rt != 0) {
1746 #if defined(RTKIT_SUPPORT)
1747         const QString rtkit_service =
1748                 QStringLiteral("org.freedesktop.RealtimeKit1");
1749         const QString rtkit_path =
1750                 QStringLiteral("/org/freedesktop/RealtimeKit1");
1751         const QString rtkit_iface = rtkit_service;
1752         thread = _gettid();
1753         QDBusConnection bus = QDBusConnection::systemBus();
1754         QDBusInterface realtimeKit(rtkit_service, rtkit_path, rtkit_iface, bus);
1755         QVariant maxRTPrio = realtimeKit.property("MaxRealtimePriority");
1756         max_prio = maxRTPrio.toUInt(&ok);
1757         if (!ok) {
1758             qWarning() << "invalid property RealtimeKit.MaxRealtimePriority";
1759             return;
1760         }
1761         if (priority > max_prio)
1762             priority = max_prio;
1763         QVariant maxRTNSec = realtimeKit.property("RTTimeNSecMax");
1764         max_rttime = maxRTNSec.toLongLong(&ok);
1765         if (!ok || max_rttime < 0) {
1766             qWarning() << "invalid property RealtimeKit.RTTimeNSecMax";
1767             return;
1768         }
1769         new_limit.rlim_cur = new_limit.rlim_max = max_rttime;
1770         rt = ::getrlimit(RLIMIT_RTTIME, &old_limit);
1771         if (rt < 0) {
1772             qWarning() << "getrlimit() failed. err=" << rt << ::strerror(rt);
1773             return;
1774         }
1775         rt = ::setrlimit(RLIMIT_RTTIME, &new_limit);
1776         if ( rt < 0) {
1777             qWarning() << "setrlimit() failed, err=" << rt << ::strerror(rt);
1778             return;
1779         }
1780         QDBusMessage reply = realtimeKit.call("MakeThreadRealtime", thread, priority);
1781         if (reply.type() == QDBusMessage::ErrorMessage )
1782             qWarning() << "error returned by RealtimeKit.MakeThreadRealtime:"
1783                         << reply.errorMessage();
1784 #endif
1785     } else {
1786         qWarning() << "pthread_setschedparam() failed, err="
1787                    << rt << ::strerror(rt);
1788     }
1789 }
1790 
1791 /**
1792  * Main input thread process loop.
1793  */
1794 void
run()1795 MidiClient::SequencerInputThread::run()
1796 {
1797     if ( priority() == TimeCriticalPriority ) {
1798         setRealtimePriority();
1799     }
1800     if (m_MidiClient != nullptr) {
1801         int npfd = snd_seq_poll_descriptors_count(m_MidiClient->getHandle(), POLLIN);
1802         pollfd* pfd = (pollfd *) calloc(npfd, sizeof(pollfd));
1803         try
1804         {
1805             snd_seq_poll_descriptors(m_MidiClient->getHandle(), pfd, npfd, POLLIN);
1806             while (!stopped() && (m_MidiClient != nullptr))
1807             {
1808                 int rt = poll(pfd, npfd, m_Wait);
1809                 if (rt > 0) {
1810                     m_MidiClient->doEvents();
1811                 }
1812             }
1813         }
1814         catch (...)
1815         {
1816             qWarning() << "exception in input thread";
1817         }
1818         free(pfd);
1819     }
1820 }
1821 
1822 /**
1823  * Default constructor
1824  */
ClientInfo()1825 ClientInfo::ClientInfo()
1826 {
1827     snd_seq_client_info_malloc(&m_Info);
1828 }
1829 
1830 /**
1831  * Copy constructor
1832  * @param other Another ClientInfo reference to be copied
1833  */
ClientInfo(const ClientInfo & other)1834 ClientInfo::ClientInfo(const ClientInfo& other)
1835 {
1836     snd_seq_client_info_malloc(&m_Info);
1837     snd_seq_client_info_copy(m_Info, other.m_Info);
1838     m_Ports = other.m_Ports;
1839 }
1840 
1841 /**
1842  * Copy constructor
1843  * @param other An existing ALSA client info object
1844  */
ClientInfo(snd_seq_client_info_t * other)1845 ClientInfo::ClientInfo(snd_seq_client_info_t* other)
1846 {
1847     snd_seq_client_info_malloc(&m_Info);
1848     snd_seq_client_info_copy(m_Info, other);
1849 }
1850 
1851 /**
1852  * Constructor
1853  * @param seq A MidiClient object
1854  * @param id A numeric client id
1855  */
ClientInfo(MidiClient * seq,int id)1856 ClientInfo::ClientInfo(MidiClient* seq, int id)
1857 {
1858     snd_seq_client_info_malloc(&m_Info);
1859     snd_seq_get_any_client_info(seq->getHandle(), id, m_Info);
1860 }
1861 
1862 /**
1863  * Destructor
1864  */
~ClientInfo()1865 ClientInfo::~ClientInfo()
1866 {
1867     freePorts();
1868     snd_seq_client_info_free(m_Info);
1869 }
1870 
1871 /**
1872  * Clone the client info object.
1873  * @return A pointer to the new object.
1874  */
1875 ClientInfo*
clone()1876 ClientInfo::clone()
1877 {
1878     return new ClientInfo(m_Info);
1879 }
1880 
1881 /**
1882  * Assignment operator
1883  * @param other Another ClientInfo object
1884  * @return This object
1885  */
1886 ClientInfo&
operator =(const ClientInfo & other)1887 ClientInfo::operator=(const ClientInfo& other)
1888 {
1889     if (this == &other)
1890         return *this;
1891     snd_seq_client_info_copy(m_Info, other.m_Info);
1892     m_Ports = other.m_Ports;
1893     return *this;
1894 }
1895 
1896 /**
1897  * Gets the client's numeric identifier.
1898  * @return The client's numeric identifier.
1899  */
1900 int
getClientId()1901 ClientInfo::getClientId()
1902 {
1903     return snd_seq_client_info_get_client(m_Info);
1904 }
1905 
1906 /**
1907  * Gets the client's type
1908  * @return The client's type.
1909  */
1910 snd_seq_client_type_t
getClientType()1911 ClientInfo::getClientType()
1912 {
1913     return snd_seq_client_info_get_type(m_Info);
1914 }
1915 
1916 /**
1917  * Gets the client's name
1918  * @return The client's name.
1919  */
1920 QString
getName()1921 ClientInfo::getName()
1922 {
1923     return QString(snd_seq_client_info_get_name(m_Info));
1924 }
1925 
1926 /**
1927  * Gets the client's broadcast filter
1928  * @return The client's broadcast filter.
1929  */
1930 bool
getBroadcastFilter()1931 ClientInfo::getBroadcastFilter()
1932 {
1933     return (snd_seq_client_info_get_broadcast_filter(m_Info) != 0);
1934 }
1935 
1936 /**
1937  * Gets the client's error bounce
1938  * @return The client's error bounce.
1939  */
1940 bool
getErrorBounce()1941 ClientInfo::getErrorBounce()
1942 {
1943     return (snd_seq_client_info_get_error_bounce(m_Info) != 0);
1944 }
1945 
1946 /**
1947  * Gets the client's event filter.
1948  * @return The client's event filter.
1949  * @deprecated Use isFiltered() instead.
1950  */
1951 const unsigned char*
getEventFilter()1952 ClientInfo::getEventFilter()
1953 {
1954     return snd_seq_client_info_get_event_filter(m_Info);
1955 }
1956 
1957 /**
1958  * Gets the client's port count.
1959  * @return The client's port count.
1960  */
1961 int
getNumPorts()1962 ClientInfo::getNumPorts()
1963 {
1964     return snd_seq_client_info_get_num_ports(m_Info);
1965 }
1966 
1967 /**
1968  * Gets the number of lost events.
1969  * @return The number of lost events.
1970  */
1971 int
getEventLost()1972 ClientInfo::getEventLost()
1973 {
1974     return snd_seq_client_info_get_event_lost(m_Info);
1975 }
1976 
1977 /**
1978  * Sets the client identifier number.
1979  * @param client The client identifier number.
1980  */
1981 void
setClient(int client)1982 ClientInfo::setClient(int client)
1983 {
1984     snd_seq_client_info_set_client(m_Info, client);
1985 }
1986 
1987 /**
1988  * Sets the client name.
1989  * @param name The client name.
1990  */
1991 void
setName(QString name)1992 ClientInfo::setName(QString name)
1993 {
1994     snd_seq_client_info_set_name(m_Info, name.toLocal8Bit().data());
1995 }
1996 
1997 /**
1998  * Sets the broadcast filter.
1999  * @param val The broadcast filter.
2000  */
2001 void
setBroadcastFilter(bool val)2002 ClientInfo::setBroadcastFilter(bool val)
2003 {
2004     snd_seq_client_info_set_broadcast_filter(m_Info, val ? 1 : 0);
2005 }
2006 
2007 /**
2008  * Sets the error bounce.
2009  * @param val The error bounce.
2010  */
2011 void
setErrorBounce(bool val)2012 ClientInfo::setErrorBounce(bool val)
2013 {
2014     snd_seq_client_info_set_error_bounce(m_Info, val ? 1 : 0);
2015 }
2016 
2017 /**
2018  * Sets the event filter.
2019  * @param filter The event filter.
2020  * @deprecated Use addFilter() instead.
2021  */
2022 void
setEventFilter(unsigned char * filter)2023 ClientInfo::setEventFilter(unsigned char *filter)
2024 {
2025     snd_seq_client_info_set_event_filter(m_Info, filter);
2026 }
2027 
2028 /**
2029  * Read the client ports.
2030  * @param seq The client instance.
2031  */
2032 void
readPorts(MidiClient * seq)2033 ClientInfo::readPorts(MidiClient* seq)
2034 {
2035     PortInfo info;
2036     freePorts();
2037     info.setClient(getClientId());
2038     info.setClientName(getName());
2039     info.setPort(-1);
2040     while (snd_seq_query_next_port(seq->getHandle(), info.m_Info) >= 0) {
2041         info.readSubscribers(seq);
2042         m_Ports.append(info);
2043     }
2044 }
2045 
2046 /**
2047  * Release the ports list.
2048  */
2049 void
freePorts()2050 ClientInfo::freePorts()
2051 {
2052     m_Ports.clear();
2053 }
2054 
2055 /**
2056  * Gets the ports list.
2057  * @return The ports list.
2058  */
2059 PortInfoList
getPorts() const2060 ClientInfo::getPorts() const
2061 {
2062     PortInfoList lst = m_Ports; // copy
2063     return lst;
2064 }
2065 
2066 /**
2067  * Gets the size of the internal object.
2068  * @return The size of the internal object.
2069  */
2070 int
getSizeOfInfo() const2071 ClientInfo::getSizeOfInfo() const
2072 {
2073     return snd_seq_client_info_sizeof();
2074 }
2075 
2076 #if SND_LIB_VERSION > 0x010010
2077 /**
2078  * Adds an event type to the client's filter.
2079  *
2080  * @param eventType The new event's type.
2081  */
2082 void
addFilter(int eventType)2083 ClientInfo::addFilter(int eventType)
2084 {
2085     snd_seq_client_info_event_filter_add(m_Info, eventType);
2086 }
2087 
2088 /**
2089  * Checks id the given event's type is filtered.
2090  * @param eventType The event's type.
2091  * @return true if the event type is filtered
2092  */
2093 bool
isFiltered(int eventType)2094 ClientInfo::isFiltered(int eventType)
2095 {
2096     return (snd_seq_client_info_event_filter_check(m_Info, eventType) != 0);
2097 }
2098 
2099 /**
2100  * Clear the client's event filter
2101  */
2102 void
clearFilter()2103 ClientInfo::clearFilter()
2104 {
2105     snd_seq_client_info_event_filter_clear(m_Info);
2106 }
2107 
2108 /**
2109  * Removes the event type from the client's filter.
2110  * @param eventType The event's type.
2111  */
2112 void
removeFilter(int eventType)2113 ClientInfo::removeFilter(int eventType)
2114 {
2115     snd_seq_client_info_event_filter_del(m_Info, eventType);
2116 }
2117 #endif
2118 
2119 /**
2120  * Default constructor
2121  */
SystemInfo()2122 SystemInfo::SystemInfo()
2123 {
2124     snd_seq_system_info_malloc(&m_Info);
2125 }
2126 
2127 /**
2128  * Copy constructor
2129  * @param other Another SystemInfo object reference to be copied
2130  */
SystemInfo(const SystemInfo & other)2131 SystemInfo::SystemInfo(const SystemInfo& other)
2132 {
2133     snd_seq_system_info_malloc(&m_Info);
2134     snd_seq_system_info_copy(m_Info, other.m_Info);
2135 }
2136 
2137 /**
2138  * Copy constructor
2139  * @param other Another ALSA system info object to be copied
2140  */
SystemInfo(snd_seq_system_info_t * other)2141 SystemInfo::SystemInfo(snd_seq_system_info_t* other)
2142 {
2143     snd_seq_system_info_malloc(&m_Info);
2144     snd_seq_system_info_copy(m_Info, other);
2145 }
2146 
2147 /**
2148  * Constructor
2149  * @param seq A MidiClient object
2150  */
SystemInfo(MidiClient * seq)2151 SystemInfo::SystemInfo(MidiClient* seq)
2152 {
2153     snd_seq_system_info_malloc(&m_Info);
2154     snd_seq_system_info(seq->getHandle(), m_Info);
2155 }
2156 
2157 /**
2158  * Destructor
2159  */
~SystemInfo()2160 SystemInfo::~SystemInfo()
2161 {
2162     snd_seq_system_info_free(m_Info);
2163 }
2164 
2165 /**
2166  * Clone the system info object
2167  * @return A pointer to the new object
2168  */
2169 SystemInfo*
clone()2170 SystemInfo::clone()
2171 {
2172     return new SystemInfo(m_Info);
2173 }
2174 
2175 /**
2176  * Assignment operator
2177  * @param other Another SystemInfo object
2178  * @return This object
2179  */
2180 SystemInfo&
operator =(const SystemInfo & other)2181 SystemInfo::operator=(const SystemInfo& other)
2182 {
2183     if (this == &other)
2184         return *this;
2185     snd_seq_system_info_copy(m_Info, other.m_Info);
2186     return *this;
2187 }
2188 
2189 /**
2190  * Get the system's maximum number of clients.
2191  * @return The maximum number of clients.
2192  */
getMaxClients()2193 int SystemInfo::getMaxClients()
2194 {
2195     return snd_seq_system_info_get_clients(m_Info);
2196 }
2197 
2198 /**
2199  * Get the system's maximum number of ports.
2200  * @return The maximum number of ports.
2201  */
getMaxPorts()2202 int SystemInfo::getMaxPorts()
2203 {
2204     return snd_seq_system_info_get_ports(m_Info);
2205 }
2206 
2207 /**
2208  * Get the system's maximum number of queues.
2209  * @return The system's maximum number of queues.
2210  */
getMaxQueues()2211 int SystemInfo::getMaxQueues()
2212 {
2213     return snd_seq_system_info_get_queues(m_Info);
2214 }
2215 
2216 /**
2217  * Get the system's maximum number of channels.
2218  * @return The system's maximum number of channels.
2219  */
getMaxChannels()2220 int SystemInfo::getMaxChannels()
2221 {
2222     return snd_seq_system_info_get_channels(m_Info);
2223 }
2224 
2225 /**
2226  * Get the system's current number of queues.
2227  * @return The system's current number of queues.
2228  */
getCurrentQueues()2229 int SystemInfo::getCurrentQueues()
2230 {
2231     return snd_seq_system_info_get_cur_queues(m_Info);
2232 }
2233 
2234 /**
2235  * Get the system's current number of clients.
2236  * @return The system's current number of clients.
2237  */
getCurrentClients()2238 int SystemInfo::getCurrentClients()
2239 {
2240     return snd_seq_system_info_get_cur_clients(m_Info);
2241 }
2242 
2243 /**
2244  * Get the system's info object size.
2245  * @return The system's info object size.
2246  */
getSizeOfInfo() const2247 int SystemInfo::getSizeOfInfo() const
2248 {
2249     return snd_seq_system_info_sizeof();
2250 }
2251 
2252 /**
2253  * Default constructor
2254  */
PoolInfo()2255 PoolInfo::PoolInfo()
2256 {
2257     snd_seq_client_pool_malloc(&m_Info);
2258 }
2259 
2260 /**
2261  * Copy constructor
2262  * @param other Another PoolInfo object reference to be copied
2263  */
PoolInfo(const PoolInfo & other)2264 PoolInfo::PoolInfo(const PoolInfo& other)
2265 {
2266     snd_seq_client_pool_malloc(&m_Info);
2267     snd_seq_client_pool_copy(m_Info, other.m_Info);
2268 }
2269 
2270 /**
2271  * Copy constructor
2272  * @param other An ALSA pool info object to be copied
2273  */
PoolInfo(snd_seq_client_pool_t * other)2274 PoolInfo::PoolInfo(snd_seq_client_pool_t* other)
2275 {
2276     snd_seq_client_pool_malloc(&m_Info);
2277     snd_seq_client_pool_copy(m_Info, other);
2278 }
2279 
2280 /**
2281  * Constructor
2282  * @param seq A MidiClient object
2283  */
PoolInfo(MidiClient * seq)2284 PoolInfo::PoolInfo(MidiClient* seq)
2285 {
2286     snd_seq_client_pool_malloc(&m_Info);
2287     snd_seq_get_client_pool(seq->getHandle(), m_Info);
2288 }
2289 
2290 /**
2291  * Destructor
2292  */
~PoolInfo()2293 PoolInfo::~PoolInfo()
2294 {
2295     snd_seq_client_pool_free(m_Info);
2296 }
2297 
2298 /**
2299  * Clone the pool info obeject
2300  * @return A pointer to the new object
2301  */
2302 PoolInfo*
clone()2303 PoolInfo::clone()
2304 {
2305     return new PoolInfo(m_Info);
2306 }
2307 
2308 /**
2309  * Assignment operator
2310  * @param other Another PoolInfo object reference to be copied
2311  * @return This object
2312  */
2313 PoolInfo&
operator =(const PoolInfo & other)2314 PoolInfo::operator=(const PoolInfo& other)
2315 {
2316     if (this == &other)
2317         return *this;
2318     snd_seq_client_pool_copy(m_Info, other.m_Info);
2319     return *this;
2320 }
2321 
2322 /**
2323  * Gets the client ID for this object.
2324  * @return The client ID.
2325  */
2326 int
getClientId()2327 PoolInfo::getClientId()
2328 {
2329     return snd_seq_client_pool_get_client(m_Info);
2330 }
2331 
2332 /**
2333  * Gets the available size on input pool.
2334  * @return The available size on input pool.
2335  */
2336 int
getInputFree()2337 PoolInfo::getInputFree()
2338 {
2339     return snd_seq_client_pool_get_input_free(m_Info);
2340 }
2341 
2342 /**
2343  * Gets the input pool size.
2344  * @return The input pool size.
2345  */
2346 int
getInputPool()2347 PoolInfo::getInputPool()
2348 {
2349     return snd_seq_client_pool_get_input_pool(m_Info);
2350 }
2351 
2352 /**
2353  * Gets the available size on output pool.
2354  * @return The available size on output pool.
2355  */
2356 int
getOutputFree()2357 PoolInfo::getOutputFree()
2358 {
2359     return snd_seq_client_pool_get_output_free(m_Info);
2360 }
2361 
2362 /**
2363  * Gets the output pool size.
2364  * @return The output pool size.
2365  */
2366 int
getOutputPool()2367 PoolInfo::getOutputPool()
2368 {
2369     return snd_seq_client_pool_get_output_pool(m_Info);
2370 }
2371 
2372 /**
2373  * Gets the output room size.
2374  * The output room is the minimum pool size for select/blocking mode.
2375  * @return The output room size.
2376  */
2377 int
getOutputRoom()2378 PoolInfo::getOutputRoom()
2379 {
2380     return snd_seq_client_pool_get_output_room(m_Info);
2381 }
2382 
2383 /**
2384  * Set the input pool size.
2385  * @param size The input pool size.
2386  */
2387 void
setInputPool(int size)2388 PoolInfo::setInputPool(int size)
2389 {
2390     snd_seq_client_pool_set_input_pool(m_Info, size);
2391 }
2392 
2393 /**
2394  * Sets the output pool size.
2395  * @param size The output pool size.
2396  */
2397 void
setOutputPool(int size)2398 PoolInfo::setOutputPool(int size)
2399 {
2400     snd_seq_client_pool_set_output_pool(m_Info, size);
2401 }
2402 
2403 /**
2404  * Sets the output room size.
2405  * The output room is the minimum pool size for select/blocking mode.
2406  *
2407  * @param size Output room size
2408  */
2409 void
setOutputRoom(int size)2410 PoolInfo::setOutputRoom(int size)
2411 {
2412     snd_seq_client_pool_set_output_room(m_Info, size);
2413 }
2414 
2415 /**
2416  * Gets the size of the client pool object.
2417  * @return The size of the client pool object.
2418  */
2419 int
getSizeOfInfo() const2420 PoolInfo::getSizeOfInfo() const
2421 {
2422     return snd_seq_client_pool_sizeof();
2423 }
2424 
2425 #if SND_LIB_VERSION > 0x010004
2426 /**
2427  * Gets the runtime ALSA library version string
2428  * @return string representing the runtime ALSA library version
2429  * @since 0.3.0
2430  */
2431 QString
getRuntimeALSALibraryVersion()2432 getRuntimeALSALibraryVersion()
2433 {
2434     return QString(snd_asoundlib_version());
2435 }
2436 
2437 /**
2438  * Gets the runtime ALSA library version number
2439  * @return integer representing the runtime ALSA library version
2440  * @since 0.3.0
2441  */
2442 int
getRuntimeALSALibraryNumber()2443 getRuntimeALSALibraryNumber()
2444 {
2445     QRegularExpression rx("(\\d+)");
2446     QString str = getRuntimeALSALibraryVersion();
2447     bool ok;
2448     int result = 0, j = 0;
2449     QRegularExpressionMatchIterator i = rx.globalMatch(str);
2450     while (i.hasNext() && (j < 3)) {
2451         QRegularExpressionMatch m = i.next();
2452         int v = m.captured(1).toInt(&ok);
2453         if (ok) {
2454             result <<= 8;
2455             result += v;
2456         }
2457         j++;
2458     }
2459     return result;
2460 }
2461 #endif // SND_LIB_VERSION > 0x010004
2462 
2463 /**
2464  * Gets the runtime ALSA drivers version string
2465  * @return string representing the runtime ALSA drivers version
2466  * @since 0.3.0
2467  */
2468 QString
getRuntimeALSADriverVersion()2469 getRuntimeALSADriverVersion()
2470 {
2471     QRegularExpression rx("([\\d\\.]+)");
2472     QString s;
2473     QFile f("/proc/asound/version");
2474     if (f.open(QFile::ReadOnly)) {
2475         QTextStream str(&f);
2476         QString sub = str.readLine().trimmed();
2477         QRegularExpressionMatch m = rx.match(sub);
2478         if (m.hasMatch()) {
2479             s = m.captured(1);
2480         }
2481     }
2482     return s;
2483 }
2484 
2485 /**
2486  * Gets the runtime ALSA drivers version number
2487  * @return integer representing the runtime ALSA drivers version
2488  * @since 0.3.0
2489  */
2490 int
getRuntimeALSADriverNumber()2491 getRuntimeALSADriverNumber()
2492 {
2493     QRegularExpression rx("(\\d+)");
2494     QString str = getRuntimeALSADriverVersion();
2495     bool ok;
2496     int result = 0, j = 0;
2497     QRegularExpressionMatchIterator i = rx.globalMatch(str);
2498     while (i.hasNext() && (j < 3)) {
2499         QRegularExpressionMatch m = i.next();
2500         int v = m.captured(1).toInt(&ok);
2501         if (ok) {
2502             result <<= 8;
2503             result += v;
2504         }
2505         j++;
2506     }
2507     return result;
2508 }
2509 
2510 /**
2511  * ALSA library version at build time.
2512  *
2513  * This string corresponds to the compilation library, which may be
2514  * different to the runtime library.
2515  * @return ALSA runtime library formatted as a QString
2516  * @see getRuntimeALSALibraryVersion
2517  */
getCompiledALSALibraryVersion()2518 QString getCompiledALSALibraryVersion()
2519 {
2520     return QStringLiteral(SND_LIB_VERSION_STR);
2521 }
2522 
2523 /**
2524  * @brief getDrumstickLibraryVersion provides the Drumstick version as an edited QString
2525  * @return Drumstick library version
2526  */
getDrumstickLibraryVersion()2527 QString getDrumstickLibraryVersion()
2528 {
2529     return QStringLiteral(QT_STRINGIFY(VERSION));
2530 }
2531 
2532 /** @} */
2533 
2534 } // namespace ALSA
2535 } // namespace drumstick
2536 
2537