X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fsession_transport.cc;h=b29816be1b63ff2ee1a7733e1a4cc80d2bfd20e2;hb=97c0cdd252b2a2852ca28bcf5fbcbe11159c5dad;hp=cf450bce8f71cb0cc4a8b39c58c703a4235ec5d1;hpb=4f460078b8cbfcb1f2d5bb7a3aa3fe5ab8f0bebb;p=ardour.git diff --git a/libs/ardour/session_transport.cc b/libs/ardour/session_transport.cc index cf450bce8f..b29816be1b 100644 --- a/libs/ardour/session_transport.cc +++ b/libs/ardour/session_transport.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 1999-2003 Paul Davis + Copyright (C) 1999-2009 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 @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -54,7 +55,7 @@ Session::request_input_change_handling () if (!(_state_of_the_state & (InitialConnecting|Deletion))) { Event* ev = new Event (Event::InputConfigurationChange, Event::Add, Event::Immediate, 0, 0.0); queue_event (ev); - } + } } void @@ -122,7 +123,7 @@ Session::request_play_loop (bool yn) ev = new Event (Event::SetLoop, Event::Add, Event::Immediate, 0, 0.0, yn); queue_event (ev); - + if (!yn && Config->get_seamless_loop() && transport_rolling()) { // request an immediate locate to refresh the diskstreams // after disabling looping @@ -175,7 +176,7 @@ Session::realtime_stop (bool abort) waiting_for_sync_offset = true; } - transport_sub_state = (Config->get_auto_return() ? AutoReturning : 0); + transport_sub_state = ((Config->get_slave_source() == None && Config->get_auto_return()) ? AutoReturning : 0); } void @@ -206,8 +207,7 @@ Session::butler_transport_work () } if (post_transport_work & PostTransportReverse) { - - + clear_clicks(); cumulative_rf_motion = 0; reset_rf_scale (0); @@ -371,17 +371,50 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished) update_latency_compensation (true, abort); } - if (Config->get_auto_return() || (post_transport_work & PostTransportLocate) || synced_to_jack()) { + if ((Config->get_slave_source() == None && Config->get_auto_return()) || + (post_transport_work & PostTransportLocate) || + (_requested_return_frame >= 0) || + synced_to_jack()) { if (pending_locate_flush) { flush_all_redirects (); } + + if (((Config->get_slave_source() == None && Config->get_auto_return()) || + synced_to_jack() || + _requested_return_frame >= 0) && + !(post_transport_work & PostTransportLocate)) { + + /* no explicit locate queued */ - if ((Config->get_auto_return() || synced_to_jack()) && !(post_transport_work & PostTransportLocate)) { + bool do_locate = false; - _transport_frame = last_stop_frame; + if (_requested_return_frame >= 0) { + _transport_frame = _requested_return_frame; + _requested_return_frame = -1; + do_locate = true; + } else { + if (play_loop && !synced_to_jack()) { + Location *location = _locations.auto_loop_location(); + + if (location != 0) { + _transport_frame = location->start(); + do_locate = true; + } else { + _transport_frame = last_stop_frame; + } + } else { + _transport_frame = last_stop_frame; + } + _requested_return_frame = -1; + } - if (synced_to_jack()) { + if (synced_to_jack() && !play_loop) { + do_locate = true; + } + + if (do_locate) { + // cerr << "non-realtimestop: transport locate to " << _transport_frame << endl; _engine.transport_locate (_transport_frame); } } @@ -409,9 +442,16 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished) } #endif - last_stop_frame = _transport_frame; + if (_requested_return_frame < 0) { + last_stop_frame = _transport_frame; + } else { + last_stop_frame = _requested_return_frame; + _requested_return_frame = -1; + } - send_full_time_code (); + have_looped = false; + + send_full_time_code (); deliver_mmc (MIDI::MachineControl::cmdStop, 0); deliver_mmc (MIDI::MachineControl::cmdLocate, _transport_frame); @@ -420,14 +460,16 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished) /* XXX its a little odd that we're doing this here when realtime_stop(), which has already executed, will have done this. + JLC - so let's not because it seems unnecessary and breaks loop record */ - +#if 0 if (!Config->get_latched_record_enable()) { g_atomic_int_set (&_record_status, Disabled); } else { g_atomic_int_set (&_record_status, Enabled); } RecordStateChanged (); /* emit signal */ +#endif } if ((post_transport_work & PostTransportLocate) && get_record_enabled()) { @@ -452,12 +494,12 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished) if (post_transport_work & PostTransportStop) { _play_range = false; - - /* do not turn off autoloop on stop */ - + play_loop = false; } - PositionChanged (_transport_frame); /* EMIT SIGNAL */ + nframes_t tf = _transport_frame; + + PositionChanged (tf); /* EMIT SIGNAL */ TransportStateChange (); /* EMIT SIGNAL */ /* and start it up again if relevant */ @@ -497,7 +539,9 @@ Session::set_play_loop (bool yn) { /* Called from event-handling context */ - if ((actively_recording() && yn) || _locations.auto_loop_location() == 0) { + Location *loc; + + if ((actively_recording() && yn) || (loc = _locations.auto_loop_location()) == 0) { return; } @@ -513,10 +557,8 @@ Session::set_play_loop (bool yn) if ((play_loop = yn)) { - Location *loc; - - if ((loc = _locations.auto_loop_location()) != 0) { + if (loc) { if (Config->get_seamless_loop()) { // set all diskstreams to use internal looping @@ -537,25 +579,17 @@ Session::set_play_loop (bool yn) } } - /* stick in the loop event */ + /* put the loop event into the event list */ Event* event = new Event (Event::AutoLoop, Event::Replace, loc->end(), loc->start(), 0.0f); merge_event (event); - /* locate to start of loop and roll if current pos is outside of the loop range */ - if (_transport_frame < loc->start() || _transport_frame > loc->end()) { - event = new Event (Event::LocateRoll, Event::Add, Event::Immediate, loc->start(), 0, !synced_to_jack()); - merge_event (event); - } - else { - // locate to current position (+ 1 to force reload) - event = new Event (Event::LocateRoll, Event::Add, Event::Immediate, _transport_frame + 1, 0, !synced_to_jack()); - merge_event (event); - } + /* locate to start of loop and roll */ + event = new Event (Event::LocateRoll, Event::Add, Event::Immediate, loc->start(), 0, !synced_to_jack()); + merge_event (event); } - } else { clear_events (Event::AutoLoop); @@ -610,10 +644,29 @@ Session::start_locate (nframes_t target_frame, bool with_roll, bool with_flush, } } +int +Session::micro_locate (nframes_t distance) +{ + boost::shared_ptr dsl = diskstreams.reader(); + + for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { + if (!(*i)->can_internal_playback_seek (distance)) { + return -1; + } + } + + for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { + (*i)->internal_playback_seek (distance); + } + + _transport_frame += distance; + return 0; +} + void Session::locate (nframes_t target_frame, bool with_roll, bool with_flush, bool with_loop) { - if (actively_recording()) { + if (actively_recording() && !with_loop) { return; } @@ -639,12 +692,12 @@ Session::locate (nframes_t target_frame, bool with_roll, bool with_flush, bool w } } - if (transport_rolling() && !Config->get_auto_play() && !with_roll && !(synced_to_jack() && play_loop)) { + if (transport_rolling() && (!auto_play_legal || !Config->get_auto_play()) && !with_roll && !(synced_to_jack() && play_loop)) { realtime_stop (false); } if ( !with_loop || loop_changing) { - + post_transport_work = PostTransportWork (post_transport_work | PostTransportLocate); if (with_roll) { @@ -704,9 +757,27 @@ Session::locate (nframes_t target_frame, bool with_roll, bool with_flush, bool w // cancel looping directly, this is called from event handling context set_play_loop (false); } + else if (al && _transport_frame == al->start()) { + if (with_loop) { + // this is only necessary for seamless looping + + boost::shared_ptr dsl = diskstreams.reader(); + + for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { + if ((*i)->record_enabled ()) { + // tell it we've looped, so it can deal with the record state + (*i)->transport_looped(_transport_frame); + } + } + } + have_looped = true; + TransportLooped(); // EMIT SIGNAL + } } loop_changing = false; + + _send_smpte_update = true; } void @@ -798,6 +869,7 @@ Session::set_transport_speed (float speed, bool abort) if ((_transport_speed && speed * _transport_speed < 0.0f) || (_last_transport_speed * speed < 0.0f) || (_last_transport_speed == 0.0f && speed < 0.0f)) { post_transport_work = PostTransportWork (post_transport_work | PostTransportReverse); + last_stop_frame = _transport_frame; } _last_transport_speed = _transport_speed; @@ -861,6 +933,7 @@ void Session::start_transport () { _last_roll_location = _transport_frame; + have_looped = false; /* if record status is Enabled, move it to Recording. if its already Recording, move it to Disabled. @@ -874,25 +947,15 @@ Session::start_transport () break; case Recording: - disable_record (false); + if (!play_loop) { + disable_record (false); + } break; default: break; } - if (!synced_to_jack() || _exporting) { - actually_start_transport (); - } else { - waiting_to_start = true; - } -} - -void -Session::actually_start_transport () -{ - waiting_to_start = false; - transport_sub_state |= PendingDeclickIn; _transport_speed = 1.0; @@ -924,7 +987,7 @@ Session::post_transport () if (post_transport_work & PostTransportLocate) { - if (((Config->get_slave_source() == None && Config->get_auto_play()) && !_exporting) || (post_transport_work & PostTransportRoll)) { + if (((Config->get_slave_source() == None && (auto_play_legal && Config->get_auto_play())) && !_exporting) || (post_transport_work & PostTransportRoll)) { start_transport (); } else { @@ -958,7 +1021,7 @@ Session::reset_rf_scale (nframes_t motion) } void -Session::set_slave_source (SlaveSource src) +Session::set_slave_source (SlaveSource src, bool stop_the_transport) { bool reverse = false; bool non_rt_required = false; @@ -983,7 +1046,9 @@ Session::set_slave_source (SlaveSource src) switch (src) { case None: - stop_transport (); + if (stop_the_transport) { + stop_transport (); + } break; case MTC: @@ -1143,6 +1208,14 @@ Session::setup_auto_play () merge_event (ev); } +void +Session::request_roll_at_and_return (nframes_t start, nframes_t return_to) +{ + Event *ev = new Event (Event::LocateRollLocate, Event::Add, Event::Immediate, return_to, 1.0); + ev->target2_frame = start; + queue_event (ev); +} + void Session::request_bounded_roll (nframes_t start, nframes_t end) { @@ -1167,11 +1240,16 @@ Session::engine_halted () g_atomic_int_set (&butler_should_do_transport_work, 0); post_transport_work = PostTransportWork (0); stop_butler (); - + realtime_stop (false); non_realtime_stop (false, 0, ignored); transport_sub_state = 0; + if (synced_to_jack()) { + /* transport is already stopped, hence the second argument */ + set_slave_source (None, false); + } + TransportStateChange (); /* EMIT SIGNAL */ } @@ -1179,9 +1257,9 @@ Session::engine_halted () void Session::xrun_recovery () { - if (Config->get_stop_recording_on_xrun() && actively_recording()) { + Xrun (transport_frame()); //EMIT SIGNAL - HaltOnXrun (); /* EMIT SIGNAL */ + if (Config->get_stop_recording_on_xrun() && actively_recording()) { /* it didn't actually halt, but we need to handle things in the same way. @@ -1202,6 +1280,11 @@ Session::update_latency_compensation (bool with_stop, bool abort) _worst_track_latency = 0; +#undef DEBUG_LATENCY +#ifdef DEBUG_LATENCY + cerr << "\n---------------------------------\nUPDATE LATENCY\n"; +#endif + boost::shared_ptr r = routes.reader (); for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { @@ -1222,6 +1305,10 @@ Session::update_latency_compensation (bool with_stop, bool abort) } } +#ifdef DEBUG_LATENCY + cerr << "\tworst was " << _worst_track_latency << endl; +#endif + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { (*i)->set_latency_delay (_worst_track_latency); } @@ -1249,3 +1336,19 @@ Session::update_latency_compensation_proxy (void* ignored) { update_latency_compensation (false, false); } + +void +Session::allow_auto_play (bool yn) +{ + auto_play_legal = yn; +} + +void +Session::reset_jack_connection (jack_client_t* jack) +{ + JACK_Slave* js; + + if (_slave && ((js = dynamic_cast (_slave)) != 0)) { + js->reset_client (jack); + } +}