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 = "default".
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 "default" 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 = "default".
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 "default" 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