/* 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "i18n.h" using namespace ARDOUR; using namespace PBD; using namespace std; void Session::process (nframes_t 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); SendFeedback (); /* EMIT SIGNAL */ } void Session::prepare_diskstreams () { boost::shared_ptr 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; int ret = 0; bool declick = get_transport_declick_required(); boost::shared_ptr 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)->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 r = routes.reader (); if (transport_sub_state & StopPendingCapture) { /* force a declick out */ declick = -1; } record_active = actively_recording(); // || (get_record_enabled() && get_punch_in()); for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { int ret; if ((*i)->hidden()) { continue; } (*i)->set_pending_declick (declick); if ((ret = (*i)->roll (nframes, _transport_frame, _transport_frame + nframes, 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 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 r = routes.reader (); if (transport_sub_state & StopPendingCapture) { /* force a declick out */ declick = -1; } for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { int ret; if ((*i)->hidden()) { continue; } if ((ret = (*i)->silent_roll (nframes, _transport_frame, _transport_frame + nframes, 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 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 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(); } } 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); } 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; } 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; 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 (); } if (!_engine.freewheeling() && session_send_mtc) { send_midi_time_code_in_another_thread (); } return; } 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 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; } 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 (); } if (!_engine.freewheeling() && session_send_mtc) { send_midi_time_code_in_another_thread (); } return; } void Session::process_audition (nframes_t nframes) { Event* ev; boost::shared_ptr r = routes.reader (); for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { if (!(*i)->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()) { 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; }