drumstick  1.0.2
playthread.cpp
Go to the documentation of this file.
00001 /*
00002     MIDI Sequencer C++ library
00003     Copyright (C) 2006-2015, Pedro Lopez-Cabanillas <plcl@users.sf.net>
00004 
00005     This library is free software; you can redistribute it and/or modify
00006     it under the terms of the GNU General Public License as published by
00007     the Free Software Foundation; either version 2 of the License, or
00008     (at your option) any later version.
00009 
00010     This library is distributed in the hope that it will be useful,
00011     but WITHOUT ANY WARRANTY; without even the implied warranty of
00012     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013     GNU General Public License for more details.
00014 
00015     You should have received a copy of the GNU General Public License along
00016     with this program; if not, write to the Free Software Foundation, Inc.,
00017     51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00018 */
00019 
00020 #include "playthread.h"
00021 #include "alsaclient.h"
00022 #include "alsaqueue.h"
00023 #include <QReadLocker>
00024 #include <QWriteLocker>
00025 
00031 namespace drumstick {
00032 
00056 const int TIMEOUT = 100;
00057 
00063 SequencerOutputThread::SequencerOutputThread(MidiClient *seq, int portId)
00064     : QThread(),
00065     m_MidiClient(seq),
00066     m_Queue(0),
00067     m_PortId(portId),
00068     m_Stopped(false),
00069     m_QueueId(0),
00070     m_npfds(0),
00071     m_pfds(0)
00072 {
00073     if (m_MidiClient != NULL) {
00074         m_Queue = m_MidiClient->getQueue();
00075         m_QueueId = m_Queue->getId();
00076     }
00077 }
00078 
00084 bool
00085 SequencerOutputThread::stopRequested()
00086 {
00087     QReadLocker locker(&m_mutex);
00088     return m_Stopped;
00089 }
00090 
00094 void
00095 SequencerOutputThread::stop()
00096 {
00097     QWriteLocker locker(&m_mutex);
00098     m_Stopped = true;
00099     locker.unlock();
00100     while (isRunning())
00101         wait(TIMEOUT);
00102 }
00103 
00108 void
00109 SequencerOutputThread::sendEchoEvent(int tick)
00110 {
00111     if (!stopRequested() && m_MidiClient != NULL) {
00112         SystemEvent ev(SND_SEQ_EVENT_ECHO);
00113         ev.setSource(m_PortId);
00114         ev.setDestination(m_MidiClient->getClientId(), m_PortId);
00115         ev.scheduleTick(m_QueueId, tick, false);
00116         sendSongEvent(&ev);
00117     }
00118 }
00119 
00124 void
00125 SequencerOutputThread::sendSongEvent(SequencerEvent* ev)
00126 {
00127     if (m_MidiClient != NULL) {
00128         while (!stopRequested() &&
00129                (snd_seq_event_output_direct(m_MidiClient->getHandle(), ev->getHandle()) < 0))
00130             poll(m_pfds, m_npfds, TIMEOUT);
00131     }
00132 }
00133 
00137 void
00138 SequencerOutputThread::drainOutput()
00139 {
00140     if (m_MidiClient != NULL) {
00141         while (!stopRequested() &&
00142                (snd_seq_drain_output(m_MidiClient->getHandle()) < 0))
00143             poll(m_pfds, m_npfds, TIMEOUT);
00144     }
00145 }
00146 
00150 void
00151 SequencerOutputThread::syncOutput()
00152 {
00153     if (!stopRequested() && m_MidiClient != NULL) {
00154         QueueStatus status = m_Queue->getStatus();
00155         while (!stopRequested() && (status.getEvents() > 0)) {
00156            usleep(TIMEOUT);
00157            status = m_Queue->getStatus();
00158         }
00159     }
00160 }
00161 
00165 void SequencerOutputThread::run()
00166 {
00167     unsigned int last_tick;
00168     if (m_MidiClient != NULL) {
00169         try  {
00170             m_npfds = snd_seq_poll_descriptors_count(m_MidiClient->getHandle(), POLLOUT);
00171             m_pfds = (pollfd*) alloca(m_npfds * sizeof(pollfd));
00172             snd_seq_poll_descriptors(m_MidiClient->getHandle(), m_pfds, m_npfds, POLLOUT);
00173             last_tick = getInitialPosition();
00174             if (last_tick == 0) {
00175                 m_Queue->start();
00176             } else {
00177                 m_Queue->setTickPosition(last_tick);
00178                 m_Queue->continueRunning();
00179             }
00180             while (!stopRequested() && hasNext()) {
00181                 SequencerEvent* ev = nextEvent();
00182                 if (getEchoResolution() > 0) {
00183                     while (!stopRequested() && (last_tick < ev->getTick())) {
00184                         last_tick += getEchoResolution();
00185                         sendEchoEvent(last_tick);
00186                     }
00187                 }
00188                 if (!stopRequested() && !SequencerEvent::isConnectionChange(ev))
00189                     sendSongEvent(ev);
00190             }
00191             if (stopRequested()) {
00192                 m_Queue->clear();
00193                 emit stopped();
00194             } else {
00195                 drainOutput();
00196                 syncOutput();
00197                 if (stopRequested())
00198                     emit stopped();
00199                 else
00200                     emit finished();
00201             }
00202             m_Queue->stop();
00203         } catch (...) {
00204             qWarning("exception in output thread");
00205         }
00206         m_npfds = 0;
00207         m_pfds = 0;
00208     }
00209 }
00210 
00215 void SequencerOutputThread::start( Priority priority )
00216 {
00217     QWriteLocker locker(&m_mutex);
00218     m_Stopped = false;
00219     QThread::start( priority );
00220 }
00221 
00222 } /* namespace drumstick */
00223