Permalink
Browse files

JACK Transport reposition

  • Loading branch information...
Igevorse committed Jun 18, 2014
1 parent c24022f commit d2b2050799cfc385e5c6de607bec658c6b6cdf8a
Showing with 119 additions and 28 deletions.
  1. +4 −0 mscore/driver.h
  2. +61 −14 mscore/jackaudio.cpp
  3. +5 −0 mscore/jackaudio.h
  4. +1 −1 mscore/jackweakapi.cpp
  5. +47 −12 mscore/seq.cpp
  6. +1 −1 mscore/seq.h
View
@@ -47,12 +47,16 @@ class Driver {
virtual void stopTransport() = 0;
virtual void startTransport() = 0;
virtual Transport getState() = 0;
+ virtual void seekTransport(int) {}
+ virtual bool instantSeek() {return true;} // False only in JackAudio
virtual int sampleRate() const = 0;
virtual void registerPort(const QString& /*name*/, bool /*input*/, bool /*midi*/) {}
virtual void unregisterPort(int) {}
virtual void putEvent(const NPlayEvent&, unsigned /*framePos*/) {}
virtual void midiRead() {}
virtual void handleTimeSigTempoChanged() {}
+ virtual void checkTransportSeek(int, int) {}
+ virtual int bufferSize() {return 0;}
};
View
@@ -25,6 +25,7 @@
// #include "msynth/synti.h"
#include "seq.h"
#include "libmscore/score.h"
+#include "libmscore/repeatlist.h"
#include <jack/midiport.h>
@@ -282,24 +283,33 @@ int JackAudio::framePos() const
return (int)n;
}
+//---------------------------------------------------------
+// freewheel_callback
+//---------------------------------------------------------
-static int bufsize_callback(jack_nframes_t /*n*/, void*)
+static void freewheel_callback(int /*starting*/, void*)
{
-// qDebug("JACK: buffersize changed %d", n);
- return 0;
}
//---------------------------------------------------------
-// freewheel_callback
+// sampleRateCallback
//---------------------------------------------------------
-static void freewheel_callback(int /*starting*/, void*)
+int sampleRateCallback(jack_nframes_t sampleRate, void*)
{
+ qDebug("JACK: sample rate changed: %d", sampleRate);
+ MScore::sampleRate = sampleRate;
+ return 0;
}
-static int srate_callback(jack_nframes_t, void*)
+//---------------------------------------------------------
+// bufferSizeCallback called if JACK buffer changed
+//---------------------------------------------------------
+
+int bufferSizeCallback(jack_nframes_t nframes, void *arg)
{
-// qDebug("JACK: sample rate changed: %d", n);
+ JackAudio* audio = (JackAudio*)arg;
+ audio->setBufferSize(nframes);
return 0;
}
@@ -339,6 +349,7 @@ void JackAudio::timebase(jack_transport_state_t state, jack_nframes_t /*nframes*
qDebug()<<"Time signature and tempo changed: "<< pos->beats_per_minute<<", bar: "<< pos->bar<<",beat: "<<pos->beat<<", tick:"<<pos->tick<<", time sig: "<<pos->beats_per_bar<<"/"<<pos->beat_type;
audio->timeSigTempoChanged = false;
}
+ // TODO: Handle new_pos
}
//---------------------------------------------------------
// processAudio
@@ -457,13 +468,14 @@ bool JackAudio::init()
jack_set_error_function(jackError);
jack_set_process_callback(client, processAudio, this);
//jack_on_shutdown(client, processShutdown, this);
- jack_set_buffer_size_callback(client, bufsize_callback, this);
- jack_set_sample_rate_callback(client, srate_callback, this);
+ jack_set_sample_rate_callback(client, sampleRateCallback, this);
jack_set_port_registration_callback(client, registration_callback, this);
jack_set_graph_order_callback(client, graph_callback, this);
jack_set_freewheel_callback (client, freewheel_callback, this);
if (jack_set_timebase_callback(client, 1, timebase, this) != 0)
- fprintf(stderr, "Unable to take over timebase.\n");
+ qDebug("Unable to take over timebase.");
+ if( jack_set_buffer_size_callback (client, bufferSizeCallback, this) != 0)
+ qDebug("Can not set bufferSizeCallback");
_segmentSize = jack_get_buffer_size(client);
MScore::sampleRate = sampleRate();
@@ -531,12 +543,12 @@ void JackAudio::stopTransport()
Transport JackAudio::getState()
{
- jack_position_t pos;
- int transportState = jack_transport_query(client, &pos);
+ int transportState = jack_transport_query(client, NULL);
switch (transportState) {
case JackTransportStopped: return Transport::STOP;
case JackTransportLooping:
case JackTransportRolling: return Transport::PLAY;
+ case JackTransportStarting: return seq->isPlaying()?Transport::PLAY:Transport::STOP;// Keep current state
default:
return Transport::STOP;
}
@@ -638,9 +650,44 @@ void JackAudio::midiRead()
// midiDriver->read();
}
+//---------------------------------------------------------
+// Called after tempo or time signature
+// changed while playback
+//---------------------------------------------------------
+
void JackAudio::handleTimeSigTempoChanged()
{
- timeSigTempoChanged = true;
+ timeSigTempoChanged = true;
}
-}
+//---------------------------------------------------------
+// checkTransportSeek
+//---------------------------------------------------------
+
+void JackAudio::checkTransportSeek(int cur_frame, int nframes)
+ {
+ // Obtaining the current JACK Transport position
+ jack_position_t pos;
+ jack_transport_query(client, &pos);
+
+ int srate = MScore::sampleRate;
+ int cur_utick = seq->score()->utime2utick((qreal)cur_frame / srate);
+ int utick = seq->score()->utime2utick((qreal)pos.frame / srate);
+
+ // Conversion is not precise, should check frames and uticks
+ if (labs((long int)cur_frame-(long int)pos.frame)>nframes+1 && abs(utick - cur_utick)> seq->score()->utime2utick((qreal)nframes / srate)+1) {
+ qDebug()<<"JACK Transport position changed, cur_frame: "<<cur_frame<<",pos.frame: "<<pos.frame<<", frame diff: "<<labs((long int)cur_frame-(long int)pos.frame)<<"cur utick:"<<cur_utick<<",seek to utick: "<<utick<<", tick diff: "<<abs(utick - cur_utick);
+ seq->seek(utick, false);
+ }
+ }
+
+//---------------------------------------------------------
+// seekTransport
+//---------------------------------------------------------
+
+void JackAudio::seekTransport(int utick)
+ {
+ qDebug()<<"jack locate to utick: "<<utick<<", frame: "<<seq->score()->utick2utime(utick) * MScore::sampleRate;
+ jack_transport_locate(client, seq->score()->utick2utime(utick) * MScore::sampleRate);
+ }
+}
View
@@ -62,13 +62,18 @@ class JackAudio : public Driver {
virtual void startTransport();
virtual void stopTransport();
virtual Transport getState() override;
+ virtual void seekTransport(int);
+ virtual bool instantSeek() {return false;} // False only in JackAudio
virtual int sampleRate() const { return jack_get_sample_rate(client); }
virtual void putEvent(const NPlayEvent&, unsigned framePos);
virtual void midiRead();
virtual void registerPort(const QString& name, bool input, bool midi);
virtual void unregisterPort(int);
virtual void handleTimeSigTempoChanged();
+ virtual void checkTransportSeek(int, int);
+ virtual int bufferSize() {return _segmentSize;}
+ void setBufferSize(int nframes) { _segmentSize = nframes;}
};
View
@@ -249,7 +249,7 @@ DECL_FUNCTION(int, jack_set_timebase_callback, (jack_client_t *client,
DECL_FUNCTION(int, jack_transport_locate, (jack_client_t *client, jack_nframes_t frame), (client, frame));
DECL_FUNCTION(jack_transport_state_t, jack_transport_query, (const jack_client_t *client, jack_position_t *pos), (client, pos));
DECL_FUNCTION(jack_nframes_t, jack_get_current_transport_frame, (const jack_client_t *client), (client));
-DECL_FUNCTION(int, jack_transport_reposition, (jack_client_t *client, jack_position_t *pos), (client, pos));
+DECL_FUNCTION(int, jack_transport_reposition, (jack_client_t *client, const jack_position_t *pos), (client, pos));
DECL_VOID_FUNCTION(jack_transport_start, (jack_client_t *client), (client));
DECL_VOID_FUNCTION(jack_transport_stop, (jack_client_t *client), (client));
// DECL_VOID_FUNCTION(jack_get_transport_info, (jack_client_t *client, jack_transport_info_t *tinfo), (client,tinfo));
View
@@ -316,10 +316,17 @@ void Seq::start()
if ((mscore->loop())) {
if(cs->selection().isRange())
setLoopSelection();
- seek(cs->repeatList()->tick2utick(cs->loopInTick()));
+ if (state == Transport::STOP) {
+ qDebug()<<"___calling seek from start with LoopInTick utick = "<<cs->repeatList()->tick2utick(cs->loopInTick());
+ seek(cs->repeatList()->tick2utick(cs->loopInTick()));
+ }
+ }
+ else {
+ if (state == Transport::STOP) {
+ qDebug()<<"___calling seek from start with playpos utick = "<<cs->repeatList()->tick2utick(cs->playPos());
+ seek(cs->repeatList()->tick2utick(cs->playPos()));
+ }
}
- else
- seek(cs->repeatList()->tick2utick(cs->playPos()));
_driver->startTransport();
}
@@ -339,7 +346,8 @@ void Seq::stop()
}
if (!_driver)
return;
- _driver->stopTransport();
+ if(_driver->getState() == Transport::PLAY)
+ _driver->stopTransport();
if (cv)
cv->setCursorOn(false);
if (cs) {
@@ -664,14 +672,16 @@ void Seq::process(unsigned n, float* buffer)
{
unsigned frames = n;
Transport driverState = _driver->getState();
+ // Checking for the reposition from Transport
+ _driver->checkTransportSeek(playTime, frames);
if (driverState != state) {
// Got a message from JACK Transport panel: Play
if (state == Transport::STOP && driverState == Transport::PLAY) {
if((preferences.useJackMidi || preferences.useJackAudio) && !getAction("play")->isChecked()) {
// Do not play while editing elements
- if(mscore->state()==STATE_NORMAL && isRunning() && canStart()) {
+ if (mscore->state() == STATE_NORMAL && isRunning() && canStart()) {
if (playlistChanged)
collectEvents();
getAction("play")->setChecked(true);
@@ -771,9 +781,20 @@ void Seq::process(unsigned n, float* buffer)
int utickLoop = cs->repeatList()->tick2utick(cs->loopOutTick());
if (utickLoop < utickEnd)
if ((*pPlayPos)->first >= utickLoop) {
-qDebug ("Process playPos = %d in/out tick = %d/%d getCurTick() = %d tickLoop = %d playTime = %d",
- (*pPlayPos)->first, cs->loopInTick(), cs->loopOutTick(), getCurTick(), utickLoop, *pPlayTime);
- emit toGui('3'); // Exit this function to avoid segmentation fault in Scoreview
+ qDebug ("Process playPos = %d in/out tick = %d/%d getCurTick() = %d tickLoop = %d playTime = %d",
+ (*pPlayPos)->first, cs->loopInTick(), cs->loopOutTick(), getCurTick(), utickLoop, *pPlayTime);
+ if (_driver->instantSeek()) {
+ emit toGui('3');
+ }
+ else { // JACK
+ int loopInUtick = cs->repeatList()->tick2utick(cs->loopInTick());
+ seek(loopInUtick);
+ if (loopInUtick != 0) {
+ int seekto = loopInUtick-2*cs->utime2utick((qreal)_driver->bufferSize() / MScore::sampleRate);
+ seek((seekto>0)?seekto:0,false);
+ }
+ }
+ // Exit this function to avoid segmentation fault in Scoreview
return;
}
}
@@ -970,11 +991,18 @@ void Seq::setPos(int utick)
// send seek message to sequencer
//---------------------------------------------------------
-void Seq::seek(int utick)
+void Seq::seek(int utick, bool can_wait)
{
if (cs == 0)
return;
+ // If driver can't seek immediately, just send a signal to seek.
+ if (!_driver->instantSeek() && can_wait) {
+ _driver->seekTransport(utick);
+ if (utick != 0)
+ return; // rewind to 0 immediately
+ }
+
if (playlistChanged)
collectEvents();
int tick = cs->repeatList()->utick2tick(utick);
@@ -992,7 +1020,8 @@ void Seq::seek(int utick, Segment* seg)
if (seg)
mscore->currentScoreView()->moveCursor(seg->tick());
- cs->setPlayPos(cs->repeatList()->utick2tick(utick));
+ int tick = cs->repeatList()->utick2tick(utick);
+ cs->setPlayPos(tick);
cs->setLayoutAll(false);
cs->end();
@@ -1003,7 +1032,7 @@ void Seq::seek(int utick, Segment* seg)
guiToSeq(SeqMsg(SeqMsgId::SEEK, utick));
guiPos = events.lower_bound(utick);
- mscore->setPos(utick);
+ mscore->setPos(tick);
unmarkNotes();
cs->update();
}
@@ -1317,6 +1346,7 @@ void Seq::heartBeatTimeout()
if (state != Transport::PLAY || inCountIn)
return;
+
int endTime = playTime;
mutex.lock();
@@ -1458,8 +1488,13 @@ void Seq::setLoopSelection()
cs->setLoopOutTick(cs->selection().tickEnd());
}
+//---------------------------------------------------------
+// Called after tempo or time signature
+// changed while playback
+//---------------------------------------------------------
+
void Seq::handleTimeSigTempoChanged()
{
- _driver->handleTimeSigTempoChanged();
+ _driver->handleTimeSigTempoChanged();
}
}
View
@@ -157,7 +157,7 @@ class Seq : public QObject, public Sequencer {
public slots:
void setRelTempo(double);
- void seek(int);
+ void seek(int, bool can_wait = true);
void stopNotes(int channel = -1);
void start();
void stop();

0 comments on commit d2b2050

Please sign in to comment.