/*
- Copyright (C) 1999-2003 Paul Davis
+ Copyright (C) 1999-2003 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
void
Session::request_play_loop (bool yn)
{
- Event* ev;
+ Event* ev;
Location *location = _locations.auto_loop_location();
if (location == 0 && yn) {
Session::realtime_stop (bool abort)
{
/* assume that when we start, we'll be moving forwards */
-
+
// FIXME: where should this really be? [DR]
//send_full_time_code();
- deliver_mmc (MIDI::MachineControl::cmdStop, _transport_frame);
+ deliver_mmc (MIDI::MachineControl::cmdStop, 0);
deliver_mmc (MIDI::MachineControl::cmdLocate, _transport_frame);
if (_transport_speed < 0.0f) {
disable_record (true);
reset_slave_state ();
-
+
_transport_speed = 0;
if (Config->get_use_video_sync()) {
/* don't seek if locate will take care of that in non_realtime_stop() */
if (!(post_transport_work & PostTransportLocate)) {
-
+
for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
if (!(*i)->hidden()) {
- if ((*i)->speed() != 1.0f || (*i)->speed() != -1.0f) {
- (*i)->seek ((nframes_t) (_transport_frame * (double) (*i)->speed()));
- }
- else {
- (*i)->seek (_transport_frame);
- }
+ (*i)->non_realtime_locate (_transport_frame);
}
if (on_entry != g_atomic_int_get (&butler_should_do_transport_work)) {
/* new request, stop seeking, and start again */
}
}
}
-
+
if (post_transport_work & PostTransportLocate) {
non_realtime_locate ();
}
if (post_transport_work & PostTransportAudition) {
non_realtime_set_audition ();
}
-
+
g_atomic_int_dec_and_test (&butler_should_do_transport_work);
}
}
}
-
+
void
Session::non_realtime_locate ()
{
saved = false;
boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
-
+
for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
if ((*i)->get_captured_frames () != 0) {
did_record = true;
}
/* stop and locate are merged here because they share a lot of common stuff */
-
+
time (&xnow);
now = localtime (&xnow);
if (did_record) {
begin_reversible_command ("capture");
-
+
Location* loc = _locations.end_location();
bool change_end = false;
-
+
if (_transport_frame < loc->end()) {
/* stopped recording before current end */
/* first capture for this session, move end back to where we are */
change_end = true;
- }
+ }
} else if (_transport_frame > loc->end()) {
-
+
/* stopped recording after the current end, extend it */
change_end = true;
}
-
+
if (change_end) {
XMLNode &before = loc->get_state();
loc->set_end(_transport_frame);
for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
(*i)->transport_stopped (*now, xnow, abort);
}
-
+
boost::shared_ptr<RouteList> r = routes.reader ();
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
(*i)->set_pending_declick (0);
}
}
-
+
if (did_record) {
commit_reversible_command ();
- }
-
+ }
+
if (_engine.running()) {
update_latency_compensation (true, abort);
}
- if ((Config->get_slave_source() == None && Config->get_auto_return()) ||
- (post_transport_work & PostTransportLocate) ||
+ 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_inserts ();
}
-
- if (((Config->get_slave_source() == None && Config->get_auto_return()) ||
+
+ if (((Config->get_slave_source() == None && Config->get_auto_return()) ||
synced_to_jack() ||
_requested_return_frame >= 0) &&
!(post_transport_work & PostTransportLocate)) {
do_locate = true;
} else {
_transport_frame = last_stop_frame;
+ _requested_return_frame = -1;
}
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);
}
- }
+ }
#ifndef LEAVE_TRANSPORT_UNADJUSTED
}
for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
if (!(*i)->hidden()) {
- if ((*i)->speed() != 1.0f || (*i)->speed() != -1.0f) {
- (*i)->seek ((nframes_t) (_transport_frame * (double) (*i)->speed()));
- }
- else {
- (*i)->seek (_transport_frame);
- }
+ (*i)->non_realtime_locate (_transport_frame);
}
if (on_entry != g_atomic_int_get (&butler_should_do_transport_work)) {
finished = false;
}
#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;
+ }
if (did_record) {
RecordStateChanged (); /* emit signal */
#endif
}
-
+
if ((post_transport_work & PostTransportLocate) && get_record_enabled()) {
/* capture start has been changed, so save pending state */
save_state ("", true);
/* always try to get rid of this */
remove_pending_capture_state ();
-
+
/* save the current state of things if appropriate */
if (did_record && !saved) {
DurationChanged (); /* EMIT SIGNAL */
}
- if (post_transport_work & PostTransportStop) {
+ if (post_transport_work & PostTransportStop) {
_play_range = false;
/* do not turn off autoloop on stop */
-
+
}
nframes_t tf = _transport_frame;
/* this is called after a process() iteration. if PendingDeclickOut was set,
it means that we were waiting to declick the output (which has just been
done) before doing something else. this is where we do that "something else".
-
+
note: called from the audio thread.
*/
Session::set_play_loop (bool yn)
{
/* Called from event-handling context */
-
+
if ((actively_recording() && yn) || _locations.auto_loop_location() == 0) {
return;
}
-
+
set_dirty();
if (yn && Config->get_seamless_loop() && synced_to_jack()) {
return;
}
-
+
if ((play_loop = yn)) {
Location *loc;
-
+
if ((loc = _locations.auto_loop_location()) != 0) {
if (Config->get_seamless_loop()) {
}
}
}
-
+
/* stick in the loop event */
-
+
Event* event = new Event (Event::AutoLoop, Event::Replace, loc->end(), loc->start(), 0.0f);
merge_event (event);
(*i)->set_loop (0);
}
}
-
+
}
}
}
}
+int
+Session::micro_locate (nframes_t distance)
+{
+ boost::shared_ptr<DiskstreamList> 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)
{
pending_locate_roll = with_roll;
pending_locate_flush = with_flush;
return;
- }
+ }
}
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) {
post_transport_work = PostTransportWork (post_transport_work | PostTransportRoll);
}
/* this is functionally what clear_clicks() does but with a tentative lock */
Glib::RWLock::WriterLock clickm (click_lock, Glib::TRY_LOCK);
-
+
if (clickm.locked()) {
-
+
for (Clicks::iterator i = clicks.begin(); i != clicks.end(); ++i) {
delete *i;
}
-
+
clicks.clear ();
}
}
/* cancel looped playback if transport pos outside of loop range */
if (play_loop) {
Location* al = _locations.auto_loop_location();
-
+
if (al && (_transport_frame < al->start() || _transport_frame > al->end())) {
// cancel looping directly, this is called from event handling context
set_play_loop (false);
// this is only necessary for seamless looping
boost::shared_ptr<DiskstreamList> 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
TransportLooped(); // EMIT SIGNAL
}
}
-
+
loop_changing = false;
_send_smpte_update = true;
for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
if ((*i)->record_enabled ()) {
//cerr << "switching to input" << __FILE__ << __LINE__ << endl << endl;
- (*i)->monitor_input (true);
+ (*i)->monitor_input (true);
}
}
}
} else {
stop_transport (abort);
}
-
+
} else if (transport_stopped() && speed == 1.0) {
/* we are stopped and we want to start rolling at speed 1 */
for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
if (Config->get_auto_input() && (*i)->record_enabled ()) {
//cerr << "switching from input" << __FILE__ << __LINE__ << endl << endl;
- (*i)->monitor_input (false);
+ (*i)->monitor_input (false);
}
}
}
if (speed < 0.0f && _transport_frame == 0) {
return;
}
-
+
clear_clicks ();
/* if we are reversing relative to the current speed, or relative to the speed
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_transport_speed = _transport_speed;
_transport_speed = speed;
-
+
boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
if ((*i)->realtime_set_speed ((*i)->speed(), true)) {
post_transport_work = PostTransportWork (post_transport_work | PostTransportSpeed);
}
}
-
+
if (post_transport_work & (PostTransportSpeed|PostTransportReverse)) {
schedule_butler_transport_work ();
}
if (_transport_speed == 0.0f) {
return;
}
-
- if (actively_recording() && !(transport_sub_state & StopPendingCapture) &&
- _worst_output_latency > current_block_size)
+
+ if (actively_recording() && !(transport_sub_state & StopPendingCapture) &&
+ _worst_output_latency > current_block_size)
{
-
+
/* we need to capture the audio that has still not yet been received by the system
at the time the stop is requested, so we have to roll past that time.
block before the actual end. we'll declick in the subsequent block,
and then we'll really be stopped.
*/
-
- Event *ev = new Event (Event::StopOnce, Event::Replace,
+
+ Event *ev = new Event (Event::StopOnce, Event::Replace,
_transport_frame + _worst_output_latency - current_block_size,
0, 0, abort);
-
+
merge_event (ev);
transport_sub_state |= StopPendingCapture;
pending_abort = abort;
return;
- }
+ }
if ((transport_sub_state & PendingDeclickOut) == 0) {
_last_roll_location = _transport_frame;
/* if record status is Enabled, move it to Recording. if its
- already Recording, move it to Disabled.
+ already Recording, move it to Disabled.
*/
switch (record_status()) {
transport_sub_state |= PendingDeclickIn;
_transport_speed = 1.0;
-
+
boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
(*i)->realtime_set_speed ((*i)->speed(), true);
if (((Config->get_slave_source() == None && (auto_play_legal && Config->get_auto_play())) && !_exporting) || (post_transport_work & PostTransportRoll)) {
start_transport ();
-
+
} else {
transport_sub_state = 0;
}
// if (src == JACK && Config->get_jack_time_master()) {
// return;
// }
-
+
if (_slave) {
delete _slave;
_slave = 0;
case None:
stop_transport ();
break;
-
+
case MTC:
if (_mtc_port) {
try {
}
_desired_transport_speed = _transport_speed;
break;
-
+
+ case MIDIClock:
+ if (_midi_clock_port) {
+ try {
+ _slave = new MIDIClock_Slave (*this, *_midi_clock_port, 24);
+ }
+
+ catch (failed_constructor& err) {
+ return;
+ }
+
+ } else {
+ error << _("No MIDI Clock port defined: MIDI Clock slaving is impossible.") << endmsg;
+ return;
+ }
+ _desired_transport_speed = _transport_speed;
+ break;
+
case JACK:
_slave = new JACK_Slave (_engine.jack());
_desired_transport_speed = _transport_speed;
break;
+
};
Config->set_slave_source (src);
-
+
boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
if (!(*i)->hidden()) {
/* Called from event-processing context */
Event* ev;
-
+
_clear_event_type (Event::RangeStop);
_clear_event_type (Event::RangeLocate);
}
list<AudioRange>::size_type sz = current_audio_range.size();
-
+
if (sz > 1) {
-
- list<AudioRange>::iterator i = current_audio_range.begin();
+
+ list<AudioRange>::iterator i = current_audio_range.begin();
list<AudioRange>::iterator next;
-
+
while (i != current_audio_range.end()) {
-
+
next = i;
++next;
-
+
/* locating/stopping is subject to delays for declicking.
*/
-
+
nframes_t requested_frame = (*i).end;
-
+
if (requested_frame > current_block_size) {
requested_frame -= current_block_size;
} else {
requested_frame = 0;
}
-
+
if (next == current_audio_range.end()) {
ev = new Event (Event::RangeStop, Event::Add, requested_frame, 0, 0.0f);
} else {
ev = new Event (Event::RangeLocate, Event::Add, requested_frame, (*next).start, 0.0f);
}
-
+
merge_event (ev);
-
+
i = next;
}
-
+
} else if (sz == 1) {
-
+
ev = new Event (Event::RangeStop, Event::Add, current_audio_range.front().end, 0, 0.0f);
merge_event (ev);
-
- }
+
+ }
/* now start rolling at the right place */
-
+
ev = new Event (Event::LocateRoll, Event::Add, Event::Immediate, current_audio_range.front().start, 0.0f, false);
merge_event (ev);
}
void
Session::request_roll_at_and_return (nframes_t start, nframes_t return_to)
{
- request_locate (start, false);
Event *ev = new Event (Event::LocateRollLocate, Event::Add, Event::Immediate, return_to, 1.0);
+ ev->target2_frame = start;
queue_event (ev);
}
/* there will be no more calls to process(), so
we'd better clean up for ourselves, right now.
- but first, make sure the butler is out of
+ but first, make sure the butler is out of
the picture.
*/
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;
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.
*/
engine_halted();
- }
+ }
}
void
_worst_track_latency = 0;
+#undef DEBUG_LATENCY
+#ifdef DEBUG_LATENCY
+ cerr << "\n---------------------------------\nUPDATE LATENCY\n";
+#endif
+
boost::shared_ptr<RouteList> r = routes.reader ();
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-
+
if (with_stop) {
- (*i)->handle_transport_stopped (abort, (post_transport_work & PostTransportLocate),
+ (*i)->handle_transport_stopped (abort, (post_transport_work & PostTransportLocate),
(!(post_transport_work & PostTransportLocate) || pending_locate_flush));
}
-
+
nframes_t old_latency = (*i)->signal_latency ();
nframes_t track_latency = (*i)->update_total_latency ();
-
+
if (old_latency != track_latency) {
(*i)->update_port_total_latencies ();
update_jack = true;
if (!(*i)->is_hidden() && ((*i)->active())) {
_worst_track_latency = max (_worst_track_latency, track_latency);
}
- }
+ }
if (update_jack) {
_engine.update_total_latencies ();
}
+#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);
}
/* reflect any changes in latencies into capture offsets
*/
-
+
boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {