mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-21 22:26:29 +01:00
All #include statements that include a header that is a part of a library bundled with ardour MUST use quotes, not angle brackets. Do this: #include "ardour/types.h" NOT this: #include <ardour/types.h> Rationale: This is best practice in general, to ensure we include the local version and not the system version. That quotes mean "local" (in some sense) and angle brackets mean "system" (in some sense) is a ubiquitous convention and IIRC right in the C spec somewhere. More pragmatically, this is required by (my) waf (stuff) for dependencies to work correctly. That is: !!! FAILURE TO DO THIS CAN RESULT IN BROKEN BUILDS !!! Failure to comply is punishable by death by torture. :) P.S. It's not that dramatic in all cases, but this (in combination with some GCC flags specific to the include type) is the best way I have found to be absolutely 100% positive the local ones are being used (and we definitely want to be absolutely 100% positive on that one). git-svn-id: svn://localhost/ardour2/branches/3.0@4655 d708f5d6-7413-0410-9779-e7cbd77b26cf
965 lines
23 KiB
C++
965 lines
23 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)
|
|
{
|
|
// This is no more the appropriate place to call cycle
|
|
// start. cycle_start needs to be called at the Route::roll()
|
|
// where the signals which we want to mixdown have been calculated.
|
|
//
|
|
MIDI::Manager::instance()->cycle_start(nframes);
|
|
|
|
_silent = false;
|
|
|
|
if (non_realtime_work_pending()) {
|
|
if (!transport_work_requested ()) {
|
|
post_transport ();
|
|
}
|
|
}
|
|
|
|
(this->*process_function) (nframes);
|
|
|
|
// the ticker is for sending time information like MidiClock
|
|
nframes_t transport_frames = transport_frame();
|
|
BBT_Time transport_bbt;
|
|
bbt_time(transport_frames, transport_bbt);
|
|
SMPTE::Time transport_smpte;
|
|
smpte_time(transport_frames, transport_smpte);
|
|
tick (transport_frames, transport_bbt, transport_smpte); /* EMIT SIGNAL */
|
|
|
|
SendFeedback (); /* EMIT SIGNAL */
|
|
|
|
MIDI::Manager::instance()->cycle_end();
|
|
}
|
|
|
|
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;
|
|
bool session_needs_butler = false;
|
|
nframes_t stop_limit;
|
|
long frames_moved;
|
|
|
|
/* 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()) {
|
|
_silent = true;
|
|
return;
|
|
}
|
|
|
|
if (events.empty() || next_event == events.end()) {
|
|
process_without_events (nframes);
|
|
return;
|
|
}
|
|
|
|
/// TODO: Figure out what happens to phi and phase, if transport speed momentarily becomes
|
|
/// 1.0 eg. during the adjustments of a slave. If that is a bug, then AudioDiskstream::process
|
|
/// is very likely broken too
|
|
if (_transport_speed == 1.0) {
|
|
frames_moved = (long) nframes;
|
|
} else {
|
|
frames_moved = (long) AudioDiskstream::
|
|
calculate_varispeed_playback_distance(nframes, phase, phi, target_phi);
|
|
}
|
|
|
|
end_frame = _transport_frame + (nframes_t)frames_moved;
|
|
|
|
{
|
|
Event* this_event;
|
|
Events::iterator the_next_one;
|
|
|
|
if (!process_can_proceed()) {
|
|
_silent = true;
|
|
return;
|
|
}
|
|
|
|
if (!_exporting && _slave) {
|
|
if (!follow_slave (nframes, 0)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (_transport_speed == 0) {
|
|
no_roll (nframes, 0);
|
|
return;
|
|
}
|
|
|
|
if (!_exporting) {
|
|
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 */
|
|
|
|
/* 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)
|
|
{
|
|
double 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 (_slave->is_always_synced() || 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.
|
|
*/
|
|
|
|
calculate_moving_average_of_slave_delta(dir, this_delta);
|
|
}
|
|
|
|
track_slave_state(slave_speed, slave_transport_frame, this_delta, starting);
|
|
|
|
if (slave_state == Running && !_slave->is_always_synced() && !Config->get_timecode_source_is_synced()) {
|
|
|
|
if (_transport_speed != 0.0f) {
|
|
|
|
/*
|
|
note that average_dir is +1 or -1
|
|
*/
|
|
|
|
float delta;
|
|
|
|
#ifdef USE_MOVING_AVERAGE_OF_SLAVE
|
|
if (average_slave_delta == 0) {
|
|
delta = this_delta;
|
|
delta *= dir;
|
|
} else {
|
|
delta = average_slave_delta;
|
|
delta *= average_dir;
|
|
}
|
|
#else
|
|
delta = this_delta;
|
|
delta *= dir;
|
|
#endif
|
|
|
|
float adjusted_speed = slave_speed + (delta / float(_current_frame_rate));
|
|
|
|
if (_slave->give_slave_full_control_over_transport_speed()) {
|
|
request_transport_speed(slave_speed);
|
|
} else {
|
|
request_transport_speed(adjusted_speed);
|
|
#ifdef DEBUG_SLAVES
|
|
cerr << "adjust using " << delta
|
|
<< " towards " << adjusted_speed
|
|
<< " ratio = " << adjusted_speed / slave_speed
|
|
<< " current = " << _transport_speed
|
|
<< " slave @ " << slave_speed
|
|
<< endl;
|
|
#endif
|
|
}
|
|
|
|
if (abs(average_slave_delta) > (long) _slave->resolution()) {
|
|
cerr << "average slave delta greater than slave resolution, going to silent motion\n";
|
|
goto silent_motion;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG_SLAVES
|
|
if (slave_speed != 0.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 (!starting && !non_realtime_work_pending()) {
|
|
/* speed is set, we're locked, and good to go */
|
|
return true;
|
|
}
|
|
|
|
silent_motion:
|
|
#ifdef DEBUG_SLAVES
|
|
cerr << "reached silent_motion:" <<endl;
|
|
#endif
|
|
|
|
follow_slave_silently(nframes, offset, slave_speed);
|
|
|
|
noroll:
|
|
/* don't move at all */
|
|
#ifdef DEBUG_SLAVES
|
|
cerr << "reached no_roll:" <<endl;
|
|
#endif
|
|
no_roll (nframes, 0);
|
|
return false;
|
|
}
|
|
|
|
void
|
|
Session::calculate_moving_average_of_slave_delta(int dir, nframes_t this_delta)
|
|
{
|
|
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++] = long(dir) * long(this_delta);
|
|
}
|
|
|
|
if (have_first_delta_accumulator) {
|
|
average_slave_delta = 0L;
|
|
for (int i = 0; i < delta_accumulator_size; ++i) {
|
|
average_slave_delta += delta_accumulator[i];
|
|
}
|
|
average_slave_delta /= long(delta_accumulator_size);
|
|
if (average_slave_delta < 0L) {
|
|
average_dir = -1;
|
|
average_slave_delta = abs(average_slave_delta);
|
|
} else {
|
|
average_dir = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
Session::track_slave_state(
|
|
float slave_speed,
|
|
nframes_t slave_transport_frame,
|
|
nframes_t this_delta,
|
|
bool starting)
|
|
{
|
|
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) {
|
|
|
|
#ifdef DEBUG_SLAVES
|
|
cerr << "waiting at " << slave_transport_frame << endl;
|
|
#endif
|
|
if (slave_transport_frame >= slave_wait_end) {
|
|
#ifdef DEBUG_SLAVES
|
|
cerr << "\tstart at " << _transport_frame << endl;
|
|
#endif
|
|
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 (long) * delta_accumulator_size);
|
|
average_slave_delta = 0L;
|
|
this_delta = 0;
|
|
}
|
|
}
|
|
|
|
if (slave_state == Running && _transport_speed == 0.0f) {
|
|
|
|
#ifdef DEBUG_SLAVES
|
|
cerr << "slave starts transport\n";
|
|
#endif
|
|
start_transport ();
|
|
}
|
|
|
|
} else { // slave_speed is 0
|
|
|
|
/* slave has stopped */
|
|
|
|
if (_transport_speed != 0.0f) {
|
|
|
|
#ifdef DEBUG_SLAVES
|
|
cerr << "slave stops transport: " << slave_speed << " frame: " << slave_transport_frame
|
|
<< " tf = " << _transport_frame << endl;
|
|
#endif
|
|
|
|
stop_transport();
|
|
}
|
|
|
|
if (slave_transport_frame != _transport_frame) {
|
|
#ifdef DEBUG_SLAVES
|
|
cerr << "slave stopped, move to " << slave_transport_frame << endl;
|
|
#endif
|
|
force_locate (slave_transport_frame, false);
|
|
}
|
|
|
|
slave_state = Stopped;
|
|
}
|
|
}
|
|
|
|
void
|
|
Session::follow_slave_silently(nframes_t nframes, nframes_t offset, float slave_speed)
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
|
|
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 (!process_can_proceed()) {
|
|
_silent = true;
|
|
return;
|
|
}
|
|
|
|
if (!_exporting && _slave) {
|
|
if (!follow_slave (nframes, 0)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (_transport_speed == 0) {
|
|
no_roll (nframes, 0);
|
|
return;
|
|
}
|
|
|
|
if (!_exporting) {
|
|
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 ();
|
|
|
|
/// TODO: Figure out what happens to phi and phase, if transport speed momentarily becomes
|
|
/// 1.0 eg. during the adjustments of a slave. If that is a bug, then AudioDiskstream::process
|
|
/// is very likely broken too
|
|
if (_transport_speed == 1.0) {
|
|
frames_moved = (long) nframes;
|
|
} else {
|
|
frames_moved = (long) AudioDiskstream::
|
|
calculate_varispeed_playback_distance(nframes, phase, phi, target_phi);
|
|
}
|
|
|
|
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 ();
|
|
|
|
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) {
|
|
|
|
/* generate silence up to the sync point, then
|
|
adjust nframes + offset to reflect whatever
|
|
is left to do.
|
|
*/
|
|
|
|
no_roll (sync_offset, 0);
|
|
nframes -= sync_offset;
|
|
offset += sync_offset;
|
|
waiting_for_sync_offset = false;
|
|
|
|
if (nframes == 0) {
|
|
return true; // done, nothing left to process
|
|
}
|
|
|
|
} else {
|
|
|
|
/* sync offset point is not within this process()
|
|
cycle, so just generate silence. and don't bother
|
|
with any fancy stuff here, just the minimal silence.
|
|
*/
|
|
|
|
g_atomic_int_inc (&processing_prohibited);
|
|
no_roll (nframes, 0);
|
|
g_atomic_int_dec_and_test (&processing_prohibited);
|
|
|
|
if (Config->get_locate_while_waiting_for_sync()) {
|
|
if (micro_locate (nframes)) {
|
|
/* XXX ERROR !!! XXX */
|
|
}
|
|
}
|
|
|
|
return true; // done, nothing left to process
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|