drumstick  2.9.0
C++ MIDI libraries using Qt objects, idioms, and style.
playthread.cpp
Go to the documentation of this file.
1 /*
2  MIDI Sequencer C++ library
3  Copyright (C) 2006-2023, 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 extern "C" {
20  #include <alsa/asoundlib.h>
21 }
22 
23 #include <QReadLocker>
24 #include <QWriteLocker>
25 #include <drumstick/alsaclient.h>
26 #include <drumstick/alsaqueue.h>
27 #include <drumstick/playthread.h>
28 
57 namespace drumstick {
58 namespace ALSA {
59 
60 const int TIMEOUT = 100;
61 
68  : QThread(),
69  m_MidiClient(seq),
70  m_Queue(nullptr),
71  m_PortId(portId),
72  m_Stopped(false),
73  m_QueueId(0),
74  m_npfds(0),
75  m_pfds(nullptr)
76 {
77  if (m_MidiClient != nullptr) {
79  m_QueueId = m_Queue->getId();
80  }
81 }
82 
88 bool
90 {
91  QReadLocker locker(&m_mutex);
92  return m_Stopped;
93 }
94 
98 void
100 {
101  QWriteLocker locker(&m_mutex);
102  m_Stopped = true;
103  locker.unlock();
104  while (isRunning()) {
105  wait(TIMEOUT);
106  }
107 }
108 
113 void
115 {
116  if (!stopRequested() && m_MidiClient != nullptr) {
117  SystemEvent ev(SND_SEQ_EVENT_ECHO);
118  ev.setSource(m_PortId);
120  ev.scheduleTick(m_QueueId, tick, false);
121  sendSongEvent(&ev);
122  }
123 }
124 
129 void
131 {
132  if (m_MidiClient != nullptr) {
133  while (!stopRequested() &&
134  (snd_seq_event_output_direct(m_MidiClient->getHandle(), ev->getHandle()) < 0)) {
135  poll(m_pfds, m_npfds, TIMEOUT);
136  }
137  }
138 }
139 
143 void
145 {
146  if (!stopRequested() && m_MidiClient != nullptr) {
147  while (!stopRequested() && (snd_seq_drain_output(m_MidiClient->getHandle()) < 0)) {
148  poll(m_pfds, m_npfds, TIMEOUT);
149  }
150  }
151 }
152 
156 void
158 {
159  if (!stopRequested() && m_MidiClient != nullptr) {
161  }
162 }
163 
168 {
169  if (m_MidiClient != nullptr) {
170  try {
171  unsigned int last_tick;
172  m_npfds = snd_seq_poll_descriptors_count(m_MidiClient->getHandle(), POLLOUT);
173  m_pfds = (pollfd*) calloc(m_npfds, sizeof(pollfd));
174  snd_seq_poll_descriptors(m_MidiClient->getHandle(), m_pfds, m_npfds, POLLOUT);
175  last_tick = getInitialPosition();
176  if (last_tick == 0) {
177  m_Queue->start();
178  } else {
179  m_Queue->setTickPosition(last_tick);
181  }
182  while (!stopRequested() && hasNext()) {
183  SequencerEvent* ev = nextEvent();
185  sendSongEvent(ev);
186  }
187  if (getEchoResolution() > 0) {
188  while (!stopRequested() && (last_tick < ev->getTick())) {
189  last_tick += getEchoResolution();
190  sendEchoEvent(last_tick);
191  }
192  }
193  }
194  if (stopRequested()) {
195  m_Queue->clear();
196  Q_EMIT playbackStopped();
197  } else {
198  drainOutput();
199  syncOutput();
200  if (stopRequested())
201  Q_EMIT playbackStopped();
202  else
203  Q_EMIT playbackFinished();
204  }
205  m_Queue->stop();
206  } catch (...) {
207  qWarning("exception in output thread");
208  }
209  m_npfds = 0;
210  free(m_pfds);
211  m_pfds = nullptr;
212  }
213 }
214 
219 void SequencerOutputThread::start( Priority priority )
220 {
221  QWriteLocker locker(&m_mutex);
222  m_Stopped = false;
223  QThread::start( priority );
224 }
225 
226 } // namespace ALSA
227 } // namespace drumstick
228 
229 
Classes managing ALSA Sequencer clients.
Classes managing ALSA Sequencer queues.
The QThread class provides platform-independent threads.
Client management.
Definition: alsaclient.h:219
void setTickPosition(snd_seq_tick_time_t pos)
Sets the queue position in musical time (ticks).
Definition: alsaqueue.cpp:905
void continueRunning()
Start the queue without resetting the last position.
Definition: alsaqueue.cpp:886
void start()
Start the queue.
Definition: alsaqueue.cpp:862
void stop()
Stop the queue.
Definition: alsaqueue.cpp:873
void clear()
Clear the queue, dropping any scheduled events.
Definition: alsaqueue.cpp:895
Base class for the event's hierarchy.
Definition: alsaevent.h:68
static bool isConnectionChange(const SequencerEvent *event)
Checks if the event's type is of type connection change.
Definition: alsaevent.cpp:183
void scheduleTick(const int queue, const int tick, const bool relative)
Sets the event to be scheduled in musical time (ticks) units.
Definition: alsaevent.cpp:278
snd_seq_event_t * getHandle()
Gets the handle of the event.
Definition: alsaevent.h:135
void setDestination(const unsigned char client, const unsigned char port)
Sets the client:port destination of the event.
Definition: alsaevent.cpp:232
void setSource(const unsigned char port)
Sets the event's source port ID.
Definition: alsaevent.cpp:242
void playbackStopped()
Signal emitted when the play-back has stopped.
void start(QThread::Priority priority=InheritPriority)
Starts the playback thread.
Definition: playthread.cpp:219
virtual unsigned int getInitialPosition()
Gets the initial position in ticks of the sequence.
Definition: playthread.h:70
virtual void sendEchoEvent(int tick)
Sends an echo event, with the same PortId as sender and destination.
Definition: playthread.cpp:114
SequencerOutputThread(MidiClient *seq, int portId)
Constructor.
Definition: playthread.cpp:67
virtual void syncOutput()
Waits until the ALSA output queue is empty (all the events have been played.)
Definition: playthread.cpp:157
int m_QueueId
MidiQueue numeric identifier.
Definition: playthread.h:123
MidiClient * m_MidiClient
MidiClient instance pointer.
Definition: playthread.h:119
virtual SequencerEvent * nextEvent()=0
Gets the next event in the sequence.
void playbackFinished()
Signal emitted when the sequence play-back has finished.
virtual void stop()
Stops playing the current sequence.
Definition: playthread.cpp:99
virtual void sendSongEvent(SequencerEvent *ev)
Sends a SequencerEvent.
Definition: playthread.cpp:130
virtual void drainOutput()
Flush the ALSA output buffer.
Definition: playthread.cpp:144
pollfd * m_pfds
Array of pollfd pointers.
Definition: playthread.h:125
QReadWriteLock m_mutex
Mutex object used for synchronization.
Definition: playthread.h:126
MidiQueue * m_Queue
MidiQueue instance pointer.
Definition: playthread.h:120
virtual bool hasNext()=0
Check if there is one more event in the sequence.
virtual unsigned int getEchoResolution()
Gets the echo event resolution in ticks.
Definition: playthread.h:77
virtual void run() override
Thread process loop.
Definition: playthread.cpp:167
int m_npfds
Number of pollfd pointers.
Definition: playthread.h:124
int m_PortId
MidiPort numeric identifier.
Definition: playthread.h:121
virtual bool stopRequested()
Checks if stop has been requested.
Definition: playthread.cpp:89
MidiQueue * getQueue()
Get the MidiQueue instance associated to this client.
snd_seq_t * getHandle()
Returns the sequencer handler managed by ALSA.
Definition: alsaclient.cpp:286
int getClientId()
Gets the client ID.
Definition: alsaclient.cpp:552
void synchronizeOutput()
Wait until all sent events are processed.
Drumstick common.
Definition: alsaclient.cpp:68
Sequencer output thread.