David Robillard
24ccaac67e
making automation + GUI + play/write/touch generic and easily reusable. Added bar controller to automation track controls (mostly relevant for MIDI CC, but added for gain and pan too Just Because). Fixed glaring "redirect" list errors. Fix plugin controls/automation loading. git-svn-id: svn://localhost/ardour2/trunk@2080 d708f5d6-7413-0410-9779-e7cbd77b26cf
884 lines
20 KiB
C++
884 lines
20 KiB
C++
/*
|
|
Copyright (C) 1999-2002 Paul Davis
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
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 <cmath>
|
|
#include <cerrno>
|
|
#include <algorithm>
|
|
#include <unistd.h>
|
|
|
|
#include <pbd/error.h>
|
|
|
|
#include <glibmm/thread.h>
|
|
|
|
#include <ardour/ardour.h>
|
|
#include <ardour/session.h>
|
|
#include <ardour/timestamps.h>
|
|
#include <ardour/audio_diskstream.h>
|
|
#include <ardour/audioengine.h>
|
|
#include <ardour/slave.h>
|
|
#include <ardour/auditioner.h>
|
|
#include <ardour/cycles.h>
|
|
#include <ardour/cycle_timer.h>
|
|
|
|
#include <midi++/manager.h>
|
|
|
|
#include "i18n.h"
|
|
|
|
using namespace ARDOUR;
|
|
using namespace PBD;
|
|
using namespace std;
|
|
|
|
/** Called by the audio engine when there is work to be done with JACK.
|
|
* @param nframes Number of frames to process.
|
|
*/
|
|
void
|
|
Session::process (nframes_t nframes)
|
|
{
|
|
MIDI::Manager::instance()->cycle_start(nframes);
|
|
|
|
if (synced_to_jack() && waiting_to_start) {
|
|
if ( _engine.transport_state() == AudioEngine::TransportRolling) {
|
|
actually_start_transport ();
|
|
}
|
|
}
|
|
|
|
if (non_realtime_work_pending()) {
|
|
if (!transport_work_requested ()) {
|
|
post_transport ();
|
|
}
|
|
}
|
|
|
|
(this->*process_function) (nframes);
|
|
|
|
MIDI::Manager::instance()->cycle_end();
|
|
|
|
SendFeedback (); /* EMIT SIGNAL */
|
|
}
|
|
|
|
void
|
|
Session::prepare_diskstreams ()
|
|
{
|
|
boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
|
|
for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
|
|
(*i)->prepare ();
|
|
}
|
|
}
|
|
|
|
int
|
|
Session::no_roll (nframes_t nframes, nframes_t offset)
|
|
{
|
|
nframes_t end_frame = _transport_frame + nframes; // FIXME: varispeed + no_roll ??
|
|
int ret = 0;
|
|
bool declick = get_transport_declick_required();
|
|
boost::shared_ptr<RouteList> r = routes.reader ();
|
|
|
|
if (_click_io) {
|
|
_click_io->silence (nframes, offset);
|
|
}
|
|
|
|
if (g_atomic_int_get (&processing_prohibited)) {
|
|
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
|
|
(*i)->silence (nframes, offset);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
|
|
|
|
if ((*i)->is_hidden()) {
|
|
continue;
|
|
}
|
|
|
|
(*i)->set_pending_declick (declick);
|
|
|
|
if ((*i)->no_roll (nframes, _transport_frame, end_frame, offset, non_realtime_work_pending(),
|
|
actively_recording(), declick)) {
|
|
error << string_compose(_("Session: error in no roll for %1"), (*i)->name()) << endmsg;
|
|
ret = -1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
Session::process_routes (nframes_t nframes, nframes_t offset)
|
|
{
|
|
bool record_active;
|
|
int declick = get_transport_declick_required();
|
|
bool rec_monitors = get_rec_monitors_input();
|
|
boost::shared_ptr<RouteList> r = routes.reader ();
|
|
|
|
if (transport_sub_state & StopPendingCapture) {
|
|
/* force a declick out */
|
|
declick = -1;
|
|
}
|
|
|
|
record_active = actively_recording(); // || (get_record_enabled() && get_punch_in());
|
|
|
|
const nframes_t start_frame = _transport_frame;
|
|
const nframes_t end_frame = _transport_frame + (nframes_t)floor(nframes * _transport_speed);
|
|
|
|
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
|
|
|
|
int ret;
|
|
|
|
if ((*i)->is_hidden()) {
|
|
continue;
|
|
}
|
|
|
|
(*i)->set_pending_declick (declick);
|
|
|
|
if ((ret = (*i)->roll (nframes, start_frame, end_frame, offset, declick, record_active, rec_monitors)) < 0) {
|
|
|
|
/* we have to do this here. Route::roll() for an AudioTrack will have called AudioDiskstream::process(),
|
|
and the DS will expect AudioDiskstream::commit() to be called. but we're aborting from that
|
|
call path, so make sure we release any outstanding locks here before we return failure.
|
|
*/
|
|
|
|
boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
|
|
for (DiskstreamList::iterator ids = dsl->begin(); ids != dsl->end(); ++ids) {
|
|
(*ids)->recover ();
|
|
}
|
|
|
|
stop_transport ();
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
Session::silent_process_routes (nframes_t nframes, nframes_t offset)
|
|
{
|
|
bool record_active = actively_recording();
|
|
int declick = get_transport_declick_required();
|
|
bool rec_monitors = get_rec_monitors_input();
|
|
boost::shared_ptr<RouteList> r = routes.reader ();
|
|
|
|
if (transport_sub_state & StopPendingCapture) {
|
|
/* force a declick out */
|
|
declick = -1;
|
|
}
|
|
|
|
const nframes_t start_frame = _transport_frame;
|
|
const nframes_t end_frame = _transport_frame + lrintf(nframes * _transport_speed);
|
|
|
|
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
|
|
|
|
int ret;
|
|
|
|
if ((*i)->is_hidden()) {
|
|
continue;
|
|
}
|
|
|
|
if ((ret = (*i)->silent_roll (nframes, start_frame, end_frame, offset, record_active, rec_monitors)) < 0) {
|
|
|
|
/* we have to do this here. Route::roll() for an AudioTrack will have called AudioDiskstream::process(),
|
|
and the DS will expect AudioDiskstream::commit() to be called. but we're aborting from that
|
|
call path, so make sure we release any outstanding locks here before we return failure.
|
|
*/
|
|
|
|
boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
|
|
for (DiskstreamList::iterator ids = dsl->begin(); ids != dsl->end(); ++ids) {
|
|
(*ids)->recover ();
|
|
}
|
|
|
|
stop_transport ();
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
Session::commit_diskstreams (nframes_t nframes, bool &needs_butler)
|
|
{
|
|
int dret;
|
|
float pworst = 1.0f;
|
|
float cworst = 1.0f;
|
|
|
|
boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
|
|
for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
|
|
|
|
if ((*i)->hidden()) {
|
|
continue;
|
|
}
|
|
|
|
/* force all diskstreams not handled by a Route to call do their stuff.
|
|
Note: the diskstreams that were handled by a route will just return zero
|
|
from this call, because they know they were processed. So in fact, this
|
|
also runs commit() for every diskstream.
|
|
*/
|
|
|
|
if ((dret = (*i)->process (_transport_frame, nframes, 0, actively_recording(), get_rec_monitors_input())) == 0) {
|
|
if ((*i)->commit (nframes)) {
|
|
needs_butler = true;
|
|
}
|
|
|
|
} else if (dret < 0) {
|
|
(*i)->recover();
|
|
}
|
|
|
|
pworst = min (pworst, (*i)->playback_buffer_load());
|
|
cworst = min (cworst, (*i)->capture_buffer_load());
|
|
}
|
|
|
|
uint32_t pmin = g_atomic_int_get (&_playback_load);
|
|
uint32_t pminold = g_atomic_int_get (&_playback_load_min);
|
|
uint32_t cmin = g_atomic_int_get (&_capture_load);
|
|
uint32_t cminold = g_atomic_int_get (&_capture_load_min);
|
|
|
|
g_atomic_int_set (&_playback_load, (uint32_t) floor (pworst * 100.0f));
|
|
g_atomic_int_set (&_capture_load, (uint32_t) floor (cworst * 100.0f));
|
|
g_atomic_int_set (&_playback_load_min, min (pmin, pminold));
|
|
g_atomic_int_set (&_capture_load_min, min (cmin, cminold));
|
|
|
|
if (actively_recording()) {
|
|
set_dirty();
|
|
}
|
|
}
|
|
|
|
/** Process callback used when the auditioner is not active */
|
|
void
|
|
Session::process_with_events (nframes_t nframes)
|
|
{
|
|
Event* ev;
|
|
nframes_t this_nframes;
|
|
nframes_t end_frame;
|
|
nframes_t offset;
|
|
nframes_t stop_limit;
|
|
long frames_moved;
|
|
bool session_needs_butler = false;
|
|
|
|
/* make sure the auditioner is silent */
|
|
|
|
if (auditioner) {
|
|
auditioner->silence (nframes, 0);
|
|
}
|
|
|
|
/* handle any pending events */
|
|
|
|
while (pending_events.read (&ev, 1) == 1) {
|
|
merge_event (ev);
|
|
}
|
|
|
|
/* if we are not in the middle of a state change,
|
|
and there are immediate events queued up,
|
|
process them.
|
|
*/
|
|
|
|
while (!non_realtime_work_pending() && !immediate_events.empty()) {
|
|
Event *ev = immediate_events.front ();
|
|
immediate_events.pop_front ();
|
|
process_event (ev);
|
|
}
|
|
|
|
/* Events caused a transport change, send an MTC Full Frame (SMPTE) message.
|
|
* This is sent whether rolling or not, to give slaves an idea of ardour time
|
|
* on locates (and allow slow slaves to position and prepare for rolling)
|
|
*/
|
|
if (_send_smpte_update) {
|
|
send_full_time_code(nframes);
|
|
}
|
|
|
|
if (!process_can_proceed()) {
|
|
no_roll (nframes, 0);
|
|
return;
|
|
}
|
|
|
|
if (events.empty() || next_event == events.end()) {
|
|
process_without_events (nframes);
|
|
return;
|
|
}
|
|
|
|
end_frame = _transport_frame + (nframes_t)abs(floor(nframes * _transport_speed));
|
|
|
|
{
|
|
Event* this_event;
|
|
Events::iterator the_next_one;
|
|
|
|
if (post_transport_work & (PostTransportLocate|PostTransportStop)) {
|
|
no_roll (nframes, 0);
|
|
return;
|
|
}
|
|
|
|
if (!_exporting && _slave) {
|
|
if (!follow_slave (nframes, 0)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (_transport_speed == 0) {
|
|
no_roll (nframes, 0);
|
|
return;
|
|
}
|
|
|
|
send_midi_time_code_for_cycle(nframes);
|
|
|
|
if (actively_recording()) {
|
|
stop_limit = max_frames;
|
|
} else {
|
|
|
|
if (Config->get_stop_at_session_end()) {
|
|
stop_limit = current_end_frame();
|
|
} else {
|
|
stop_limit = max_frames;
|
|
}
|
|
}
|
|
|
|
if (maybe_stop (stop_limit)) {
|
|
no_roll (nframes, 0);
|
|
return;
|
|
}
|
|
|
|
this_event = *next_event;
|
|
the_next_one = next_event;
|
|
++the_next_one;
|
|
|
|
offset = 0;
|
|
|
|
/* yes folks, here it is, the actual loop where we really truly
|
|
process some audio */
|
|
while (nframes) {
|
|
|
|
this_nframes = nframes; /* real (jack) time relative */
|
|
frames_moved = (long) floor (_transport_speed * nframes); /* transport relative */
|
|
|
|
/* running an event, position transport precisely to its time */
|
|
if (this_event && this_event->action_frame <= end_frame && this_event->action_frame >= _transport_frame) {
|
|
/* this isn't quite right for reverse play */
|
|
frames_moved = (long) (this_event->action_frame - _transport_frame);
|
|
this_nframes = (nframes_t) abs( floor(frames_moved / _transport_speed) );
|
|
}
|
|
|
|
if (this_nframes) {
|
|
|
|
click (_transport_frame, nframes, offset);
|
|
|
|
/* now process frames between now and the first event in this block */
|
|
prepare_diskstreams ();
|
|
|
|
if (process_routes (this_nframes, offset)) {
|
|
no_roll (nframes, 0);
|
|
return;
|
|
}
|
|
|
|
commit_diskstreams (this_nframes, session_needs_butler);
|
|
|
|
nframes -= this_nframes;
|
|
offset += this_nframes;
|
|
|
|
if (frames_moved < 0) {
|
|
decrement_transport_position (-frames_moved);
|
|
} else {
|
|
increment_transport_position (frames_moved);
|
|
}
|
|
|
|
maybe_stop (stop_limit);
|
|
check_declick_out ();
|
|
}
|
|
|
|
/* now handle this event and all others scheduled for the same time */
|
|
|
|
while (this_event && this_event->action_frame == _transport_frame) {
|
|
process_event (this_event);
|
|
|
|
if (the_next_one == events.end()) {
|
|
this_event = 0;
|
|
} else {
|
|
this_event = *the_next_one;
|
|
++the_next_one;
|
|
}
|
|
}
|
|
|
|
/* if an event left our state changing, do the right thing */
|
|
|
|
if (non_realtime_work_pending()) {
|
|
no_roll (nframes, offset);
|
|
break;
|
|
}
|
|
|
|
/* this is necessary to handle the case of seamless looping */
|
|
end_frame = _transport_frame + (nframes_t) floor (nframes * _transport_speed);
|
|
|
|
}
|
|
|
|
set_next_event ();
|
|
|
|
} /* implicit release of route lock */
|
|
|
|
if (session_needs_butler)
|
|
summon_butler ();
|
|
}
|
|
|
|
void
|
|
Session::reset_slave_state ()
|
|
{
|
|
average_slave_delta = 1800;
|
|
delta_accumulator_cnt = 0;
|
|
have_first_delta_accumulator = false;
|
|
slave_state = Stopped;
|
|
}
|
|
|
|
bool
|
|
Session::transport_locked () const
|
|
{
|
|
Slave* sl = _slave;
|
|
|
|
if (!locate_pending() && ((Config->get_slave_source() == None) || (sl && sl->ok() && sl->locked()))) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
Session::follow_slave (nframes_t nframes, nframes_t offset)
|
|
{
|
|
float slave_speed;
|
|
nframes_t slave_transport_frame;
|
|
nframes_t this_delta;
|
|
int dir;
|
|
bool starting;
|
|
|
|
if (!_slave->ok()) {
|
|
stop_transport ();
|
|
Config->set_slave_source (None);
|
|
goto noroll;
|
|
}
|
|
|
|
_slave->speed_and_position (slave_speed, slave_transport_frame);
|
|
|
|
if (!_slave->locked()) {
|
|
goto noroll;
|
|
}
|
|
|
|
if (slave_transport_frame > _transport_frame) {
|
|
this_delta = slave_transport_frame - _transport_frame;
|
|
dir = 1;
|
|
} else {
|
|
this_delta = _transport_frame - slave_transport_frame;
|
|
dir = -1;
|
|
}
|
|
|
|
if ((starting = _slave->starting())) {
|
|
slave_speed = 0.0f;
|
|
}
|
|
|
|
#if 0
|
|
cerr << "delta = " << (int) (dir * this_delta)
|
|
<< " speed = " << slave_speed
|
|
<< " ts = " << _transport_speed
|
|
<< " M@ "<< slave_transport_frame << " S@ " << _transport_frame
|
|
<< " avgdelta = " << average_slave_delta
|
|
<< endl;
|
|
#endif
|
|
|
|
if (Config->get_timecode_source_is_synced()) {
|
|
|
|
/* if the TC source is synced, then we assume that its
|
|
speed is binary: 0.0 or 1.0
|
|
*/
|
|
|
|
if (slave_speed != 0.0f) {
|
|
slave_speed = 1.0f;
|
|
}
|
|
|
|
} else {
|
|
|
|
/* TC source is able to drift relative to us (slave)
|
|
so we need to keep track of the drift and adjust
|
|
our speed to remain locked.
|
|
*/
|
|
|
|
if (delta_accumulator_cnt >= delta_accumulator_size) {
|
|
have_first_delta_accumulator = true;
|
|
delta_accumulator_cnt = 0;
|
|
}
|
|
|
|
if (delta_accumulator_cnt != 0 || this_delta < _current_frame_rate) {
|
|
delta_accumulator[delta_accumulator_cnt++] = dir*this_delta;
|
|
}
|
|
|
|
if (have_first_delta_accumulator) {
|
|
average_slave_delta = 0;
|
|
for (int i = 0; i < delta_accumulator_size; ++i) {
|
|
average_slave_delta += delta_accumulator[i];
|
|
}
|
|
average_slave_delta /= delta_accumulator_size;
|
|
if (average_slave_delta < 0) {
|
|
average_dir = -1;
|
|
average_slave_delta = -average_slave_delta;
|
|
} else {
|
|
average_dir = 1;
|
|
}
|
|
// cerr << "avgdelta = " << average_slave_delta*average_dir << endl;
|
|
}
|
|
}
|
|
|
|
if (slave_speed != 0.0f) {
|
|
|
|
/* slave is running */
|
|
|
|
switch (slave_state) {
|
|
case Stopped:
|
|
if (_slave->requires_seekahead()) {
|
|
slave_wait_end = slave_transport_frame + _current_frame_rate;
|
|
locate (slave_wait_end, false, false);
|
|
slave_state = Waiting;
|
|
starting = true;
|
|
|
|
} else {
|
|
|
|
slave_state = Running;
|
|
|
|
Location* al = _locations.auto_loop_location();
|
|
|
|
if (al && play_loop && (slave_transport_frame < al->start() || slave_transport_frame > al->end())) {
|
|
// cancel looping
|
|
request_play_loop(false);
|
|
}
|
|
|
|
if (slave_transport_frame != _transport_frame) {
|
|
locate (slave_transport_frame, false, false);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case Waiting:
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
|
|
}
|
|
|
|
if (slave_state == Waiting) {
|
|
|
|
// cerr << "waiting at " << slave_transport_frame << endl;
|
|
if (slave_transport_frame >= slave_wait_end) {
|
|
// cerr << "\tstart at " << _transport_frame << endl;
|
|
|
|
slave_state = Running;
|
|
|
|
bool ok = true;
|
|
nframes_t frame_delta = slave_transport_frame - _transport_frame;
|
|
|
|
boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
|
|
|
|
for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
|
|
if (!(*i)->can_internal_playback_seek (frame_delta)) {
|
|
ok = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ok) {
|
|
for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
|
|
(*i)->internal_playback_seek (frame_delta);
|
|
}
|
|
_transport_frame += frame_delta;
|
|
|
|
} else {
|
|
// cerr << "cannot micro-seek\n";
|
|
/* XXX what? */
|
|
}
|
|
|
|
memset (delta_accumulator, 0, sizeof (nframes_t) * delta_accumulator_size);
|
|
average_slave_delta = 0;
|
|
this_delta = 0;
|
|
}
|
|
}
|
|
|
|
if (slave_state == Running && _transport_speed == 0.0f) {
|
|
|
|
// cerr << "slave starts transport\n";
|
|
|
|
start_transport ();
|
|
}
|
|
|
|
} else {
|
|
|
|
/* slave has stopped */
|
|
|
|
if (_transport_speed != 0.0f) {
|
|
|
|
// cerr << "slave stops transport: " << slave_speed << " frame: " << slave_transport_frame
|
|
// << " tf = " << _transport_frame
|
|
// << endl;
|
|
|
|
if (Config->get_slave_source() == JACK) {
|
|
last_stop_frame = _transport_frame;
|
|
}
|
|
|
|
stop_transport();
|
|
}
|
|
|
|
if (slave_transport_frame != _transport_frame) {
|
|
// cerr << "slave stopped, move to " << slave_transport_frame << endl;
|
|
force_locate (slave_transport_frame, false);
|
|
}
|
|
|
|
slave_state = Stopped;
|
|
}
|
|
|
|
if (slave_state == Running && !Config->get_timecode_source_is_synced()) {
|
|
|
|
|
|
if (_transport_speed != 0.0f) {
|
|
|
|
/*
|
|
note that average_dir is +1 or -1
|
|
*/
|
|
|
|
const float adjust_seconds = 1.0f;
|
|
float delta;
|
|
|
|
//if (average_slave_delta == 0) {
|
|
delta = this_delta;
|
|
delta *= dir;
|
|
// } else {
|
|
// delta = average_slave_delta;
|
|
// delta *= average_dir;
|
|
// }
|
|
|
|
float adjusted_speed = slave_speed +
|
|
(delta / (adjust_seconds * _current_frame_rate));
|
|
|
|
// cerr << "adjust using " << delta
|
|
// << " towards " << adjusted_speed
|
|
// << " ratio = " << adjusted_speed / slave_speed
|
|
// << " current = " << _transport_speed
|
|
// << " slave @ " << slave_speed
|
|
// << endl;
|
|
|
|
request_transport_speed (adjusted_speed);
|
|
|
|
#if 1
|
|
if ((nframes_t) average_slave_delta > _slave->resolution()) {
|
|
// cerr << "not locked\n";
|
|
goto silent_motion;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if (!starting && !non_realtime_work_pending()) {
|
|
/* speed is set, we're locked, and good to go */
|
|
return true;
|
|
}
|
|
|
|
silent_motion:
|
|
|
|
if (slave_speed && _transport_speed) {
|
|
|
|
/* something isn't right, but we should move with the master
|
|
for now.
|
|
*/
|
|
|
|
bool need_butler;
|
|
|
|
prepare_diskstreams ();
|
|
silent_process_routes (nframes, offset);
|
|
commit_diskstreams (nframes, need_butler);
|
|
|
|
if (need_butler) {
|
|
summon_butler ();
|
|
}
|
|
|
|
int32_t frames_moved = (int32_t) floor (_transport_speed * nframes);
|
|
|
|
if (frames_moved < 0) {
|
|
decrement_transport_position (-frames_moved);
|
|
} else {
|
|
increment_transport_position (frames_moved);
|
|
}
|
|
|
|
nframes_t stop_limit;
|
|
|
|
if (actively_recording()) {
|
|
stop_limit = max_frames;
|
|
} else {
|
|
if (Config->get_stop_at_session_end()) {
|
|
stop_limit = current_end_frame();
|
|
} else {
|
|
stop_limit = max_frames;
|
|
}
|
|
}
|
|
|
|
maybe_stop (stop_limit);
|
|
}
|
|
|
|
noroll:
|
|
/* don't move at all */
|
|
no_roll (nframes, 0);
|
|
return false;
|
|
}
|
|
|
|
void
|
|
Session::process_without_events (nframes_t nframes)
|
|
{
|
|
bool session_needs_butler = false;
|
|
nframes_t stop_limit;
|
|
long frames_moved;
|
|
nframes_t offset = 0;
|
|
|
|
{
|
|
if (post_transport_work & (PostTransportLocate|PostTransportStop)) {
|
|
no_roll (nframes, 0);
|
|
return;
|
|
}
|
|
|
|
if (!_exporting && _slave) {
|
|
if (!follow_slave (nframes, 0)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (_transport_speed == 0) {
|
|
no_roll (nframes, 0);
|
|
return;
|
|
}
|
|
|
|
send_midi_time_code_for_cycle(nframes);
|
|
|
|
if (actively_recording()) {
|
|
stop_limit = max_frames;
|
|
} else {
|
|
if (Config->get_stop_at_session_end()) {
|
|
stop_limit = current_end_frame();
|
|
} else {
|
|
stop_limit = max_frames;
|
|
}
|
|
}
|
|
|
|
if (maybe_stop (stop_limit)) {
|
|
no_roll (nframes, 0);
|
|
return;
|
|
}
|
|
|
|
if (maybe_sync_start (nframes, offset)) {
|
|
return;
|
|
}
|
|
|
|
click (_transport_frame, nframes, offset);
|
|
|
|
prepare_diskstreams ();
|
|
|
|
frames_moved = (long) floor (_transport_speed * nframes);
|
|
|
|
if (process_routes (nframes, offset)) {
|
|
no_roll (nframes, offset);
|
|
return;
|
|
}
|
|
|
|
commit_diskstreams (nframes, session_needs_butler);
|
|
|
|
if (frames_moved < 0) {
|
|
decrement_transport_position (-frames_moved);
|
|
} else {
|
|
increment_transport_position (frames_moved);
|
|
}
|
|
|
|
maybe_stop (stop_limit);
|
|
check_declick_out ();
|
|
|
|
} /* implicit release of route lock */
|
|
|
|
if (session_needs_butler)
|
|
summon_butler ();
|
|
}
|
|
|
|
/** Process callback used when the auditioner is active.
|
|
* @param nframes number of frames to process.
|
|
*/
|
|
void
|
|
Session::process_audition (nframes_t nframes)
|
|
{
|
|
Event* ev;
|
|
boost::shared_ptr<RouteList> r = routes.reader ();
|
|
|
|
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
|
|
if (!(*i)->is_hidden()) {
|
|
(*i)->silence (nframes, 0);
|
|
}
|
|
}
|
|
|
|
/* run the auditioner, and if it says we need butler service, ask for it */
|
|
|
|
if (auditioner->play_audition (nframes) > 0) {
|
|
summon_butler ();
|
|
}
|
|
|
|
/* handle pending events */
|
|
|
|
while (pending_events.read (&ev, 1) == 1) {
|
|
merge_event (ev);
|
|
}
|
|
|
|
/* if we are not in the middle of a state change,
|
|
and there are immediate events queued up,
|
|
process them.
|
|
*/
|
|
|
|
while (!non_realtime_work_pending() && !immediate_events.empty()) {
|
|
Event *ev = immediate_events.front ();
|
|
immediate_events.pop_front ();
|
|
process_event (ev);
|
|
}
|
|
|
|
if (!auditioner->active()) {
|
|
/* auditioner no longer active, so go back to the normal process callback */
|
|
process_function = &Session::process_with_events;
|
|
}
|
|
}
|
|
|
|
bool
|
|
Session::maybe_sync_start (nframes_t& nframes, nframes_t& offset)
|
|
{
|
|
nframes_t sync_offset;
|
|
|
|
if (!waiting_for_sync_offset) {
|
|
return false;
|
|
}
|
|
|
|
if (_engine.get_sync_offset (sync_offset) && sync_offset < nframes) {
|
|
|
|
no_roll (sync_offset, 0);
|
|
nframes -= sync_offset;
|
|
offset += sync_offset;
|
|
waiting_for_sync_offset = false;
|
|
|
|
if (nframes == 0) {
|
|
return true; // done
|
|
}
|
|
|
|
} else {
|
|
no_roll (nframes, 0);
|
|
return true; // done
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|