1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301
|
//=============================================================================
// MusE Score
// Linux Music Score Editor
//
// Copyright (C) 2002-2009 Werner Schweer and others
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================
#ifndef __SEQ_H__
#define __SEQ_H__
#include "libmscore/rendermidi.h"
#include "libmscore/sequencer.h"
#include "libmscore/fraction.h"
#include "synthesizer/event.h"
#include "driver.h"
#include "libmscore/fifo.h"
#include "libmscore/tempo.h"
class QTimer;
namespace Ms {
class Note;
class MasterScore;
class Score;
class Painter;
class Measure;
class Fraction;
class Driver;
class Part;
class Channel;
class ScoreView;
class MasterSynthesizer;
class Segment;
enum class POS : char;
//---------------------------------------------------------
// SeqMsg
// message format for gui -> sequencer messages
//---------------------------------------------------------
enum class SeqMsgId : char {
NO_MESSAGE,
TEMPO_CHANGE,
PLAY, SEEK,
MIDI_INPUT_EVENT
};
struct SeqMsg {
SeqMsgId id;
union {
int intVal;
qreal realVal;
};
NPlayEvent event;
SeqMsg() {}
SeqMsg(SeqMsgId _id, int val) : id(_id), intVal(val) {}
SeqMsg(SeqMsgId _id, qreal val) : id(_id), realVal(val) {}
SeqMsg(SeqMsgId _id, const NPlayEvent& e) : id(_id), event(e) {}
};
//---------------------------------------------------------
// SeqMsgFifo
//---------------------------------------------------------
static const int SEQ_MSG_FIFO_SIZE = 1024*8;
class SeqMsgFifo : public FifoBase {
SeqMsg messages[SEQ_MSG_FIFO_SIZE];
public:
SeqMsgFifo();
virtual ~SeqMsgFifo() {}
void enqueue(const SeqMsg&); // put object on fifo
SeqMsg dequeue(); // remove object from fifo
};
// this are also the jack audio transport states:
enum class Transport : char {
STOP=0,
PLAY=1,
STARTING=3,
NET_STARTING=4
};
//---------------------------------------------------------
// Seq
// sequencer
//---------------------------------------------------------
class Seq : public QObject, public Sequencer {
Q_OBJECT
mutable QMutex mutex;
MasterScore* cs;
ScoreView* cv;
bool running; // true if sequencer is available
Transport state; // STOP, PLAY, STARTING=3
bool inCountIn;
// When we begin playing count in, JACK should play the ticks, but shouldn't run
// JACK Transport to prevent playing in other applications. Before playing
// count in we have to disconnect from JACK Transport by switching to the fake transport.
// Also we save current preferences.useJackTransport value to useJackTransportSavedFlag
// to restore it when count in ends. After this all applications start playing in sync.
bool useJackTransportSavedFlag;
int maxMidiOutPort; // Maximum count of midi out ports in all opened scores
Fraction prevTimeSig;
double prevTempo;
bool oggInit;
bool playlistChanged;
SeqMsgFifo toSeq;
SeqMsgFifo fromSeq;
Driver* _driver;
MasterSynthesizer* _synti;
double meterValue[2];
double meterPeakValue[2];
int peakTimer[2];
EventMap events; // playlist for playback mode
EventMap::const_iterator eventsEnd;
EventMap renderEvents; // event list that is rendered in background
RangeMap renderEventsStatus;
MidiRenderer midi;
QFuture<void> midiRenderFuture;
bool allowBackgroundRendering = false; // should be set to true only when playing, so no
// score changes are possible.
EventMap countInEvents; // playlist of any metronome countin clicks
QQueue<NPlayEvent> _liveEventQueue; // playlist for score editing and note entry (rendered live)
int playFrame; // current play position in samples, relative to the first frame of playback
int countInPlayFrame; // current play position in samples, relative to the first frame of countin
int endUTick; // the final tick of midi events collected by collectEvents()
EventMap::const_iterator playPos; // moved in real time thread
EventMap::const_iterator countInPlayPos;
EventMap::const_iterator guiPos; // moved in gui thread
QList<const Note*> markedNotes; // notes marked as sounding
uint tackRemain; // metronome state (remaining audio samples)
uint tickRemain;
qreal tackVolume; // relative volumes
qreal tickVolume;
qreal metronomeVolume; // overall volume
unsigned initialMillisecondTimestampWithLatency; // millisecond timestamp (relative to PortAudio's initialization) of start of playback
QTimer* heartBeatTimer;
QTimer* noteTimer;
/**
* Preferences cached for faster access in realtime context.
* Using QSettings-based Ms::Preferences directly results in
* audible glitches on some systems (esp. MacOS, see #280493).
*/
struct CachedPreferences {
int portMidiOutputLatencyMilliseconds = 0;
bool jackTimeBaseMaster = false;
bool useJackTransport = false;
bool useJackMidi = false;
bool useJackAudio = false;
bool useAlsaAudio = false;
bool usePortAudio = false;
bool usePulseAudio = false;
void update();
};
CachedPreferences cachedPrefs;
void startTransport();
void stopTransport();
void renderChunk(const MidiRenderer::Chunk&, EventMap*);
void updateEventsEnd();
void setPos(int);
void playEvent(const NPlayEvent&, unsigned framePos);
void guiToSeq(const SeqMsg& msg);
void metronome(unsigned n, float* l, bool force);
void seekCommon(int utick);
void unmarkNotes();
void updateSynthesizerState(int tick1, int tick2);
void addCountInClicks();
int getPlayStartUtick();
inline QQueue<NPlayEvent>* liveEventQueue() { return &_liveEventQueue; }
private slots:
void seqMessage(int msg, int arg = 0);
void heartBeatTimeout();
void midiInputReady();
void setPlaylistChanged() { playlistChanged = true; }
void handleTimeSigTempoChanged();
public slots:
void setRelTempo(double);
void seek(int utick);
void seekRT(int utick);
void stopNotes(int channel = -1, bool realTime = false);
void start();
void stop();
void setPos(POS, unsigned);
void setMetronomeGain(float val) { metronomeVolume = val; }
signals:
void started();
void stopped();
int toGui(int, int arg = 0);
void heartBeat(int, int, int);
void tempoChanged();
void timeSigChanged();
public:
Seq();
~Seq();
bool canStart();
void rewindStart();
void loopStart();
void seekEnd();
void nextMeasure();
void nextChord();
void prevMeasure();
void prevChord();
void collectEvents(int utick);
void ensureBufferAsync(int utick);
void guiStop();
void stopWait();
void setLoopIn();
void setLoopOut();
void setLoopSelection();
bool init(bool hotPlug = false);
void exit();
bool isRunning() const { return running; }
bool isPlaying() const { return state == Transport::PLAY; }
bool isStopped() const { return state == Transport::STOP; }
void processMessages();
void process(unsigned framesPerPeriod, float* buffer);
int getEndUTick() const { return endUTick; }
bool isRealtime() const { return true; }
void sendMessage(SeqMsg&) const;
void setController(int, int, int);
virtual void sendEvent(const NPlayEvent&);
void setScoreView(ScoreView*);
MasterScore* score() const { return cs; }
ScoreView* viewer() const { return cv; }
void initInstruments(bool realTime = false);
Driver* driver() { return _driver; }
void setDriver(Driver* d) { _driver = d; }
MasterSynthesizer* synti() const { return _synti; }
void setMasterSynthesizer(MasterSynthesizer* ms) { _synti = ms; }
int getCurTick();
double curTempo() const;
void putEvent(const NPlayEvent&, unsigned framePos = 0);
void startNoteTimer(int duration);
virtual void startNote(int channel, int, int, double nt) override;
virtual void startNote(int channel, int, int, int, double nt) override;
virtual void playMetronomeBeat(BeatType type) override;
void eventToGui(NPlayEvent);
void stopNoteTimer();
void recomputeMaxMidiOutPort();
float metronomeGain() const { return metronomeVolume; }
void setInitialMillisecondTimestampWithLatency();
unsigned getCurrentMillisecondTimestampWithLatency(unsigned framePos) const;
void preferencesChanged() { cachedPrefs.update(); }
};
extern Seq* seq;
extern void initSequencer();
extern bool initMidi();
} // namespace Ms
#endif
|