X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fdiskstream.cc;h=f2da7982fe5e002e8748f1933e2e9c344f58eb1f;hb=551e20b926e164b3579d5e6b39f5e2fcb1ced93e;hp=f9f4424c483ee33bfcb173d66c0f152f381f8d4b;hpb=c7b867bddbcc63f712fdba91e526733ffc28387d;p=ardour.git diff --git a/libs/ardour/diskstream.cc b/libs/ardour/diskstream.cc index f9f4424c48..f2da7982fe 100644 --- a/libs/ardour/diskstream.cc +++ b/libs/ardour/diskstream.cc @@ -29,30 +29,23 @@ #include #include #include -#include +#include #include "pbd/error.h" #include "pbd/basename.h" -#include -#include "pbd/xml++.h" #include "pbd/memento_command.h" +#include "pbd/xml++.h" +#include "pbd/stacktrace.h" -#include "ardour/ardour.h" -#include "ardour/audioengine.h" #include "ardour/debug.h" #include "ardour/diskstream.h" -#include "ardour/utils.h" -#include "ardour/configuration.h" -#include "ardour/audiofilesource.h" -#include "ardour/send.h" +#include "ardour/io.h" +#include "ardour/pannable.h" +#include "ardour/profile.h" #include "ardour/playlist.h" -#include "ardour/cycle_timer.h" -#include "ardour/region.h" -#include "ardour/panner.h" #include "ardour/session.h" -#include "ardour/io.h" -#include "ardour/route.h" +#include "ardour/track.h" #include "i18n.h" #include @@ -61,12 +54,8 @@ using namespace std; using namespace ARDOUR; using namespace PBD; -/* XXX This goes uninitialized when there is no ~/.config/ardour3 directory. - * I can't figure out why, so this will do for now (just stole the - * default from configuration_vars.h). 0 is not a good value for - * allocating buffer sizes.. - */ -ARDOUR::nframes_t Diskstream::disk_io_chunk_frames = 1024 * 256; +ARDOUR::framecnt_t Diskstream::disk_read_chunk_frames = default_disk_read_chunk_frames (); +ARDOUR::framecnt_t Diskstream::disk_write_chunk_frames = default_disk_write_chunk_frames (); PBD::Signal0 Diskstream::DiskOverrun; PBD::Signal0 Diskstream::DiskUnderrun; @@ -74,95 +63,78 @@ PBD::Signal0 Diskstream::DiskUnderrun; Diskstream::Diskstream (Session &sess, const string &name, Flag flag) : SessionObject(sess, name) , i_am_the_modifier (0) - , _route (0) + , _track (0) , _record_enabled (0) + , _record_safe (0) , _visible_speed (1.0f) , _actual_speed (1.0f) , _buffer_reallocation_required (false) , _seek_required (false) - , force_refill (false) , capture_start_frame (0) , capture_captured (0) , was_recording (false) , adjust_capture_position (0) , _capture_offset (0) , _roll_delay (0) - , first_recordable_frame (max_frames) - , last_recordable_frame (max_frames) + , first_recordable_frame (max_framepos) + , last_recordable_frame (max_framepos) , last_possibly_recording (0) , _alignment_style (ExistingMaterial) - , _scrubbing (false) + , _alignment_choice (Automatic) , _slaved (false) , loop_location (0) , overwrite_frame (0) , overwrite_offset (0) - , pending_overwrite (false) + , _pending_overwrite (false) , overwrite_queued (false) - , input_change_pending (NoChange) , wrap_buffer_size (0) , speed_buffer_size (0) , _speed (1.0) , _target_speed (_speed) , file_frame (0) , playback_sample (0) - , playback_distance (0) - , _read_data_count (0) - , _write_data_count (0) , in_set_state (false) - , _persistent_alignment_style (ExistingMaterial) - , first_input_change (true) - , scrub_start (0) - , scrub_buffer_size (0) - , scrub_offset (0) , _flags (flag) - + , deprecated_io_node (0) { } Diskstream::Diskstream (Session& sess, const XMLNode& /*node*/) : SessionObject(sess, "unnamed diskstream") , i_am_the_modifier (0) - , _route (0) + , _track (0) , _record_enabled (0) + , _record_safe (0) , _visible_speed (1.0f) , _actual_speed (1.0f) , _buffer_reallocation_required (false) , _seek_required (false) - , force_refill (false) , capture_start_frame (0) , capture_captured (0) , was_recording (false) , adjust_capture_position (0) , _capture_offset (0) , _roll_delay (0) - , first_recordable_frame (max_frames) - , last_recordable_frame (max_frames) + , first_recordable_frame (max_framepos) + , last_recordable_frame (max_framepos) , last_possibly_recording (0) , _alignment_style (ExistingMaterial) - , _scrubbing (false) + , _alignment_choice (Automatic) , _slaved (false) , loop_location (0) , overwrite_frame (0) , overwrite_offset (0) - , pending_overwrite (false) + , _pending_overwrite (false) , overwrite_queued (false) - , input_change_pending (NoChange) , wrap_buffer_size (0) , speed_buffer_size (0) , _speed (1.0) , _target_speed (_speed) , file_frame (0) , playback_sample (0) - , playback_distance (0) - , _read_data_count (0) - , _write_data_count (0) , in_set_state (false) - , _persistent_alignment_style (ExistingMaterial) - , first_input_change (true) - , scrub_start (0) - , scrub_buffer_size (0) - , scrub_offset (0) , _flags (Recordable) + , deprecated_io_node (0) { } @@ -173,33 +145,45 @@ Diskstream::~Diskstream () if (_playlist) { _playlist->release (); } + + delete deprecated_io_node; } void -Diskstream::set_route (Route& r) +Diskstream::set_track (Track* t) { - _route = &r; - _io = _route->input(); + _track = t; + _io = _track->input(); ic_connection.disconnect(); _io->changed.connect_same_thread (ic_connection, boost::bind (&Diskstream::handle_input_change, this, _1, _2)); - input_change_pending = ConfigurationChanged; - non_realtime_input_change (); - set_align_style_from_io (); + if (_io->n_ports() != ChanCount::ZERO) { + input_change_pending.type = IOChange::Type (IOChange::ConfigurationChanged|IOChange::ConnectionsChanged); + non_realtime_input_change (); + } - _route->Destroyed.connect_same_thread (*this, boost::bind (&Diskstream::route_going_away, this)); + _track->Destroyed.connect_same_thread (*this, boost::bind (&Diskstream::route_going_away, this)); } void Diskstream::handle_input_change (IOChange change, void * /*src*/) { - Glib::Mutex::Lock lm (state_lock); + Glib::Threads::Mutex::Lock lm (state_lock); - if (!(input_change_pending & change)) { - input_change_pending = IOChange (input_change_pending|change); - _session.request_input_change_handling (); - } + if (change.type & (IOChange::ConfigurationChanged|IOChange::ConnectionsChanged)) { + + /* rather than handle this here on a DS-by-DS basis we defer to the + session transport/butler thread, and let it tackle + as many diskstreams as need it in one shot. this avoids many repeated + takings of the audioengine process lock. + */ + + if (!(input_change_pending.type & change.type)) { + input_change_pending.type = IOChange::Type (input_change_pending.type | change.type); + _session.request_input_change_handling (); + } + } } void @@ -207,7 +191,7 @@ Diskstream::non_realtime_set_speed () { if (_buffer_reallocation_required) { - Glib::Mutex::Lock lm (state_lock); + Glib::Threads::Mutex::Lock lm (state_lock); allocate_temporary_buffers (); _buffer_reallocation_required = false; @@ -215,7 +199,7 @@ Diskstream::non_realtime_set_speed () if (_seek_required) { if (speed() != 1.0f || speed() != -1.0f) { - seek ((nframes_t) (_session.transport_frame() * (double) speed()), true); + seek ((framepos_t) (_session.transport_frame() * (double) speed()), true); } else { seek (_session.transport_frame(), true); @@ -238,8 +222,8 @@ Diskstream::realtime_set_speed (double sp, bool global) if (new_speed != _actual_speed) { - nframes_t required_wrap_size = (nframes_t) floor (_session.get_block_size() * - fabs (new_speed)) + 1; + framecnt_t required_wrap_size = (framecnt_t) ceil (_session.get_block_size() * + fabs (new_speed)) + 2; if (required_wrap_size > wrap_buffer_size) { _buffer_reallocation_required = true; @@ -267,22 +251,59 @@ Diskstream::set_capture_offset () return; } - _capture_offset = _io->latency(); + switch (_alignment_style) { + case ExistingMaterial: + _capture_offset = _io->latency(); + break; + + case CaptureTime: + default: + _capture_offset = 0; + break; + } + + DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1: using IO latency, capture offset set to %2 with style = %3\n", name(), _capture_offset, enum_2_string (_alignment_style))); } + void -Diskstream::set_align_style (AlignStyle a) +Diskstream::set_align_style (AlignStyle a, bool force) { if (record_enabled() && _session.actively_recording()) { return; } - if (a != _alignment_style) { + if ((a != _alignment_style) || force) { _alignment_style = a; + set_capture_offset (); AlignmentStyleChanged (); } } +void +Diskstream::set_align_choice (AlignChoice a, bool force) +{ + if (record_enabled() && _session.actively_recording()) { + return; + } + + if ((a != _alignment_choice) || force) { + _alignment_choice = a; + + switch (_alignment_choice) { + case Automatic: + set_align_style_from_io (); + break; + case UseExistingMaterial: + set_align_style (ExistingMaterial); + break; + case UseCaptureTime: + set_align_style (CaptureTime); + break; + } + } +} + int Diskstream::set_loop (Location *location) { @@ -295,51 +316,45 @@ Diskstream::set_loop (Location *location) loop_location = location; - LoopSet (location); /* EMIT SIGNAL */ + LoopSet (location); /* EMIT SIGNAL */ return 0; } -ARDOUR::nframes_t -Diskstream::get_capture_start_frame (uint32_t n) +/** Get the start position (in session frames) of the nth capture in the current pass */ +ARDOUR::framepos_t +Diskstream::get_capture_start_frame (uint32_t n) const { - Glib::Mutex::Lock lm (capture_info_lock); + Glib::Threads::Mutex::Lock lm (capture_info_lock); if (capture_info.size() > n) { + /* this is a completed capture */ return capture_info[n]->start; - } - else { + } else { + /* this is the currently in-progress capture */ return capture_start_frame; } } -ARDOUR::nframes_t -Diskstream::get_captured_frames (uint32_t n) +ARDOUR::framecnt_t +Diskstream::get_captured_frames (uint32_t n) const { - Glib::Mutex::Lock lm (capture_info_lock); + Glib::Threads::Mutex::Lock lm (capture_info_lock); if (capture_info.size() > n) { + /* this is a completed capture */ return capture_info[n]->frames; - } - else { + } else { + /* this is the currently in-progress capture */ return capture_captured; } } void -Diskstream::set_roll_delay (ARDOUR::nframes_t nframes) +Diskstream::set_roll_delay (ARDOUR::framecnt_t nframes) { _roll_delay = nframes; } -void -Diskstream::set_speed (double sp) -{ - _session.request_diskstream_speed (*this, sp); - - /* to force a rebuffering at the right place */ - playlist_modified(); -} - int Diskstream::use_playlist (boost::shared_ptr playlist) { @@ -347,10 +362,10 @@ Diskstream::use_playlist (boost::shared_ptr playlist) return 0; } - bool no_prior_playlist = true; + bool prior_playlist = false; { - Glib::Mutex::Lock lm (state_lock); + Glib::Threads::Mutex::Lock lm (state_lock); if (playlist == _playlist) { return 0; @@ -360,7 +375,7 @@ Diskstream::use_playlist (boost::shared_ptr playlist) if (_playlist) { _playlist->release(); - no_prior_playlist = false; + prior_playlist = true; } _playlist = playlist; @@ -371,8 +386,9 @@ Diskstream::use_playlist (boost::shared_ptr playlist) } _playlist->ContentsChanged.connect_same_thread (playlist_connections, boost::bind (&Diskstream::playlist_modified, this)); + _playlist->LayeringChanged.connect_same_thread (playlist_connections, boost::bind (&Diskstream::playlist_modified, this)); _playlist->DropReferences.connect_same_thread (playlist_connections, boost::bind (&Diskstream::playlist_deleted, this, boost::weak_ptr(_playlist))); - _playlist->RangesMoved.connect_same_thread (playlist_connections, boost::bind (&Diskstream::playlist_ranges_moved, this, _1)); + _playlist->RangesMoved.connect_same_thread (playlist_connections, boost::bind (&Diskstream::playlist_ranges_moved, this, _1, _2)); } /* don't do this if we've already asked for it *or* if we are setting up @@ -380,8 +396,8 @@ Diskstream::use_playlist (boost::shared_ptr playlist) take care of the buffer refill. */ - if (!overwrite_queued && no_prior_playlist) { - _session.request_overwrite_buffer (this); + if (!overwrite_queued && prior_playlist) { + _session.request_overwrite_buffer (_track); overwrite_queued = true; } @@ -401,7 +417,7 @@ void Diskstream::playlist_modified () { if (!i_am_the_modifier && !overwrite_queued) { - _session.request_overwrite_buffer (this); + _session.request_overwrite_buffer (_track); overwrite_queued = true; } } @@ -430,36 +446,107 @@ Diskstream::set_name (const string& str) if (_name != str) { assert(playlist()); playlist()->set_name (str); - SessionObject::set_name(str); - - if (!in_set_state && recordable()) { - /* rename existing capture files so that they have the correct name */ - return rename_write_sources (); - } else { - return false; - } } + return true; +} +bool +Diskstream::set_write_source_name (const std::string& str) { + _write_source_name = str; return true; } -void -Diskstream::remove_region_from_last_capture (boost::weak_ptr wregion) +XMLNode& +Diskstream::get_state () { - boost::shared_ptr region (wregion.lock()); + XMLNode* node = new XMLNode ("Diskstream"); + char buf[64]; + LocaleGuard lg (X_("C")); + + node->add_property ("flags", enum_2_string (_flags)); + node->add_property ("playlist", _playlist->name()); + node->add_property("name", _name); + id().print (buf, sizeof (buf)); + node->add_property("id", buf); + snprintf (buf, sizeof(buf), "%f", _visible_speed); + node->add_property ("speed", buf); + node->add_property ("capture-alignment", enum_2_string (_alignment_choice)); + node->add_property ("record-safe", _record_safe ? "yes" : "no"); + + if (_extra_xml) { + node->add_child_copy (*_extra_xml); + } - if (!region) { - return; + return *node; +} + +int +Diskstream::set_state (const XMLNode& node, int /*version*/) +{ + const XMLProperty* prop; + + if ((prop = node.property ("name")) != 0) { + _name = prop->value(); } - _last_capture_regions.remove (region); + if (deprecated_io_node) { + set_id (*deprecated_io_node); + } else { + set_id (node); + } + + if ((prop = node.property ("flags")) != 0) { + _flags = Flag (string_2_enum (prop->value(), _flags)); + } + + if (Profile->get_trx() && (_flags & Destructive)) { + error << string_compose (_("%1: this session uses destructive tracks, which are not supported"), PROGRAM_NAME) << endmsg; + return -1; + } + + if ((prop = node.property (X_("capture-alignment"))) != 0) { + set_align_choice (AlignChoice (string_2_enum (prop->value(), _alignment_choice)), true); + } else { + set_align_choice (Automatic, true); + } + + if ((prop = node.property ("playlist")) == 0) { + return -1; + } + + if (find_and_use_playlist (prop->value())) { + return -1; + } + + if ((prop = node.property ("speed")) != 0) { + double sp = atof (prop->value().c_str()); + + if (realtime_set_speed (sp, false)) { + non_realtime_set_speed (); + } + } + + if ((prop = node.property ("record-safe")) != 0) { + _record_safe = PBD::string_is_affirmative (prop->value()) ? 1 : 0; + } + + return 0; } void -Diskstream::playlist_ranges_moved (list< Evoral::RangeMove > const & movements_frames) +Diskstream::playlist_ranges_moved (list< Evoral::RangeMove > const & movements_frames, bool from_undo) { - if (!_route || Config->get_automation_follows_regions () == false) { + /* If we're coming from an undo, it will have handled + automation undo (it must, since automation-follows-regions + can lose automation data). Hence we can do nothing here. + */ + + if (from_undo) { + return; + } + + if (!_track || Config->get_automation_follows_regions () == false) { return; } @@ -473,19 +560,26 @@ Diskstream::playlist_ranges_moved (list< Evoral::RangeMove > const & } /* move panner automation */ - boost::shared_ptr p = _route->main_outs()->panner (); - if (p) { - for (uint32_t i = 0; i < p->npanners (); ++i) { - boost::shared_ptr pan_alist = p->streampanner(i).pan_control()->alist(); - XMLNode & before = pan_alist->get_state (); - pan_alist->move_ranges (movements); - _session.add_command (new MementoCommand ( - *pan_alist.get(), &before, &pan_alist->get_state ())); - } - } + boost::shared_ptr pannable = _track->pannable(); + Evoral::ControlSet::Controls& c (pannable->controls()); + + for (Evoral::ControlSet::Controls::iterator ci = c.begin(); ci != c.end(); ++ci) { + boost::shared_ptr ac = boost::dynamic_pointer_cast(ci->second); + if (!ac) { + continue; + } + boost::shared_ptr alist = ac->alist(); + + XMLNode & before = alist->get_state (); + bool const things_moved = alist->move_ranges (movements); + if (things_moved) { + _session.add_command (new MementoCommand ( + *alist.get(), &before, &alist->get_state ())); + } + } /* move processor automation */ - _route->foreach_processor (boost::bind (&Diskstream::move_processor_automation, this, _1, movements_frames)); + _track->foreach_processor (boost::bind (&Diskstream::move_processor_automation, this, _1, movements_frames)); } void @@ -497,27 +591,28 @@ Diskstream::move_processor_automation (boost::weak_ptr p, list< Evora } list< Evoral::RangeMove > movements; - for (list< Evoral::RangeMove >::const_iterator i = movements_frames.begin(); - i != movements_frames.end(); ++i) { + for (list< Evoral::RangeMove >::const_iterator i = movements_frames.begin(); i != movements_frames.end(); ++i) { movements.push_back(Evoral::RangeMove(i->from, i->length, i->to)); } set const a = processor->what_can_be_automated (); - for (set::iterator i = a.begin (); i != a.end (); ++i) { + for (set::const_iterator i = a.begin (); i != a.end (); ++i) { boost::shared_ptr al = processor->automation_control(*i)->alist(); XMLNode & before = al->get_state (); - al->move_ranges (movements); - _session.add_command ( - new MementoCommand ( - *al.get(), &before, &al->get_state () - ) - ); + bool const things_moved = al->move_ranges (movements); + if (things_moved) { + _session.add_command ( + new MementoCommand ( + *al.get(), &before, &al->get_state () + ) + ); + } } } void -Diskstream::check_record_status (nframes_t transport_frame, nframes_t /*nframes*/, bool can_record) +Diskstream::check_record_status (framepos_t transport_frame, bool can_record) { int possibly_recording; int rolling; @@ -525,99 +620,74 @@ Diskstream::check_record_status (nframes_t transport_frame, nframes_t /*nframes* const int transport_rolling = 0x4; const int track_rec_enabled = 0x2; const int global_rec_enabled = 0x1; + const int fully_rec_enabled = (transport_rolling|track_rec_enabled|global_rec_enabled); /* merge together the 3 factors that affect record status, and compute - what has changed. - */ + * what has changed. + */ rolling = _session.transport_speed() != 0.0f; - possibly_recording = (rolling << 2) | (record_enabled() << 1) | can_record; + possibly_recording = (rolling << 2) | ((int)record_enabled() << 1) | (int)can_record; change = possibly_recording ^ last_possibly_recording; if (possibly_recording == last_possibly_recording) { return; } - /* change state */ + const framecnt_t existing_material_offset = _session.worst_playback_latency(); - /* if per-track or global rec-enable turned on while the other was already on, we've started recording */ + if (possibly_recording == fully_rec_enabled) { - if (((change & track_rec_enabled) && record_enabled() && (!(change & global_rec_enabled) && can_record)) || - ((change & global_rec_enabled) && can_record && (!(change & track_rec_enabled) && record_enabled()))) { + if (last_possibly_recording == fully_rec_enabled) { + return; + } - /* starting to record: compute first+last frames */ + capture_start_frame = _session.transport_frame(); + first_recordable_frame = capture_start_frame + _capture_offset; + last_recordable_frame = max_framepos; - first_recordable_frame = transport_frame + _capture_offset; - last_recordable_frame = max_frames; - capture_start_frame = transport_frame; + DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1: @ %7 (%9) FRF = %2 CSF = %4 CO = %5, EMO = %6 RD = %8 WOL %10 WTL %11\n", + name(), first_recordable_frame, last_recordable_frame, capture_start_frame, + _capture_offset, + existing_material_offset, + transport_frame, + _roll_delay, + _session.transport_frame(), + _session.worst_output_latency(), + _session.worst_track_latency())); - if (!(last_possibly_recording & transport_rolling) && (possibly_recording & transport_rolling)) { - /* was stopped, now rolling (and recording) */ + if (_alignment_style == ExistingMaterial) { + first_recordable_frame += existing_material_offset; + DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("\tshift FRF by EMO %1\n", + first_recordable_frame)); + } - if (_alignment_style == ExistingMaterial) { - - first_recordable_frame += _session.worst_output_latency(); - - DEBUG_TRACE (DEBUG::Latency, string_compose ("Offset rec from stop. Capture offset: %1 Worst O/P Latency: %2 Roll Delay: %3 First Recordable Frame: %4 Transport Frame: %5\n", - _capture_offset, _session.worst_output_latency(), _roll_delay, first_recordable_frame, transport_frame)); - } else { - first_recordable_frame += _roll_delay; - } + prepare_record_status (capture_start_frame); - } else { - - /* was rolling, but record state changed */ - - if (_alignment_style == ExistingMaterial) { - - /* manual punch in happens at the correct transport frame - because the user hit a button. but to get alignment correct - we have to back up the position of the new region to the - appropriate spot given the roll delay. - */ - - - /* autopunch toggles recording at the precise - transport frame, and then the DS waits - to start recording for a time that depends - on the output latency. - */ - - first_recordable_frame += _session.worst_output_latency(); - - DEBUG_TRACE (DEBUG::Latency, string_compose ("Punch in manual/auto. Capture offset: %1 Worst O/P Latency: %2 Roll Delay: %3 First Recordable Frame: %4 Transport Frame: %5\n", - _capture_offset, _session.worst_output_latency(), _roll_delay, first_recordable_frame, transport_frame)); - } else { + } else { - if (_session.config.get_punch_in()) { - first_recordable_frame += _roll_delay; - } else { - capture_start_frame -= _roll_delay; - } - } + if (last_possibly_recording == fully_rec_enabled) { - } + /* we were recording last time */ - prepare_record_status(capture_start_frame); + if (change & transport_rolling) { - } else if (!record_enabled() || !can_record) { + /* transport-change (stopped rolling): last_recordable_frame was set in ::prepare_to_stop(). We + * had to set it there because we likely rolled past the stopping point to declick out, + * and then backed up. + */ - /* stop recording */ + } else { + /* punch out */ - last_recordable_frame = transport_frame + _capture_offset; + last_recordable_frame = _session.transport_frame() + _capture_offset; - if (_alignment_style == ExistingMaterial) { - last_recordable_frame += _session.worst_output_latency(); - } else { - last_recordable_frame += _roll_delay; + if (_alignment_style == ExistingMaterial) { + last_recordable_frame += existing_material_offset; + } + } } - - //first_recordable_frame = max_frames; - - DEBUG_TRACE (DEBUG::Latency, string_compose ("Stop record - %6 | %7. Capture offset: %1 Worst O/P Latency: %2 Roll Delay: %3 First Recordable Frame: %4 Transport Frame: %5\n", - _capture_offset, _session.worst_output_latency(), _roll_delay, first_recordable_frame, transport_frame, - can_record, record_enabled())); } last_possibly_recording = possibly_recording; @@ -630,46 +700,169 @@ Diskstream::route_going_away () } void -Diskstream::calculate_record_range(OverlapType ot, sframes_t transport_frame, nframes_t nframes, - nframes_t& rec_nframes, nframes_t& rec_offset) +Diskstream::calculate_record_range (Evoral::OverlapType ot, framepos_t transport_frame, framecnt_t nframes, + framecnt_t & rec_nframes, framecnt_t & rec_offset) { switch (ot) { - case OverlapNone: + case Evoral::OverlapNone: rec_nframes = 0; break; - case OverlapInternal: + case Evoral::OverlapInternal: /* ---------- recrange - |---| transrange - */ + * |---| transrange + */ rec_nframes = nframes; rec_offset = 0; break; - case OverlapStart: + case Evoral::OverlapStart: /* |--------| recrange - -----| transrange - */ + * -----| transrange + */ rec_nframes = transport_frame + nframes - first_recordable_frame; if (rec_nframes) { rec_offset = first_recordable_frame - transport_frame; } break; - case OverlapEnd: + case Evoral::OverlapEnd: /* |--------| recrange - |-------- transrange - */ + * |-------- transrange + */ rec_nframes = last_recordable_frame - transport_frame; rec_offset = 0; break; - case OverlapExternal: + case Evoral::OverlapExternal: /* |--------| recrange - -------------- transrange - */ + * -------------- transrange + */ rec_nframes = last_recordable_frame - first_recordable_frame; rec_offset = first_recordable_frame - transport_frame; break; } + + DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1 rec? %2 @ %3 (for %4) FRF %5 LRF %6 : rf %7 @ %8\n", + _name, enum_2_string (ot), transport_frame, nframes, + first_recordable_frame, last_recordable_frame, rec_nframes, rec_offset)); } + +void +Diskstream::prepare_to_stop (framepos_t transport_frame, framepos_t audible_frame) +{ + switch (_alignment_style) { + case ExistingMaterial: + last_recordable_frame = transport_frame + _capture_offset; + DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose("%1: prepare to stop sets last recordable frame to %2 + %3 = %4\n", _name, transport_frame, _capture_offset, last_recordable_frame)); + break; + + case CaptureTime: + last_recordable_frame = audible_frame; // note that capture_offset is zero + /* we may already have captured audio before the last_recordable_frame (audible frame), + so deal with this. + */ + if (last_recordable_frame > capture_start_frame) { + capture_captured = min (capture_captured, last_recordable_frame - capture_start_frame); + } + DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose("%1: prepare to stop sets last recordable frame to audible frame @ %2\n", _name, audible_frame)); + break; + } + +} + +void +Diskstream::engage_record_enable () +{ + g_atomic_int_set (&_record_enabled, 1); +} + +void +Diskstream::disengage_record_enable () +{ + g_atomic_int_set (&_record_enabled, 0); +} + +void +Diskstream::engage_record_safe () +{ + g_atomic_int_set (&_record_safe, 1); +} + +void +Diskstream::disengage_record_safe () +{ + g_atomic_int_set (&_record_safe, 0); +} + +framecnt_t +Diskstream::default_disk_read_chunk_frames() +{ + return 65536; +} + +framecnt_t +Diskstream::default_disk_write_chunk_frames () +{ + return 65536; +} + +void +Diskstream::set_buffering_parameters (BufferingPreset bp) +{ + framecnt_t read_chunk_size; + framecnt_t read_buffer_size; + framecnt_t write_chunk_size; + framecnt_t write_buffer_size; + + if (!get_buffering_presets (bp, read_chunk_size, read_buffer_size, write_chunk_size, write_buffer_size)) { + return; + } + + disk_read_chunk_frames = read_chunk_size; + disk_write_chunk_frames = write_chunk_size; + Config->set_audio_capture_buffer_seconds (write_buffer_size); + Config->set_audio_playback_buffer_seconds (read_buffer_size); + + cerr << "Set buffering params to " << disk_read_chunk_frames << '|' << disk_write_chunk_frames << '|' + << Config->get_audio_playback_buffer_seconds() << '|' + << Config->get_audio_capture_buffer_seconds () + << endl; +} + +bool +Diskstream::get_buffering_presets (BufferingPreset bp, + framecnt_t& read_chunk_size, + framecnt_t& read_buffer_size, + framecnt_t& write_chunk_size, + framecnt_t& write_buffer_size) +{ + switch (bp) { + case Small: + read_chunk_size = 65536; /* samples */ + write_chunk_size = 65536; /* samples */ + read_buffer_size = 5; /* seconds */ + write_buffer_size = 5; /* seconds */ + break; + + case Medium: + read_chunk_size = 262144; /* samples */ + write_chunk_size = 131072; /* samples */ + read_buffer_size = 10; /* seconds */ + write_buffer_size = 10; /* seconds */ + break; + + case Large: + read_chunk_size = 524288; /* samples */ + write_chunk_size = 131072; /* samples */ + read_buffer_size = 20; /* seconds */ + write_buffer_size = 20; /* seconds */ + break; + + default: + return false; + } + + return true; +} +