2012-05-26 14:49:10 +02:00
|
|
|
//=============================================================================
|
|
|
|
// MuseScore
|
|
|
|
// Linux Music Score Editor
|
|
|
|
// $Id: seq.cpp 5660 2012-05-22 14:17:39Z wschweer $
|
|
|
|
//
|
|
|
|
// 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"
|
|
|
|
|
2013-04-02 20:46:07 +02:00
|
|
|
#include "synthesizer/msynthesizer.h"
|
2012-05-26 14:49:10 +02:00
|
|
|
#include "libmscore/slur.h"
|
|
|
|
#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"
|
2013-08-09 01:20:07 +02:00
|
|
|
#include "libmscore/cursor.h"
|
2012-05-26 14:49:10 +02:00
|
|
|
|
|
|
|
#include "click.h"
|
|
|
|
|
|
|
|
#include <vorbis/vorbisfile.h>
|
|
|
|
|
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 const int AUDIO_BUFFER_SIZE = 1024 * 512; // 2 MB
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// 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 += n;
|
|
|
|
}
|
|
|
|
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()
|
|
|
|
{
|
|
|
|
running = false;
|
|
|
|
playlistChanged = false;
|
|
|
|
cs = 0;
|
|
|
|
cv = 0;
|
2013-04-29 18:23:03 +02:00
|
|
|
tackRest = 0;
|
|
|
|
tickRest = 0;
|
2012-05-26 14:49:10 +02:00
|
|
|
|
|
|
|
endTick = 0;
|
|
|
|
state = TRANSPORT_STOP;
|
|
|
|
oggInit = false;
|
2013-04-02 20:46:07 +02:00
|
|
|
_driver = 0;
|
2013-04-08 10:31:17 +02:00
|
|
|
playPos = events.cbegin();
|
2012-05-26 14:49:10 +02:00
|
|
|
|
|
|
|
playTime = 0;
|
2013-08-09 01:20:07 +02:00
|
|
|
loopInPos = 0;
|
|
|
|
loopOutPos = 0;
|
2012-05-26 14:49:10 +02:00
|
|
|
metronomeVolume = 0.3;
|
|
|
|
|
|
|
|
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);
|
2013-05-03 11:37:28 +02:00
|
|
|
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();
|
|
|
|
|
|
|
|
connect(this, SIGNAL(toGui(int)), this, SLOT(seqMessage(int)), Qt::QueuedConnection);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// Seq
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
Seq::~Seq()
|
|
|
|
{
|
2013-04-02 20:46:07 +02:00
|
|
|
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;
|
|
|
|
cs = cv ? cv->score() : 0;
|
|
|
|
|
|
|
|
if (!heartBeatTimer->isActive())
|
2013-08-09 01:20:07 +02:00
|
|
|
heartBeatTimer->start(20); // 20 msec
|
2012-05-26 14:49:10 +02:00
|
|
|
|
|
|
|
playlistChanged = true;
|
2013-04-02 20:46:07 +02:00
|
|
|
_synti->reset();
|
2013-07-03 15:02:49 +02:00
|
|
|
if (cs)
|
2012-05-26 14:49:10 +02:00
|
|
|
initInstruments();
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// selectionChanged
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Seq::selectionChanged(int mode)
|
|
|
|
{
|
2013-04-02 20:46:07 +02:00
|
|
|
if (cs == 0 || _driver == 0)
|
2012-05-26 14:49:10 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
int tick = cs->pos();
|
|
|
|
if (tick == -1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if ((mode != SEL_LIST) || (state == TRANSPORT_STOP))
|
|
|
|
cs->setPlayPos(tick);
|
2013-02-20 17:53:15 +01:00
|
|
|
else
|
2012-05-26 14:49:10 +02:00
|
|
|
seek(tick);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// init
|
|
|
|
// return false on error
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
bool Seq::init()
|
|
|
|
{
|
2013-07-18 09:37:49 +02:00
|
|
|
if (!_driver || !_driver->start()) {
|
2012-05-26 14:49:10 +02:00
|
|
|
qDebug("Cannot start I/O");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
running = true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// exit
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Seq::exit()
|
|
|
|
{
|
2013-04-02 20:46:07 +02:00
|
|
|
if (_driver) {
|
2012-05-26 14:49:10 +02:00
|
|
|
if (MScore::debugMode)
|
|
|
|
qDebug("Stop I/O\n");
|
|
|
|
stopWait();
|
2013-04-02 20:46:07 +02:00
|
|
|
delete _driver;
|
|
|
|
_driver = 0;
|
2012-05-26 14:49:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// inputPorts
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
QList<QString> Seq::inputPorts()
|
|
|
|
{
|
2013-04-02 20:46:07 +02:00
|
|
|
if (_driver)
|
|
|
|
return _driver->inputPorts();
|
2012-05-26 14:49:10 +02:00
|
|
|
QList<QString> a;
|
|
|
|
return a;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// rewindStart
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Seq::rewindStart()
|
|
|
|
{
|
|
|
|
seek(0);
|
|
|
|
}
|
2013-08-08 11:38:32 +02:00
|
|
|
|
2012-05-26 14:49:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// canStart
|
|
|
|
// return true if sequencer can be started
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
bool Seq::canStart()
|
|
|
|
{
|
2013-04-02 20:46:07 +02:00
|
|
|
if (!_driver)
|
2012-05-26 14:49:10 +02:00
|
|
|
return false;
|
|
|
|
if (events.empty() || cs->playlistDirty() || playlistChanged)
|
|
|
|
collectEvents();
|
|
|
|
return (!events.empty() && endTick != 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// start
|
|
|
|
// called from gui thread
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Seq::start()
|
|
|
|
{
|
|
|
|
if (events.empty() || cs->playlistDirty() || playlistChanged)
|
|
|
|
collectEvents();
|
|
|
|
if (cs->playMode() == PLAYMODE_AUDIO) {
|
|
|
|
if (!oggInit) {
|
|
|
|
vorbisData.pos = 0;
|
|
|
|
vorbisData.data = cs->audio()->data();
|
|
|
|
int n = ov_open_callbacks(&vorbisData, &vf, 0, 0, ovCallbacks);
|
|
|
|
if (n < 0) {
|
|
|
|
printf("ogg open failed: %d\n", n);
|
|
|
|
}
|
|
|
|
oggInit = true;
|
|
|
|
}
|
|
|
|
}
|
2013-08-09 01:20:07 +02:00
|
|
|
if (mscore->loop()) {
|
|
|
|
seek(loopInPos);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
seek(cs->playPos());
|
2013-04-02 20:46:07 +02:00
|
|
|
_driver->startTransport();
|
2012-05-26 14:49:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// stop
|
|
|
|
// called from gui thread
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Seq::stop()
|
|
|
|
{
|
|
|
|
if (state == TRANSPORT_STOP)
|
|
|
|
return;
|
|
|
|
if (oggInit) {
|
|
|
|
ov_clear(&vf);
|
|
|
|
oggInit = false;
|
|
|
|
}
|
2013-04-02 20:46:07 +02:00
|
|
|
if (!_driver)
|
2012-05-26 14:49:10 +02:00
|
|
|
return;
|
2013-04-02 20:46:07 +02:00
|
|
|
_driver->stopTransport();
|
2012-05-26 14:49:10 +02:00
|
|
|
if (cv)
|
|
|
|
cv->setCursorOn(false);
|
|
|
|
if (cs) {
|
|
|
|
cs->setLayoutAll(false);
|
|
|
|
cs->setUpdateAll();
|
|
|
|
cs->end();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-29 19:42:38 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// stopWait
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Seq::stopWait()
|
|
|
|
{
|
|
|
|
stop();
|
|
|
|
QWaitCondition sleep;
|
|
|
|
int idx = 0;
|
|
|
|
while (state != TRANSPORT_STOP) {
|
|
|
|
printf("state %d\n", state);
|
|
|
|
mutex.lock();
|
|
|
|
sleep.wait(&mutex, 100);
|
|
|
|
mutex.unlock();
|
|
|
|
++idx;
|
|
|
|
if (idx > 10)
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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)
|
2012-05-26 14:49:10 +02:00
|
|
|
cs->end();
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// 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();
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
|
2013-02-20 17:53:15 +01:00
|
|
|
cs->setPlayPos(cs->utime2utick(qreal(playTime) / qreal(MScore::sampleRate)));
|
2012-05-26 14:49:10 +02:00
|
|
|
cs->end();
|
|
|
|
emit stopped();
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// seqSignal
|
|
|
|
// sequencer message to GUI
|
|
|
|
// execution environment: gui thread
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Seq::seqMessage(int msg)
|
|
|
|
{
|
|
|
|
switch(msg) {
|
2013-08-09 01:20:07 +02:00
|
|
|
case '3': // LOOP playback (called when the end of the score is reached)
|
|
|
|
getAction("play")->trigger();
|
|
|
|
emit started();
|
|
|
|
break;
|
2013-04-29 20:16:06 +02:00
|
|
|
case '2':
|
2012-05-26 14:49:10 +02:00
|
|
|
guiStop();
|
|
|
|
// heartBeatTimer->stop();
|
2013-04-02 20:46:07 +02:00
|
|
|
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();
|
2013-04-02 20:46:07 +02:00
|
|
|
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\n", msg);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// playEvent
|
|
|
|
// send one event to the synthesizer
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2013-04-25 09:50:55 +02:00
|
|
|
void Seq::playEvent(const NPlayEvent& event)
|
2012-05-26 14:49:10 +02:00
|
|
|
{
|
|
|
|
int type = event.type();
|
|
|
|
if (type == ME_NOTEON) {
|
|
|
|
bool mute;
|
|
|
|
const Note* note = event.note();
|
|
|
|
|
|
|
|
if (note) {
|
|
|
|
Instrument* instr = note->staff()->part()->instr();
|
|
|
|
const Channel& a = instr->channel(note->subchannel());
|
|
|
|
mute = a.mute || a.soloMute;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
mute = false;
|
|
|
|
|
|
|
|
if (!mute)
|
|
|
|
putEvent(event);
|
|
|
|
}
|
|
|
|
else if (type == ME_CONTROLLER)
|
|
|
|
putEvent(event);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// processMessages
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Seq::processMessages()
|
|
|
|
{
|
|
|
|
for (;;) {
|
|
|
|
if (toSeq.isEmpty())
|
|
|
|
break;
|
|
|
|
SeqMsg msg = toSeq.dequeue();
|
|
|
|
switch(msg.id) {
|
|
|
|
case SEQ_TEMPO_CHANGE:
|
|
|
|
{
|
|
|
|
if (playTime != 0) {
|
|
|
|
int tick = cs->utime2utick(qreal(playTime) / qreal(MScore::sampleRate));
|
2013-02-20 17:53:15 +01:00
|
|
|
cs->tempomap()->setRelTempo(msg.realVal);
|
2012-05-26 14:49:10 +02:00
|
|
|
cs->repeatList()->update();
|
|
|
|
playTime = cs->utick2utime(tick) * MScore::sampleRate;
|
|
|
|
}
|
|
|
|
else
|
2013-02-20 17:53:15 +01:00
|
|
|
cs->tempomap()->setRelTempo(msg.realVal);
|
2012-05-26 14:49:10 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SEQ_PLAY:
|
|
|
|
putEvent(msg.event);
|
|
|
|
break;
|
|
|
|
case SEQ_SEEK:
|
2013-02-20 17:53:15 +01:00
|
|
|
setPos(msg.intVal);
|
2012-05-26 14:49:10 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// metronome
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Seq::metronome(unsigned n, float* p)
|
|
|
|
{
|
|
|
|
if (!mscore->metronome()) {
|
|
|
|
tickRest = 0;
|
|
|
|
tackRest = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (tickRest) {
|
2013-04-29 18:23:03 +02:00
|
|
|
tackRest = 0;
|
2012-05-26 14:49:10 +02:00
|
|
|
int idx = tickLength - tickRest;
|
|
|
|
int nn = n < tickRest ? n : tickRest;
|
|
|
|
for (int i = 0; i < nn; ++i) {
|
|
|
|
qreal v = tick[idx] * metronomeVolume;
|
|
|
|
*p++ += v;
|
|
|
|
*p++ += v;
|
|
|
|
++idx;
|
|
|
|
}
|
|
|
|
tickRest -= nn;
|
|
|
|
}
|
|
|
|
if (tackRest) {
|
|
|
|
int idx = tackLength - tackRest;
|
|
|
|
int nn = n < tackRest ? n : tackRest;
|
|
|
|
for (int i = 0; i < nn; ++i) {
|
|
|
|
qreal v = tack[idx] * metronomeVolume;
|
|
|
|
*p++ += v;
|
|
|
|
*p++ += v;
|
|
|
|
++idx;
|
|
|
|
}
|
|
|
|
tackRest -= nn;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// process
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Seq::process(unsigned n, float* buffer)
|
|
|
|
{
|
|
|
|
unsigned frames = n;
|
2013-04-02 20:46:07 +02:00
|
|
|
int driverState = _driver->getState();
|
2013-08-09 01:20:07 +02:00
|
|
|
if (driverState != state) {
|
2013-04-29 19:42:38 +02:00
|
|
|
if (state == TRANSPORT_STOP && driverState == TRANSPORT_PLAY) {
|
|
|
|
state = TRANSPORT_PLAY;
|
|
|
|
emit toGui('1');
|
|
|
|
}
|
|
|
|
else if (state == TRANSPORT_PLAY && driverState == TRANSPORT_STOP) {
|
|
|
|
state = TRANSPORT_STOP;
|
|
|
|
stopNotes();
|
|
|
|
// send sustain off
|
|
|
|
// TODO: channel?
|
|
|
|
putEvent(NPlayEvent(ME_CONTROLLER, 0, CTRL_SUSTAIN, 0));
|
2013-08-09 01:20:07 +02:00
|
|
|
if (playPos == events.cend()) {
|
|
|
|
if (mscore->loop()) {
|
|
|
|
qDebug("MScore::Seq:: loop active. playPos = %d cs->%d\n", playPos->first,cs->pos());
|
|
|
|
emit toGui('3');
|
|
|
|
}
|
|
|
|
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 ?\n",
|
|
|
|
state, driverState);
|
|
|
|
}
|
|
|
|
|
2012-06-25 15:07:59 +02:00
|
|
|
memset(buffer, 0, sizeof(float) * n * 2);
|
2012-05-26 14:49:10 +02:00
|
|
|
float* p = buffer;
|
|
|
|
processMessages();
|
|
|
|
|
|
|
|
if (state == TRANSPORT_PLAY) {
|
2013-06-28 16:59:19 +02:00
|
|
|
if(!cs)
|
|
|
|
return;
|
2012-05-26 14:49:10 +02:00
|
|
|
//
|
|
|
|
// play events for one segment
|
|
|
|
//
|
|
|
|
unsigned framePos = 0;
|
|
|
|
int endTime = playTime + frames;
|
2013-04-08 10:31:17 +02:00
|
|
|
for (; playPos != events.cend();) {
|
|
|
|
int f = cs->utick2utime(playPos->first) * MScore::sampleRate;
|
2012-05-26 14:49:10 +02:00
|
|
|
if (f >= endTime)
|
|
|
|
break;
|
|
|
|
int n = f - playTime;
|
|
|
|
if (n < 0) {
|
2013-04-08 10:31:17 +02:00
|
|
|
qDebug("%d: %d - %d\n", playPos->first, f, playTime);
|
2013-07-31 19:47:33 +02:00
|
|
|
n = 0;
|
2012-05-26 14:49:10 +02:00
|
|
|
}
|
|
|
|
if (n) {
|
|
|
|
if (cs->playMode() == PLAYMODE_SYNTHESIZER) {
|
|
|
|
metronome(n, p);
|
2013-04-02 20:46:07 +02:00
|
|
|
_synti->process(n, p);
|
2012-05-26 14:49:10 +02:00
|
|
|
p += n * 2;
|
|
|
|
playTime += n;
|
|
|
|
frames -= n;
|
|
|
|
framePos += n;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
while (n > 0) {
|
|
|
|
int section;
|
|
|
|
float** pcm;
|
|
|
|
long rn = ov_read_float(&vf, &pcm, n, §ion);
|
|
|
|
if (rn == 0)
|
|
|
|
break;
|
|
|
|
for (int i = 0; i < rn; ++i) {
|
|
|
|
*p++ = pcm[0][i];
|
|
|
|
*p++ = pcm[1][i];
|
|
|
|
}
|
|
|
|
playTime += rn;
|
|
|
|
frames -= rn;
|
|
|
|
framePos += rn;
|
|
|
|
n -= rn;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-04-25 09:50:55 +02:00
|
|
|
const NPlayEvent& event = playPos->second;
|
2012-05-26 14:49:10 +02:00
|
|
|
playEvent(event);
|
|
|
|
if (event.type() == ME_TICK1)
|
|
|
|
tickRest = tickLength;
|
|
|
|
else if (event.type() == ME_TICK2)
|
|
|
|
tackRest = tackLength;
|
2013-02-20 17:53:15 +01:00
|
|
|
mutex.lock();
|
|
|
|
++playPos;
|
|
|
|
mutex.unlock();
|
2012-05-26 14:49:10 +02:00
|
|
|
}
|
|
|
|
if (frames) {
|
|
|
|
if (cs->playMode() == PLAYMODE_SYNTHESIZER) {
|
|
|
|
metronome(frames, p);
|
2013-04-02 20:46:07 +02:00
|
|
|
_synti->process(frames, p);
|
2012-05-26 14:49:10 +02:00
|
|
|
playTime += frames;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
int n = frames;
|
|
|
|
while (n > 0) {
|
|
|
|
int section;
|
|
|
|
float** pcm;
|
|
|
|
long rn = ov_read_float(&vf, &pcm, n, §ion);
|
|
|
|
if (rn == 0)
|
|
|
|
break;
|
|
|
|
for (int i = 0; i < rn; ++i) {
|
|
|
|
*p++ = pcm[0][i];
|
|
|
|
*p++ = pcm[1][i];
|
|
|
|
}
|
|
|
|
playTime += rn;
|
|
|
|
frames -= rn;
|
|
|
|
framePos += rn;
|
|
|
|
n -= rn;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-04-29 19:42:38 +02:00
|
|
|
if (playPos == events.cend())
|
2013-04-02 20:46:07 +02:00
|
|
|
_driver->stopTransport();
|
2012-05-26 14:49:10 +02:00
|
|
|
}
|
|
|
|
else {
|
2013-04-02 20:46:07 +02:00
|
|
|
_synti->process(frames, 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
|
|
|
//
|
|
|
|
float lv = 0.0f;
|
|
|
|
float rv = 0.0f;
|
2013-03-27 20:10:14 +01:00
|
|
|
p = buffer;
|
2012-05-26 14:49:10 +02:00
|
|
|
for (unsigned i = 0; i < n; ++i) {
|
2013-04-03 12:49:55 +02:00
|
|
|
qreal val = *p;
|
2013-03-27 20:10:14 +01:00
|
|
|
lv = qMax(lv, fabsf(val));
|
|
|
|
*p++ = val;
|
|
|
|
|
2013-04-03 12:49:55 +02:00
|
|
|
val = *p;
|
2013-03-27 20:10:14 +01:00
|
|
|
rv = qMax(lv, fabsf(val));
|
|
|
|
*p++ = val;
|
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;
|
|
|
|
}
|
|
|
|
}
|
2013-08-08 11:38:32 +02:00
|
|
|
|
2012-05-26 14:49:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// initInstruments
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Seq::initInstruments()
|
|
|
|
{
|
|
|
|
foreach(const MidiMapping& mm, *cs->midiMapping()) {
|
|
|
|
Channel* channel = mm.articulation;
|
2013-04-25 09:50:55 +02:00
|
|
|
foreach(const MidiCoreEvent& e, channel->init) {
|
2012-05-26 14:49:10 +02:00
|
|
|
if (e.type() == ME_INVALID)
|
|
|
|
continue;
|
2013-04-25 09:50:55 +02:00
|
|
|
NPlayEvent event(e.type(), channel->channel, e.dataA(), e.dataB());
|
|
|
|
sendEvent(event);
|
2012-05-26 14:49:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// collectEvents
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Seq::collectEvents()
|
|
|
|
{
|
2012-06-23 15:54:01 +02:00
|
|
|
//do not collect even while playing
|
2013-03-27 20:10:14 +01:00
|
|
|
if (state == TRANSPORT_PLAY)
|
2012-06-23 15:54:01 +02:00
|
|
|
return;
|
2012-05-26 14:49:10 +02:00
|
|
|
events.clear();
|
|
|
|
|
2013-05-03 14:45:25 +02:00
|
|
|
mutex.lock();
|
2012-11-19 09:29:46 +01:00
|
|
|
cs->renderMidi(&events);
|
2012-05-26 14:49:10 +02:00
|
|
|
endTick = 0;
|
2013-05-03 11:37:28 +02:00
|
|
|
|
2012-05-26 14:49:10 +02:00
|
|
|
if (!events.empty()) {
|
2013-04-08 10:31:17 +02:00
|
|
|
auto e = events.cend();
|
2012-05-26 14:49:10 +02:00
|
|
|
--e;
|
2013-04-08 10:31:17 +02:00
|
|
|
endTick = e->first;
|
2012-05-26 14:49:10 +02:00
|
|
|
}
|
2013-05-03 14:45:25 +02:00
|
|
|
playPos = events.cbegin();
|
|
|
|
mutex.unlock();
|
2012-05-26 14:49:10 +02:00
|
|
|
|
|
|
|
playlistChanged = false;
|
|
|
|
cs->setPlaylistDirty(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// getCurTick
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
int Seq::getCurTick()
|
|
|
|
{
|
|
|
|
return cs->utime2utick(qreal(playTime) / qreal(MScore::sampleRate));
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// setRelTempo
|
|
|
|
// relTempo = 1.0 = normal tempo
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Seq::setRelTempo(double relTempo)
|
|
|
|
{
|
2013-02-20 17:53:15 +01:00
|
|
|
guiToSeq(SeqMsg(SEQ_TEMPO_CHANGE, relTempo));
|
2012-05-26 14:49:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// setPos
|
|
|
|
// seek
|
|
|
|
// realtime environment
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Seq::setPos(int utick)
|
|
|
|
{
|
2013-03-20 17:08:12 +01:00
|
|
|
if (cs == 0)
|
|
|
|
return;
|
2012-05-26 14:49:10 +02:00
|
|
|
stopNotes();
|
|
|
|
|
2013-05-03 14:24:14 +02:00
|
|
|
int ucur;
|
|
|
|
if (playPos != events.end())
|
|
|
|
ucur = cs->repeatList()->utick2tick(playPos->first);
|
|
|
|
else
|
|
|
|
ucur = utick - 1;
|
2013-04-29 19:42:38 +02:00
|
|
|
if (utick != ucur)
|
|
|
|
updateSynthesizerState(ucur, utick);
|
|
|
|
|
2012-05-26 14:49:10 +02:00
|
|
|
playTime = cs->utick2utime(utick) * MScore::sampleRate;
|
2013-02-20 17:53:15 +01:00
|
|
|
mutex.lock();
|
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
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// seek
|
|
|
|
// send seek message to sequencer
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2012-07-02 18:43:11 +02:00
|
|
|
void Seq::seek(int utick)
|
2012-05-26 14:49:10 +02:00
|
|
|
{
|
|
|
|
if (cs == 0)
|
|
|
|
return;
|
2013-02-20 17:53:15 +01:00
|
|
|
|
2013-08-09 01:20:07 +02:00
|
|
|
qDebug ("seek : utick=%d",utick);
|
2013-04-25 14:35:32 +02:00
|
|
|
if (events.empty() || cs->playlistDirty() || playlistChanged)
|
|
|
|
collectEvents();
|
2013-05-03 14:45:25 +02:00
|
|
|
int tick = cs->repeatList()->utick2tick(utick);
|
2012-05-26 14:49:10 +02:00
|
|
|
Segment* seg = cs->tick2segment(tick);
|
2013-05-03 14:45:25 +02:00
|
|
|
seek(utick, seg);
|
2013-02-20 17:53:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// seek
|
|
|
|
// send seek message to sequencer
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Seq::seek(int utick, Segment* seg)
|
|
|
|
{
|
2013-05-03 14:45:25 +02:00
|
|
|
if (seg)
|
2013-07-03 11:17:14 +02:00
|
|
|
mscore->currentScoreView()->moveCursor(seg->tick());
|
2013-05-03 14:45:25 +02:00
|
|
|
|
2012-07-02 18:43:11 +02:00
|
|
|
cs->setPlayPos(utick);
|
2012-05-26 14:49:10 +02:00
|
|
|
cs->setLayoutAll(false);
|
|
|
|
cs->end();
|
2012-11-19 09:29:46 +01:00
|
|
|
|
2012-05-26 14:49:10 +02:00
|
|
|
if (cs->playMode() == PLAYMODE_AUDIO) {
|
2012-07-02 18:43:11 +02:00
|
|
|
ogg_int64_t sp = cs->utick2utime(utick) * MScore::sampleRate;
|
2012-05-26 14:49:10 +02:00
|
|
|
ov_pcm_seek(&vf, sp);
|
|
|
|
}
|
|
|
|
|
2013-02-20 17:53:15 +01:00
|
|
|
guiToSeq(SeqMsg(SEQ_SEEK, utick));
|
2013-07-19 15:43:50 +02:00
|
|
|
guiPos = events.lower_bound(utick);
|
2012-07-02 18:43:11 +02:00
|
|
|
mscore->setPos(utick);
|
2013-02-20 17:53:15 +01:00
|
|
|
unmarkNotes();
|
|
|
|
cs->update();
|
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)
|
|
|
|
return;
|
2013-04-25 09:50:55 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// 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)
|
|
|
|
{
|
2013-04-02 20:46:07 +02:00
|
|
|
_synti->allNotesOff(channel);
|
2012-05-26 14:49:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// setController
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Seq::setController(int channel, int ctrl, int data)
|
|
|
|
{
|
2013-04-25 09:50:55 +02:00
|
|
|
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
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2013-04-25 09:50:55 +02:00
|
|
|
void Seq::sendEvent(const NPlayEvent& ev)
|
2012-05-26 14:49:10 +02:00
|
|
|
{
|
2013-02-20 17:53:15 +01:00
|
|
|
guiToSeq(SeqMsg(SEQ_PLAY, ev));
|
2012-05-26 14:49:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// nextMeasure
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Seq::nextMeasure()
|
|
|
|
{
|
2013-04-08 10:31:17 +02:00
|
|
|
Measure* m = cs->tick2measure(guiPos->first);
|
2012-05-26 14:49:10 +02:00
|
|
|
if (m) {
|
2013-02-21 11:04:41 +01:00
|
|
|
if (m->nextMeasure())
|
|
|
|
m = m->nextMeasure();
|
|
|
|
seek(m->tick());
|
2012-05-26 14:49:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// nextChord
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Seq::nextChord()
|
|
|
|
{
|
2013-04-08 10:31:17 +02:00
|
|
|
int tick = guiPos->first;
|
|
|
|
for (auto i = guiPos; i != events.cend(); ++i) {
|
|
|
|
if (i->second.type() == ME_NOTEON && i->first > tick && i->second.velo()) {
|
|
|
|
seek(i->first);
|
2012-05-26 14:49:10 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// prevMeasure
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Seq::prevMeasure()
|
|
|
|
{
|
2013-02-21 11:04:41 +01:00
|
|
|
auto i = guiPos;
|
|
|
|
if (i == events.begin())
|
2012-05-26 14:49:10 +02:00
|
|
|
return;
|
2013-02-21 11:04:41 +01:00
|
|
|
--i;
|
2013-04-08 10:31:17 +02:00
|
|
|
Measure* m = cs->tick2measure(i->first);
|
2012-05-26 14:49:10 +02:00
|
|
|
if (m) {
|
2013-04-08 10:31:17 +02:00
|
|
|
if ((i->first == m->tick()) && m->prevMeasure())
|
2013-02-20 17:53:15 +01:00
|
|
|
m = m->prevMeasure();
|
|
|
|
seek(m->tick());
|
2012-05-26 14:49:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// prevChord
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Seq::prevChord()
|
|
|
|
{
|
2013-04-08 10:31:17 +02:00
|
|
|
int tick = playPos->first;
|
2012-05-26 14:49:10 +02:00
|
|
|
//find the chord just before playpos
|
2013-04-08 10:31:17 +02:00
|
|
|
EventMap::const_iterator i = events.upper_bound(cs->playPos());
|
2012-05-26 14:49:10 +02:00
|
|
|
for (;;) {
|
2013-04-08 10:31:17 +02:00
|
|
|
if (i->second.type() == ME_NOTEON) {
|
2013-04-25 09:50:55 +02:00
|
|
|
const NPlayEvent& n = i->second;
|
2013-04-08 10:31:17 +02:00
|
|
|
if (i->first < tick && n.velo()) {
|
|
|
|
tick = 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) {
|
2013-04-25 09:50:55 +02:00
|
|
|
const NPlayEvent& n = i->second;
|
2013-04-08 10:31:17 +02:00
|
|
|
if (i->first < tick && n.velo()) {
|
|
|
|
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\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// guiToSeq
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Seq::guiToSeq(const SeqMsg& msg)
|
|
|
|
{
|
2013-04-02 20:46:07 +02:00
|
|
|
if (!_driver || !running)
|
2012-05-26 14:49:10 +02:00
|
|
|
return;
|
|
|
|
toSeq.enqueue(msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// eventToGui
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2013-04-25 09:50:55 +02:00
|
|
|
void Seq::eventToGui(NPlayEvent e)
|
2012-05-26 14:49:10 +02:00
|
|
|
{
|
2013-02-20 17:53:15 +01:00
|
|
|
fromSeq.enqueue(SeqMsg(SEQ_MIDI_INPUT_EVENT, e));
|
2012-05-26 14:49:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// midiInputReady
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Seq::midiInputReady()
|
|
|
|
{
|
2013-04-02 20:46:07 +02:00
|
|
|
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\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
messages[widx] = msg;
|
|
|
|
push();
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// dequeue
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
SeqMsg SeqMsgFifo::dequeue()
|
|
|
|
{
|
|
|
|
SeqMsg msg = messages[ridx];
|
|
|
|
pop();
|
|
|
|
return msg;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// putEvent
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2013-04-25 09:50:55 +02:00
|
|
|
void Seq::putEvent(const NPlayEvent& event)
|
2012-05-26 14:49:10 +02:00
|
|
|
{
|
|
|
|
if (!cs)
|
|
|
|
return;
|
|
|
|
int channel = event.channel();
|
2013-03-27 15:16:23 +01:00
|
|
|
if (channel >= cs->midiMapping()->size()) {
|
|
|
|
qDebug("bad channel value");
|
|
|
|
return;
|
|
|
|
}
|
2013-04-04 13:01:58 +02:00
|
|
|
int syntiIdx= _synti->index(cs->midiMapping(channel)->articulation->synti);
|
2013-04-02 20:46:07 +02:00
|
|
|
_synti->play(event, syntiIdx);
|
2012-05-26 14:49:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// heartBeat
|
|
|
|
// update GUI
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2013-05-03 11:37:28 +02:00
|
|
|
void Seq::heartBeatTimeout()
|
2012-05-26 14:49:10 +02:00
|
|
|
{
|
2013-08-09 01:20:07 +02:00
|
|
|
if (state == TRANSPORT_PLAY) {
|
|
|
|
//
|
|
|
|
// loop back to "In position" if "Out position" is reached
|
|
|
|
//
|
|
|
|
if ((mscore->loop()) && (getCurTick() >= loopOutPos) && (loopOutPos > loopInPos)) {
|
|
|
|
qDebug("----> Loop test. getCurTick() = %d cs->pos() = %d cs->playPos() = %d", getCurTick(), cs->pos(), cs->playPos());
|
|
|
|
//emit toGui('4');
|
|
|
|
seek(loopInPos);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2012-05-26 14:49:10 +02:00
|
|
|
SynthControl* sc = mscore->getSynthControl();
|
2013-04-02 20:46:07 +02:00
|
|
|
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
|
|
|
|
|
|
|
while (!fromSeq.isEmpty()) {
|
|
|
|
SeqMsg msg = fromSeq.dequeue();
|
|
|
|
if (msg.id == SEQ_MIDI_INPUT_EVENT) {
|
|
|
|
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());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-26 14:49:10 +02:00
|
|
|
if (state != TRANSPORT_PLAY)
|
|
|
|
return;
|
|
|
|
int endTime = playTime;
|
|
|
|
|
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();
|
|
|
|
|
2013-06-25 10:41:15 +02:00
|
|
|
QRectF r;
|
2013-04-08 10:31:17 +02:00
|
|
|
for (;guiPos != events.cend(); ++guiPos) {
|
|
|
|
if (guiPos->first > ppos->first)
|
2012-05-26 14:49:10 +02:00
|
|
|
break;
|
2013-04-25 09:50:55 +02:00
|
|
|
const NPlayEvent& n = guiPos->second;
|
2013-02-20 17:53:15 +01:00
|
|
|
if (n.type() == ME_NOTEON) {
|
2012-05-26 14:49:10 +02:00
|
|
|
const Note* note1 = n.note();
|
|
|
|
if (n.velo()) {
|
|
|
|
while (note1) {
|
2013-02-20 17:53:15 +01:00
|
|
|
note1->setMark(true);
|
2012-05-26 14:49:10 +02:00
|
|
|
markedNotes.append(note1);
|
2013-06-25 10:41:15 +02:00
|
|
|
r |= note1->canvasBoundingRect();
|
2012-05-26 14:49:10 +02:00
|
|
|
note1 = note1->tieFor() ? note1->tieFor()->endNote() : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
while (note1) {
|
2013-02-20 17:53:15 +01:00
|
|
|
note1->setMark(false);
|
2013-06-25 10:41:15 +02:00
|
|
|
r |= note1->canvasBoundingRect();
|
2012-05-26 14:49:10 +02:00
|
|
|
markedNotes.removeOne(note1);
|
|
|
|
note1 = note1->tieFor() ? note1->tieFor()->endNote() : 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-04-08 10:31:17 +02:00
|
|
|
int utick = ppos->first;
|
2012-07-02 18:43:11 +02:00
|
|
|
int tick = cs->repeatList()->utick2tick(utick);
|
2012-05-26 14:49:10 +02:00
|
|
|
mscore->currentScoreView()->moveCursor(tick);
|
|
|
|
mscore->setPos(tick);
|
2013-05-03 11:37:28 +02:00
|
|
|
|
|
|
|
emit(heartBeat(tick, utick, endTime));
|
2012-05-26 14:49:10 +02:00
|
|
|
|
|
|
|
PianorollEditor* pre = mscore->getPianorollEditor();
|
|
|
|
if (pre && pre->isVisible())
|
|
|
|
pre->heartBeat(this);
|
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
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Seq::updateSynthesizerState(int tick1, int tick2)
|
|
|
|
{
|
|
|
|
if (tick1 > tick2)
|
|
|
|
tick1 = 0;
|
|
|
|
EventMap::const_iterator i1 = events.lower_bound(tick1);
|
|
|
|
EventMap::const_iterator i2 = events.upper_bound(tick2);
|
|
|
|
|
|
|
|
for (; i1 != i2; ++i1) {
|
|
|
|
if (i1->second.type() == ME_CONTROLLER)
|
|
|
|
playEvent(i1->second);
|
|
|
|
}
|
|
|
|
}
|
2013-05-03 11:37:28 +02:00
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// curTempo
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
double Seq::curTempo() const
|
|
|
|
{
|
|
|
|
return cs->tempomap()->tempo(playPos->first);
|
|
|
|
}
|
|
|
|
|
2013-08-09 01:20:07 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// set Loop in position
|
|
|
|
//---------------------------------------------------------
|
|
|
|
void Seq::setLoopIn()
|
|
|
|
{
|
|
|
|
loopInPos = cs->pos();
|
|
|
|
qDebug ("setLoopIn : loopInPos = %d\n",loopInPos);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// set Loop in position
|
|
|
|
//---------------------------------------------------------
|
|
|
|
void Seq::setLoopOut()
|
|
|
|
{
|
|
|
|
loopOutPos = cs->pos()+cs->inputState().ticks();
|
|
|
|
qDebug ("setLoopOut : loopOutPos = %d ; cs->pos() = %d + cs->inputState().ticks() =%d \n",loopOutPos,cs->pos(),cs->inputState().ticks());
|
|
|
|
}
|
|
|
|
}
|