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 <QReadLocker>
21 #include <QWriteLocker>
22 #include <cmath>
23 #include <cstdio>
24 #include <drumstick/alsatimer.h>
25 /**
26  * @file alsatimer.cpp
27  * Implementation of classes managing ALSA Timers
28  */
29 
30 namespace drumstick {
31 namespace ALSA {
32 
33 /**
34  * @addtogroup ALSATimer
35  * @{
36  *
37  * Timers provide periodic time events to applications, and also to the ALSA
38  * sequencer.
39  *
40  * There are two mechanisms to deliver the timer events. To use the callback
41  * mechanism, a class must be derived from TimerEventHandler, and a instance
42  * of the derived class must be assigned to the Timer instance using
43  * Timer::setHandler(). If the handler is not assigned, then the Timer instance
44  * will generate the signal Timer::timerExpired().
45  *
46  * Classes:
47  *
48  * TimerInfo: ALSA Timer information container.
49  *
50  * This class is used to hold properties about ALSA Timers.
51  *
52  * TimerId: ALSA Timer identifier container.
53  *
54  * This class provides an unique identifier for a Timer.
55  *
56  * TimerGlobalInfo: Global timer information container.
57  *
58  * This class provides global timer parameters.
59  *
60  * TimerQuery: ALSA Timer inquiry helper.
61  *
62  * This class provides a mechanism to enumerate the available system timers.
63  *
64  * TimerParams: ALSA Timer parameters container.
65  *
66  * This class provides several parameters about a Timer.
67  *
68  * TimerStatus: ALSA Timer status container.
69  *
70  * This class provides some status information about a Timer.
71  *
72  * TimerEventHandler: ALSA Timer events handler.
73  *
74  * This abstract class is used to define an interface that other class can
75  * implement to receive timer events.
76  *
77  * Timer: ALSA Timer management.
78  *
79  * This class represents an ALSA timer object.
80  *
81  * @see https://www.alsa-project.org/alsa-doc/alsa-lib/group___timer.html
82  * @}
83  */
84 
85 /**
86  * Constructor
87  */
TimerInfo()88 TimerInfo::TimerInfo()
89 {
90     snd_timer_info_malloc(&m_Info);
91 }
92 
93 /**
94  * Cosntructor
95  * @param other ALSA timer info object pointer
96  */
TimerInfo(const snd_timer_info_t * other)97 TimerInfo::TimerInfo(const snd_timer_info_t *other)
98 {
99     snd_timer_info_malloc(&m_Info);
100     snd_timer_info_copy(m_Info, other);
101 }
102 
103 /**
104  * Copy constructor
105  * @param other Existing TimerInfo object reference
106  */
TimerInfo(const TimerInfo & other)107 TimerInfo::TimerInfo(const TimerInfo& other)
108 {
109     snd_timer_info_malloc(&m_Info);
110     snd_timer_info_copy(m_Info, other.m_Info);
111 }
112 
113 /**
114  * Destructor
115  */
~TimerInfo()116 TimerInfo::~TimerInfo()
117 {
118     snd_timer_info_free(m_Info);
119 }
120 
121 /**
122  * Copy the current object
123  * @return Pointer to the new object
124  */
125 TimerInfo*
clone()126 TimerInfo::clone()
127 {
128     return new TimerInfo(m_Info);
129 }
130 
131 /**
132  * Assignment operator
133  * @param other Existing TimerInfo object reference
134  * @return a reference of this object
135  */
136 TimerInfo&
operator =(const TimerInfo & other)137 TimerInfo::operator=(const TimerInfo& other)
138 {
139     if (this == &other)
140         return *this;
141     snd_timer_info_copy(m_Info, other.m_Info);
142     return *this;
143 }
144 
145 /**
146  * Check if the timer is slave (depends on another device)
147  * @return True if the timer is slave
148  */
149 bool
isSlave()150 TimerInfo::isSlave()
151 {
152     return (snd_timer_info_is_slave(m_Info) != 0);
153 }
154 
155 /**
156  * Gets the card number
157  * @return Card number
158  */
159 int
getCard()160 TimerInfo::getCard()
161 {
162     return snd_timer_info_get_card(m_Info);
163 }
164 
165 /**
166  * Gets the string identifier
167  * @return String identifier
168  */
169 QString
getId()170 TimerInfo::getId()
171 {
172     return QString(snd_timer_info_get_id(m_Info));
173 }
174 
175 /**
176  * Gets the timer name
177  * @return Timer name
178  */
179 QString
getName()180 TimerInfo::getName()
181 {
182     return QString(snd_timer_info_get_name(m_Info));
183 }
184 
185 /**
186  * Gets the timer resolution (timer period in nanoseconds)
187  * @return Timer resolution in nanos
188  */
189 long
getResolution()190 TimerInfo::getResolution()
191 {
192     return snd_timer_info_get_resolution(m_Info);
193 }
194 
195 /**
196  * Gets the timer frequency in Hz
197  * @return Timer frequency in Hz
198  */
199 long
getFrequency()200 TimerInfo::getFrequency()
201 {
202     long res = getResolution();
203     if (res > 0)
204     {
205         return 1000000000L / res;
206     }
207     return 0;
208 }
209 
210 /**
211  * Gets the size of the ALSA timer info object
212  * @return Size of the ALSA object
213  */
214 int
getSizeOfInfo() const215 TimerInfo::getSizeOfInfo() const
216 {
217     return snd_timer_info_sizeof();
218 }
219 
220 /**
221  * Gets the maximum timer ticks
222  * @return Maximum timer ticks
223  * @deprecated
224  */
225 long
getTicks()226 TimerInfo::getTicks()
227 {
228     return snd_timer_info_get_ticks(m_Info);
229 }
230 
231 /**
232  * Constructor
233  */
TimerId()234 TimerId::TimerId()
235 {
236     snd_timer_id_malloc(&m_Info);
237 }
238 
239 /**
240  * Constructor
241  * @param other ALSA timer ID object pointer
242  */
TimerId(const snd_timer_id_t * other)243 TimerId::TimerId(const snd_timer_id_t *other)
244 {
245     snd_timer_id_malloc(&m_Info);
246     snd_timer_id_copy(m_Info, other);
247     if (getCard() < 0)
248         setCard(0);
249     if (getDevice() < 0)
250         setDevice(0);
251     if (getSubdevice() < 0)
252         setSubdevice(0);
253 }
254 
255 /**
256  * Copy constructor
257  * @param other Existing TimerId object reference
258  */
TimerId(const TimerId & other)259 TimerId::TimerId(const TimerId& other)
260 {
261     snd_timer_id_malloc(&m_Info);
262     snd_timer_id_copy(m_Info, other.m_Info);
263     if (getCard() < 0)
264         setCard(0);
265     if (getDevice() < 0)
266         setDevice(0);
267     if (getSubdevice() < 0)
268         setSubdevice(0);
269 }
270 
271 /**
272  * Constructor
273  * @param cls  Class
274  * @param scls Subclass
275  * @param card Card
276  * @param dev  Device
277  * @param sdev Subdevice
278  */
TimerId(int cls,int scls,int card,int dev,int sdev)279 TimerId::TimerId(int cls, int scls, int card, int dev, int sdev)
280 {
281     snd_timer_id_malloc(&m_Info);
282     setClass(cls);
283     setSlaveClass(scls);
284     setCard(card);
285     setDevice(dev);
286     setSubdevice(sdev);
287 }
288 
289 /**
290  * Destructor
291  */
~TimerId()292 TimerId::~TimerId()
293 {
294     snd_timer_id_free(m_Info);
295 }
296 
297 /**
298  * Copy the object
299  * @return Pointer to the new object
300  */
301 TimerId*
clone()302 TimerId::clone()
303 {
304     return new TimerId(m_Info);
305 }
306 
307 /**
308  * Assignment operator
309  * @param other Existing TimerId object reference
310  * @return This object
311  */
312 TimerId&
operator =(const TimerId & other)313 TimerId::operator=(const TimerId& other)
314 {
315     if (this == &other)
316         return *this;
317     snd_timer_id_copy(m_Info, other.m_Info);
318     if (getCard() < 0)
319     setCard(0);
320     if (getDevice() < 0)
321     setDevice(0);
322     if (getSubdevice() < 0)
323     setSubdevice(0);
324     return *this;
325 }
326 
327 /**
328  * Set the class identifier. Existing classes:
329  * <ul>
330  * <li> SND_TIMER_CLASS_SLAVE: slave timer</li>
331  * <li> SND_TIMER_CLASS_GLOBAL: global timer</li>
332  * <li> SND_TIMER_CLASS_CARD: card timer</li>
333  * <li> SND_TIMER_CLASS_PCM: PCM timer</li>
334  * </ul>
335  * @param devclass Class identifier.
336  */
337 void
setClass(int devclass)338 TimerId::setClass(int devclass)
339 {
340     snd_timer_id_set_class(m_Info, devclass);
341 }
342 
343 /**
344  * Gets the class identifier.
345  * @return Class identifier
346  * @see setClass()
347  */
348 int
getClass()349 TimerId::getClass()
350 {
351     return snd_timer_id_get_class(m_Info);
352 }
353 
354 /**
355  * Sets the Slave class
356  * @param devsclass Slave class
357  */
358 void
setSlaveClass(int devsclass)359 TimerId::setSlaveClass(int devsclass)
360 {
361     snd_timer_id_set_sclass(m_Info, devsclass);
362 }
363 
364 /**
365  * Gets the slave class
366  * @return Slave class
367  */
368 int
getSlaveClass()369 TimerId::getSlaveClass()
370 {
371     return snd_timer_id_get_sclass(m_Info);
372 }
373 
374 /**
375  * Sets the card number
376  * @param card Card number
377  */
378 void
setCard(int card)379 TimerId::setCard(int card)
380 {
381     snd_timer_id_set_card(m_Info, card);
382 }
383 
384 /**
385  * Gets the card number
386  * @return Card number
387  */
388 int
getCard()389 TimerId::getCard()
390 {
391     return snd_timer_id_get_card(m_Info);
392 }
393 
394 /**
395  * Sets the device number
396  * @param device Device number
397  */
398 void
setDevice(int device)399 TimerId::setDevice(int device)
400 {
401     snd_timer_id_set_device(m_Info, device);
402 }
403 
404 /**
405  * Gets the device number
406  * @return Device number
407  */
408 int
getDevice()409 TimerId::getDevice()
410 {
411     return snd_timer_id_get_device(m_Info);
412 }
413 
414 /**
415  * Sets the subdevice number
416  * @param subdevice Subdevice number
417  */
418 void
setSubdevice(int subdevice)419 TimerId::setSubdevice(int subdevice)
420 {
421     snd_timer_id_set_subdevice (m_Info, subdevice);
422 }
423 
424 /**
425  * Gets the subdevice number
426  * @return Subdevice number
427  */
428 int
getSubdevice()429 TimerId::getSubdevice()
430 {
431     return snd_timer_id_get_subdevice(m_Info);
432 }
433 
434 /**
435  * Gets the size of the ALSA timer ID object
436  * @return Size of the ALSA object
437  */
438 int
getSizeOfInfo() const439 TimerId::getSizeOfInfo() const
440 {
441     return snd_timer_id_sizeof();
442 }
443 
444 /**
445  * Constructor
446  * @param deviceName Device name, usually "hw"
447  * @param openMode Open mode (unknown values)
448  */
TimerQuery(const QString & deviceName,int openMode)449 TimerQuery::TimerQuery(const QString& deviceName, int openMode)
450 {
451     DRUMSTICK_ALSA_CHECK_WARNING( snd_timer_query_open( &m_Info,
452                                          deviceName.toLocal8Bit().data(),
453                                          openMode ));
454     readTimers();
455 }
456 
457 /**
458  * Constructor
459  * @param deviceName Device name, usually "hw"
460  * @param openMode Open mode (unknown values)
461  * @param conf ALSA configuration object pointer
462  */
TimerQuery(const QString & deviceName,int openMode,snd_config_t * conf)463 TimerQuery::TimerQuery( const QString& deviceName, int openMode,
464                         snd_config_t* conf )
465 {
466     DRUMSTICK_ALSA_CHECK_WARNING( snd_timer_query_open_lconf( &m_Info,
467                                                deviceName.toLocal8Bit().data(),
468                                                openMode, conf ));
469     readTimers();
470 }
471 
472 /**
473  * Destructor
474  */
~TimerQuery()475 TimerQuery::~TimerQuery()
476 {
477     freeTimers();
478     snd_timer_query_close(m_Info);
479 }
480 
481 /**
482  * Enumerate the available timers storing the results into an internal list
483  */
484 void
readTimers()485 TimerQuery::readTimers()
486 {
487     TimerId tid;
488     snd_timer_id_set_class(tid.m_Info, SND_TIMER_CLASS_NONE);
489     for(;;)
490     {
491         int rc = snd_timer_query_next_device(m_Info, tid.m_Info);
492         if ((rc < 0) || (tid.getClass() < 0)) {
493             break;
494         }
495         m_timers.append(tid);
496     }
497 }
498 
499 /**
500  * Release the internal list of timers
501  */
502 void
freeTimers()503 TimerQuery::freeTimers()
504 {
505     m_timers.clear();
506 }
507 
508 /**
509  * Get a TimerGlobalInfo object
510  * @return TimerGlobalInfo object reference
511  */
512 TimerGlobalInfo&
getGlobalInfo()513 TimerQuery::getGlobalInfo()
514 {
515     snd_timer_query_info(m_Info, m_GlobalInfo.m_Info);
516     return m_GlobalInfo;
517 }
518 
519 /**
520  * Sets the global parameters
521  * @param params Pointer to an ALSA timer global parameters object
522  */
523 void
setGlobalParams(snd_timer_gparams_t * params)524 TimerQuery::setGlobalParams(snd_timer_gparams_t* params)
525 {
526     snd_timer_query_params(m_Info, params);
527 }
528 
529 /**
530  * Gets the global timer parameters
531  * @param params Pointer to an ALSA timer global parameters object
532  */
533 void
getGlobalParams(snd_timer_gparams_t * params)534 TimerQuery::getGlobalParams(snd_timer_gparams_t* params)
535 {
536     snd_timer_query_params(m_Info, params);
537 }
538 
539 /**
540  * Gets the global timer status
541  * @param status Pointer to an ALSA timer global status object
542  */
543 void
getGlobalStatus(snd_timer_gstatus_t * status)544 TimerQuery::getGlobalStatus(snd_timer_gstatus_t *status)
545 {
546     snd_timer_query_status(m_Info, status);
547 }
548 
549 /**
550  * Default constructor
551  */
TimerGlobalInfo()552 TimerGlobalInfo::TimerGlobalInfo()
553 {
554     snd_timer_ginfo_malloc(&m_Info);
555 }
556 
557 /**
558  * Constructor
559  * @param other ALSA global info object pointer
560  */
TimerGlobalInfo(const snd_timer_ginfo_t * other)561 TimerGlobalInfo::TimerGlobalInfo(const snd_timer_ginfo_t* other)
562 {
563     snd_timer_ginfo_malloc(&m_Info);
564     snd_timer_ginfo_copy(m_Info, other);
565 }
566 
567 /**
568  * Copy constructor
569  * @param other Existing TimerGlobalInfo object reference
570  */
TimerGlobalInfo(const TimerGlobalInfo & other)571 TimerGlobalInfo::TimerGlobalInfo(const TimerGlobalInfo& other)
572 {
573     snd_timer_ginfo_malloc(&m_Info);
574     snd_timer_ginfo_copy(m_Info, other.m_Info);
575 }
576 
577 /**
578  * Destructor
579  */
~TimerGlobalInfo()580 TimerGlobalInfo::~TimerGlobalInfo()
581 {
582     snd_timer_ginfo_free(m_Info);
583 }
584 
585 /**
586  * Copy the current object
587  * @return Pointer to the new object
588  */
589 TimerGlobalInfo*
clone()590 TimerGlobalInfo::clone()
591 {
592     return new TimerGlobalInfo(m_Info);
593 }
594 
595 /**
596  * Assignment operator
597  * @param other Existing TimerGlobalInfo object reference
598  * @return This object
599  */
600 TimerGlobalInfo&
operator =(const TimerGlobalInfo & other)601 TimerGlobalInfo::operator=(const TimerGlobalInfo& other)
602 {
603     if (this == &other)
604         return *this;
605     snd_timer_ginfo_copy(m_Info, other.m_Info);
606     return *this;
607 }
608 
609 /**
610  * Sets the timer identifier
611  * @param tid TimerId object reference
612  */
613 void
setTimerId(const TimerId & tid)614 TimerGlobalInfo::setTimerId(const TimerId& tid)
615 {
616     m_Id = tid;
617     snd_timer_ginfo_set_tid (m_Info, m_Id.m_Info);
618 }
619 
620 /**
621  * Gets the timer identifier
622  * @return TimerId object reference
623  */
624 TimerId&
getTimerId()625 TimerGlobalInfo::getTimerId()
626 {
627     m_Id = TimerId(snd_timer_ginfo_get_tid (m_Info));
628     return m_Id;
629 }
630 
631 /**
632  * Gets the flags
633  * @return Undocumented flags
634  */
635 unsigned int
getFlags()636 TimerGlobalInfo::getFlags()
637 {
638     return snd_timer_ginfo_get_flags (m_Info);
639 }
640 
641 /**
642  * Gets the card number
643  * @return Card number
644  */
645 int
getCard()646 TimerGlobalInfo::getCard()
647 {
648     return snd_timer_ginfo_get_card (m_Info);
649 }
650 
651 /**
652  * Gets the timer ID string
653  * @return Timer ID string
654  */
655 QString
getId()656 TimerGlobalInfo::getId()
657 {
658     return QString(snd_timer_ginfo_get_id (m_Info));
659 }
660 
661 /**
662  * Gets the timer name
663  * @return Timer name
664  */
665 QString
getName()666 TimerGlobalInfo::getName()
667 {
668     return QString(snd_timer_ginfo_get_name (m_Info));
669 }
670 
671 /**
672  * Gets the timer resolution in ns
673  * @return Timer resolution in ns
674  */
675 unsigned long
getResolution()676 TimerGlobalInfo::getResolution()
677 {
678     return snd_timer_ginfo_get_resolution (m_Info);
679 }
680 
681 /**
682  * Gets timer minimal resolution in ns
683  * @return Minimal resolution in ns
684  */
685 unsigned long
getMinResolution()686 TimerGlobalInfo::getMinResolution()
687 {
688     return snd_timer_ginfo_get_resolution_min (m_Info);
689 }
690 
691 /**
692  * Gets timer maximal resolution in ns
693  * @return Maximal resolution in ns
694  */
695 unsigned long
getMaxResolution()696 TimerGlobalInfo::getMaxResolution()
697 {
698     return snd_timer_ginfo_get_resolution_max(m_Info);
699 }
700 
701 /**
702  * Gets current timer clients
703  * @return Current clients
704  */
705 unsigned int
getClients()706 TimerGlobalInfo::getClients()
707 {
708     return snd_timer_ginfo_get_clients(m_Info);
709 }
710 
711 /**
712  * Gets the size of the ALSA timer global info object
713  * @return Size of the ALSA object
714  */
715 int
getSizeOfInfo() const716 TimerGlobalInfo::getSizeOfInfo() const
717 {
718     return snd_timer_ginfo_sizeof();
719 }
720 
721 /**
722  * Default constructor
723  */
TimerParams()724 TimerParams::TimerParams()
725 {
726     snd_timer_params_malloc (&m_Info);
727 }
728 
729 /**
730  * Constructor
731  * @param other Pointer to an ALSA timer parameters object
732  */
TimerParams(const snd_timer_params_t * other)733 TimerParams::TimerParams(const snd_timer_params_t *other)
734 {
735     snd_timer_params_malloc (&m_Info);
736     snd_timer_params_copy (m_Info, other);
737 }
738 
739 /**
740  * Copy constructor
741  * @param other Existing TimerParams object reference
742  */
TimerParams(const TimerParams & other)743 TimerParams::TimerParams(const TimerParams& other)
744 {
745     snd_timer_params_malloc (&m_Info);
746     snd_timer_params_copy (m_Info, other.m_Info);
747 }
748 
749 /**
750  * Destructor
751  * @return
752  */
~TimerParams()753 TimerParams::~TimerParams()
754 {
755     snd_timer_params_free (m_Info);
756 }
757 
758 /**
759  * Copy the current object
760  * @return Pointer to the new object
761  */
762 TimerParams*
clone()763 TimerParams::clone()
764 {
765     return new TimerParams(m_Info);
766 }
767 
768 /**
769  * Assignment operator
770  * @param other Existing TimerParams object reference
771  * @return This object
772  */
773 TimerParams&
operator =(const TimerParams & other)774 TimerParams::operator=(const TimerParams& other)
775 {
776     if (this == &other)
777         return *this;
778     snd_timer_params_copy (m_Info, other.m_Info);
779     return *this;
780 }
781 
782 /**
783  * Sets the automatic start flag
784  * @param auto_start Value for the automatic start flag
785  */
786 void
setAutoStart(bool auto_start)787 TimerParams::setAutoStart(bool auto_start)
788 {
789     snd_timer_params_set_auto_start (m_Info, auto_start ? 1 : 0);
790 }
791 
792 /**
793  * Gets the automatic start flag
794  * @return True if the timer starts automatically
795  */
796 bool
getAutoStart()797 TimerParams::getAutoStart()
798 {
799     return (snd_timer_params_get_auto_start (m_Info) != 0);
800 }
801 
802 /**
803  * Sets the exclusive flag
804  * @param exclusive True if the timer has the exclusive flag
805  */
806 void
setExclusive(bool exclusive)807 TimerParams::setExclusive(bool exclusive)
808 {
809     snd_timer_params_set_exclusive (m_Info, exclusive ? 1 : 0);
810 }
811 
812 /**
813  * Gets the timer's exclusive flag
814  * @return True if the timer has the exclusive flag
815  */
816 bool
getExclusive()817 TimerParams::getExclusive()
818 {
819     return (snd_timer_params_get_exclusive (m_Info) != 0);
820 }
821 
822 /**
823  * Sets the timer early event
824  * @param early_event Timer early event
825  */
826 void
setEarlyEvent(bool early_event)827 TimerParams::setEarlyEvent(bool early_event)
828 {
829     snd_timer_params_set_early_event (m_Info, early_event ? 1 : 0);
830 }
831 
832 /**
833  * Gets the timer early event
834  * @return Timer early event
835  */
836 bool
getEarlyEvent()837 TimerParams::getEarlyEvent()
838 {
839     return (snd_timer_params_get_early_event (m_Info) != 0);
840 }
841 
842 /**
843  * Sets the timer ticks
844  * @param ticks Timer ticks
845  */
846 void
setTicks(long ticks)847 TimerParams::setTicks(long ticks)
848 {
849     snd_timer_params_set_ticks (m_Info, ticks);
850 }
851 
852 /**
853  * Gets the timer ticks
854  * @return Timer ticks
855  */
856 long
getTicks()857 TimerParams::getTicks()
858 {
859     return snd_timer_params_get_ticks (m_Info);
860 }
861 
862 /**
863  * Sets the queue size (32-1024)
864  * @param queue_size Queue size
865  */
866 void
setQueueSize(long queue_size)867 TimerParams::setQueueSize(long queue_size)
868 {
869     snd_timer_params_set_queue_size (m_Info, queue_size);
870 }
871 
872 /**
873  * Gets the queue size
874  * @return Queue size
875  */
876 long
getQueueSize()877 TimerParams::getQueueSize()
878 {
879     return snd_timer_params_get_queue_size (m_Info);
880 }
881 
882 /**
883  * Sets the event filter
884  * @param filter Event filter
885  */
886 void
setFilter(unsigned int filter)887 TimerParams::setFilter(unsigned int filter)
888 {
889     snd_timer_params_set_filter (m_Info, filter);
890 }
891 
892 /**
893  * Gets the event filter
894  * @return Event filter
895  */
896 unsigned int
getFilter()897 TimerParams::getFilter()
898 {
899     return snd_timer_params_get_filter (m_Info);
900 }
901 
902 /**
903  * Gets the size of the ALSA timer parameters object
904  * @return Size of the ALSA object
905  */
906 int
getSizeOfInfo() const907 TimerParams::getSizeOfInfo() const
908 {
909     return snd_timer_params_sizeof();
910 }
911 
912 /**
913  * Default constructor
914  */
TimerStatus()915 TimerStatus::TimerStatus()
916 {
917     snd_timer_status_malloc (&m_Info);
918 }
919 
920 /**
921  * Constructor
922  * @param other Pointer to an existing ALSA timer status object
923  */
TimerStatus(const snd_timer_status_t * other)924 TimerStatus::TimerStatus(const snd_timer_status_t *other)
925 {
926     snd_timer_status_malloc (&m_Info);
927     snd_timer_status_copy (m_Info, other);
928 }
929 
930 /**
931  * Copy constructor
932  * @param other Existing TimerStatus object reference
933  */
TimerStatus(const TimerStatus & other)934 TimerStatus::TimerStatus(const TimerStatus& other)
935 {
936     snd_timer_status_malloc (&m_Info);
937     snd_timer_status_copy (m_Info, other.m_Info);
938 }
939 
940 /**
941  * Destructor
942  */
~TimerStatus()943 TimerStatus::~TimerStatus()
944 {
945     snd_timer_status_free (m_Info);
946 }
947 
948 /**
949  * Copy the current object
950  * @return Pointer to the new object
951  */
952 TimerStatus*
clone()953 TimerStatus::clone()
954 {
955     return new TimerStatus(m_Info);
956 }
957 
958 /**
959  * Assignment operator
960  * @param other Existing TimerStatus object reference
961  * @return This object
962  */
963 TimerStatus&
operator =(const TimerStatus & other)964 TimerStatus::operator=(const TimerStatus& other)
965 {
966     if (this == &other)
967         return *this;
968     snd_timer_status_copy (m_Info, other.m_Info);
969     return *this;
970 }
971 
972 /**
973  * Gets the high resolution time-stamp
974  * @return High resolution time-stamp
975  */
976 snd_htimestamp_t
getTimestamp()977 TimerStatus::getTimestamp()
978 {
979     return snd_timer_status_get_timestamp (m_Info);
980 }
981 
982 /**
983  * Gets the resolution in us
984  * @return Resolution in us
985  */
986 long
getResolution()987 TimerStatus::getResolution()
988 {
989     return snd_timer_status_get_resolution (m_Info);
990 }
991 
992 /**
993  * Gets the master tick lost count
994  * @return Master tick lost count
995  */
996 long
getLost()997 TimerStatus::getLost()
998 {
999     return snd_timer_status_get_lost (m_Info);
1000 }
1001 
1002 /**
1003  * Gets the overrun count
1004  * @return Overrun count
1005  */
1006 long
getOverrun()1007 TimerStatus::getOverrun()
1008 {
1009     return snd_timer_status_get_overrun (m_Info);
1010 }
1011 
1012 /**
1013  * Gets the count of used queue elements
1014  * @return Count of used queue elements
1015  */
1016 long
getQueue()1017 TimerStatus::getQueue()
1018 {
1019     return snd_timer_status_get_queue (m_Info);
1020 }
1021 
1022 /**
1023  * Gets the size of the ALSA timer status object
1024  * @return Size of the ALSA object
1025  */
1026 int
getSizeOfInfo() const1027 TimerStatus::getSizeOfInfo() const
1028 {
1029     return snd_timer_status_sizeof();
1030 }
1031 
1032 /**
1033  * Constructor.
1034  * Open flags can be a combination of the following constants:
1035  * <ul>
1036  * <li>SND_TIMER_OPEN_NONBLOCK: non-blocking behavior</li>
1037  * <li>SND_TIMER_OPEN_TREAD: enhanced read, use time-stamps and event notification</li>
1038  * </ul>
1039  * @param deviceName Name of the device
1040  * @param openMode Open mode flags bitmap
1041  * @param parent Optional parent object
1042  */
Timer(const QString & deviceName,int openMode,QObject * parent)1043 Timer::Timer( const QString& deviceName, int openMode, QObject* parent )
1044     : QObject(parent),
1045     m_asyncHandler(nullptr),
1046     m_handler(nullptr),
1047     m_thread(nullptr),
1048     m_deviceName(deviceName)
1049 {
1050     DRUMSTICK_ALSA_CHECK_ERROR( snd_timer_open( &m_Info, m_deviceName.toLocal8Bit().data(),
1051                                  openMode ));
1052 }
1053 
1054 /**
1055  * Constructor.
1056  * Open flags can be a combination of the following constants:
1057  * <ul>
1058  * <li>SND_TIMER_OPEN_NONBLOCK: non-blocking behavior</li>
1059  * <li>SND_TIMER_OPEN_TREAD: enhanced read, use time-stamps and event notification</li>
1060  * </ul>
1061  * @param deviceName Name of the device
1062  * @param openMode Open mode flags bitmap
1063  * @param conf ALSA configuration object pointer
1064  * @param parent Optional parent object
1065  */
Timer(const QString & deviceName,int openMode,snd_config_t * conf,QObject * parent)1066 Timer::Timer( const QString& deviceName, int openMode, snd_config_t* conf,
1067               QObject* parent )
1068     : QObject(parent),
1069     m_asyncHandler(nullptr),
1070     m_handler(nullptr),
1071     m_thread(nullptr),
1072     m_deviceName(deviceName)
1073 {
1074     DRUMSTICK_ALSA_CHECK_ERROR( snd_timer_open_lconf( &m_Info,
1075                                        m_deviceName.toLocal8Bit().data(),
1076                                        openMode, conf ));
1077 }
1078 
1079 /**
1080  * Constructor
1081  * Open flags can be a combination of the following constants:
1082  * <ul>
1083  * <li>SND_TIMER_OPEN_NONBLOCK: non-blocking behavior</li>
1084  * <li>SND_TIMER_OPEN_TREAD: enhanced read, use time-stamps and event notification</li>
1085  * </ul>
1086  * @param id TimerId object reference
1087  * @param openMode Open mode flags bitmap
1088  * @param parent Optional parent object
1089  */
Timer(TimerId & id,int openMode,QObject * parent)1090 Timer::Timer( TimerId& id, int openMode, QObject* parent )
1091     : QObject(parent),
1092     m_asyncHandler(nullptr),
1093     m_handler(nullptr),
1094     m_thread(nullptr)
1095 {
1096     m_deviceName = QString("hw:CLASS=%1,SCLASS=%2,CARD=%3,DEV=%4,SUBDEV=%5")
1097     .arg(id.getClass())
1098     .arg(id.getSlaveClass())
1099     .arg(id.getCard())
1100     .arg(id.getDevice())
1101     .arg(id.getSubdevice());
1102     DRUMSTICK_ALSA_CHECK_ERROR( snd_timer_open( &m_Info,
1103                                  m_deviceName.toLocal8Bit().data(),
1104                                  openMode ));
1105 }
1106 
1107 /**
1108  * Constructor.
1109  * Open flags can be a combination of the following constants:
1110  * <ul>
1111  * <li>SND_TIMER_OPEN_NONBLOCK: non-blocking behavior</li>
1112  * <li>SND_TIMER_OPEN_TREAD: enhanced read, use time-stamps and event notification</li>
1113  * </ul>
1114  * @param cls Class
1115  * @param scls Subclass
1116  * @param card Card
1117  * @param dev Device
1118  * @param sdev Subdevice
1119  * @param openMode Open mode flags bitmap
1120  * @param parent Optional parent object
1121  */
Timer(int cls,int scls,int card,int dev,int sdev,int openMode,QObject * parent)1122 Timer::Timer( int cls, int scls, int card, int dev, int sdev,
1123               int openMode, QObject* parent )
1124     : QObject(parent),
1125     m_asyncHandler(nullptr),
1126     m_handler(nullptr),
1127     m_thread(nullptr)
1128 {
1129     m_deviceName = QString("hw:CLASS=%1,SCLASS=%2,CARD=%3,DEV=%4,SUBDEV=%5")
1130         .arg(cls)
1131         .arg(scls)
1132         .arg(card)
1133         .arg(dev)
1134         .arg(sdev);
1135     DRUMSTICK_ALSA_CHECK_ERROR( snd_timer_open( &m_Info,
1136                                  m_deviceName.toLocal8Bit().data(),
1137                                  openMode ));
1138 }
1139 
1140 /**
1141  * Destructor.
1142  */
~Timer()1143 Timer::~Timer()
1144 {
1145     stopEvents();
1146     if (m_thread != nullptr)
1147         delete m_thread;
1148     DRUMSTICK_ALSA_CHECK_WARNING(snd_timer_close(m_Info));
1149 }
1150 
1151 /**
1152  * Adds an asynchronous timer handler function.
1153  * @param callback Function handler
1154  * @param private_data Any data that will be passed to the callback
1155  */
1156 void
addAsyncTimerHandler(snd_async_callback_t callback,void * private_data)1157 Timer::addAsyncTimerHandler(snd_async_callback_t callback, void *private_data)
1158 {
1159     DRUMSTICK_ALSA_CHECK_WARNING(snd_async_add_timer_handler(&m_asyncHandler, m_Info, callback, private_data));
1160 }
1161 
1162 /**
1163  * Gets the ALSA timer handle
1164  * @return ALSA timer handle
1165  */
1166 snd_timer_t*
getTimerHandle()1167 Timer::getTimerHandle()
1168 {
1169     return snd_async_handler_get_timer(m_asyncHandler);
1170 }
1171 
1172 /**
1173  * Gets the count of poll descriptors
1174  * @return Count of poll descriptors
1175  */
1176 int
getPollDescriptorsCount()1177 Timer::getPollDescriptorsCount()
1178 {
1179     return snd_timer_poll_descriptors_count(m_Info);
1180 }
1181 
1182 /**
1183  * Gets poll descriptors
1184  * @param pfds  Pointer to a pollfd array
1185  * @param space Number of pollfd elements available
1186  */
1187 void
pollDescriptors(struct pollfd * pfds,unsigned int space)1188 Timer::pollDescriptors(struct pollfd *pfds, unsigned int space)
1189 {
1190     DRUMSTICK_ALSA_CHECK_WARNING(snd_timer_poll_descriptors(m_Info, pfds, space));
1191 }
1192 
1193 /**
1194  * Gets returned events from poll descriptors
1195  * @param pfds Pointer to a pollfd array
1196  * @param nfds Number of pollfd elements available
1197  * @param revents Returned events
1198  */
1199 void
pollDescriptorsRevents(struct pollfd * pfds,unsigned int nfds,unsigned short * revents)1200 Timer::pollDescriptorsRevents(struct pollfd *pfds, unsigned int nfds, unsigned short *revents)
1201 {
1202     DRUMSTICK_ALSA_CHECK_WARNING(snd_timer_poll_descriptors_revents(m_Info, pfds, nfds, revents));
1203 }
1204 
1205 /**
1206  * Gets the timer info object
1207  * @return TimerInfo object reference
1208  */
1209 TimerInfo&
getTimerInfo()1210 Timer::getTimerInfo()
1211 {
1212     snd_timer_info (m_Info, m_TimerInfo.m_Info);
1213     return m_TimerInfo;
1214 }
1215 
1216 /**
1217  * Sets the timer parameters
1218  * @param params TimerParams object reference
1219  */
1220 void
setTimerParams(const TimerParams & params)1221 Timer::setTimerParams(const TimerParams& params)
1222 {
1223     DRUMSTICK_ALSA_CHECK_WARNING( snd_timer_params(m_Info, params.m_Info) );
1224 }
1225 
1226 /**
1227  * Gets the timer status
1228  * @return TimerStatus object reference
1229  */
1230 TimerStatus&
getTimerStatus()1231 Timer::getTimerStatus()
1232 {
1233     DRUMSTICK_ALSA_CHECK_WARNING( snd_timer_status(m_Info, m_TimerStatus.m_Info) );
1234     return m_TimerStatus;
1235 }
1236 
1237 /**
1238  * Start rolling the timer
1239  */
1240 void
start()1241 Timer::start()
1242 {
1243     DRUMSTICK_ALSA_CHECK_WARNING(snd_timer_start(m_Info));
1244 }
1245 
1246 /**
1247  * Stop rolling the timer
1248  */
1249 void
stop()1250 Timer::stop()
1251 {
1252     DRUMSTICK_ALSA_CHECK_WARNING(snd_timer_stop(m_Info));
1253 }
1254 
1255 /**
1256  * Continue rolling the timer
1257  */
1258 void
continueRunning()1259 Timer::continueRunning()
1260 {
1261     DRUMSTICK_ALSA_CHECK_WARNING(snd_timer_continue(m_Info));
1262 }
1263 
1264 /**
1265  * Read bytes from the timer handle
1266  * @param buffer Buffer to store the input bytes
1267  * @param size  Input buffer size in bytes
1268  * @return Bytes read from the timer
1269  */
1270 ssize_t
read(void * buffer,size_t size)1271 Timer::read(void *buffer, size_t size)
1272 {
1273     return snd_timer_read(m_Info, buffer, size);
1274 }
1275 
1276 /**
1277  * Internal function to deliver the timer events using one of the two available
1278  * methods:
1279  * <ul>
1280  * <li>TimerEventHandler instance pointer provided in Timer::setHandler()</li>
1281  * <li>A signal Timer::timerExpired() is emitted, otherwise</li>
1282  * </ul>
1283  */
1284 void
doEvents()1285 Timer::doEvents()
1286 {
1287     snd_timer_tread_t tr;
1288     while ( read(&tr, sizeof(tr)) == sizeof(tr) ) {
1289         int msecs = ((tr.tstamp.tv_sec - m_last_time.tv_sec) * 1000) +
1290                     round((tr.tstamp.tv_nsec - m_last_time.tv_nsec) / 1000000.0);
1291         m_last_time = tr.tstamp;
1292         if ( m_handler != nullptr )
1293             m_handler->handleTimerEvent(tr.val, msecs);
1294         else
1295             emit timerExpired(tr.val, msecs);
1296     }
1297 }
1298 
1299 /**
1300  * Starts the events dispatching thread
1301  */
startEvents()1302 void Timer::startEvents()
1303 {
1304     m_last_time = getTimerStatus().getTimestamp();
1305     if (m_thread == nullptr) {
1306         m_thread = new TimerInputThread(this, 500);
1307         m_thread->start();
1308     }
1309 }
1310 
1311 /**
1312  * Stops the events dispatching thread
1313  */
stopEvents()1314 void Timer::stopEvents()
1315 {
1316     int counter = 0;
1317     if (m_thread != nullptr) {
1318         m_thread->stop();
1319         while (!m_thread->wait(500) && (counter < 10)) {
1320             counter++;
1321         }
1322         if (!m_thread->isFinished()) {
1323             m_thread->terminate();
1324         }
1325         delete m_thread;
1326     }
1327 }
1328 
1329 /**
1330  * Check and return the best available global TimerId in the system, meaning
1331  * the timer with higher frequency (or lesser period, resolution).
1332  * @return A TimerId object
1333  * @since 0.3.0
1334  */
1335 TimerId
bestGlobalTimerId()1336 Timer::bestGlobalTimerId()
1337 {
1338     TimerId id;
1339     snd_timer_t* timer;
1340     snd_timer_info_t* info;
1341     long res, best_res = LONG_MAX;
1342     char timername[64];
1343     int test_devs[] = {
1344           SND_TIMER_GLOBAL_SYSTEM
1345         , SND_TIMER_GLOBAL_RTC
1346 #ifdef SND_TIMER_GLOBAL_HPET
1347         , SND_TIMER_GLOBAL_HPET
1348 #endif
1349 #ifdef SND_TIMER_GLOBAL_HRTIMER
1350         , SND_TIMER_GLOBAL_HRTIMER
1351 #endif
1352     };
1353     int max_global_timers = sizeof(test_devs)/sizeof(int);
1354     int clas = SND_TIMER_CLASS_GLOBAL;
1355     int scls = SND_TIMER_SCLASS_NONE;
1356     int card = 0;
1357     int dev  = SND_TIMER_GLOBAL_SYSTEM;
1358     int sdev = 0;
1359     int err = 0;
1360     int is_slave = 0;
1361     int i;
1362     snd_timer_info_alloca(&info);
1363     // default system timer
1364     id.setClass(clas);
1365     id.setSlaveClass(scls);
1366     id.setCard(card);
1367     id.setDevice(dev);
1368     id.setSubdevice(sdev);
1369     // select a non slave timer with the lowest resolution value
1370     for( i = 0; i < max_global_timers; ++i )
1371     {
1372         dev = test_devs[i];
1373         sprintf( timername, "hw:CLASS=%i,SCLASS=%i,CARD=%i,DEV=%i,SUBDEV=%i",
1374                  clas, scls, card, dev, sdev );
1375         err = snd_timer_open(&timer, timername, SND_TIMER_OPEN_NONBLOCK);
1376         if (err < 0) continue;
1377         err = snd_timer_info(timer, info);
1378         if (err == 0) {
1379             is_slave = snd_timer_info_is_slave(info);
1380             res = snd_timer_info_get_resolution(info);
1381             if ((is_slave == 0) && (best_res > res)) {
1382                 best_res = res;
1383                 id.setDevice(dev);
1384             }
1385         }
1386         snd_timer_close(timer);
1387     }
1388     return id;
1389 }
1390 
1391 /**
1392  * Check and return the best available global Timer in the system, meaning
1393  * the timer with higher frequency (or lesser period, resolution).
1394  * @param openMode Open mode flags
1395  * @param parent Optional parent object
1396  * @return A new Timer instance pointer
1397  */
1398 Timer*
bestGlobalTimer(int openMode,QObject * parent)1399 Timer::bestGlobalTimer(int openMode, QObject* parent)
1400 {
1401     TimerId id = bestGlobalTimerId();
1402     return new Timer(id, openMode, parent);
1403 }
1404 
1405 /**
1406  * Loop reading and dispatching timer events.
1407  */
1408 void
run()1409 Timer::TimerInputThread::run()
1410 {
1411     int err, count;
1412     struct pollfd *fds;
1413     if (m_timer == nullptr) return;
1414 
1415     count = m_timer->getPollDescriptorsCount();
1416     fds = (pollfd *) calloc(count, sizeof(struct pollfd));
1417     if (fds == nullptr) {
1418         qWarning() << "allocation error!";
1419         return;
1420     }
1421     fds->events = POLLIN;
1422     fds->revents = 0;
1423 
1424     try {
1425         while (!stopped() && (m_timer != nullptr)) {
1426             m_timer->pollDescriptors(fds, count);
1427             if ((err = poll(fds, count, m_Wait)) < 0) {
1428                 qWarning() << "poll error " << err << "(" << strerror(err) << ")";
1429                 free(fds);
1430                 return;
1431             }
1432             if (err == 0) {
1433                 qWarning() << "timer time out";
1434                 free(fds);
1435                 return;
1436             }
1437             m_timer->doEvents();
1438         }
1439     } catch (...) {
1440         qWarning() << "exception in input thread";
1441     }
1442     free(fds);
1443 }
1444 
1445 /**
1446  * Returns the rolling state of the timer thread
1447  * @return The stopped state
1448  */
1449 bool
stopped()1450 Timer::TimerInputThread::stopped()
1451 {
1452 	QReadLocker locker(&m_mutex);
1453     return m_Stopped;
1454 }
1455 
1456 /**
1457  * Stop the thread
1458  */
1459 void
stop()1460 Timer::TimerInputThread::stop()
1461 {
1462 	QWriteLocker locker(&m_mutex);
1463     m_Stopped = true;
1464 }
1465 
1466 } // namespace ALSA
1467 } // namespace drumstick
1468 
1469