MuseScore/mscore/seq.cpp

1765 lines
62 KiB
C++
Raw Normal View History

2012-05-26 14:49:10 +02:00
//=============================================================================
// MuseScore
// Linux Music Score Editor
//
// Copyright (C) 2002-2011 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.
//=============================================================================
#include "config.h"
#include "seq.h"
#include "musescore.h"
#include "synthesizer/msynthesizer.h"
#include "libmscore/rendermidi.h"
2012-05-26 14:49:10 +02:00
#include "libmscore/slur.h"
2013-08-22 12:18:14 +02:00
#include "libmscore/tie.h"
2012-05-26 14:49:10 +02:00
#include "libmscore/score.h"
#include "libmscore/segment.h"
#include "libmscore/note.h"
#include "libmscore/chord.h"
#include "libmscore/tempo.h"
#include "scoreview.h"
#include "playpanel.h"
#include "libmscore/staff.h"
#include "libmscore/measure.h"
#include "preferences.h"
#include "libmscore/part.h"
#include "libmscore/ottava.h"
#include "libmscore/utils.h"
#include "libmscore/repeatlist.h"
#include "libmscore/audio.h"
#include "synthcontrol.h"
#include "pianoroll.h"
#include "pianotools.h"
2012-05-26 14:49:10 +02:00
#include "click.h"
#define OV_EXCLUDE_STATIC_CALLBACKS
2012-05-26 14:49:10 +02:00
#include <vorbis/vorbisfile.h>
2018-04-02 01:35:20 +02:00
#ifdef USE_PORTMIDI
2017-05-14 17:43:28 +02:00
#if defined(Q_OS_MAC) || defined(Q_OS_WIN)
#include "portmidi/porttime/porttime.h"
#else
#include <porttime.h>
#endif
2018-04-02 01:35:20 +02:00
#endif
2017-05-14 17:43:28 +02:00
2013-05-13 18:49:17 +02:00
namespace Ms {
2012-05-26 14:49:10 +02:00
Seq* seq;
static const int guiRefresh = 10; // Hz
static const int peakHoldTime = 1400; // msec
static const int peakHold = (peakHoldTime * guiRefresh) / 1000;
static OggVorbis_File vf;
static constexpr int minUtickBufferSize = 480 * 4 * 10; // about 10 measures of 4/4 time signature
2014-06-17 14:35:16 +02:00
#if 0 // yet(?) unused
2012-05-26 14:49:10 +02:00
static const int AUDIO_BUFFER_SIZE = 1024 * 512; // 2 MB
2014-06-17 14:35:16 +02:00
#endif
2012-05-26 14:49:10 +02:00
//---------------------------------------------------------
// VorbisData
//---------------------------------------------------------
struct VorbisData {
int pos; // current position in audio->data()
QByteArray data;
};
static VorbisData vorbisData;
static size_t ovRead(void* ptr, size_t size, size_t nmemb, void* datasource);
static int ovSeek(void* datasource, ogg_int64_t offset, int whence);
static long ovTell(void* datasource);
static ov_callbacks ovCallbacks = {
ovRead, ovSeek, 0, ovTell
};
//---------------------------------------------------------
// ovRead
//---------------------------------------------------------
static size_t ovRead(void* ptr, size_t size, size_t nmemb, void* datasource)
{
VorbisData* vd = (VorbisData*)datasource;
size_t n = size * nmemb;
if (vd->data.size() < int(vd->pos + n))
n = vd->data.size() - vd->pos;
if (n) {
const char* src = vd->data.data() + vd->pos;
memcpy(ptr, src, n);
vd->pos += int(n);
2012-05-26 14:49:10 +02:00
}
return n;
}
//---------------------------------------------------------
// ovSeek
//---------------------------------------------------------
static int ovSeek(void* datasource, ogg_int64_t offset, int whence)
{
VorbisData* vd = (VorbisData*)datasource;
switch(whence) {
case SEEK_SET:
vd->pos = offset;
break;
case SEEK_CUR:
vd->pos += offset;
break;
case SEEK_END:
vd->pos = vd->data.size() - offset;
break;
}
return 0;
}
//---------------------------------------------------------
// ovTell
//---------------------------------------------------------
static long ovTell(void* datasource)
{
VorbisData* vd = (VorbisData*)datasource;
return vd->pos;
}
//---------------------------------------------------------
// Seq
//---------------------------------------------------------
Seq::Seq()
: midi(nullptr)
2012-05-26 14:49:10 +02:00
{
running = false;
playlistChanged = false;
cs = 0;
cv = 0;
tackRemain = 0;
tickRemain = 0;
maxMidiOutPort = 0;
2012-05-26 14:49:10 +02:00
2017-05-14 17:43:28 +02:00
endUTick = 0;
state = Transport::STOP;
2012-05-26 14:49:10 +02:00
oggInit = false;
_driver = 0;
2013-04-08 10:31:17 +02:00
playPos = events.cbegin();
2017-05-14 17:43:28 +02:00
playFrame = 0;
2012-05-26 14:49:10 +02:00
metronomeVolume = 0.3;
2014-07-07 17:31:41 +02:00
useJackTransportSavedFlag = false;
2012-05-26 14:49:10 +02:00
inCountIn = false;
countInPlayPos = countInEvents.cbegin();
2017-05-14 17:43:28 +02:00
countInPlayFrame = 0;
2012-05-26 14:49:10 +02:00
meterValue[0] = 0.0;
meterValue[1] = 0.0;
meterPeakValue[0] = 0.0;
meterPeakValue[1] = 0.0;
peakTimer[0] = 0;
peakTimer[1] = 0;
heartBeatTimer = new QTimer(this);
connect(heartBeatTimer, SIGNAL(timeout()), this, SLOT(heartBeatTimeout()));
2012-05-26 14:49:10 +02:00
noteTimer = new QTimer(this);
noteTimer->setSingleShot(true);
connect(noteTimer, SIGNAL(timeout()), this, SLOT(stopNotes()));
noteTimer->stop();
2014-07-07 17:31:41 +02:00
connect(this, SIGNAL(toGui(int, int)), this, SLOT(seqMessage(int, int)), Qt::QueuedConnection);
prevTimeSig.setNumerator(0);
prevTempo = 0;
connect(this, SIGNAL(timeSigChanged()),this,SLOT(handleTimeSigTempoChanged()));
connect(this, SIGNAL(tempoChanged()),this,SLOT(handleTimeSigTempoChanged()));
2017-05-14 17:43:28 +02:00
initialMillisecondTimestampWithLatency = 0;
2012-05-26 14:49:10 +02:00
}
//---------------------------------------------------------
// Seq
//---------------------------------------------------------
Seq::~Seq()
{
delete _driver;
2012-05-26 14:49:10 +02:00
}
//---------------------------------------------------------
// setScoreView
//---------------------------------------------------------
void Seq::setScoreView(ScoreView* v)
{
if (oggInit) {
ov_clear(&vf);
oggInit = false;
}
if (cv !=v && cs) {
2013-02-20 17:53:15 +01:00
unmarkNotes();
2012-05-26 14:49:10 +02:00
stopWait();
}
cv = v;
2014-04-02 10:49:54 +02:00
if (cs)
disconnect(cs, SIGNAL(playlistChanged()), this, SLOT(setPlaylistChanged()));
2016-03-11 12:18:46 +01:00
cs = cv ? cv->score()->masterScore() : 0;
midi = MidiRenderer(cs);
midi.setMinChunkSize(10);
2012-05-26 14:49:10 +02:00
if (!heartBeatTimer->isActive())
heartBeatTimer->start(20); // msec
2012-05-26 14:49:10 +02:00
playlistChanged = true;
_synti->reset();
2014-04-02 10:49:54 +02:00
if (cs) {
2012-05-26 14:49:10 +02:00
initInstruments();
2014-04-02 10:49:54 +02:00
connect(cs, SIGNAL(playlistChanged()), this, SLOT(setPlaylistChanged()));
}
2012-05-26 14:49:10 +02:00
}
//---------------------------------------------------------
// init
// return false on error
//---------------------------------------------------------
bool Seq::init(bool hotPlug)
2012-05-26 14:49:10 +02:00
{
if (!_driver || !_driver->start(hotPlug)) {
2012-05-26 14:49:10 +02:00
qDebug("Cannot start I/O");
running = false;
2012-05-26 14:49:10 +02:00
return false;
}
running = true;
return true;
}
//---------------------------------------------------------
// exit
//---------------------------------------------------------
void Seq::exit()
{
if (_driver) {
2012-05-26 14:49:10 +02:00
if (MScore::debugMode)
qDebug("Stop I/O");
2012-05-26 14:49:10 +02:00
stopWait();
delete _driver;
_driver = 0;
2012-05-26 14:49:10 +02:00
}
}
//---------------------------------------------------------
// rewindStart
//---------------------------------------------------------
void Seq::rewindStart()
{
seek(0);
}
2013-08-13 05:01:38 +02:00
//---------------------------------------------------------
// loopStart
//---------------------------------------------------------
void Seq::loopStart()
{
start();
// qDebug("LoopStart. playPos = %d", playPos);
2013-08-13 05:01:38 +02:00
}
2012-05-26 14:49:10 +02:00
//---------------------------------------------------------
// canStart
// return true if sequencer can be started
//---------------------------------------------------------
bool Seq::canStart()
{
if (!_driver)
2012-05-26 14:49:10 +02:00
return false;
collectEvents(getPlayStartUtick());
2017-05-14 17:43:28 +02:00
return (!events.empty() && endUTick != 0);
2012-05-26 14:49:10 +02:00
}
//---------------------------------------------------------
// start
// called from gui thread
//---------------------------------------------------------
void Seq::start()
{
2017-05-14 17:43:28 +02:00
if (!_driver) {
qDebug("No driver!");
return;
}
allowBackgroundRendering = true;
collectEvents(getPlayStartUtick());
if (cs->playMode() == PlayMode::AUDIO) {
2012-05-26 14:49:10 +02:00
if (!oggInit) {
vorbisData.pos = 0;
vorbisData.data = cs->audio()->data();
int n = ov_open_callbacks(&vorbisData, &vf, 0, 0, ovCallbacks);
if (n < 0) {
2014-03-04 16:48:06 +01:00
qDebug("ogg open failed: %d", n);
2012-05-26 14:49:10 +02:00
}
oggInit = true;
}
}
if (!preferences.getBool(PREF_IO_JACK_USEJACKTRANSPORT) || (preferences.getBool(PREF_IO_JACK_USEJACKTRANSPORT) && state == Transport::STOP))
seek(getPlayStartUtick());
if (preferences.getBool(PREF_IO_JACK_USEJACKTRANSPORT) && mscore->countIn() && state == Transport::STOP) {
2014-07-07 17:31:41 +02:00
// Ready to start playing count in, switching to fake transport
// to prevent playing in other applications with our ticks simultaneously
useJackTransportSavedFlag = true;
preferences.setPreference(PREF_IO_JACK_USEJACKTRANSPORT, false);
}
_driver->startTransport();
2012-05-26 14:49:10 +02:00
}
//---------------------------------------------------------
// stop
// called from gui thread
//---------------------------------------------------------
void Seq::stop()
{
const bool seqStopped = (state == Transport::STOP);
const bool driverStopped = !_driver || _driver->getState() == Transport::STOP;
if (seqStopped && driverStopped)
2012-05-26 14:49:10 +02:00
return;
allowBackgroundRendering = false;
2012-05-26 14:49:10 +02:00
if (oggInit) {
ov_clear(&vf);
oggInit = false;
}
if (!_driver)
2012-05-26 14:49:10 +02:00
return;
if (!preferences.getBool(PREF_IO_JACK_USEJACKTRANSPORT) || (preferences.getBool(PREF_IO_JACK_USEJACKTRANSPORT) && _driver->getState() == Transport::PLAY))
2014-06-18 21:36:53 +02:00
_driver->stopTransport();
2012-05-26 14:49:10 +02:00
if (cv)
cv->setCursorOn(false);
if (midiRenderFuture.isRunning())
midiRenderFuture.waitForFinished();
2012-05-26 14:49:10 +02:00
if (cs) {
cs->setUpdateAll();
cs->update();
2012-05-26 14:49:10 +02:00
}
}
2013-04-29 19:42:38 +02:00
//---------------------------------------------------------
// stopWait
//---------------------------------------------------------
void Seq::stopWait()
{
stop();
QWaitCondition sleep;
int idx = 0;
while (state != Transport::STOP) {
2015-03-06 22:54:52 +01:00
qDebug("State %d", (int)state);
2013-04-29 19:42:38 +02:00
mutex.lock();
sleep.wait(&mutex, 100);
mutex.unlock();
++idx;
Q_ASSERT(idx <= 10);
2013-04-29 19:42:38 +02:00
}
}
2012-05-26 14:49:10 +02:00
//---------------------------------------------------------
// seqStarted
//---------------------------------------------------------
void MuseScore::seqStarted()
{
2013-02-20 17:53:15 +01:00
if (cv)
2012-05-26 14:49:10 +02:00
cv->setCursorOn(true);
2013-02-20 17:53:15 +01:00
if (cs)
cs->update();
2012-05-26 14:49:10 +02:00
}
//---------------------------------------------------------
// seqStopped
// JACK has stopped
// executed in gui environment
//---------------------------------------------------------
void MuseScore::seqStopped()
{
cv->setCursorOn(false);
}
2013-02-20 17:53:15 +01:00
//---------------------------------------------------------
// unmarkNotes
//---------------------------------------------------------
void Seq::unmarkNotes()
{
foreach(const Note* n, markedNotes) {
n->setMark(false);
cs->addRefresh(n->canvasBoundingRect());
}
markedNotes.clear();
PianoTools* piano = mscore->pianoTools();
if (piano && piano->isVisible())
piano->setPlaybackNotes(markedNotes);
2013-02-20 17:53:15 +01:00
}
2012-05-26 14:49:10 +02:00
//---------------------------------------------------------
// guiStop
//---------------------------------------------------------
void Seq::guiStop()
{
QAction* a = getAction("play");
a->setChecked(false);
2013-02-20 17:53:15 +01:00
unmarkNotes();
if (!cs)
2012-05-26 14:49:10 +02:00
return;
int tck = cs->repeatList().utick2tick(cs->utime2utick(qreal(playFrame) / qreal(MScore::sampleRate)));
cs->setPlayPos(Fraction::fromTicks(tck));
cs->update();
2012-05-26 14:49:10 +02:00
emit stopped();
}
//---------------------------------------------------------
// seqSignal
// sequencer message to GUI
// execution environment: gui thread
//---------------------------------------------------------
2014-07-07 17:31:41 +02:00
void Seq::seqMessage(int msg, int arg)
2012-05-26 14:49:10 +02:00
{
switch(msg) {
2014-07-07 17:31:41 +02:00
case '5': {
// Update the screen after seeking from the realtime thread
Segment* seg = cs->tick2segment(Fraction::fromTicks(arg));
2014-07-07 17:31:41 +02:00
if (seg)
mscore->currentScoreView()->moveCursor(seg->tick());
cs->setPlayPos(Fraction::fromTicks(arg));
cs->update();
2014-07-07 17:31:41 +02:00
break;
}
2013-09-12 15:57:14 +02:00
case '4': // Restart the playback at the end of the score
loopStart();
break;
case '3': // Loop restart while playing
seek(cs->repeatList().tick2utick(cs->loopInTick().ticks()));
break;
2013-04-29 20:16:06 +02:00
case '2':
2012-05-26 14:49:10 +02:00
guiStop();
// heartBeatTimer->stop();
if (_driver && mscore->getSynthControl()) {
2012-05-26 14:49:10 +02:00
meterValue[0] = .0f;
meterValue[1] = .0f;
meterPeakValue[0] = .0f;
meterPeakValue[1] = .0f;
peakTimer[0] = 0;
peakTimer[1] = 0;
mscore->getSynthControl()->setMeter(0.0, 0.0, 0.0, 0.0);
}
2013-04-29 19:42:38 +02:00
seek(0);
2012-05-26 14:49:10 +02:00
break;
case '0': // STOP
guiStop();
// heartBeatTimer->stop();
if (_driver && mscore->getSynthControl()) {
2012-05-26 14:49:10 +02:00
meterValue[0] = .0f;
meterValue[1] = .0f;
meterPeakValue[0] = .0f;
meterPeakValue[1] = .0f;
peakTimer[0] = 0;
peakTimer[1] = 0;
mscore->getSynthControl()->setMeter(0.0, 0.0, 0.0, 0.0);
}
break;
case '1': // PLAY
emit started();
// heartBeatTimer->start(1000/guiRefresh);
break;
default:
qDebug("MScore::Seq:: unknown seq msg %d", msg);
2012-05-26 14:49:10 +02:00
break;
}
}
//---------------------------------------------------------
// playEvent
// send one event to the synthesizer
//---------------------------------------------------------
void Seq::playEvent(const NPlayEvent& event, unsigned framePos)
2012-05-26 14:49:10 +02:00
{
int type = event.type();
if (type == ME_NOTEON) {
if (!event.isMuted()) {
if (event.discard()) { // ignore noteoff but restrike noteon
if (event.velo() > 0)
putEvent(NPlayEvent(ME_NOTEON, event.channel(), event.pitch(), 0) ,framePos);
else
return;
}
putEvent(event, framePos);
}
2012-05-26 14:49:10 +02:00
}
2015-06-01 19:49:48 +02:00
else if (type == ME_CONTROLLER || type == ME_PITCHBEND)
putEvent(event, framePos);
2012-05-26 14:49:10 +02:00
}
//---------------------------------------------------------
// recomputeMaxMidiOutPort
// Computes the maximum number of midi out ports
// in all opened scores
//---------------------------------------------------------
2016-03-11 12:18:46 +01:00
void Seq::recomputeMaxMidiOutPort()
{
if (!(preferences.getBool(PREF_IO_JACK_USEJACKMIDI) || preferences.getBool(PREF_IO_ALSA_USEALSAAUDIO)))
return;
int max = 0;
2016-03-11 12:18:46 +01:00
for (Score * s : MuseScoreCore::mscoreCore->scores()) {
if (s->masterScore()->midiPortCount() > max)
max = s->masterScore()->midiPortCount();
}
maxMidiOutPort = max;
}
2012-05-26 14:49:10 +02:00
//---------------------------------------------------------
// processMessages
2014-07-07 17:31:41 +02:00
// from gui to process thread
2012-05-26 14:49:10 +02:00
//---------------------------------------------------------
void Seq::processMessages()
{
for (;;) {
2016-02-06 22:03:43 +01:00
if (toSeq.empty())
2012-05-26 14:49:10 +02:00
break;
SeqMsg msg = toSeq.dequeue();
switch(msg.id) {
case SeqMsgId::TEMPO_CHANGE:
2012-05-26 14:49:10 +02:00
{
if (!cs)
continue;
2017-05-14 17:43:28 +02:00
if (playFrame != 0) {
int utick = cs->utime2utick(qreal(playFrame) / qreal(MScore::sampleRate));
2013-02-20 17:53:15 +01:00
cs->tempomap()->setRelTempo(msg.realVal);
2017-05-14 17:43:28 +02:00
playFrame = cs->utick2utime(utick) * MScore::sampleRate;
if (preferences.getBool(PREF_IO_JACK_TIMEBASEMASTER) && preferences.getBool(PREF_IO_JACK_USEJACKTRANSPORT))
2014-07-07 17:31:41 +02:00
_driver->seekTransport(utick + 2 * cs->utime2utick(qreal((_driver->bufferSize()) + 1) / qreal(MScore::sampleRate)));
2012-05-26 14:49:10 +02:00
}
else
2013-02-20 17:53:15 +01:00
cs->tempomap()->setRelTempo(msg.realVal);
cs->masterScore()->updateRepeatListTempo();
prevTempo = curTempo();
emit tempoChanged();
2012-05-26 14:49:10 +02:00
}
break;
case SeqMsgId::PLAY:
2012-05-26 14:49:10 +02:00
putEvent(msg.event);
break;
case SeqMsgId::SEEK:
2013-02-20 17:53:15 +01:00
setPos(msg.intVal);
2012-05-26 14:49:10 +02:00
break;
default:
break;
2012-05-26 14:49:10 +02:00
}
}
}
//---------------------------------------------------------
// metronome
//---------------------------------------------------------
void Seq::metronome(unsigned n, float* p, bool force)
2012-05-26 14:49:10 +02:00
{
if (!mscore->metronome() && !force) {
tickRemain = 0;
tackRemain = 0;
2012-05-26 14:49:10 +02:00
return;
}
if (tickRemain) {
tackRemain = 0;
int idx = tickLength - tickRemain;
int nn = n < tickRemain ? n : tickRemain;
2012-05-26 14:49:10 +02:00
for (int i = 0; i < nn; ++i) {
qreal v = tick[idx] * tickVolume * metronomeVolume;
2012-05-26 14:49:10 +02:00
*p++ += v;
*p++ += v;
++idx;
}
tickRemain -= nn;
2012-05-26 14:49:10 +02:00
}
if (tackRemain) {
int idx = tackLength - tackRemain;
int nn = n < tackRemain ? n : tackRemain;
2012-05-26 14:49:10 +02:00
for (int i = 0; i < nn; ++i) {
qreal v = tack[idx] * tackVolume * metronomeVolume;
2012-05-26 14:49:10 +02:00
*p++ += v;
*p++ += v;
++idx;
}
tackRemain -= nn;
2012-05-26 14:49:10 +02:00
}
}
//---------------------------------------------------------
// addCountInClicks
//---------------------------------------------------------
void Seq::addCountInClicks()
{
const Fraction plPos = cs->playPos();
Measure* m = cs->tick2measure(plPos);
Fraction msrTick = m->tick();
qreal tempo = cs->tempomap()->tempo(msrTick.ticks());
TimeSigFrac timeSig = cs->sigmap()->timesig(m->tick()).nominal();
const int clickTicks = timeSig.isBeatedCompound(tempo) ? timeSig.beatTicks() : timeSig.dUnitTicks();
// add at least one full measure of just clicks.
Fraction endTick = Fraction::fromTicks(timeSig.ticksPerMeasure());
// add extra clicks if...
endTick += plPos - msrTick; // ...not starting playback at beginning of measure
if (m->isAnacrusis()) // ...measure is incomplete (anacrusis)
endTick += Fraction::fromTicks(timeSig.ticksPerMeasure()) - m->ticks();
for (int t = 0; t < endTick.ticks(); t += clickTicks) {
const int rtick = t % timeSig.ticksPerMeasure();
countInEvents.insert(std::pair<int,NPlayEvent>(t, NPlayEvent(timeSig.rtick2beatType(rtick))));
}
NPlayEvent event;
event.setType(ME_INVALID);
event.setPitch(0);
countInEvents.insert( std::pair<int,NPlayEvent>(endTick.ticks(), event));
// initialize play parameters to count-in events
countInPlayPos = countInEvents.cbegin();
2017-05-14 17:43:28 +02:00
countInPlayFrame = 0;
}
2014-06-05 08:35:39 +02:00
//-------------------------------------------------------------------
2012-05-26 14:49:10 +02:00
// process
2014-06-05 08:35:39 +02:00
// This function is called in a realtime context. This
// means that no blocking operations are allowed which
// includes memory allocation. The usual thread synchronisation
// methods like semaphores can also not be used.
//-------------------------------------------------------------------
2012-05-26 14:49:10 +02:00
2017-05-14 17:43:28 +02:00
void Seq::process(unsigned framesPerPeriod, float* buffer)
2012-05-26 14:49:10 +02:00
{
2017-05-14 17:43:28 +02:00
unsigned framesRemain = framesPerPeriod; // the number of frames remaining to be processed by this call to Seq::process
Transport driverState = _driver->getState();
// Checking for the reposition from JACK Transport
2017-05-14 17:43:28 +02:00
_driver->checkTransportSeek(playFrame, framesRemain, inCountIn);
if (driverState != state) {
2014-05-24 21:30:33 +02:00
// Got a message from JACK Transport panel: Play
if (state == Transport::STOP && driverState == Transport::PLAY) {
if ((preferences.getBool(PREF_IO_JACK_USEJACKMIDI) || preferences.getBool(PREF_IO_JACK_USEJACKAUDIO)) && !getAction("play")->isChecked()) {
2014-05-24 21:30:33 +02:00
// Do not play while editing elements
2014-07-07 17:31:41 +02:00
if (mscore->state() != STATE_NORMAL || !isRunning() || !canStart())
2014-05-24 21:30:33 +02:00
return;
2014-07-07 17:31:41 +02:00
getAction("play")->setChecked(true);
getAction("play")->triggered(true);
// If we just launch MuseScore and press "Play" on JACK Transport with time 0:00
// MuseScore doesn't seek to 0 and guiPos is uninitialized, so let's make it manually
if (preferences.getBool(PREF_IO_JACK_USEJACKTRANSPORT) && getCurTick() == 0)
2014-07-07 17:31:41 +02:00
seekRT(0);
// Switching to fake transport while playing count in
// to prevent playing in other applications with our ticks simultaneously
if (preferences.getBool(PREF_IO_JACK_USEJACKTRANSPORT) && mscore->countIn()) {
2014-07-07 17:31:41 +02:00
// Stopping real JACK Transport
_driver->stopTransport();
// Starting fake transport
useJackTransportSavedFlag = preferences.getBool(PREF_IO_JACK_USEJACKTRANSPORT);
preferences.setPreference(PREF_IO_JACK_USEJACKTRANSPORT, false);
2014-07-07 17:31:41 +02:00
_driver->startTransport();
2014-05-24 21:30:33 +02:00
}
}
// Initializing instruments every time we start playback.
// External synth can have wrong values, for example
// if we switch between scores
initInstruments(true);
2014-05-24 21:30:33 +02:00
// Need to change state after calling collectEvents()
state = Transport::PLAY;
2014-05-31 07:23:36 +02:00
if (mscore->countIn() && cs->playMode() == PlayMode::SYNTHESIZER) {
countInEvents.clear();
inCountIn = true;
}
2013-04-29 19:42:38 +02:00
emit toGui('1');
}
2014-05-24 21:30:33 +02:00
// Got a message from JACK Transport panel: Stop
else if (state == Transport::PLAY && driverState == Transport::STOP) {
state = Transport::STOP;
2014-05-24 21:30:33 +02:00
// Muting all notes
stopNotes(-1, true);
initInstruments(true);
if (playPos == eventsEnd) {
if (mscore->loop()) {
qDebug("Seq.cpp - Process - Loop whole score. playPos = %d, cs->pos() = %d", playPos->first, cs->pos().ticks());
2013-09-12 15:57:14 +02:00
emit toGui('4');
return;
2013-08-19 11:21:21 +02:00
}
else {
emit toGui('2');
}
}
else {
emit toGui('0');
}
2013-04-29 19:42:38 +02:00
}
2012-05-26 14:49:10 +02:00
else if (state != driverState)
qDebug("Seq: state transition %d -> %d ?",
2015-03-06 22:54:52 +01:00
(int)state, (int)driverState);
2012-05-26 14:49:10 +02:00
}
2017-05-14 17:43:28 +02:00
memset(buffer, 0, sizeof(float) * framesPerPeriod * 2); // assume two channels
2012-05-26 14:49:10 +02:00
float* p = buffer;
2012-05-26 14:49:10 +02:00
processMessages();
if (state == Transport::PLAY) {
2014-07-07 17:31:41 +02:00
if (!cs)
2013-06-28 16:59:19 +02:00
return;
2017-05-14 17:43:28 +02:00
// if currently in count-in, these pointers will reference data in the count-in
EventMap::const_iterator* pPlayPos = &playPos;
EventMap::const_iterator pEventsEnd = eventsEnd;
2017-05-14 17:43:28 +02:00
int* pPlayFrame = &playFrame;
if (inCountIn) {
if (countInEvents.size() == 0)
addCountInClicks();
pEventsEnd = countInEvents.cend();
2017-05-14 17:43:28 +02:00
pPlayPos = &countInPlayPos;
pPlayFrame = &countInPlayFrame;
}
2017-05-14 17:43:28 +02:00
2012-05-26 14:49:10 +02:00
//
// play events for one segment
//
2017-05-14 17:43:28 +02:00
unsigned framePos = 0; // frame currently being processed relative to the first frame of this call to Seq::process
int periodEndFrame = *pPlayFrame + framesPerPeriod; // the ending frame (relative to start of playback) of the period being processed by this call to Seq::process
int scoreEndUTick = cs->repeatList().tick2utick(cs->lastMeasure()->endTick().ticks());
while (*pPlayPos != pEventsEnd) {
2017-05-14 17:43:28 +02:00
int playPosUTick = (*pPlayPos)->first;
int n; // current frame (relative to start of playback) that is being synthesized
2017-05-14 17:43:28 +02:00
if (inCountIn) {
qreal beatsPerSecond = curTempo() * cs->tempomap()->relTempo(); // relTempo needed here to ensure that bps changes as we slide the tempo bar
qreal ticksPerSecond = beatsPerSecond * MScore::division;
qreal playPosSeconds = playPosUTick / ticksPerSecond;
int playPosFrame = playPosSeconds * MScore::sampleRate;
if (playPosFrame >= periodEndFrame)
break;
2017-05-14 17:43:28 +02:00
n = playPosFrame - *pPlayFrame;
if (n < 0) {
2017-05-14 17:43:28 +02:00
qDebug("Count-in: playPosUTick %d: n = %d - %d", playPosUTick, playPosFrame, *pPlayFrame);
n = 0;
}
2012-05-26 14:49:10 +02:00
}
else {
2017-05-14 17:43:28 +02:00
qreal playPosSeconds = cs->utick2utime(playPosUTick);
int playPosFrame = playPosSeconds * MScore::sampleRate;
if (playPosFrame >= periodEndFrame)
break;
2017-05-14 17:43:28 +02:00
n = playPosFrame - *pPlayFrame;
if (n < 0) {
2017-05-14 17:43:28 +02:00
qDebug("%d: %d - %d", playPosUTick, playPosFrame, *pPlayFrame);
n = 0;
}
if (mscore->loop()) {
int loopOutUTick = cs->repeatList().tick2utick(cs->loopOutTick().ticks());
2017-05-14 17:43:28 +02:00
if (loopOutUTick < scoreEndUTick) {
// Also make sure we are not "before" the loop
if (playPosUTick >= loopOutUTick || cs->repeatList().utick2tick(playPosUTick) < cs->loopInTick().ticks()) {
qDebug ("Process: playPosUTick = %d, cs->loopInTick().ticks() = %d, cs->loopOutTick().ticks() = %d, getCurTick() = %d, loopOutUTick = %d, playFrame = %d",
playPosUTick, cs->loopInTick().ticks(), cs->loopOutTick().ticks(), getCurTick(), loopOutUTick, *pPlayFrame);
if (preferences.getBool(PREF_IO_JACK_USEJACKTRANSPORT)) {
int loopInUTick = cs->repeatList().tick2utick(cs->loopInTick().ticks());
2017-05-14 17:43:28 +02:00
_driver->seekTransport(loopInUTick);
if (loopInUTick != 0) {
int seekto = loopInUTick - 2 * cs->utime2utick((qreal)_driver->bufferSize() / MScore::sampleRate);
2014-07-07 17:31:41 +02:00
seekRT((seekto > 0) ? seekto : 0 );
2014-06-18 21:36:53 +02:00
}
}
else {
emit toGui('3');
}
2014-06-18 21:36:53 +02:00
// Exit this function to avoid segmentation fault in Scoreview
return;
}
}
}
}
2012-05-26 14:49:10 +02:00
if (n) {
if (cs->playMode() == PlayMode::SYNTHESIZER) {
metronome(n, p, inCountIn);
_synti->process(n, p);
2012-05-26 14:49:10 +02:00
p += n * 2;
2017-05-14 17:43:28 +02:00
*pPlayFrame += n;
framesRemain -= n;
framePos += n;
2012-05-26 14:49:10 +02:00
}
else {
while (n > 0) {
int section;
float** pcm;
long rn = ov_read_float(&vf, &pcm, n, &section);
if (rn == 0)
break;
for (int i = 0; i < rn; ++i) {
*p++ = pcm[0][i];
*p++ = pcm[1][i];
}
2017-05-14 17:43:28 +02:00
*pPlayFrame += rn;
framesRemain -= rn;
framePos += rn;
n -= rn;
2012-05-26 14:49:10 +02:00
}
}
}
const NPlayEvent& event = (*pPlayPos)->second;
playEvent(event, framePos);
if (event.type() == ME_TICK1) {
tickRemain = tickLength;
tickVolume = event.velo() ? qreal(event.value()) / 127.0 : 1.0;
}
else if (event.type() == ME_TICK2) {
tackRemain = tackLength;
tackVolume = event.velo() ? qreal(event.value()) / 127.0 : 1.0;
}
2013-02-20 17:53:15 +01:00
mutex.lock();
++(*pPlayPos);
2013-02-20 17:53:15 +01:00
mutex.unlock();
2012-05-26 14:49:10 +02:00
}
2017-05-14 17:43:28 +02:00
if (framesRemain) {
if (cs->playMode() == PlayMode::SYNTHESIZER) {
2017-05-14 17:43:28 +02:00
metronome(framesRemain, p, inCountIn);
_synti->process(framesRemain, p);
*pPlayFrame += framesRemain;
2012-05-26 14:49:10 +02:00
}
else {
2017-05-14 17:43:28 +02:00
int n = framesRemain;
2012-05-26 14:49:10 +02:00
while (n > 0) {
int section;
float** pcm;
long rn = ov_read_float(&vf, &pcm, n, &section);
if (rn == 0)
break;
for (int i = 0; i < rn; ++i) {
*p++ = pcm[0][i];
*p++ = pcm[1][i];
}
2017-05-14 17:43:28 +02:00
*pPlayFrame += rn;
framesRemain -= rn;
framePos += rn;
n -= rn;
2012-05-26 14:49:10 +02:00
}
}
}
if (*pPlayPos == pEventsEnd) {
2014-07-07 17:31:41 +02:00
if (inCountIn) {
inCountIn = false;
2014-07-07 17:31:41 +02:00
// Connecting to JACK Transport if MuseScore was temporarily disconnected from it
if (useJackTransportSavedFlag) {
// Stopping fake driver
_driver->stopTransport();
preferences.setPreference(PREF_IO_JACK_USEJACKTRANSPORT, true);
2014-07-07 17:31:41 +02:00
// Starting the real JACK Transport. All applications play in sync now
_driver->startTransport();
}
}
else
_driver->stopTransport();
}
2012-05-26 14:49:10 +02:00
}
else {
// Outside of playback mode
while (!liveEventQueue()->empty()) {
const NPlayEvent& event = liveEventQueue()->dequeue();
if (event.type() == ME_TICK1) {
tickRemain = tickLength;
tickVolume = event.velo() ? qreal(event.value()) / 127.0 : 1.0;
}
else if (event.type() == ME_TICK2) {
tackRemain = tackLength;
tackVolume = event.velo() ? qreal(event.value()) / 127.0 : 1.0;
}
}
2017-05-14 17:43:28 +02:00
if (framesRemain) {
metronome(framesRemain, p, true);
_synti->process(framesRemain, p);
}
2012-05-26 14:49:10 +02:00
}
//
2013-03-27 20:10:14 +01:00
// metering / master gain
2012-05-26 14:49:10 +02:00
//
2016-02-15 09:33:54 +01:00
qreal lv = 0.0f;
qreal rv = 0.0f;
2013-03-27 20:10:14 +01:00
p = buffer;
2017-05-14 17:43:28 +02:00
for (unsigned i = 0; i < framesRemain; ++i) {
2013-04-03 12:49:55 +02:00
qreal val = *p;
2016-02-15 09:33:54 +01:00
lv = qMax(lv, qAbs(val));
p++;
2013-03-27 20:10:14 +01:00
2013-04-03 12:49:55 +02:00
val = *p;
2017-08-18 17:21:22 +02:00
rv = qMax(rv, qAbs(val));
p++;
2012-05-26 14:49:10 +02:00
}
meterValue[0] = lv;
meterValue[1] = rv;
if (meterPeakValue[0] < lv) {
meterPeakValue[0] = lv;
peakTimer[0] = 0;
}
if (meterPeakValue[1] < rv) {
meterPeakValue[1] = rv;
peakTimer[1] = 0;
}
}
2012-05-26 14:49:10 +02:00
//---------------------------------------------------------
// initInstruments
//---------------------------------------------------------
void Seq::initInstruments(bool realTime)
2012-05-26 14:49:10 +02:00
{
// Add midi out ports if necessary
if (cs && (preferences.getBool(PREF_IO_JACK_USEJACKMIDI) || preferences.getBool(PREF_IO_ALSA_USEALSAAUDIO))) {
// Increase the maximum number of midi ports if user adds staves/instruments
2016-03-11 12:18:46 +01:00
int scoreMaxMidiPort = cs->masterScore()->midiPortCount();
if (maxMidiOutPort < scoreMaxMidiPort)
maxMidiOutPort = scoreMaxMidiPort;
// if maxMidiOutPort is equal to existing ports number, it will do nothing
if (_driver)
_driver->updateOutPortCount(maxMidiOutPort + 1);
}
for (const MidiMapping& mm : cs->midiMapping()) {
const Channel* channel = mm.articulation();
2018-12-22 11:43:23 +01:00
for (const MidiCoreEvent& e : channel->initList()) {
2012-05-26 14:49:10 +02:00
if (e.type() == ME_INVALID)
continue;
fix #275313: rework mixer ui 2 Moving PartEditBase into separate file. Creating new files for building mixer. Creating art assets/UI design for new components. Styling the track control. Adding track area. Separating out score from update. Creating instances of mixer UI. Creating part per voice now. Can click on tracks to select them now. Can now switch bwtewwn tracks. Setting patch channel now. Setting enabled off when no track selected. Improving slider ui. Turning Channel into a class and adding listener to it. Somewhat stabalized sharing track objects between interfaces. Can now apply volume changes to both expanded and collapsed tracks. Pan knob is now working. Encapsulating the rest of the fields in Channel. Mute and solo now working. Reverb and chorus now working. Drumkit checkbox now working. Port and channel somewhat working. Adding support for colors per track. Part name change now working. Separating out MixerTrackItem Finishing moving MixerTrackItem to new file. Cleaning up code. Moving PartEditBase into separate file. Creating new files for building mixer. Creating art assets/UI design for new components. Styling the track control. Adding track area. Separating out score from update. Creating instances of mixer UI. Creating part per voice now. Can click on tracks to select them now. Can now switch bwtewwn tracks. Setting patch channel now. Setting enabled off when no track selected. Improving slider ui. Turning Channel into a class and adding listener to it. Somewhat stabalized sharing track objects between interfaces. Can now apply volume changes to both expanded and collapsed tracks. Pan knob is now working. Encapsulating the rest of the fields in Channel. Mute and solo now working. Reverb and chorus now working. Drumkit checkbox now working. Port and channel somewhat working. Adding support for colors per track. Part name change now working. Separating out MixerTrackItem Finishing moving MixerTrackItem to new file. Cleaning up code. Setting color in collapsed mode now affects all channels. Using shared_ptr to track MixerTrackItem. Part changes now affect all instruments. Creating new track UI object to handle parts. Using shard_ptr to track MixerTrackItem objects. setting port and channel data now. Changing to horizontal layout. Fixing knob display. Chaning track control appearance. Setting init slider window size. Switchong back to vertical orientation. Fixing a few UI bugs in the slider. Tracks now left aligned. Moving details panel above mixer. Now changing track selection when user clicks on sliders. Pan and volume controls now reflect track color. Showing volume and pan values in tooltips. Creating a new slider control for mixer. Switching Channel's volume, pan, reverb and chorus and chaning them to doubles with a decimal range. No longer writing out vol, pan, chor, reverb when at default values. Nolonger writing vol, pan, chorus, reverb as controler values in output file. Now testing against default values on write. More export fixes. Manually editing test files to reflect new channel parameters. Manually editing more test files to reflect new channel parameters. Manually editing more test files to reflect new channel parameters. More test changes to make Travis happy. More test changes to make Travis happy. Importing MusicXML now matches new volume, pan ranges. Changing range of pan. Fixing a few bugs with calculating MIDI. Altering test files for Travis. fix #275313: rework-mixer-ui-2 Moving PartEditBase into separate file. Creating new files for building mixer. Creating art assets/UI design for new components. Styling the track control. Adding track area. Separating out score from update. Creating instances of mixer UI. Creating part per voice now. Can click on tracks to select them now. Can now switch bwtewwn tracks. Setting patch channel now. Setting enabled off when no track selected. Improving slider ui. Turning Channel into a class and adding listener to it. Somewhat stabalized sharing track objects between interfaces. Can now apply volume changes to both expanded and collapsed tracks. Pan knob is now working. Encapsulating the rest of the fields in Channel. Mute and solo now working. Reverb and chorus now working. Drumkit checkbox now working. Port and channel somewhat working. Adding support for colors per track. Part name change now working. Separating out MixerTrackItem Finishing moving MixerTrackItem to new file. Cleaning up code. Moving PartEditBase into separate file. Creating new files for building mixer. Creating art assets/UI design for new components. Styling the track control. Adding track area. Separating out score from update. Creating instances of mixer UI. Creating part per voice now. Can click on tracks to select them now. Can now switch bwtewwn tracks. Setting patch channel now. Setting enabled off when no track selected. Improving slider ui. Turning Channel into a class and adding listener to it. Somewhat stabalized sharing track objects between interfaces. Can now apply volume changes to both expanded and collapsed tracks. Pan knob is now working. Encapsulating the rest of the fields in Channel. Mute and solo now working. Reverb and chorus now working. Drumkit checkbox now working. Port and channel somewhat working. Adding support for colors per track. Part name change now working. Separating out MixerTrackItem Finishing moving MixerTrackItem to new file. Cleaning up code. Setting color in collapsed mode now affects all channels. Using shared_ptr to track MixerTrackItem. Part changes now affect all instruments. Creating new track UI object to handle parts. Using shard_ptr to track MixerTrackItem objects. setting port and channel data now. Changing to horizontal layout. Fixing knob display. Chaning track control appearance. Setting init slider window size. Switchong back to vertical orientation. Fixing a few UI bugs in the slider. Tracks now left aligned. Moving details panel above mixer. Now changing track selection when user clicks on sliders. Pan and volume controls now reflect track color. Showing volume and pan values in tooltips. Creating a new slider control for mixer. Switching Channel's volume, pan, reverb and chorus and chaning them to doubles with a decimal range. No longer writing out vol, pan, chor, reverb when at default values. Nolonger writing vol, pan, chorus, reverb as controler values in output file. Now testing against default values on write. More export fixes. Manually editing test files to reflect new channel parameters. Manually editing more test files to reflect new channel parameters. Manually editing more test files to reflect new channel parameters. More test changes to make Travis happy. More test changes to make Travis happy. Importing MusicXML now matches new volume, pan ranges. Changing range of pan. Fixing a few bugs with calculating MIDI. Altering test files for Travis. Restoring the volume, pan, chorus, reverb to original char data type & range. UI now shows different 'user friendly' ranges. Overwriting tests with versions from master. mtest/libmscore/compat114/clef_missing_first-ref.mscx mtest/libmscore/compat114/hor_frame_and_mmrest-ref.mscx mtest/musicxml/io/testInstrumentChangeMIDIportExport_ref.xml mtest/musicxml/io/testUninitializedDivisions_ref.xml Restoring test files to original state. Restoring test files to original state. Restoring old values for importing files. Restoring part methods. mtest/importmidi/simplify_8th_dotted_no_staccato.mscx mtest/libmscore/compat114/clef_missing_first-ref.mscx mtest/libmscore/compat114/hor_frame_and_mmrest-ref.mscx mtest/musicxml/io/testInstrumentChangeMIDIportExport_ref.xml mtest/musicxml/io/testUninitializedDivisions_ref.xml Rearranging UI components for better feel. Improving UI. Fixed crash when changing part name. Adding support for two lighting modes. Showing part name over channel expansion. Adding master gain control to mixer. Changing color of gain slider. Adapting to latest source in main. Changing master gain slider to use decibel calculation. CSS now set on tracks whenever a Paint event received. Restoring mixer slider values to refect MIDI ranges. Fixing crash when drumkit checked. Fixing crash when closing score. Fixing alignment in mixer details. Tweaking UI for better appearance.
2018-11-13 18:43:19 +01:00
NPlayEvent event(e.type(), channel->channel(), e.dataA(), e.dataB());
if (realTime)
putEvent(event);
else
sendEvent(event);
2012-05-26 14:49:10 +02:00
}
2015-06-03 18:36:59 +02:00
// Setting pitch bend sensitivity to 12 semitones for external synthesizers
if ((preferences.getBool(PREF_IO_JACK_USEJACKMIDI) || preferences.getBool(PREF_IO_ALSA_USEALSAAUDIO)) && mm.channel() != 9) {
2015-06-03 18:36:59 +02:00
if (realTime) {
fix #275313: rework mixer ui 2 Moving PartEditBase into separate file. Creating new files for building mixer. Creating art assets/UI design for new components. Styling the track control. Adding track area. Separating out score from update. Creating instances of mixer UI. Creating part per voice now. Can click on tracks to select them now. Can now switch bwtewwn tracks. Setting patch channel now. Setting enabled off when no track selected. Improving slider ui. Turning Channel into a class and adding listener to it. Somewhat stabalized sharing track objects between interfaces. Can now apply volume changes to both expanded and collapsed tracks. Pan knob is now working. Encapsulating the rest of the fields in Channel. Mute and solo now working. Reverb and chorus now working. Drumkit checkbox now working. Port and channel somewhat working. Adding support for colors per track. Part name change now working. Separating out MixerTrackItem Finishing moving MixerTrackItem to new file. Cleaning up code. Moving PartEditBase into separate file. Creating new files for building mixer. Creating art assets/UI design for new components. Styling the track control. Adding track area. Separating out score from update. Creating instances of mixer UI. Creating part per voice now. Can click on tracks to select them now. Can now switch bwtewwn tracks. Setting patch channel now. Setting enabled off when no track selected. Improving slider ui. Turning Channel into a class and adding listener to it. Somewhat stabalized sharing track objects between interfaces. Can now apply volume changes to both expanded and collapsed tracks. Pan knob is now working. Encapsulating the rest of the fields in Channel. Mute and solo now working. Reverb and chorus now working. Drumkit checkbox now working. Port and channel somewhat working. Adding support for colors per track. Part name change now working. Separating out MixerTrackItem Finishing moving MixerTrackItem to new file. Cleaning up code. Setting color in collapsed mode now affects all channels. Using shared_ptr to track MixerTrackItem. Part changes now affect all instruments. Creating new track UI object to handle parts. Using shard_ptr to track MixerTrackItem objects. setting port and channel data now. Changing to horizontal layout. Fixing knob display. Chaning track control appearance. Setting init slider window size. Switchong back to vertical orientation. Fixing a few UI bugs in the slider. Tracks now left aligned. Moving details panel above mixer. Now changing track selection when user clicks on sliders. Pan and volume controls now reflect track color. Showing volume and pan values in tooltips. Creating a new slider control for mixer. Switching Channel's volume, pan, reverb and chorus and chaning them to doubles with a decimal range. No longer writing out vol, pan, chor, reverb when at default values. Nolonger writing vol, pan, chorus, reverb as controler values in output file. Now testing against default values on write. More export fixes. Manually editing test files to reflect new channel parameters. Manually editing more test files to reflect new channel parameters. Manually editing more test files to reflect new channel parameters. More test changes to make Travis happy. More test changes to make Travis happy. Importing MusicXML now matches new volume, pan ranges. Changing range of pan. Fixing a few bugs with calculating MIDI. Altering test files for Travis. fix #275313: rework-mixer-ui-2 Moving PartEditBase into separate file. Creating new files for building mixer. Creating art assets/UI design for new components. Styling the track control. Adding track area. Separating out score from update. Creating instances of mixer UI. Creating part per voice now. Can click on tracks to select them now. Can now switch bwtewwn tracks. Setting patch channel now. Setting enabled off when no track selected. Improving slider ui. Turning Channel into a class and adding listener to it. Somewhat stabalized sharing track objects between interfaces. Can now apply volume changes to both expanded and collapsed tracks. Pan knob is now working. Encapsulating the rest of the fields in Channel. Mute and solo now working. Reverb and chorus now working. Drumkit checkbox now working. Port and channel somewhat working. Adding support for colors per track. Part name change now working. Separating out MixerTrackItem Finishing moving MixerTrackItem to new file. Cleaning up code. Moving PartEditBase into separate file. Creating new files for building mixer. Creating art assets/UI design for new components. Styling the track control. Adding track area. Separating out score from update. Creating instances of mixer UI. Creating part per voice now. Can click on tracks to select them now. Can now switch bwtewwn tracks. Setting patch channel now. Setting enabled off when no track selected. Improving slider ui. Turning Channel into a class and adding listener to it. Somewhat stabalized sharing track objects between interfaces. Can now apply volume changes to both expanded and collapsed tracks. Pan knob is now working. Encapsulating the rest of the fields in Channel. Mute and solo now working. Reverb and chorus now working. Drumkit checkbox now working. Port and channel somewhat working. Adding support for colors per track. Part name change now working. Separating out MixerTrackItem Finishing moving MixerTrackItem to new file. Cleaning up code. Setting color in collapsed mode now affects all channels. Using shared_ptr to track MixerTrackItem. Part changes now affect all instruments. Creating new track UI object to handle parts. Using shard_ptr to track MixerTrackItem objects. setting port and channel data now. Changing to horizontal layout. Fixing knob display. Chaning track control appearance. Setting init slider window size. Switchong back to vertical orientation. Fixing a few UI bugs in the slider. Tracks now left aligned. Moving details panel above mixer. Now changing track selection when user clicks on sliders. Pan and volume controls now reflect track color. Showing volume and pan values in tooltips. Creating a new slider control for mixer. Switching Channel's volume, pan, reverb and chorus and chaning them to doubles with a decimal range. No longer writing out vol, pan, chor, reverb when at default values. Nolonger writing vol, pan, chorus, reverb as controler values in output file. Now testing against default values on write. More export fixes. Manually editing test files to reflect new channel parameters. Manually editing more test files to reflect new channel parameters. Manually editing more test files to reflect new channel parameters. More test changes to make Travis happy. More test changes to make Travis happy. Importing MusicXML now matches new volume, pan ranges. Changing range of pan. Fixing a few bugs with calculating MIDI. Altering test files for Travis. Restoring the volume, pan, chorus, reverb to original char data type & range. UI now shows different 'user friendly' ranges. Overwriting tests with versions from master. mtest/libmscore/compat114/clef_missing_first-ref.mscx mtest/libmscore/compat114/hor_frame_and_mmrest-ref.mscx mtest/musicxml/io/testInstrumentChangeMIDIportExport_ref.xml mtest/musicxml/io/testUninitializedDivisions_ref.xml Restoring test files to original state. Restoring test files to original state. Restoring old values for importing files. Restoring part methods. mtest/importmidi/simplify_8th_dotted_no_staccato.mscx mtest/libmscore/compat114/clef_missing_first-ref.mscx mtest/libmscore/compat114/hor_frame_and_mmrest-ref.mscx mtest/musicxml/io/testInstrumentChangeMIDIportExport_ref.xml mtest/musicxml/io/testUninitializedDivisions_ref.xml Rearranging UI components for better feel. Improving UI. Fixed crash when changing part name. Adding support for two lighting modes. Showing part name over channel expansion. Adding master gain control to mixer. Changing color of gain slider. Adapting to latest source in main. Changing master gain slider to use decibel calculation. CSS now set on tracks whenever a Paint event received. Restoring mixer slider values to refect MIDI ranges. Fixing crash when drumkit checked. Fixing crash when closing score. Fixing alignment in mixer details. Tweaking UI for better appearance.
2018-11-13 18:43:19 +01:00
putEvent(NPlayEvent(ME_CONTROLLER, channel->channel(), CTRL_LRPN, 0));
putEvent(NPlayEvent(ME_CONTROLLER, channel->channel(), CTRL_HRPN, 0));
putEvent(NPlayEvent(ME_CONTROLLER, channel->channel(), CTRL_HDATA,12));
fix #275313: rework mixer ui 2 Moving PartEditBase into separate file. Creating new files for building mixer. Creating art assets/UI design for new components. Styling the track control. Adding track area. Separating out score from update. Creating instances of mixer UI. Creating part per voice now. Can click on tracks to select them now. Can now switch bwtewwn tracks. Setting patch channel now. Setting enabled off when no track selected. Improving slider ui. Turning Channel into a class and adding listener to it. Somewhat stabalized sharing track objects between interfaces. Can now apply volume changes to both expanded and collapsed tracks. Pan knob is now working. Encapsulating the rest of the fields in Channel. Mute and solo now working. Reverb and chorus now working. Drumkit checkbox now working. Port and channel somewhat working. Adding support for colors per track. Part name change now working. Separating out MixerTrackItem Finishing moving MixerTrackItem to new file. Cleaning up code. Moving PartEditBase into separate file. Creating new files for building mixer. Creating art assets/UI design for new components. Styling the track control. Adding track area. Separating out score from update. Creating instances of mixer UI. Creating part per voice now. Can click on tracks to select them now. Can now switch bwtewwn tracks. Setting patch channel now. Setting enabled off when no track selected. Improving slider ui. Turning Channel into a class and adding listener to it. Somewhat stabalized sharing track objects between interfaces. Can now apply volume changes to both expanded and collapsed tracks. Pan knob is now working. Encapsulating the rest of the fields in Channel. Mute and solo now working. Reverb and chorus now working. Drumkit checkbox now working. Port and channel somewhat working. Adding support for colors per track. Part name change now working. Separating out MixerTrackItem Finishing moving MixerTrackItem to new file. Cleaning up code. Setting color in collapsed mode now affects all channels. Using shared_ptr to track MixerTrackItem. Part changes now affect all instruments. Creating new track UI object to handle parts. Using shard_ptr to track MixerTrackItem objects. setting port and channel data now. Changing to horizontal layout. Fixing knob display. Chaning track control appearance. Setting init slider window size. Switchong back to vertical orientation. Fixing a few UI bugs in the slider. Tracks now left aligned. Moving details panel above mixer. Now changing track selection when user clicks on sliders. Pan and volume controls now reflect track color. Showing volume and pan values in tooltips. Creating a new slider control for mixer. Switching Channel's volume, pan, reverb and chorus and chaning them to doubles with a decimal range. No longer writing out vol, pan, chor, reverb when at default values. Nolonger writing vol, pan, chorus, reverb as controler values in output file. Now testing against default values on write. More export fixes. Manually editing test files to reflect new channel parameters. Manually editing more test files to reflect new channel parameters. Manually editing more test files to reflect new channel parameters. More test changes to make Travis happy. More test changes to make Travis happy. Importing MusicXML now matches new volume, pan ranges. Changing range of pan. Fixing a few bugs with calculating MIDI. Altering test files for Travis. fix #275313: rework-mixer-ui-2 Moving PartEditBase into separate file. Creating new files for building mixer. Creating art assets/UI design for new components. Styling the track control. Adding track area. Separating out score from update. Creating instances of mixer UI. Creating part per voice now. Can click on tracks to select them now. Can now switch bwtewwn tracks. Setting patch channel now. Setting enabled off when no track selected. Improving slider ui. Turning Channel into a class and adding listener to it. Somewhat stabalized sharing track objects between interfaces. Can now apply volume changes to both expanded and collapsed tracks. Pan knob is now working. Encapsulating the rest of the fields in Channel. Mute and solo now working. Reverb and chorus now working. Drumkit checkbox now working. Port and channel somewhat working. Adding support for colors per track. Part name change now working. Separating out MixerTrackItem Finishing moving MixerTrackItem to new file. Cleaning up code. Moving PartEditBase into separate file. Creating new files for building mixer. Creating art assets/UI design for new components. Styling the track control. Adding track area. Separating out score from update. Creating instances of mixer UI. Creating part per voice now. Can click on tracks to select them now. Can now switch bwtewwn tracks. Setting patch channel now. Setting enabled off when no track selected. Improving slider ui. Turning Channel into a class and adding listener to it. Somewhat stabalized sharing track objects between interfaces. Can now apply volume changes to both expanded and collapsed tracks. Pan knob is now working. Encapsulating the rest of the fields in Channel. Mute and solo now working. Reverb and chorus now working. Drumkit checkbox now working. Port and channel somewhat working. Adding support for colors per track. Part name change now working. Separating out MixerTrackItem Finishing moving MixerTrackItem to new file. Cleaning up code. Setting color in collapsed mode now affects all channels. Using shared_ptr to track MixerTrackItem. Part changes now affect all instruments. Creating new track UI object to handle parts. Using shard_ptr to track MixerTrackItem objects. setting port and channel data now. Changing to horizontal layout. Fixing knob display. Chaning track control appearance. Setting init slider window size. Switchong back to vertical orientation. Fixing a few UI bugs in the slider. Tracks now left aligned. Moving details panel above mixer. Now changing track selection when user clicks on sliders. Pan and volume controls now reflect track color. Showing volume and pan values in tooltips. Creating a new slider control for mixer. Switching Channel's volume, pan, reverb and chorus and chaning them to doubles with a decimal range. No longer writing out vol, pan, chor, reverb when at default values. Nolonger writing vol, pan, chorus, reverb as controler values in output file. Now testing against default values on write. More export fixes. Manually editing test files to reflect new channel parameters. Manually editing more test files to reflect new channel parameters. Manually editing more test files to reflect new channel parameters. More test changes to make Travis happy. More test changes to make Travis happy. Importing MusicXML now matches new volume, pan ranges. Changing range of pan. Fixing a few bugs with calculating MIDI. Altering test files for Travis. Restoring the volume, pan, chorus, reverb to original char data type & range. UI now shows different 'user friendly' ranges. Overwriting tests with versions from master. mtest/libmscore/compat114/clef_missing_first-ref.mscx mtest/libmscore/compat114/hor_frame_and_mmrest-ref.mscx mtest/musicxml/io/testInstrumentChangeMIDIportExport_ref.xml mtest/musicxml/io/testUninitializedDivisions_ref.xml Restoring test files to original state. Restoring test files to original state. Restoring old values for importing files. Restoring part methods. mtest/importmidi/simplify_8th_dotted_no_staccato.mscx mtest/libmscore/compat114/clef_missing_first-ref.mscx mtest/libmscore/compat114/hor_frame_and_mmrest-ref.mscx mtest/musicxml/io/testInstrumentChangeMIDIportExport_ref.xml mtest/musicxml/io/testUninitializedDivisions_ref.xml Rearranging UI components for better feel. Improving UI. Fixed crash when changing part name. Adding support for two lighting modes. Showing part name over channel expansion. Adding master gain control to mixer. Changing color of gain slider. Adapting to latest source in main. Changing master gain slider to use decibel calculation. CSS now set on tracks whenever a Paint event received. Restoring mixer slider values to refect MIDI ranges. Fixing crash when drumkit checked. Fixing crash when closing score. Fixing alignment in mixer details. Tweaking UI for better appearance.
2018-11-13 18:43:19 +01:00
putEvent(NPlayEvent(ME_CONTROLLER, channel->channel(), CTRL_LRPN, 127));
putEvent(NPlayEvent(ME_CONTROLLER, channel->channel(), CTRL_HRPN, 127));
2015-06-03 18:36:59 +02:00
}
else {
fix #275313: rework mixer ui 2 Moving PartEditBase into separate file. Creating new files for building mixer. Creating art assets/UI design for new components. Styling the track control. Adding track area. Separating out score from update. Creating instances of mixer UI. Creating part per voice now. Can click on tracks to select them now. Can now switch bwtewwn tracks. Setting patch channel now. Setting enabled off when no track selected. Improving slider ui. Turning Channel into a class and adding listener to it. Somewhat stabalized sharing track objects between interfaces. Can now apply volume changes to both expanded and collapsed tracks. Pan knob is now working. Encapsulating the rest of the fields in Channel. Mute and solo now working. Reverb and chorus now working. Drumkit checkbox now working. Port and channel somewhat working. Adding support for colors per track. Part name change now working. Separating out MixerTrackItem Finishing moving MixerTrackItem to new file. Cleaning up code. Moving PartEditBase into separate file. Creating new files for building mixer. Creating art assets/UI design for new components. Styling the track control. Adding track area. Separating out score from update. Creating instances of mixer UI. Creating part per voice now. Can click on tracks to select them now. Can now switch bwtewwn tracks. Setting patch channel now. Setting enabled off when no track selected. Improving slider ui. Turning Channel into a class and adding listener to it. Somewhat stabalized sharing track objects between interfaces. Can now apply volume changes to both expanded and collapsed tracks. Pan knob is now working. Encapsulating the rest of the fields in Channel. Mute and solo now working. Reverb and chorus now working. Drumkit checkbox now working. Port and channel somewhat working. Adding support for colors per track. Part name change now working. Separating out MixerTrackItem Finishing moving MixerTrackItem to new file. Cleaning up code. Setting color in collapsed mode now affects all channels. Using shared_ptr to track MixerTrackItem. Part changes now affect all instruments. Creating new track UI object to handle parts. Using shard_ptr to track MixerTrackItem objects. setting port and channel data now. Changing to horizontal layout. Fixing knob display. Chaning track control appearance. Setting init slider window size. Switchong back to vertical orientation. Fixing a few UI bugs in the slider. Tracks now left aligned. Moving details panel above mixer. Now changing track selection when user clicks on sliders. Pan and volume controls now reflect track color. Showing volume and pan values in tooltips. Creating a new slider control for mixer. Switching Channel's volume, pan, reverb and chorus and chaning them to doubles with a decimal range. No longer writing out vol, pan, chor, reverb when at default values. Nolonger writing vol, pan, chorus, reverb as controler values in output file. Now testing against default values on write. More export fixes. Manually editing test files to reflect new channel parameters. Manually editing more test files to reflect new channel parameters. Manually editing more test files to reflect new channel parameters. More test changes to make Travis happy. More test changes to make Travis happy. Importing MusicXML now matches new volume, pan ranges. Changing range of pan. Fixing a few bugs with calculating MIDI. Altering test files for Travis. fix #275313: rework-mixer-ui-2 Moving PartEditBase into separate file. Creating new files for building mixer. Creating art assets/UI design for new components. Styling the track control. Adding track area. Separating out score from update. Creating instances of mixer UI. Creating part per voice now. Can click on tracks to select them now. Can now switch bwtewwn tracks. Setting patch channel now. Setting enabled off when no track selected. Improving slider ui. Turning Channel into a class and adding listener to it. Somewhat stabalized sharing track objects between interfaces. Can now apply volume changes to both expanded and collapsed tracks. Pan knob is now working. Encapsulating the rest of the fields in Channel. Mute and solo now working. Reverb and chorus now working. Drumkit checkbox now working. Port and channel somewhat working. Adding support for colors per track. Part name change now working. Separating out MixerTrackItem Finishing moving MixerTrackItem to new file. Cleaning up code. Moving PartEditBase into separate file. Creating new files for building mixer. Creating art assets/UI design for new components. Styling the track control. Adding track area. Separating out score from update. Creating instances of mixer UI. Creating part per voice now. Can click on tracks to select them now. Can now switch bwtewwn tracks. Setting patch channel now. Setting enabled off when no track selected. Improving slider ui. Turning Channel into a class and adding listener to it. Somewhat stabalized sharing track objects between interfaces. Can now apply volume changes to both expanded and collapsed tracks. Pan knob is now working. Encapsulating the rest of the fields in Channel. Mute and solo now working. Reverb and chorus now working. Drumkit checkbox now working. Port and channel somewhat working. Adding support for colors per track. Part name change now working. Separating out MixerTrackItem Finishing moving MixerTrackItem to new file. Cleaning up code. Setting color in collapsed mode now affects all channels. Using shared_ptr to track MixerTrackItem. Part changes now affect all instruments. Creating new track UI object to handle parts. Using shard_ptr to track MixerTrackItem objects. setting port and channel data now. Changing to horizontal layout. Fixing knob display. Chaning track control appearance. Setting init slider window size. Switchong back to vertical orientation. Fixing a few UI bugs in the slider. Tracks now left aligned. Moving details panel above mixer. Now changing track selection when user clicks on sliders. Pan and volume controls now reflect track color. Showing volume and pan values in tooltips. Creating a new slider control for mixer. Switching Channel's volume, pan, reverb and chorus and chaning them to doubles with a decimal range. No longer writing out vol, pan, chor, reverb when at default values. Nolonger writing vol, pan, chorus, reverb as controler values in output file. Now testing against default values on write. More export fixes. Manually editing test files to reflect new channel parameters. Manually editing more test files to reflect new channel parameters. Manually editing more test files to reflect new channel parameters. More test changes to make Travis happy. More test changes to make Travis happy. Importing MusicXML now matches new volume, pan ranges. Changing range of pan. Fixing a few bugs with calculating MIDI. Altering test files for Travis. Restoring the volume, pan, chorus, reverb to original char data type & range. UI now shows different 'user friendly' ranges. Overwriting tests with versions from master. mtest/libmscore/compat114/clef_missing_first-ref.mscx mtest/libmscore/compat114/hor_frame_and_mmrest-ref.mscx mtest/musicxml/io/testInstrumentChangeMIDIportExport_ref.xml mtest/musicxml/io/testUninitializedDivisions_ref.xml Restoring test files to original state. Restoring test files to original state. Restoring old values for importing files. Restoring part methods. mtest/importmidi/simplify_8th_dotted_no_staccato.mscx mtest/libmscore/compat114/clef_missing_first-ref.mscx mtest/libmscore/compat114/hor_frame_and_mmrest-ref.mscx mtest/musicxml/io/testInstrumentChangeMIDIportExport_ref.xml mtest/musicxml/io/testUninitializedDivisions_ref.xml Rearranging UI components for better feel. Improving UI. Fixed crash when changing part name. Adding support for two lighting modes. Showing part name over channel expansion. Adding master gain control to mixer. Changing color of gain slider. Adapting to latest source in main. Changing master gain slider to use decibel calculation. CSS now set on tracks whenever a Paint event received. Restoring mixer slider values to refect MIDI ranges. Fixing crash when drumkit checked. Fixing crash when closing score. Fixing alignment in mixer details. Tweaking UI for better appearance.
2018-11-13 18:43:19 +01:00
sendEvent(NPlayEvent(ME_CONTROLLER, channel->channel(), CTRL_LRPN, 0));
sendEvent(NPlayEvent(ME_CONTROLLER, channel->channel(), CTRL_HRPN, 0));
sendEvent(NPlayEvent(ME_CONTROLLER, channel->channel(), CTRL_HDATA,12));
fix #275313: rework mixer ui 2 Moving PartEditBase into separate file. Creating new files for building mixer. Creating art assets/UI design for new components. Styling the track control. Adding track area. Separating out score from update. Creating instances of mixer UI. Creating part per voice now. Can click on tracks to select them now. Can now switch bwtewwn tracks. Setting patch channel now. Setting enabled off when no track selected. Improving slider ui. Turning Channel into a class and adding listener to it. Somewhat stabalized sharing track objects between interfaces. Can now apply volume changes to both expanded and collapsed tracks. Pan knob is now working. Encapsulating the rest of the fields in Channel. Mute and solo now working. Reverb and chorus now working. Drumkit checkbox now working. Port and channel somewhat working. Adding support for colors per track. Part name change now working. Separating out MixerTrackItem Finishing moving MixerTrackItem to new file. Cleaning up code. Moving PartEditBase into separate file. Creating new files for building mixer. Creating art assets/UI design for new components. Styling the track control. Adding track area. Separating out score from update. Creating instances of mixer UI. Creating part per voice now. Can click on tracks to select them now. Can now switch bwtewwn tracks. Setting patch channel now. Setting enabled off when no track selected. Improving slider ui. Turning Channel into a class and adding listener to it. Somewhat stabalized sharing track objects between interfaces. Can now apply volume changes to both expanded and collapsed tracks. Pan knob is now working. Encapsulating the rest of the fields in Channel. Mute and solo now working. Reverb and chorus now working. Drumkit checkbox now working. Port and channel somewhat working. Adding support for colors per track. Part name change now working. Separating out MixerTrackItem Finishing moving MixerTrackItem to new file. Cleaning up code. Setting color in collapsed mode now affects all channels. Using shared_ptr to track MixerTrackItem. Part changes now affect all instruments. Creating new track UI object to handle parts. Using shard_ptr to track MixerTrackItem objects. setting port and channel data now. Changing to horizontal layout. Fixing knob display. Chaning track control appearance. Setting init slider window size. Switchong back to vertical orientation. Fixing a few UI bugs in the slider. Tracks now left aligned. Moving details panel above mixer. Now changing track selection when user clicks on sliders. Pan and volume controls now reflect track color. Showing volume and pan values in tooltips. Creating a new slider control for mixer. Switching Channel's volume, pan, reverb and chorus and chaning them to doubles with a decimal range. No longer writing out vol, pan, chor, reverb when at default values. Nolonger writing vol, pan, chorus, reverb as controler values in output file. Now testing against default values on write. More export fixes. Manually editing test files to reflect new channel parameters. Manually editing more test files to reflect new channel parameters. Manually editing more test files to reflect new channel parameters. More test changes to make Travis happy. More test changes to make Travis happy. Importing MusicXML now matches new volume, pan ranges. Changing range of pan. Fixing a few bugs with calculating MIDI. Altering test files for Travis. fix #275313: rework-mixer-ui-2 Moving PartEditBase into separate file. Creating new files for building mixer. Creating art assets/UI design for new components. Styling the track control. Adding track area. Separating out score from update. Creating instances of mixer UI. Creating part per voice now. Can click on tracks to select them now. Can now switch bwtewwn tracks. Setting patch channel now. Setting enabled off when no track selected. Improving slider ui. Turning Channel into a class and adding listener to it. Somewhat stabalized sharing track objects between interfaces. Can now apply volume changes to both expanded and collapsed tracks. Pan knob is now working. Encapsulating the rest of the fields in Channel. Mute and solo now working. Reverb and chorus now working. Drumkit checkbox now working. Port and channel somewhat working. Adding support for colors per track. Part name change now working. Separating out MixerTrackItem Finishing moving MixerTrackItem to new file. Cleaning up code. Moving PartEditBase into separate file. Creating new files for building mixer. Creating art assets/UI design for new components. Styling the track control. Adding track area. Separating out score from update. Creating instances of mixer UI. Creating part per voice now. Can click on tracks to select them now. Can now switch bwtewwn tracks. Setting patch channel now. Setting enabled off when no track selected. Improving slider ui. Turning Channel into a class and adding listener to it. Somewhat stabalized sharing track objects between interfaces. Can now apply volume changes to both expanded and collapsed tracks. Pan knob is now working. Encapsulating the rest of the fields in Channel. Mute and solo now working. Reverb and chorus now working. Drumkit checkbox now working. Port and channel somewhat working. Adding support for colors per track. Part name change now working. Separating out MixerTrackItem Finishing moving MixerTrackItem to new file. Cleaning up code. Setting color in collapsed mode now affects all channels. Using shared_ptr to track MixerTrackItem. Part changes now affect all instruments. Creating new track UI object to handle parts. Using shard_ptr to track MixerTrackItem objects. setting port and channel data now. Changing to horizontal layout. Fixing knob display. Chaning track control appearance. Setting init slider window size. Switchong back to vertical orientation. Fixing a few UI bugs in the slider. Tracks now left aligned. Moving details panel above mixer. Now changing track selection when user clicks on sliders. Pan and volume controls now reflect track color. Showing volume and pan values in tooltips. Creating a new slider control for mixer. Switching Channel's volume, pan, reverb and chorus and chaning them to doubles with a decimal range. No longer writing out vol, pan, chor, reverb when at default values. Nolonger writing vol, pan, chorus, reverb as controler values in output file. Now testing against default values on write. More export fixes. Manually editing test files to reflect new channel parameters. Manually editing more test files to reflect new channel parameters. Manually editing more test files to reflect new channel parameters. More test changes to make Travis happy. More test changes to make Travis happy. Importing MusicXML now matches new volume, pan ranges. Changing range of pan. Fixing a few bugs with calculating MIDI. Altering test files for Travis. Restoring the volume, pan, chorus, reverb to original char data type & range. UI now shows different 'user friendly' ranges. Overwriting tests with versions from master. mtest/libmscore/compat114/clef_missing_first-ref.mscx mtest/libmscore/compat114/hor_frame_and_mmrest-ref.mscx mtest/musicxml/io/testInstrumentChangeMIDIportExport_ref.xml mtest/musicxml/io/testUninitializedDivisions_ref.xml Restoring test files to original state. Restoring test files to original state. Restoring old values for importing files. Restoring part methods. mtest/importmidi/simplify_8th_dotted_no_staccato.mscx mtest/libmscore/compat114/clef_missing_first-ref.mscx mtest/libmscore/compat114/hor_frame_and_mmrest-ref.mscx mtest/musicxml/io/testInstrumentChangeMIDIportExport_ref.xml mtest/musicxml/io/testUninitializedDivisions_ref.xml Rearranging UI components for better feel. Improving UI. Fixed crash when changing part name. Adding support for two lighting modes. Showing part name over channel expansion. Adding master gain control to mixer. Changing color of gain slider. Adapting to latest source in main. Changing master gain slider to use decibel calculation. CSS now set on tracks whenever a Paint event received. Restoring mixer slider values to refect MIDI ranges. Fixing crash when drumkit checked. Fixing crash when closing score. Fixing alignment in mixer details. Tweaking UI for better appearance.
2018-11-13 18:43:19 +01:00
sendEvent(NPlayEvent(ME_CONTROLLER, channel->channel(), CTRL_LRPN, 127));
sendEvent(NPlayEvent(ME_CONTROLLER, channel->channel(), CTRL_HRPN, 127));
2015-06-03 18:36:59 +02:00
}
}
2012-05-26 14:49:10 +02:00
}
}
//---------------------------------------------------------
// renderChunk
//---------------------------------------------------------
void Seq::renderChunk(const MidiRenderer::Chunk& ch, EventMap* eventMap)
{
midi.renderChunk(ch, eventMap, mscore->synthesizerState(), /* metronome */ true);
renderEventsStatus.setOccupied(ch.utick1(), ch.utick2());
}
//---------------------------------------------------------
// updateEventsEnd
//---------------------------------------------------------
void Seq::updateEventsEnd()
{
auto end = events.cend();
eventsEnd = end;
endUTick = events.empty() ? 0 : (--end)->first;
}
2012-05-26 14:49:10 +02:00
//---------------------------------------------------------
// collectEvents
//---------------------------------------------------------
void Seq::collectEvents(int utick)
2012-05-26 14:49:10 +02:00
{
2012-06-23 15:54:01 +02:00
//do not collect even while playing
if (state == Transport::PLAY && playlistChanged)
2012-06-23 15:54:01 +02:00
return;
mutex.lock();
2012-05-26 14:49:10 +02:00
if (midiRenderFuture.isRunning())
midiRenderFuture.waitForFinished();
if (playlistChanged) {
midi.setScoreChanged();
events.clear();
renderEvents.clear();
renderEventsStatus.clear();
}
else if (!renderEvents.empty()) {
events.insert(renderEvents.begin(), renderEvents.end());
renderEvents.clear();
}
int unrenderedUtick = renderEventsStatus.occupiedRangeEnd(utick);
while (unrenderedUtick - utick < minUtickBufferSize) {
const MidiRenderer::Chunk chunk = midi.getChunkAt(unrenderedUtick);
if (!chunk)
break;
renderChunk(chunk, &events);
unrenderedUtick = renderEventsStatus.occupiedRangeEnd(utick);
2012-05-26 14:49:10 +02:00
}
updateEventsEnd();
playPos = events.cbegin();
2012-05-26 14:49:10 +02:00
playlistChanged = false;
mutex.unlock();
}
//---------------------------------------------------------
// ensureBufferAsync
//---------------------------------------------------------
void Seq::ensureBufferAsync(int utick)
{
if (mutex.tryLock()) { // sync with possible collectEvents calls
if (midiRenderFuture.isRunning() || !allowBackgroundRendering) {
mutex.unlock();
return;
}
if (!renderEvents.empty()) {
// TODO: use C++17 map::merge()?
events.insert(renderEvents.begin(), renderEvents.end());
updateEventsEnd();
renderEvents.clear();
}
const int unrenderedUtick = renderEventsStatus.occupiedRangeEnd(utick);
if (unrenderedUtick - utick < minUtickBufferSize) {
const MidiRenderer::Chunk chunk = midi.getChunkAt(unrenderedUtick);
if (chunk) {
midiRenderFuture = QtConcurrent::run([this, chunk]() {
renderChunk(chunk, &renderEvents);
});
}
}
mutex.unlock();
}
2012-05-26 14:49:10 +02:00
}
//---------------------------------------------------------
// getCurTick
//---------------------------------------------------------
int Seq::getCurTick()
{
2017-05-14 17:43:28 +02:00
return cs->utime2utick(qreal(playFrame) / qreal(MScore::sampleRate));
2012-05-26 14:49:10 +02:00
}
//---------------------------------------------------------
// setRelTempo
// relTempo = 1.0 = normal tempo
//---------------------------------------------------------
void Seq::setRelTempo(double relTempo)
{
guiToSeq(SeqMsg(SeqMsgId::TEMPO_CHANGE, relTempo));
2012-05-26 14:49:10 +02:00
}
//---------------------------------------------------------
// setPos
// seek
// realtime environment
//---------------------------------------------------------
void Seq::setPos(int utick)
{
if (cs == 0)
return;
stopNotes(-1, true);
2012-05-26 14:49:10 +02:00
2013-05-03 14:24:14 +02:00
int ucur;
mutex.lock();
2013-05-03 14:24:14 +02:00
if (playPos != events.end())
ucur = cs->repeatList().utick2tick(playPos->first);
2013-05-03 14:24:14 +02:00
else
ucur = utick - 1;
2013-04-29 19:42:38 +02:00
if (utick != ucur)
updateSynthesizerState(ucur, utick);
2017-05-14 17:43:28 +02:00
playFrame = cs->utick2utime(utick) * MScore::sampleRate;
2013-04-08 10:31:17 +02:00
playPos = events.lower_bound(utick);
2013-02-20 17:53:15 +01:00
mutex.unlock();
2012-05-26 14:49:10 +02:00
}
//---------------------------------------------------------
// getPlayStartUtick
//---------------------------------------------------------
int Seq::getPlayStartUtick()
{
if ((mscore->loop())) {
if (preferences.getBool(PREF_APP_PLAYBACK_LOOPTOSELECTIONONPLAY)) {
setLoopSelection();
}
return cs->repeatList().tick2utick(cs->loopInTick().ticks());
}
return cs->repeatList().tick2utick(cs->playPos().ticks());
}
2012-05-26 14:49:10 +02:00
//---------------------------------------------------------
2014-07-07 17:31:41 +02:00
// seekCommon
// a common part of seek() and seekRT(), contains code
// that could be safely called from any thread.
// Do not use explicitly, use seek() or seekRT()
2012-05-26 14:49:10 +02:00
//---------------------------------------------------------
2014-07-07 17:31:41 +02:00
void Seq::seekCommon(int utick)
2012-05-26 14:49:10 +02:00
{
if (cs == 0)
return;
2013-02-20 17:53:15 +01:00
collectEvents(utick);
2014-07-07 17:31:41 +02:00
if (cs->playMode() == PlayMode::AUDIO) {
ogg_int64_t sp = cs->utick2utime(utick) * MScore::sampleRate;
ov_pcm_seek(&vf, sp);
2014-06-18 21:36:53 +02:00
}
2014-07-07 17:31:41 +02:00
guiPos = events.lower_bound(utick);
mscore->setPos(Fraction::fromTicks(cs->repeatList().utick2tick(utick)));
2014-07-07 17:31:41 +02:00
unmarkNotes();
2013-02-20 17:53:15 +01:00
}
//---------------------------------------------------------
// seek
2014-07-07 17:31:41 +02:00
// send seek message to sequencer
// gui thread
2013-02-20 17:53:15 +01:00
//---------------------------------------------------------
2014-07-07 17:31:41 +02:00
void Seq::seek(int utick)
2013-02-20 17:53:15 +01:00
{
if (preferences.getBool(PREF_IO_JACK_USEJACKTRANSPORT)) {
2017-05-14 17:43:28 +02:00
if (utick > endUTick)
2014-07-07 17:31:41 +02:00
utick = 0;
_driver->seekTransport(utick);
if (utick != 0)
return;
}
seekCommon(utick);
2013-05-03 14:45:25 +02:00
int t = cs->repeatList().utick2tick(utick);
Segment* seg = cs->tick2segment(Fraction::fromTicks(t));
2013-05-03 14:45:25 +02:00
if (seg)
2013-07-03 11:17:14 +02:00
mscore->currentScoreView()->moveCursor(seg->tick());
cs->setPlayPos(Fraction::fromTicks(t));
cs->update();
2014-07-07 17:31:41 +02:00
guiToSeq(SeqMsg(SeqMsgId::SEEK, utick));
}
2012-11-19 09:29:46 +01:00
2014-07-07 17:31:41 +02:00
//---------------------------------------------------------
// seekRT
// realtime thread
//---------------------------------------------------------
2012-05-26 14:49:10 +02:00
2014-07-07 17:31:41 +02:00
void Seq::seekRT(int utick)
{
if (preferences.getBool(PREF_IO_JACK_USEJACKTRANSPORT) && utick > endUTick)
2014-07-07 17:31:41 +02:00
utick = 0;
seekCommon(utick);
setPos(utick);
// Update the screen in GUI thread
emit toGui('5', cs->repeatList().utick2tick(utick));
2012-05-26 14:49:10 +02:00
}
//---------------------------------------------------------
// startNote
//---------------------------------------------------------
2012-06-28 14:50:47 +02:00
void Seq::startNote(int channel, int pitch, int velo, double nt)
2012-05-26 14:49:10 +02:00
{
if (state != Transport::STOP && state != Transport::PLAY)
2012-05-26 14:49:10 +02:00
return;
NPlayEvent ev(ME_NOTEON, channel, pitch, velo);
2012-05-26 14:49:10 +02:00
ev.setTuning(nt);
sendEvent(ev);
}
2012-06-28 14:50:47 +02:00
void Seq::startNote(int channel, int pitch, int velo, int duration, double nt)
2012-05-26 14:49:10 +02:00
{
stopNotes();
2012-06-28 14:50:47 +02:00
startNote(channel, pitch, velo, nt);
2012-05-26 14:49:10 +02:00
startNoteTimer(duration);
}
//---------------------------------------------------------
// playMetronomeBeat
//---------------------------------------------------------
void Seq::playMetronomeBeat(BeatType type)
{
if (state != Transport::STOP)
return;
liveEventQueue()->enqueue(NPlayEvent(type));
}
2012-05-26 14:49:10 +02:00
//---------------------------------------------------------
// startNoteTimer
//---------------------------------------------------------
void Seq::startNoteTimer(int duration)
{
if (duration) {
noteTimer->setInterval(duration);
noteTimer->start();
}
}
//---------------------------------------------------------
// stopNoteTimer
//---------------------------------------------------------
void Seq::stopNoteTimer()
{
if (noteTimer->isActive()) {
noteTimer->stop();
stopNotes();
}
}
//---------------------------------------------------------
// stopNotes
//---------------------------------------------------------
void Seq::stopNotes(int channel, bool realTime)
2012-05-26 14:49:10 +02:00
{
auto send = [this, realTime](const NPlayEvent& event) {
if (realTime)
putEvent(event);
else
sendEvent(event);
};
// Stop notes in all channels
if (channel == -1) {
for(int ch = 0; ch < int(cs->midiMapping().size()); ch++) {
2014-12-20 21:56:46 +01:00
send(NPlayEvent(ME_CONTROLLER, ch, CTRL_SUSTAIN, 0));
send(NPlayEvent(ME_CONTROLLER, ch, CTRL_ALL_NOTES_OFF, 0));
2015-06-03 18:36:59 +02:00
if (cs->midiChannel(ch) != 9)
send(NPlayEvent(ME_PITCHBEND, ch, 0, 64));
2014-12-20 21:56:46 +01:00
}
}
else {
2014-12-20 21:56:46 +01:00
send(NPlayEvent(ME_CONTROLLER, channel, CTRL_SUSTAIN, 0));
send(NPlayEvent(ME_CONTROLLER, channel, CTRL_ALL_NOTES_OFF, 0));
2015-06-03 18:36:59 +02:00
if (cs->midiChannel(channel) != 9)
send(NPlayEvent(ME_PITCHBEND, channel, 0, 64));
}
if (preferences.getBool(PREF_IO_ALSA_USEALSAAUDIO) || preferences.getBool(PREF_IO_JACK_USEJACKAUDIO) || preferences.getBool(PREF_IO_PULSEAUDIO_USEPULSEAUDIO) || preferences.getBool(PREF_IO_PORTAUDIO_USEPORTAUDIO))
_synti->allNotesOff(channel);
2012-05-26 14:49:10 +02:00
}
//---------------------------------------------------------
// setController
//---------------------------------------------------------
void Seq::setController(int channel, int ctrl, int data)
{
NPlayEvent event(ME_CONTROLLER, channel, ctrl, data);
2012-05-26 14:49:10 +02:00
sendEvent(event);
}
//---------------------------------------------------------
// sendEvent
// called from GUI context to send a midi event to
// midi out or synthesizer
//---------------------------------------------------------
void Seq::sendEvent(const NPlayEvent& ev)
2012-05-26 14:49:10 +02:00
{
guiToSeq(SeqMsg(SeqMsgId::PLAY, ev));
2012-05-26 14:49:10 +02:00
}
//---------------------------------------------------------
// nextMeasure
//---------------------------------------------------------
void Seq::nextMeasure()
{
Measure* m = cs->tick2measure(Fraction::fromTicks(guiPos->first));
2012-05-26 14:49:10 +02:00
if (m) {
if (m->nextMeasure())
m = m->nextMeasure();
seek(m->tick().ticks());
2012-05-26 14:49:10 +02:00
}
}
//---------------------------------------------------------
// nextChord
//---------------------------------------------------------
void Seq::nextChord()
{
int t = guiPos->first;
for (auto i = guiPos; i != eventsEnd; ++i) {
if (i->second.type() == ME_NOTEON && i->first > t && i->second.velo()) {
2013-04-08 10:31:17 +02:00
seek(i->first);
2012-05-26 14:49:10 +02:00
break;
}
}
}
//---------------------------------------------------------
// prevMeasure
//---------------------------------------------------------
void Seq::prevMeasure()
{
auto i = guiPos;
if (i == events.begin())
2012-05-26 14:49:10 +02:00
return;
--i;
Measure* m = cs->tick2measure(Fraction::fromTicks(i->first));
2012-05-26 14:49:10 +02:00
if (m) {
if ((i->first == m->tick().ticks()) && m->prevMeasure())
2013-02-20 17:53:15 +01:00
m = m->prevMeasure();
seek(m->tick().ticks());
2012-05-26 14:49:10 +02:00
}
}
//---------------------------------------------------------
// prevChord
//---------------------------------------------------------
void Seq::prevChord()
{
int t = playPos->first;
2012-05-26 14:49:10 +02:00
//find the chord just before playpos
EventMap::const_iterator i = events.upper_bound(cs->repeatList().tick2utick(t));
2012-05-26 14:49:10 +02:00
for (;;) {
2013-04-08 10:31:17 +02:00
if (i->second.type() == ME_NOTEON) {
const NPlayEvent& n = i->second;
if (i->first < t && n.velo()) {
t = i->first;
2012-05-26 14:49:10 +02:00
break;
}
}
2013-04-08 10:31:17 +02:00
if (i == events.cbegin())
2012-05-26 14:49:10 +02:00
break;
--i;
}
//go the previous chord
2013-04-08 10:31:17 +02:00
if (i != events.cbegin()) {
2012-05-26 14:49:10 +02:00
i = playPos;
for (;;) {
2013-04-08 10:31:17 +02:00
if (i->second.type() == ME_NOTEON) {
const NPlayEvent& n = i->second;
if (i->first < t && n.velo()) {
2013-04-08 10:31:17 +02:00
seek(i->first);
2012-05-26 14:49:10 +02:00
break;
}
}
2013-04-08 10:31:17 +02:00
if (i == events.cbegin())
2012-05-26 14:49:10 +02:00
break;
--i;
}
}
}
//---------------------------------------------------------
// seekEnd
//---------------------------------------------------------
void Seq::seekEnd()
{
qDebug("seek to end");
2012-05-26 14:49:10 +02:00
}
//---------------------------------------------------------
// guiToSeq
//---------------------------------------------------------
void Seq::guiToSeq(const SeqMsg& msg)
{
if (!_driver || !running)
2012-05-26 14:49:10 +02:00
return;
toSeq.enqueue(msg);
}
//---------------------------------------------------------
// eventToGui
//---------------------------------------------------------
void Seq::eventToGui(NPlayEvent e)
2012-05-26 14:49:10 +02:00
{
fromSeq.enqueue(SeqMsg(SeqMsgId::MIDI_INPUT_EVENT, e));
2012-05-26 14:49:10 +02:00
}
//---------------------------------------------------------
// midiInputReady
//---------------------------------------------------------
void Seq::midiInputReady()
{
if (_driver)
_driver->midiRead();
2012-05-26 14:49:10 +02:00
}
//---------------------------------------------------------
// SeqMsgFifo
//---------------------------------------------------------
SeqMsgFifo::SeqMsgFifo()
{
maxCount = SEQ_MSG_FIFO_SIZE;
clear();
}
//---------------------------------------------------------
// enqueue
//---------------------------------------------------------
void SeqMsgFifo::enqueue(const SeqMsg& msg)
{
int i = 0;
int n = 50;
QMutex mutex;
QWaitCondition qwc;
mutex.lock();
for (; i < n; ++i) {
if (!isFull())
break;
qwc.wait(&mutex,100);
}
2013-02-15 14:50:03 +01:00
mutex.unlock();
2012-05-26 14:49:10 +02:00
if (i == n) {
qDebug("===SeqMsgFifo: overflow");
2012-05-26 14:49:10 +02:00
return;
}
messages[widx] = msg;
push();
}
//---------------------------------------------------------
// dequeue
//---------------------------------------------------------
SeqMsg SeqMsgFifo::dequeue()
{
SeqMsg msg = messages[ridx];
pop();
return msg;
}
//---------------------------------------------------------
// putEvent
//---------------------------------------------------------
void Seq::putEvent(const NPlayEvent& event, unsigned framePos)
2012-05-26 14:49:10 +02:00
{
if (!cs)
return;
int channel = event.channel();
if (channel >= int(cs->midiMapping().size())) {
qDebug("bad channel value %d >= %d", channel, int(cs->midiMapping().size()));
return;
}
2017-05-14 17:43:28 +02:00
// audio
int syntiIdx= _synti->index(cs->midiMapping(channel)->articulation()->synti());
_synti->play(event, syntiIdx);
2017-05-14 17:43:28 +02:00
// midi
if (_driver != 0 && (preferences.getBool(PREF_IO_JACK_USEJACKMIDI) || preferences.getBool(PREF_IO_ALSA_USEALSAAUDIO) || preferences.getBool(PREF_IO_PORTAUDIO_USEPORTAUDIO)))
_driver->putEvent(event, framePos);
2012-05-26 14:49:10 +02:00
}
//---------------------------------------------------------
// heartBeat
// update GUI
//---------------------------------------------------------
void Seq::heartBeatTimeout()
2012-05-26 14:49:10 +02:00
{
SynthControl* sc = mscore->getSynthControl();
if (sc && _driver) {
2012-05-26 14:49:10 +02:00
if (++peakTimer[0] >= peakHold)
meterPeakValue[0] *= .7f;
if (++peakTimer[1] >= peakHold)
meterPeakValue[1] *= .7f;
sc->setMeter(meterValue[0], meterValue[1], meterPeakValue[0], meterPeakValue[1]);
}
2013-02-20 17:53:15 +01:00
2016-02-06 22:03:43 +01:00
while (!fromSeq.empty()) {
2013-02-20 17:53:15 +01:00
SeqMsg msg = fromSeq.dequeue();
if (msg.id == SeqMsgId::MIDI_INPUT_EVENT) {
2013-02-20 17:53:15 +01:00
int type = msg.event.type();
if (type == ME_NOTEON)
mscore->midiNoteReceived(msg.event.channel(), msg.event.pitch(), msg.event.velo());
else if (type == ME_NOTEOFF)
mscore->midiNoteReceived(msg.event.channel(), msg.event.pitch(), 0);
else if (type == ME_CONTROLLER)
mscore->midiCtrlReceived(msg.event.controller(), msg.event.value());
}
}
if (state != Transport::PLAY || inCountIn)
2012-05-26 14:49:10 +02:00
return;
2014-06-18 21:36:53 +02:00
2017-05-14 17:43:28 +02:00
int endFrame = playFrame;
2012-05-26 14:49:10 +02:00
2013-02-20 17:53:15 +01:00
mutex.lock();
auto ppos = playPos;
2013-04-08 10:31:17 +02:00
if (ppos != events.cbegin())
2013-02-20 17:53:15 +01:00
--ppos;
mutex.unlock();
ensureBufferAsync(ppos->first);
2014-07-07 17:31:41 +02:00
if (cs && cs->sigmap()->timesig(getCurTick()).nominal()!=prevTimeSig) {
2014-06-05 17:50:29 +02:00
prevTimeSig = cs->sigmap()->timesig(getCurTick()).nominal();
emit timeSigChanged();
}
2014-07-07 17:31:41 +02:00
if (cs && curTempo()!=prevTempo) {
2014-06-05 17:50:29 +02:00
prevTempo = curTempo();
emit tempoChanged();
}
2013-06-25 10:41:15 +02:00
QRectF r;
for (;guiPos != eventsEnd; ++guiPos) {
if (guiPos->first > ppos->first)
break;
if (mscore->loop())
if (guiPos->first >= cs->repeatList().tick2utick(cs->loopOutTick().ticks()))
break;
const NPlayEvent& n = guiPos->second;
2013-02-20 17:53:15 +01:00
if (n.type() == ME_NOTEON) {
const Note* note1 = n.note();
if (n.velo()) {
2012-05-26 14:49:10 +02:00
while (note1) {
for (ScoreElement* se : note1->linkList()) {
if (!se->isNote())
continue;
Note* currentNote = toNote(se);
currentNote->setMark(true);
markedNotes.append(currentNote);
r |= currentNote->canvasBoundingRect();
}
note1 = note1->tieFor() ? note1->tieFor()->endNote() : 0;
}
}
else {
while (note1) {
for (ScoreElement* se : note1->linkList()) {
if (!se->isNote())
continue;
Note* currentNote = toNote(se);
currentNote->setMark(false);
r |= currentNote->canvasBoundingRect();
markedNotes.removeOne(currentNote);
}
2012-05-26 14:49:10 +02:00
note1 = note1->tieFor() ? note1->tieFor()->endNote() : 0;
}
}
}
}
2013-04-08 10:31:17 +02:00
int utick = ppos->first;
int t = cs->repeatList().utick2tick(utick);
mscore->currentScoreView()->moveCursor(Fraction::fromTicks(t));
mscore->setPos(Fraction::fromTicks(t));
emit(heartBeat(t, utick, endFrame));
2012-05-26 14:49:10 +02:00
PianorollEditor* pre = mscore->getPianorollEditor();
if (pre && pre->isVisible())
pre->heartBeat(this);
PianoTools* piano = mscore->pianoTools();
if (piano && piano->isVisible())
piano->setPlaybackNotes(markedNotes);
2013-06-25 10:41:15 +02:00
cv->update(cv->toPhysical(r));
2012-05-26 14:49:10 +02:00
}
2013-04-25 14:35:32 +02:00
//---------------------------------------------------------
// updateSynthesizerState
// collect all controller events between tick1 and tick2
// and send them to the synthesizer
2014-10-01 20:17:30 +02:00
// Called from RT thread
2013-04-25 14:35:32 +02:00
//---------------------------------------------------------
void Seq::updateSynthesizerState(int tick1, int tick2)
{
if (tick1 > tick2)
tick1 = 0;
2014-10-01 20:17:30 +02:00
// Making a local copy of events to avoid touching it
// from different threads at the same time
EventMap ev = events;
EventMap::const_iterator i1 = ev.lower_bound(tick1);
EventMap::const_iterator i2 = ev.upper_bound(tick2);
2013-04-25 14:35:32 +02:00
for (; i1 != i2; ++i1) {
if (i1->second.type() == ME_CONTROLLER)
playEvent(i1->second, 0);
2013-04-25 14:35:32 +02:00
}
}
//---------------------------------------------------------
// curTempo
//---------------------------------------------------------
double Seq::curTempo() const
{
if (playPos != events.end())
return cs ? cs->tempomap()->tempo(playPos->first) : 0.0;
return 0.0;
}
//---------------------------------------------------------
// set Loop in position
//---------------------------------------------------------
void Seq::setLoopIn()
{
Fraction t;
if (state == Transport::PLAY) { // If in playback mode, set the In position where note is being played
auto ppos = playPos;
if (ppos != events.cbegin())
--ppos; // We have to go back one pos to get the correct note that has just been played
t = Fraction::fromTicks(cs->repeatList().utick2tick(ppos->first));
}
2013-10-18 12:21:01 +02:00
else
t = cs->pos(); // Otherwise, use the selected note.
if (t >= cs->loopOutTick()) // If In pos >= Out pos, reset Out pos to end of score
cs->setPos(POS::RIGHT, cs->lastMeasure()->endTick());
cs->setPos(POS::LEFT, t);
}
2013-08-19 08:53:30 +02:00
//---------------------------------------------------------
// set Loop Out position
//---------------------------------------------------------
void Seq::setLoopOut()
{
Fraction t;
if (state == Transport::PLAY) { // If in playback mode, set the Out position where note is being played
t = Fraction::fromTicks(cs->repeatList().utick2tick(playPos->first));
}
2013-10-18 12:21:01 +02:00
else
t = cs->pos() + cs->inputState().ticks(); // Otherwise, use the selected note.
if (t <= cs->loopInTick()) // If Out pos <= In pos, reset In pos to beginning of score
cs->setPos(POS::LEFT, Fraction(0,1));
2014-09-22 04:55:58 +02:00
else
if (t > cs->lastMeasure()->endTick())
t = cs->lastMeasure()->endTick();
cs->setPos(POS::RIGHT, t);
if (state == Transport::PLAY)
guiToSeq(SeqMsg(SeqMsgId::SEEK, t.ticks()));
}
void Seq::setPos(POS, unsigned t)
2013-11-28 16:13:41 +01:00
{
qDebug("seq: setPos %d", t);
2013-11-28 16:13:41 +01:00
}
//---------------------------------------------------------
// set Loop In/Out position based on the selection
//---------------------------------------------------------
void Seq::setLoopSelection()
{
const Score* score = mscore->currentScore();
Q_ASSERT(!score || score->masterScore() == cs);
if (score && score->selection().isRange()) {
cs->setLoopInTick(score->selection().tickStart());
cs->setLoopOutTick(score->selection().tickEnd());
}
2013-08-13 05:01:38 +02:00
}
2014-06-18 21:36:53 +02:00
//---------------------------------------------------------
// Called after tempo or time signature
// changed while playback
//---------------------------------------------------------
void Seq::handleTimeSigTempoChanged()
{
2014-06-18 21:36:53 +02:00
_driver->handleTimeSigTempoChanged();
}
2017-05-14 17:43:28 +02:00
//---------------------------------------------------------
// setInitialMillisecondTimestampWithLatency
// Called whenever seq->process() starts.
// Sets a starting reference time for which subsequent PortMidi events will be offset from.
// Time is relative to the start of PortMidi's initialization.
//---------------------------------------------------------
void Seq::setInitialMillisecondTimestampWithLatency()
{
#ifdef USE_PORTMIDI
initialMillisecondTimestampWithLatency = Pt_Time() + preferences.getInt(PREF_IO_PORTMIDI_OUTPUTLATENCYMILLISECONDS);
//qDebug("PortMidi initialMillisecondTimestampWithLatency: %d = %d + %d", initialMillisecondTimestampWithLatency, unsigned(Pt_Time()), preferences.getInt(PREF_IO_PORTMIDI_OUTPUTLATENCYMILLISECONDS));
2017-05-14 17:43:28 +02:00
#endif
}
//---------------------------------------------------------
// getCurrentMillisecondTimestampWithLatency
// Called when midi messages are sent to PortMidi device.
// Returns the time in milliseconds of the current play cursor.
// Time is relative to the start of PortMidi's initialization.
//---------------------------------------------------------
unsigned Seq::getCurrentMillisecondTimestampWithLatency(unsigned framePos) const
{
#ifdef USE_PORTMIDI
unsigned playTimeMilliseconds = unsigned(framePos * 1000) / unsigned(MScore::sampleRate);
//qDebug("PortMidi timestamp = %d + %d", initialMillisecondTimestampWithLatency, playTimeMilliseconds);
return initialMillisecondTimestampWithLatency + playTimeMilliseconds;
#else
qDebug("Shouldn't be using this function if not using PortMidi");
return 0;
#endif
}
2013-08-19 08:53:30 +02:00
}