X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fdiskstream.cc;h=e2dfc271002458fab5a588dd9c508cbeeb9c6094;hb=e374518ca9c4643874eee3877e270eef32d95f18;hp=7b3db9aab420f022a737577b0cd5ad497103ce44;hpb=0c4c6e031a3624cfc74c2eef6e79527b7c49eca8;p=ardour.git diff --git a/libs/ardour/diskstream.cc b/libs/ardour/diskstream.cc index 7b3db9aab4..e2dfc27100 100644 --- a/libs/ardour/diskstream.cc +++ b/libs/ardour/diskstream.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2000-2006 Paul Davis + Copyright (C) 2000-2006 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 @@ -15,7 +15,6 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - $Id: diskstream.cc 567 2006-06-07 14:54:12Z trutkin $ */ #include @@ -34,22 +33,27 @@ #include -#include -#include +#include "pbd/error.h" +#include "pbd/basename.h" #include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "pbd/xml++.h" +#include "pbd/memento_command.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/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 "i18n.h" #include @@ -58,27 +62,24 @@ using namespace std; using namespace ARDOUR; using namespace PBD; -/* XXX This goes uninitialized when there is no ~/.ardour2 directory. +/* XXX This goes uninitialized when there is no ~/.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.. */ -nframes_t Diskstream::disk_io_chunk_frames = 1024 * 256; +ARDOUR::nframes_t Diskstream::disk_io_chunk_frames = 1024 * 256; sigc::signal Diskstream::DiskOverrun; sigc::signal Diskstream::DiskUnderrun; Diskstream::Diskstream (Session &sess, const string &name, Flag flag) - : _name (name) - , _session (sess) - , _playlist(NULL) + : SessionObject(sess, name) { init (flag); } - -Diskstream::Diskstream (Session& sess, const XMLNode& node) - : _session (sess) - , _playlist(NULL) + +Diskstream::Diskstream (Session& sess, const XMLNode& /*node*/) + : SessionObject(sess, "unnamed diskstream") { init (Recordable); } @@ -87,7 +88,7 @@ void Diskstream::init (Flag f) { _flags = f; - _io = 0; + _route = 0; _alignment_style = ExistingMaterial; _persistent_alignment_style = ExistingMaterial; first_input_change = true; @@ -111,40 +112,44 @@ Diskstream::init (Flag f) loop_location = 0; wrap_buffer_size = 0; speed_buffer_size = 0; - last_phase = 0; - phi = (uint64_t) (0x1000000); + _speed = 1.0; + _target_speed = _speed; file_frame = 0; playback_sample = 0; playback_distance = 0; _read_data_count = 0; _write_data_count = 0; + commit_should_unlock = false; pending_overwrite = false; overwrite_frame = 0; overwrite_queued = false; input_change_pending = NoChange; - - _n_channels = 0; } Diskstream::~Diskstream () { - // Taken by derived class destrctors.. should assure locked here somehow? - //Glib::Mutex::Lock lm (state_lock); + DEBUG_TRACE (DEBUG::Destruction, string_compose ("Diskstream %1 deleted\n", _name)); - if (_playlist) - _playlist->unref (); + if (_playlist) { + _playlist->release (); + } } void -Diskstream::set_io (IO& io) +Diskstream::set_route (Route& r) { - _io = &io; + _route = &r; + _io = _route->input(); + input_change_pending = ConfigurationChanged; + non_realtime_input_change (); set_align_style_from_io (); + + _route->GoingAway.connect (mem_fun (*this, &Diskstream::route_going_away)); } void -Diskstream::handle_input_change (IOChange change, void *src) +Diskstream::handle_input_change (IOChange change, void * /*src*/) { Glib::Mutex::Lock lm (state_lock); @@ -182,23 +187,23 @@ Diskstream::realtime_set_speed (double sp, bool global) { bool changed = false; double new_speed = sp * _session.transport_speed(); - + if (_visible_speed != sp) { _visible_speed = sp; changed = true; } - + if (new_speed != _actual_speed) { - - nframes_t required_wrap_size = (nframes_t) floor (_session.get_block_size() * + + nframes_t required_wrap_size = (nframes_t) floor (_session.get_block_size() * fabs (new_speed)) + 1; - + if (required_wrap_size > wrap_buffer_size) { _buffer_reallocation_required = true; } - + _actual_speed = new_speed; - phi = (uint64_t) (0x1000000 * fabs(_actual_speed)); + _target_speed = fabs(_actual_speed); } if (changed) { @@ -221,7 +226,9 @@ Diskstream::prepare () void Diskstream::recover () { - state_lock.unlock(); + if (commit_should_unlock) { + state_lock.unlock(); + } _processed = false; } @@ -233,7 +240,7 @@ Diskstream::set_capture_offset () return; } - _capture_offset = _io->input_latency(); + _capture_offset = _io->latency(); } void @@ -265,7 +272,7 @@ Diskstream::set_loop (Location *location) return 0; } -nframes_t +ARDOUR::nframes_t Diskstream::get_capture_start_frame (uint32_t n) { Glib::Mutex::Lock lm (capture_info_lock); @@ -278,7 +285,7 @@ Diskstream::get_capture_start_frame (uint32_t n) } } -nframes_t +ARDOUR::nframes_t Diskstream::get_captured_frames (uint32_t n) { Glib::Mutex::Lock lm (capture_info_lock); @@ -292,7 +299,7 @@ Diskstream::get_captured_frames (uint32_t n) } void -Diskstream::set_roll_delay (nframes_t nframes) +Diskstream::set_roll_delay (ARDOUR::nframes_t nframes) { _roll_delay = nframes; } @@ -307,7 +314,7 @@ Diskstream::set_speed (double sp) } int -Diskstream::use_playlist (Playlist* playlist) +Diskstream::use_playlist (boost::shared_ptr playlist) { { Glib::Mutex::Lock lm (state_lock); @@ -316,30 +323,36 @@ Diskstream::use_playlist (Playlist* playlist) return 0; } - plstate_connection.disconnect(); plmod_connection.disconnect (); plgone_connection.disconnect (); + plregion_connection.disconnect (); if (_playlist) { - _playlist->unref(); + _playlist->release(); } - + _playlist = playlist; - _playlist->ref(); + _playlist->use(); if (!in_set_state && recordable()) { reset_write_sources (false); } - + plmod_connection = _playlist->Modified.connect (mem_fun (*this, &Diskstream::playlist_modified)); - plgone_connection = _playlist->GoingAway.connect (bind (mem_fun (*this, &Diskstream::playlist_deleted), _playlist)); + plgone_connection = _playlist->GoingAway.connect (bind (mem_fun (*this, &Diskstream::playlist_deleted), boost::weak_ptr(_playlist))); + plregion_connection = _playlist->RangesMoved.connect (mem_fun (*this, &Diskstream::playlist_ranges_moved)); } - if (!overwrite_queued) { + /* don't do this if we've already asked for it *or* if we are setting up + the diskstream for the very first time - the input changed handling will + take care of the buffer refill. + */ + + if (!overwrite_queued && !(_session.state_of_the_state() & Session::CannotSave)) { _session.request_overwrite_buffer (this); overwrite_queued = true; } - + PlaylistChanged (); /* EMIT SIGNAL */ _session.set_dirty (); @@ -347,7 +360,7 @@ Diskstream::use_playlist (Playlist* playlist) } void -Diskstream::playlist_changed (Change ignored) +Diskstream::playlist_changed (Change) { playlist_modified (); } @@ -358,37 +371,45 @@ Diskstream::playlist_modified () if (!i_am_the_modifier && !overwrite_queued) { _session.request_overwrite_buffer (this); overwrite_queued = true; - } + } } void -Diskstream::playlist_deleted (Playlist* pl) +Diskstream::playlist_deleted (boost::weak_ptr wpl) { - /* this catches an ordering issue with session destruction. playlists - are destroyed before diskstreams. we have to invalidate any handles - we have to the playlist. - */ + boost::shared_ptr pl (wpl.lock()); - _playlist = 0; + if (pl == _playlist) { + + /* this catches an ordering issue with session destruction. playlists + are destroyed before diskstreams. we have to invalidate any handles + we have to the playlist. + */ + + if (_playlist) { + _playlist.reset (); + } + } } -int -Diskstream::set_name (string str) +bool +Diskstream::set_name (const string& str) { if (str != _name) { assert(playlist()); playlist()->set_name (str); - _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 -1; + return false; } } - return 0; + return true; } void @@ -399,7 +420,228 @@ Diskstream::remove_region_from_last_capture (boost::weak_ptr wregion) if (!region) { return; } - + _last_capture_regions.remove (region); } +void +Diskstream::playlist_ranges_moved (list< Evoral::RangeMove > const & movements_frames) +{ + if (!_route || Config->get_automation_follows_regions () == false) { + return; + } + + list< Evoral::RangeMove > movements; + + 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)); + } + + /* 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 ())); + } + } + + /* move processor automation */ + _route->foreach_processor (sigc::bind (sigc::mem_fun (*this, &Diskstream::move_processor_automation), movements_frames)); +} + +void +Diskstream::move_processor_automation (boost::weak_ptr p, + list< Evoral::RangeMove > const & movements_frames) +{ + boost::shared_ptr processor (p.lock ()); + if (!processor) { + return; + } + + list< Evoral::RangeMove > movements; + 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) { + 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 () + ) + ); + } +} + +void +Diskstream::check_record_status (nframes_t transport_frame, nframes_t /*nframes*/, bool can_record) +{ + int possibly_recording; + int rolling; + int change; + const int transport_rolling = 0x4; + const int track_rec_enabled = 0x2; + const int global_rec_enabled = 0x1; + + /* merge together the 3 factors that affect record status, and compute + what has changed. + */ + + rolling = _session.transport_speed() != 0.0f; + possibly_recording = (rolling << 2) | (record_enabled() << 1) | can_record; + change = possibly_recording ^ last_possibly_recording; + + if (possibly_recording == last_possibly_recording) { + return; + } + + /* change state */ + + /* if per-track or global rec-enable turned on while the other was already on, we've started recording */ + + 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()))) { + + /* starting to record: compute first+last frames */ + + first_recordable_frame = transport_frame + _capture_offset; + last_recordable_frame = max_frames; + capture_start_frame = transport_frame; + + if (!(last_possibly_recording & transport_rolling) && (possibly_recording & transport_rolling)) { + + /* was stopped, now rolling (and recording) */ + + if (_alignment_style == ExistingMaterial) { + first_recordable_frame += _session.worst_output_latency(); + } else { + first_recordable_frame += _roll_delay; + } + + } else { + + /* was rolling, but record state changed */ + + if (_alignment_style == ExistingMaterial) { + + + if (!_session.config.get_punch_in()) { + + /* 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. + */ + + capture_start_frame -= _roll_delay; + + /* XXX paul notes (august 2005): i don't know why + this is needed. + */ + + first_recordable_frame += _capture_offset; + + } else { + + /* 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(); + } + + } else { + + if (_session.config.get_punch_in()) { + first_recordable_frame += _roll_delay; + } else { + capture_start_frame -= _roll_delay; + } + } + + } + + prepare_record_status(capture_start_frame); + + } else if (!record_enabled() || !can_record) { + + /* stop recording */ + + last_recordable_frame = transport_frame + _capture_offset; + + if (_alignment_style == ExistingMaterial) { + last_recordable_frame += _session.worst_output_latency(); + } else { + last_recordable_frame += _roll_delay; + } + + first_recordable_frame = max_frames; + } + + last_possibly_recording = possibly_recording; +} + +void +Diskstream::route_going_away () +{ + _io.reset (); +} + +void +Diskstream::calculate_record_range(OverlapType ot, sframes_t transport_frame, nframes_t nframes, + nframes_t& rec_nframes, nframes_t& rec_offset) +{ + switch (ot) { + case OverlapNone: + rec_nframes = 0; + break; + + case OverlapInternal: + /* ---------- recrange + |---| transrange + */ + rec_nframes = nframes; + rec_offset = 0; + break; + + case OverlapStart: + /* |--------| recrange + -----| transrange + */ + rec_nframes = transport_frame + nframes - first_recordable_frame; + if (rec_nframes) { + rec_offset = first_recordable_frame - transport_frame; + } + break; + + case OverlapEnd: + /* |--------| recrange + |-------- transrange + */ + rec_nframes = last_recordable_frame - transport_frame; + rec_offset = 0; + break; + + case OverlapExternal: + /* |--------| recrange + -------------- transrange + */ + rec_nframes = last_recordable_frame - first_recordable_frame; + rec_offset = first_recordable_frame - transport_frame; + break; + } +}