From 68e57101add5f84faba4a29ac17f410f2f9d5e44 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Mon, 10 Apr 2017 11:44:48 +0100 Subject: [PATCH] fully remove diskstream code --- gtk2_ardour/ardour_ui.cc | 7 +- gtk2_ardour/luainstance.cc | 3 +- gtk2_ardour/luasignal_syms.h | 4 +- libs/ardour/ardour/audio_diskstream.h | 270 --- libs/ardour/ardour/diskstream.h | 369 --- libs/ardour/ardour/midi_diskstream.h | 194 -- libs/ardour/ardour/public_diskstream.h | 82 - libs/ardour/ardour/rc_configuration_vars.h | 4 +- libs/ardour/audio_diskstream.cc | 2551 -------------------- libs/ardour/audio_track.cc | 1 - libs/ardour/audio_track_importer.cc | 7 +- libs/ardour/diskstream.cc | 902 ------- libs/ardour/enums.cc | 12 +- libs/ardour/import.cc | 1 - libs/ardour/midi_diskstream.cc | 1582 ------------ libs/ardour/rc_configuration.cc | 7 +- libs/ardour/track.cc | 1 - libs/ardour/wscript | 3 - 18 files changed, 25 insertions(+), 5975 deletions(-) delete mode 100644 libs/ardour/ardour/audio_diskstream.h delete mode 100644 libs/ardour/ardour/diskstream.h delete mode 100644 libs/ardour/ardour/midi_diskstream.h delete mode 100644 libs/ardour/ardour/public_diskstream.h delete mode 100644 libs/ardour/audio_diskstream.cc delete mode 100644 libs/ardour/diskstream.cc delete mode 100644 libs/ardour/midi_diskstream.cc diff --git a/gtk2_ardour/ardour_ui.cc b/gtk2_ardour/ardour_ui.cc index 0ba520217f..0e75652856 100644 --- a/gtk2_ardour/ardour_ui.cc +++ b/gtk2_ardour/ardour_ui.cc @@ -86,7 +86,8 @@ #include "ardour/audioengine.h" #include "ardour/audiofilesource.h" #include "ardour/automation_watch.h" -#include "ardour/diskstream.h" +#include "ardour/disk_reader.h" +#include "ardour/disk_writer.h" #include "ardour/filename_extensions.h" #include "ardour/filesystem_paths.h" #include "ardour/ltc_file_reader.h" @@ -396,8 +397,8 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir) rec_button.set_name ("transport recenable button"); midi_panic_button.set_name ("transport button"); - ARDOUR::Diskstream::DiskOverrun.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::disk_overrun_handler, this), gui_context()); - ARDOUR::Diskstream::DiskUnderrun.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::disk_underrun_handler, this), gui_context()); + ARDOUR::DiskWriter::Overrun.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::disk_overrun_handler, this), gui_context()); + ARDOUR::DiskReader::Underrun.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::disk_underrun_handler, this), gui_context()); ARDOUR::Session::VersionMismatch.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::session_format_mismatch, this, _1, _2), gui_context()); diff --git a/gtk2_ardour/luainstance.cc b/gtk2_ardour/luainstance.cc index 8d7d972d04..ff2c8d3164 100644 --- a/gtk2_ardour/luainstance.cc +++ b/gtk2_ardour/luainstance.cc @@ -23,7 +23,8 @@ #include "gtkmm2ext/gui_thread.h" #include "ardour/audioengine.h" -#include "ardour/diskstream.h" +#include "ardour/disk_reader.h" +#include "ardour/disk_writer.h" #include "ardour/plugin_manager.h" #include "ardour/route.h" #include "ardour/session.h" diff --git a/gtk2_ardour/luasignal_syms.h b/gtk2_ardour/luasignal_syms.h index 37f2624e05..d7fa837076 100644 --- a/gtk2_ardour/luasignal_syms.h +++ b/gtk2_ardour/luasignal_syms.h @@ -77,8 +77,8 @@ STATIC(PluginListChanged, &(PluginManager::instance().PluginListChanged), 0) STATIC(PluginStatusesChanged, &(PluginManager::instance().PluginStatusesChanged), 0) // Diskstream static global -STATIC(DiskOverrun, &ARDOUR::Diskstream::DiskOverrun, 0) -STATIC(DiskUnderrun, &ARDOUR::Diskstream::DiskUnderrun, 0) +STATIC(DiskOverrun, &ARDOUR::DiskWriter::Overrun, 0) +STATIC(DiskUnderrun, &ARDOUR::DiskReader::Underrun, 0) // Region static STATIC(RegionPropertyChanged, &ARDOUR::Region::RegionPropertyChanged, 2) diff --git a/libs/ardour/ardour/audio_diskstream.h b/libs/ardour/ardour/audio_diskstream.h deleted file mode 100644 index 21541c6fb8..0000000000 --- a/libs/ardour/ardour/audio_diskstream.h +++ /dev/null @@ -1,270 +0,0 @@ -/* - 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 - 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. -*/ - -#ifndef __ardour_audio_diskstream_h__ -#define __ardour_audio_diskstream_h__ - - -#include -#include -#include -#include -#include - -#include - -#include - -#include "pbd/fastlog.h" -#include "pbd/ringbufferNPT.h" -#include "pbd/stateful.h" -#include "pbd/rcu.h" - -#include "ardour/ardour.h" -#include "ardour/utils.h" -#include "ardour/diskstream.h" -#include "ardour/audioplaylist.h" -#include "ardour/port.h" -#include "ardour/interpolation.h" - -struct tm; - -namespace ARDOUR { - -class AudioEngine; -class Send; -class Session; -class AudioPlaylist; -class AudioFileSource; -class IO; - -class LIBARDOUR_API AudioDiskstream : public Diskstream -{ - public: - AudioDiskstream (Session &, const std::string& name, Diskstream::Flag f = Recordable); - AudioDiskstream (Session &, const XMLNode&); - ~AudioDiskstream(); - - float playback_buffer_load() const; - float capture_buffer_load() const; - - std::string input_source (uint32_t n=0) const { - boost::shared_ptr c = channels.reader(); - if (n < c->size()) { - return (*c)[n]->source.name; - } else { - return ""; - } - } - - void set_record_enabled (bool yn); - void set_record_safe (bool yn); - - boost::shared_ptr audio_playlist () { return boost::dynamic_pointer_cast(_playlist); } - - int use_playlist (boost::shared_ptr); - int use_new_playlist (); - int use_copy_playlist (); - - Sample *playback_buffer (uint32_t n = 0) { - boost::shared_ptr c = channels.reader(); - if (n < c->size()) - return (*c)[n]->current_playback_buffer; - return 0; - } - - Sample *capture_buffer (uint32_t n = 0) { - boost::shared_ptr c = channels.reader(); - if (n < c->size()) - return (*c)[n]->current_capture_buffer; - return 0; - } - - boost::shared_ptr write_source (uint32_t n=0) { - boost::shared_ptr c = channels.reader(); - if (n < c->size()) - return (*c)[n]->write_source; - return boost::shared_ptr(); - } - - int add_channel (uint32_t how_many); - int remove_channel (uint32_t how_many); - - bool set_name (std::string const &); - bool set_write_source_name (const std::string& str); - - /* stateful */ - - XMLNode& get_state(void); - int set_state(const XMLNode& node, int version); - - void request_input_monitoring (bool); - - static void swap_by_ptr (Sample *first, Sample *last) { - while (first < last) { - Sample tmp = *first; - *first++ = *last; - *last-- = tmp; - } - } - - - protected: - friend class Session; - - /* the Session is the only point of access for these - because they require that the Session is "inactive" - while they are called. - */ - - void set_pending_overwrite(bool); - int overwrite_existing_buffers (); - void set_block_size (pframes_t); - int internal_playback_seek (framecnt_t distance); - int can_internal_playback_seek (framecnt_t distance); - void reset_write_sources (bool, bool force = false); - void non_realtime_input_change (); - void non_realtime_locate (framepos_t location); - - protected: - friend class Auditioner; - friend class AudioTrack; - int seek (framepos_t which_sample, bool complete_refill = false); - - int process (BufferSet&, framepos_t transport_frame, pframes_t nframes, framecnt_t &, bool need_disk_signal); - frameoffset_t calculate_playback_distance (pframes_t nframes); - bool commit (framecnt_t); - - private: - struct ChannelSource { - std::string name; - - bool is_physical () const; - void request_input_monitoring (bool) const; - }; - - /** Information about one of our channels */ - struct ChannelInfo : public boost::noncopyable { - - ChannelInfo (framecnt_t playback_buffer_size, - framecnt_t capture_buffer_size, - framecnt_t speed_buffer_size, - framecnt_t wrap_buffer_size); - ~ChannelInfo (); - - Sample *playback_wrap_buffer; - Sample *capture_wrap_buffer; - Sample *speed_buffer; - - boost::shared_ptr write_source; - - /** Information about the Port that our audio data comes from */ - ChannelSource source; - - Sample *current_capture_buffer; - Sample *current_playback_buffer; - - /** A ringbuffer for data to be played back, written to in the - butler thread, read from in the process thread. - */ - PBD::RingBufferNPT *playback_buf; - PBD::RingBufferNPT *capture_buf; - - Sample* scrub_buffer; - Sample* scrub_forward_buffer; - Sample* scrub_reverse_buffer; - - PBD::RingBufferNPT::rw_vector playback_vector; - PBD::RingBufferNPT::rw_vector capture_vector; - - PBD::RingBufferNPT * capture_transition_buf; - // the following are used in the butler thread only - framecnt_t curr_capture_cnt; - - void resize_playback (framecnt_t); - void resize_capture (framecnt_t); - }; - - typedef std::vector ChannelList; - - CubicInterpolation interpolation; - - /* The two central butler operations */ - int do_flush (RunContext context, bool force = false); - int do_refill () { return _do_refill(_mixdown_buffer, _gain_buffer, 0); } - - - int read (Sample* buf, Sample* mixdown_buffer, float* gain_buffer, - framepos_t& start, framecnt_t cnt, - int channel, bool reversed); - - void finish_capture (boost::shared_ptr); - void transport_stopped_wallclock (struct tm&, time_t, bool abort); - void transport_looped (framepos_t transport_frame); - - void init (); - - void init_channel (ChannelInfo &chan); - void destroy_channel (ChannelInfo &chan); - - int use_new_write_source (uint32_t n=0); - - int find_and_use_playlist (const std::string &); - - void allocate_temporary_buffers (); - - int use_pending_capture_data (XMLNode& node); - - void get_input_sources (); - void prepare_record_status(framepos_t capture_start_frame); - void set_align_style_from_io(); - void setup_destructive_playlist (); - void use_destructive_playlist (); - - void adjust_playback_buffering (); - void adjust_capture_buffering (); - - bool prep_record_enable (); - bool prep_record_disable (); - - // Working buffers for do_refill (butler thread) - static void allocate_working_buffers(); - static void free_working_buffers(); - - static Sample* _mixdown_buffer; - static gain_t* _gain_buffer; - - std::vector > capturing_sources; - - SerializedRCUManager channels; - - protected: - int _do_refill_with_alloc (bool one_chunk_only); - - /* really */ - private: - int _do_refill (Sample *mixdown_buffer, float *gain_buffer, framecnt_t fill_level); - - int add_channel_to (boost::shared_ptr, uint32_t how_many); - int remove_channel_from (boost::shared_ptr, uint32_t how_many); - -}; - -} // namespace ARDOUR - -#endif /* __ardour_audio_diskstream_h__ */ diff --git a/libs/ardour/ardour/diskstream.h b/libs/ardour/ardour/diskstream.h deleted file mode 100644 index ea8e44a0ad..0000000000 --- a/libs/ardour/ardour/diskstream.h +++ /dev/null @@ -1,369 +0,0 @@ -/* - 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 - 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. - -*/ - -#ifndef __ardour_diskstream_h__ -#define __ardour_diskstream_h__ - -#include -#include -#include -#include -#include -#include - -#include - -#include "evoral/Range.hpp" - -#include "ardour/ardour.h" -#include "ardour/chan_count.h" -#include "ardour/session_object.h" -#include "ardour/libardour_visibility.h" -#include "ardour/types.h" -#include "ardour/utils.h" -#include "ardour/public_diskstream.h" - -struct tm; - -namespace ARDOUR { - -class IO; -class Playlist; -class Processor; -class Source; -class Session; -class Track; -class Location; -class BufferSet; - -/** Parent class for classes which can stream data to and from disk. - * These are used by Tracks to get playback and put recorded data. - */ -class LIBARDOUR_API Diskstream : public SessionObject, public PublicDiskstream -{ - public: - enum Flag { - Recordable = 0x1, - Hidden = 0x2, - Destructive = 0x4, - NonLayered = 0x8 - }; - - Diskstream (Session &, const std::string& name, Flag f = Recordable); - Diskstream (Session &, const XMLNode&); - virtual ~Diskstream(); - - virtual bool set_name (const std::string& str); - virtual bool set_write_source_name (const std::string& str); - - std::string write_source_name () const { - if (_write_source_name.empty()) { - return name(); - } else { - return _write_source_name; - } - } - - virtual std::string steal_write_source_name () { return std::string(); } - - boost::shared_ptr io() const { return _io; } - void set_track (ARDOUR::Track *); - - /** @return A number between 0 and 1, where 0 indicates that the playback buffer - * is dry (ie the disk subsystem could not keep up) and 1 indicates that the - * buffer is full. - */ - virtual float playback_buffer_load() const = 0; - virtual float capture_buffer_load() const = 0; - - void set_flag (Flag f) { _flags = Flag (_flags | f); } - void unset_flag (Flag f) { _flags = Flag (_flags & ~f); } - - AlignStyle alignment_style() const { return _alignment_style; } - AlignChoice alignment_choice() const { return _alignment_choice; } - void set_align_style (AlignStyle, bool force=false); - void set_align_choice (AlignChoice a, bool force=false); - - framecnt_t roll_delay() const { return _roll_delay; } - void set_roll_delay (framecnt_t); - - bool record_enabled() const { return g_atomic_int_get (const_cast(&_record_enabled)); } - bool record_safe () const { return g_atomic_int_get (const_cast(&_record_safe)); } - virtual void set_record_enabled (bool yn) = 0; - virtual void set_record_safe (bool yn) = 0; - - bool destructive() const { return _flags & Destructive; } - - bool hidden() const { return _flags & Hidden; } - bool recordable() const { return _flags & Recordable; } - bool non_layered() const; // { return _flags & NonLayered; } - bool reversed() const { return _actual_speed < 0.0f; } - double speed() const { return _visible_speed; } - - virtual void punch_in() {} - virtual void punch_out() {} - - void non_realtime_set_speed (); - virtual void non_realtime_locate (framepos_t /*location*/) {}; - virtual void playlist_modified (); - - boost::shared_ptr playlist () { return _playlist; } - - virtual int use_playlist (boost::shared_ptr); - virtual int use_new_playlist () = 0; - virtual int use_copy_playlist () = 0; - - /** @return Start position of currently-running capture (in session frames) */ - framepos_t current_capture_start() const { return capture_start_frame; } - framepos_t current_capture_end() const { return capture_start_frame + capture_captured; } - framepos_t get_capture_start_frame (uint32_t n = 0) const; - framecnt_t get_captured_frames (uint32_t n = 0) const; - - ChanCount n_channels() { return _n_channels; } - - static framecnt_t disk_read_frames() { return disk_read_chunk_frames; } - static framecnt_t disk_write_frames() { return disk_write_chunk_frames; } - static void set_disk_read_chunk_frames (framecnt_t n) { disk_read_chunk_frames = n; } - static void set_disk_write_chunk_frames (framecnt_t n) { disk_write_chunk_frames = n; } - static framecnt_t default_disk_read_chunk_frames (); - static framecnt_t default_disk_write_chunk_frames (); - - static void set_buffering_parameters (BufferingPreset bp); - - /* Stateful */ - virtual XMLNode& get_state(void); - virtual int set_state(const XMLNode&, int version); - - virtual void request_input_monitoring (bool) {} - virtual void ensure_input_monitoring (bool) {} - - framecnt_t capture_offset() const { return _capture_offset; } - virtual void set_capture_offset (); - - bool slaved() const { return _slaved; } - void set_slaved(bool yn) { _slaved = yn; } - - int set_loop (Location *loc); - - std::list >& last_capture_sources () { return _last_capture_sources; } - - void handle_input_change (IOChange, void *src); - - void move_processor_automation (boost::weak_ptr, - std::list > const &); - - /** For non-butler contexts (allocates temporary working buffers) - * - * This accessible method has a default argument; derived classes - * must inherit the virtual method that we call which does NOT - * have a default argument, to avoid complications with inheritance - */ - int do_refill_with_alloc(bool partial_fill = true) { - return _do_refill_with_alloc (partial_fill); - } - virtual void set_block_size (pframes_t) = 0; - - bool pending_overwrite () const { - return _pending_overwrite; - } - - PBD::Signal0 RecordEnableChanged; - PBD::Signal0 RecordSafeChanged; - PBD::Signal0 SpeedChanged; - PBD::Signal0 ReverseChanged; - /* Emitted when this diskstream is set to use a different playlist */ - PBD::Signal0 PlaylistChanged; - PBD::Signal0 AlignmentStyleChanged; - PBD::Signal1 LoopSet; - - static PBD::Signal0 DiskOverrun; - static PBD::Signal0 DiskUnderrun; - - protected: - friend class Session; - friend class Butler; - - /* the Session is the only point of access for these because they require - * that the Session is "inactive" while they are called. - */ - - virtual void set_pending_overwrite (bool) = 0; - virtual int overwrite_existing_buffers () = 0; - virtual int internal_playback_seek (framecnt_t distance) = 0; - virtual int can_internal_playback_seek (framecnt_t distance) = 0; - virtual void reset_write_sources (bool, bool force = false) = 0; - virtual void non_realtime_input_change () = 0; - /* accessible method has default argument, so use standard C++ "trick" - to avoid complications with inheritance, by adding this virtual - method which does NOT have a default argument. - */ - virtual int _do_refill_with_alloc (bool partial_fill) = 0; - - protected: - friend class Auditioner; - virtual int seek (framepos_t which_sample, bool complete_refill = false) = 0; - - protected: - friend class Track; - - virtual int process (BufferSet&, framepos_t transport_frame, pframes_t nframes, framecnt_t &, bool need_disk_signal) = 0; - virtual frameoffset_t calculate_playback_distance (pframes_t nframes) = 0; - virtual bool commit (framecnt_t) = 0; - - //private: - - enum TransitionType { - CaptureStart = 0, - CaptureEnd - }; - - struct CaptureTransition { - TransitionType type; - framepos_t capture_val; ///< The start or end file frame position - }; - - /* The two central butler operations */ - virtual int do_flush (RunContext context, bool force = false) = 0; - virtual int do_refill () = 0; - - /* XXX fix this redundancy ... */ - - virtual void playlist_changed (const PBD::PropertyChange&); - virtual void playlist_deleted (boost::weak_ptr); - virtual void playlist_ranges_moved (std::list< Evoral::RangeMove > const &, bool); - - virtual void transport_stopped_wallclock (struct tm&, time_t, bool abort) = 0; - virtual void transport_looped (framepos_t transport_frame) = 0; - - struct CaptureInfo { - framepos_t start; - framecnt_t frames; - }; - - virtual int use_new_write_source (uint32_t n=0) = 0; - - virtual int find_and_use_playlist (const std::string&) = 0; - - virtual void allocate_temporary_buffers () = 0; - - virtual bool realtime_set_speed (double, bool global_change); - - std::list > _last_capture_sources; - - virtual int use_pending_capture_data (XMLNode& node) = 0; - - virtual void check_record_status (framepos_t transport_frame, bool can_record); - virtual void prepare_record_status (framepos_t /*capture_start_frame*/) {} - virtual void set_align_style_from_io() {} - virtual void setup_destructive_playlist () {} - virtual void use_destructive_playlist () {} - virtual void prepare_to_stop (framepos_t transport_pos, framepos_t audible_frame); - - void engage_record_enable (); - void disengage_record_enable (); - void engage_record_safe (); - void disengage_record_safe (); - - virtual bool prep_record_enable () = 0; - virtual bool prep_record_disable () = 0; - - void calculate_record_range ( - Evoral::OverlapType ot, framepos_t transport_frame, framecnt_t nframes, - framecnt_t& rec_nframes, framecnt_t& rec_offset - ); - - static framecnt_t disk_read_chunk_frames; - static framecnt_t disk_write_chunk_frames; - - std::vector capture_info; - mutable Glib::Threads::Mutex capture_info_lock; - - uint32_t i_am_the_modifier; - - boost::shared_ptr _io; - Track* _track; - ChanCount _n_channels; - - boost::shared_ptr _playlist; - - gint _record_enabled; - gint _record_safe; - double _visible_speed; - double _actual_speed; - /* items needed for speed change logic */ - bool _buffer_reallocation_required; - bool _seek_required; - - /** Start of currently running capture in session frames */ - framepos_t capture_start_frame; - framecnt_t capture_captured; - bool was_recording; - framecnt_t adjust_capture_position; - framecnt_t _capture_offset; - /** The number of frames by which this diskstream's output should be delayed - with respect to the transport frame. This is used for latency compensation. - */ - framecnt_t _roll_delay; - framepos_t first_recordable_frame; - framepos_t last_recordable_frame; - int last_possibly_recording; - AlignStyle _alignment_style; - AlignChoice _alignment_choice; - bool _slaved; - Location* loop_location; - framepos_t overwrite_frame; - off_t overwrite_offset; - bool _pending_overwrite; - bool overwrite_queued; - IOChange input_change_pending; - framecnt_t wrap_buffer_size; - framecnt_t speed_buffer_size; - - double _speed; - double _target_speed; - - /** The next frame position that we should be reading from in our playlist */ - framepos_t file_frame; - framepos_t playback_sample; - - bool in_set_state; - - std::string _write_source_name; - - Glib::Threads::Mutex state_lock; - - PBD::ScopedConnectionList playlist_connections; - - PBD::ScopedConnection ic_connection; - - Flag _flags; - XMLNode* deprecated_io_node; - - void route_going_away (); - - static bool 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); -}; - -}; /* namespace ARDOUR */ - -#endif /* __ardour_diskstream_h__ */ diff --git a/libs/ardour/ardour/midi_diskstream.h b/libs/ardour/ardour/midi_diskstream.h deleted file mode 100644 index ff66222d02..0000000000 --- a/libs/ardour/ardour/midi_diskstream.h +++ /dev/null @@ -1,194 +0,0 @@ -/* - Copyright (C) 2000 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. - - $Id: diskstream.h 579 2006-06-12 19:56:37Z essej $ -*/ - -#ifndef __ardour_midi_diskstream_h__ -#define __ardour_midi_diskstream_h__ - - -#include -#include -#include -#include -#include -#include - -#include - -#include "pbd/fastlog.h" -#include "pbd/ringbufferNPT.h" - -#include "ardour/ardour.h" -#include "ardour/diskstream.h" -#include "ardour/midi_buffer.h" -#include "ardour/utils.h" -#include "ardour/interpolation.h" - -struct tm; - -namespace ARDOUR { - -class IO; -class MidiEngine; -class MidiPlaylist; -class MidiPort; -class MidiRingbuffer; -class MidiSource; -class SMFSource; -class Send; -class Session; - -template class MidiRingBuffer; - -class LIBARDOUR_API MidiDiskstream : public Diskstream -{ - public: - MidiDiskstream (Session &, const std::string& name, Diskstream::Flag f = Recordable); - MidiDiskstream (Session &, const XMLNode&); - ~MidiDiskstream(); - - float playback_buffer_load() const; - float capture_buffer_load() const; - - void flush_playback (framepos_t, framepos_t); - - void set_record_enabled (bool yn); - void set_record_safe (bool yn); - - void reset_tracker (); - void resolve_tracker (Evoral::EventSink& buffer, framepos_t time); - - boost::shared_ptr midi_playlist (); - - int use_playlist (boost::shared_ptr); - int use_new_playlist (); - int use_copy_playlist (); - - bool set_name (std::string const &); - bool set_write_source_name (const std::string& str); - - /* stateful */ - XMLNode& get_state(void); - int set_state(const XMLNode&, int version); - - void ensure_input_monitoring (bool); - - boost::shared_ptr write_source () { return _write_source; } - - void set_note_mode (NoteMode m); - - /** Emitted when some MIDI data has been received for recording. - * Parameter is the source that it is destined for. - * A caller can get a copy of the data with get_gui_feed_buffer () - */ - PBD::Signal1 > DataRecorded; - - boost::shared_ptr get_gui_feed_buffer () const; - - protected: - friend class Session; - friend class Butler; - - /* the Session is the only point of access for these - because they require that the Session is "inactive" - while they are called. - */ - - void set_pending_overwrite(bool); - int overwrite_existing_buffers (); - void set_block_size (pframes_t); - int internal_playback_seek (framecnt_t distance); - int can_internal_playback_seek (framecnt_t distance); - std::string steal_write_source_name(); - void reset_write_sources (bool, bool force = false); - void non_realtime_input_change (); - void non_realtime_locate (framepos_t location); - - static void set_readahead_frames (framecnt_t frames_ahead) { midi_readahead = frames_ahead; } - - protected: - friend class MidiTrack; - friend class Auditioner; - - int seek (framepos_t which_sample, bool complete_refill = false); - int _do_refill_with_alloc (bool one_chunk_only); - int process (BufferSet&, framepos_t transport_frame, pframes_t nframes, framecnt_t &, bool need_diskstream); - frameoffset_t calculate_playback_distance (pframes_t nframes); - bool commit (framecnt_t nframes); - - static framecnt_t midi_readahead; - - private: - void get_playback (MidiBuffer& dst, framecnt_t); - - /* The two central butler operations */ - int do_flush (RunContext context, bool force = false); - int do_refill (); - - int read (framepos_t& start, framecnt_t cnt, bool reversed); - - void finish_capture (); - void transport_stopped_wallclock (struct tm&, time_t, bool abort); - void transport_looped (framepos_t transport_frame); - - void init (); - - int use_new_write_source (uint32_t n=0); - - int find_and_use_playlist (const std::string&); - - void allocate_temporary_buffers (); - - int use_pending_capture_data (XMLNode& node); - - void get_input_sources (); - void set_align_style_from_io(); - - /* fixed size buffers per instance of ardour for now (non-dynamic) - */ - - void adjust_playback_buffering () {} - void adjust_capture_buffering () {} - - bool prep_record_enable (); - bool prep_record_disable (); - - MidiRingBuffer* _playback_buf; - MidiRingBuffer* _capture_buf; - boost::weak_ptr _source_port; - boost::shared_ptr _write_source; - NoteMode _note_mode; - gint _frames_written_to_ringbuffer; - gint _frames_read_from_ringbuffer; - volatile gint _frames_pending_write; - volatile gint _num_captured_loops; - framepos_t _accumulated_capture_offset; - - /** A buffer that we use to put newly-arrived MIDI data in for - the GUI to read (so that it can update itself). - */ - MidiBuffer _gui_feed_buffer; - mutable Glib::Threads::Mutex _gui_feed_buffer_mutex; - - CubicMidiInterpolation interpolation; -}; - -}; /* namespace ARDOUR */ - -#endif /* __ardour_midi_diskstream_h__ */ diff --git a/libs/ardour/ardour/public_diskstream.h b/libs/ardour/ardour/public_diskstream.h deleted file mode 100644 index a80219ff80..0000000000 --- a/libs/ardour/ardour/public_diskstream.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - Copyright (C) 2010 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. - -*/ - -#ifndef __ardour_public_diskstream_h__ -#define __ardour_public_diskstream_h__ - -namespace ARDOUR { - -class Playlist; -class Source; -class Location; - -/** Public interface to a Diskstream */ -class LIBARDOUR_API PublicDiskstream -{ -public: - virtual ~PublicDiskstream() {} - - virtual boost::shared_ptr playlist () = 0; - virtual void request_input_monitoring (bool) = 0; - virtual void ensure_input_monitoring (bool) = 0; - virtual bool destructive () const = 0; - virtual std::list > & last_capture_sources () = 0; - virtual void set_capture_offset () = 0; - virtual std::string steal_write_source_name () = 0; - virtual void reset_write_sources (bool, bool force = false) = 0; - virtual float playback_buffer_load () const = 0; - virtual float capture_buffer_load () const = 0; - virtual int do_refill () = 0; - virtual int do_flush (RunContext, bool force = false) = 0; - virtual void set_pending_overwrite (bool) = 0; - virtual int seek (framepos_t, bool complete_refill = false) = 0; - virtual bool hidden () const = 0; - virtual int can_internal_playback_seek (framecnt_t) = 0; - virtual int internal_playback_seek (framecnt_t) = 0; - virtual void non_realtime_input_change () = 0; - virtual void non_realtime_locate (framepos_t) = 0; - virtual void non_realtime_set_speed () = 0; - virtual int overwrite_existing_buffers () = 0; - virtual framecnt_t get_captured_frames (uint32_t n = 0) const = 0; - virtual int set_loop (Location *) = 0; - virtual void transport_looped (framepos_t) = 0; - virtual bool realtime_set_speed (double, bool) = 0; - virtual void transport_stopped_wallclock (struct tm &, time_t, bool) = 0; - virtual bool pending_overwrite () const = 0; - virtual double speed () const = 0; - virtual void prepare_to_stop (framepos_t,framepos_t) = 0; - virtual void set_slaved (bool) = 0; - virtual ChanCount n_channels () = 0; - virtual framepos_t get_capture_start_frame (uint32_t n = 0) const = 0; - virtual AlignStyle alignment_style () const = 0; - virtual framepos_t current_capture_start () const = 0; - virtual framepos_t current_capture_end () const = 0; - virtual void playlist_modified () = 0; - // XXX DISK removed virtual int use_playlist (boost::shared_ptr) = 0; - virtual void set_align_style (AlignStyle, bool force=false) = 0; - virtual void set_align_choice (AlignChoice, bool force=false) = 0; - virtual int use_copy_playlist () = 0; - virtual int use_new_playlist () = 0; - virtual void adjust_playback_buffering () = 0; - virtual void adjust_capture_buffering () = 0; -}; - -} - -#endif diff --git a/libs/ardour/ardour/rc_configuration_vars.h b/libs/ardour/ardour/rc_configuration_vars.h index 66719e8d06..b0d8cf192e 100644 --- a/libs/ardour/ardour/rc_configuration_vars.h +++ b/libs/ardour/ardour/rc_configuration_vars.h @@ -89,8 +89,8 @@ CONFIG_VARIABLE (bool, use_tranzport, "use-tranzport", false) /* disk operations */ -CONFIG_VARIABLE (uint32_t, minimum_disk_read_bytes, "minimum-disk-read-bytes", ARDOUR::Diskstream::default_disk_read_chunk_frames() * sizeof (ARDOUR::Sample)) -CONFIG_VARIABLE (uint32_t, minimum_disk_write_bytes, "minimum-disk-write-bytes", ARDOUR::Diskstream::default_disk_write_chunk_frames() * sizeof (ARDOUR::Sample)) + CONFIG_VARIABLE (uint32_t, minimum_disk_read_bytes, "minimum-disk-read-bytes", ARDOUR::DiskReader::default_chunk_frames() * sizeof (ARDOUR::Sample)) +CONFIG_VARIABLE (uint32_t, minimum_disk_write_bytes, "minimum-disk-write-bytes", ARDOUR::DiskWriter::default_chunk_frames() * sizeof (ARDOUR::Sample)) CONFIG_VARIABLE (float, midi_readahead, "midi-readahead", 1.0) CONFIG_VARIABLE (BufferingPreset, buffering_preset, "buffering-preset", Medium) CONFIG_VARIABLE (float, audio_capture_buffer_seconds, "capture-buffer-seconds", 5.0) diff --git a/libs/ardour/audio_diskstream.cc b/libs/ardour/audio_diskstream.cc deleted file mode 100644 index 7ef5cf1c11..0000000000 --- a/libs/ardour/audio_diskstream.cc +++ /dev/null @@ -1,2551 +0,0 @@ -/* - 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 - 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 "pbd/gstdio_compat.h" -#include "pbd/error.h" -#include "pbd/xml++.h" -#include "pbd/memento_command.h" -#include "pbd/enumwriter.h" -#include "pbd/stateful_diff_command.h" - -#include "ardour/analyser.h" -#include "ardour/audio_buffer.h" -#include "ardour/audio_diskstream.h" -#include "ardour/audio_port.h" -#include "ardour/audioengine.h" -#include "ardour/audiofilesource.h" -#include "ardour/audioplaylist.h" -#include "ardour/audioregion.h" -#include "ardour/butler.h" -#include "ardour/debug.h" -#include "ardour/io.h" -#include "ardour/playlist_factory.h" -#include "ardour/profile.h" -#include "ardour/region_factory.h" -#include "ardour/session.h" -#include "ardour/session_playlists.h" -#include "ardour/sndfile_helpers.h" -#include "ardour/source_factory.h" -#include "ardour/track.h" -#include "ardour/types.h" -#include "ardour/utils.h" - -#include "pbd/i18n.h" -#include - -using namespace std; -using namespace ARDOUR; -using namespace PBD; - -Sample* AudioDiskstream::_mixdown_buffer = 0; -gain_t* AudioDiskstream::_gain_buffer = 0; - -AudioDiskstream::AudioDiskstream (Session &sess, const string &name, Diskstream::Flag flag) - : Diskstream(sess, name, flag) - , channels (new ChannelList) -{ - /* prevent any write sources from being created */ - - in_set_state = true; - use_new_playlist (); - in_set_state = false; - - if (flag & Destructive) { - use_destructive_playlist (); - } -} - -AudioDiskstream::AudioDiskstream (Session& sess, const XMLNode& node) - : Diskstream(sess, node) - , channels (new ChannelList) -{ - in_set_state = true; - init (); - - if (set_state (node, Stateful::loading_state_version)) { - in_set_state = false; - throw failed_constructor(); - } - - in_set_state = false; - - if (destructive()) { - use_destructive_playlist (); - } -} - -void -AudioDiskstream::init () -{ - /* there are no channels at this point, so these - two calls just get speed_buffer_size and wrap_buffer - size setup without duplicating their code. - */ - - set_block_size (_session.get_block_size()); - allocate_temporary_buffers (); -} - -AudioDiskstream::~AudioDiskstream () -{ - DEBUG_TRACE (DEBUG::Destruction, string_compose ("Audio Diskstream %1 destructor\n", _name)); - - { - RCUWriter writer (channels); - boost::shared_ptr c = writer.get_copy(); - - for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) { - delete *chan; - } - - c->clear(); - } - - channels.flush (); -} - -void -AudioDiskstream::allocate_working_buffers() -{ - /* with varifill buffer refilling, we compute the read size in bytes (to optimize - for disk i/o bandwidth) and then convert back into samples. These buffers - need to reflect the maximum size we could use, which is 4MB reads, or 2M samples - using 16 bit samples. - */ - _mixdown_buffer = new Sample[2*1048576]; - _gain_buffer = new gain_t[2*1048576]; -} - -void -AudioDiskstream::free_working_buffers() -{ - delete [] _mixdown_buffer; - delete [] _gain_buffer; - _mixdown_buffer = 0; - _gain_buffer = 0; -} - -void -AudioDiskstream::non_realtime_input_change () -{ - bool need_write_sources = false; - - { - Glib::Threads::Mutex::Lock lm (state_lock); - - if (input_change_pending.type == IOChange::NoChange) { - return; - } - - boost::shared_ptr cr = channels.reader(); - if (!cr->empty() && !cr->front()->write_source) { - need_write_sources = true; - } - - if (input_change_pending.type & IOChange::ConfigurationChanged) { - RCUWriter writer (channels); - boost::shared_ptr c = writer.get_copy(); - - _n_channels.set(DataType::AUDIO, c->size()); - - if (_io->n_ports().n_audio() > _n_channels.n_audio()) { - add_channel_to (c, _io->n_ports().n_audio() - _n_channels.n_audio()); - } else if (_io->n_ports().n_audio() < _n_channels.n_audio()) { - remove_channel_from (c, _n_channels.n_audio() - _io->n_ports().n_audio()); - } - - need_write_sources = true; - } - - if (input_change_pending.type & IOChange::ConnectionsChanged) { - get_input_sources (); - set_capture_offset (); - set_align_style_from_io (); - } - - input_change_pending = IOChange::NoChange; - - /* implicit unlock */ - } - - if (need_write_sources) { - reset_write_sources (false); - } - - /* now refill channel buffers */ - - if (speed() != 1.0f || speed() != -1.0f) { - seek ((framepos_t) (_session.transport_frame() * (double) speed())); - } else { - seek (_session.transport_frame()); - } -} - -void -AudioDiskstream::non_realtime_locate (framepos_t location) -{ - /* now refill channel buffers */ - - if (speed() != 1.0f || speed() != -1.0f) { - seek ((framepos_t) (location * (double) speed()), true); - } else { - seek (location, true); - } -} - -void -AudioDiskstream::get_input_sources () -{ - boost::shared_ptr c = channels.reader(); - - uint32_t n; - ChannelList::iterator chan; - uint32_t ni = _io->n_ports().n_audio(); - vector connections; - - for (n = 0, chan = c->begin(); chan != c->end() && n < ni; ++chan, ++n) { - - connections.clear (); - - if ((_io->nth (n).get()) && (_io->nth (n)->get_connections (connections) == 0)) { - if (!(*chan)->source.name.empty()) { - // _source->disable_metering (); - } - (*chan)->source.name = string(); - } else { - (*chan)->source.name = connections[0]; - } - } -} - -int -AudioDiskstream::find_and_use_playlist (const string& name) -{ - boost::shared_ptr playlist; - - if ((playlist = boost::dynamic_pointer_cast (_session.playlists->by_name (name))) == 0) { - playlist = boost::dynamic_pointer_cast (PlaylistFactory::create (DataType::AUDIO, _session, name)); - } - - if (!playlist) { - error << string_compose(_("AudioDiskstream: Playlist \"%1\" isn't an audio playlist"), name) << endmsg; - return -1; - } - - return use_playlist (playlist); -} - -int -AudioDiskstream::use_playlist (boost::shared_ptr playlist) -{ - assert(boost::dynamic_pointer_cast(playlist)); - - Diskstream::use_playlist(playlist); - - return 0; -} - -int -AudioDiskstream::use_new_playlist () -{ - string newname; - boost::shared_ptr playlist; - - if (!in_set_state && destructive()) { - return 0; - } - - if (_playlist) { - newname = Playlist::bump_name (_playlist->name(), _session); - } else { - newname = Playlist::bump_name (_name, _session); - } - - if ((playlist = boost::dynamic_pointer_cast (PlaylistFactory::create (DataType::AUDIO, _session, newname, hidden()))) != 0) { - - return use_playlist (playlist); - - } else { - return -1; - } -} - -int -AudioDiskstream::use_copy_playlist () -{ - assert(audio_playlist()); - - if (destructive()) { - return 0; - } - - if (_playlist == 0) { - error << string_compose(_("AudioDiskstream %1: there is no existing playlist to make a copy of!"), _name) << endmsg; - return -1; - } - - string newname; - boost::shared_ptr playlist; - - newname = Playlist::bump_name (_playlist->name(), _session); - - if ((playlist = boost::dynamic_pointer_cast(PlaylistFactory::create (audio_playlist(), newname))) != 0) { - playlist->reset_shares(); - return use_playlist (playlist); - } else { - return -1; - } -} - -void -AudioDiskstream::setup_destructive_playlist () -{ - SourceList srcs; - boost::shared_ptr c = channels.reader(); - - for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) { - srcs.push_back ((*chan)->write_source); - } - - /* a single full-sized region */ - - assert (!srcs.empty ()); - - PropertyList plist; - plist.add (Properties::name, _name.val()); - plist.add (Properties::start, 0); - plist.add (Properties::length, max_framepos - srcs.front()->natural_position()); - - boost::shared_ptr region (RegionFactory::create (srcs, plist)); - _playlist->add_region (region, srcs.front()->natural_position()); - - /* apply region properties and update write sources */ - use_destructive_playlist(); -} - -void -AudioDiskstream::use_destructive_playlist () -{ - /* this is called from the XML-based constructor or ::set_destructive. when called, - we already have a playlist and a region, but we need to - set up our sources for write. we use the sources associated - with the (presumed single, full-extent) region. - */ - - boost::shared_ptr rp; - { - const RegionList& rl (_playlist->region_list_property().rlist()); - if (rl.size() > 0) { - /* this can happen when dragging a region onto a tape track */ - assert((rl.size() == 1)); - rp = rl.front(); - } - } - - if (!rp) { - reset_write_sources (false, true); - return; - } - - boost::shared_ptr region = boost::dynamic_pointer_cast (rp); - - if (region == 0) { - throw failed_constructor(); - } - - /* be sure to stretch the region out to the maximum length (non-musical)*/ - - region->set_length (max_framepos - region->position(), 0); - - uint32_t n; - ChannelList::iterator chan; - boost::shared_ptr c = channels.reader(); - - for (n = 0, chan = c->begin(); chan != c->end(); ++chan, ++n) { - (*chan)->write_source = boost::dynamic_pointer_cast(region->source (n)); - assert((*chan)->write_source); - (*chan)->write_source->set_allow_remove_if_empty (false); - - /* this might be false if we switched modes, so force it */ - - // should be set when creating the source or loading the state - assert ((*chan)->write_source->destructive()); - } - - /* the source list will never be reset for a destructive track */ -} - -void -AudioDiskstream::prepare_record_status(framepos_t capture_start_frame) -{ - if (recordable() && destructive()) { - boost::shared_ptr c = channels.reader(); - for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) { - - RingBufferNPT::rw_vector transitions; - (*chan)->capture_transition_buf->get_write_vector (&transitions); - - if (transitions.len[0] > 0) { - transitions.buf[0]->type = CaptureStart; - transitions.buf[0]->capture_val = capture_start_frame; - (*chan)->capture_transition_buf->increment_write_ptr(1); - } else { - // bad! - fatal << X_("programming error: capture_transition_buf is full on rec start! inconceivable!") - << endmsg; - } - } - } -} - - -/** Do some record stuff [not described in this comment!] - * - * Also: - * - Setup playback_distance with the nframes, or nframes adjusted - * for current varispeed, if appropriate. - * - Setup current_playback_buffer in each ChannelInfo to point to data - * that someone can read playback_distance worth of data from. - */ -int -AudioDiskstream::process (BufferSet& bufs, framepos_t transport_frame, pframes_t nframes, framecnt_t& playback_distance, bool need_disk_signal) -{ - uint32_t n; - boost::shared_ptr c = channels.reader(); - ChannelList::iterator chan; - framecnt_t rec_offset = 0; - framecnt_t rec_nframes = 0; - bool collect_playback = false; - bool can_record = _session.actively_recording (); - - playback_distance = 0; - - if (!_io || !_io->active()) { - return 0; - } - - check_record_status (transport_frame, can_record); - - if (nframes == 0) { - return 0; - } - - Glib::Threads::Mutex::Lock sm (state_lock, Glib::Threads::TRY_LOCK); - - if (!sm.locked()) { - return 1; - } - - adjust_capture_position = 0; - - for (chan = c->begin(); chan != c->end(); ++chan) { - (*chan)->current_capture_buffer = 0; - (*chan)->current_playback_buffer = 0; - } - - // Safeguard against situations where process() goes haywire when autopunching - // and last_recordable_frame < first_recordable_frame - - if (last_recordable_frame < first_recordable_frame) { - last_recordable_frame = max_framepos; - } - - if (record_enabled()) { - - Evoral::OverlapType ot = Evoral::coverage (first_recordable_frame, last_recordable_frame, transport_frame, transport_frame + nframes); - // XXX should this be transport_frame + nframes - 1 ? coverage() expects its parameter ranges to include their end points - // XXX also, first_recordable_frame & last_recordable_frame may both be == max_framepos: coverage() will return OverlapNone in that case. Is thak OK? - calculate_record_range (ot, transport_frame, nframes, rec_nframes, rec_offset); - - DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1: this time record %2 of %3 frames, offset %4\n", _name, rec_nframes, nframes, rec_offset)); - - if (rec_nframes && !was_recording) { - capture_captured = 0; - was_recording = true; - } - } - - if (can_record && !_last_capture_sources.empty()) { - _last_capture_sources.clear (); - } - - if (rec_nframes) { - - uint32_t limit = _io->n_ports ().n_audio(); - - /* one or more ports could already have been removed from _io, but our - channel setup hasn't yet been updated. prevent us from trying to - use channels that correspond to missing ports. note that the - process callback (from which this is called) is always atomic - with respect to port removal/addition. - */ - - for (n = 0, chan = c->begin(); chan != c->end() && n < limit; ++chan, ++n) { - - ChannelInfo* chaninfo (*chan); - - chaninfo->capture_buf->get_write_vector (&chaninfo->capture_vector); - - if (rec_nframes <= (framecnt_t) chaninfo->capture_vector.len[0]) { - - chaninfo->current_capture_buffer = chaninfo->capture_vector.buf[0]; - - /* note: grab the entire port buffer, but only copy what we were supposed to - for recording, and use rec_offset - */ - - boost::shared_ptr const ap = _io->audio (n); - assert(ap); - assert(rec_nframes <= (framecnt_t) ap->get_audio_buffer(nframes).capacity()); - - Sample *buf = bufs.get_audio (n).data(rec_offset); - memcpy (chaninfo->current_capture_buffer, buf, sizeof (Sample) * rec_nframes); - - } else { - - framecnt_t total = chaninfo->capture_vector.len[0] + chaninfo->capture_vector.len[1]; - - if (rec_nframes > total) { - DEBUG_TRACE (DEBUG::Butler, string_compose ("%1 overrun in %2, rec_nframes = %3 total space = %4\n", - DEBUG_THREAD_SELF, name(), rec_nframes, total)); - DiskOverrun (); - return -1; - } - - boost::shared_ptr const ap = _io->audio (n); - assert(ap); - - Sample *buf = bufs.get_audio (n).data(rec_offset); - framecnt_t first = chaninfo->capture_vector.len[0]; - - memcpy (chaninfo->capture_wrap_buffer, buf, sizeof (Sample) * first); - memcpy (chaninfo->capture_vector.buf[0], buf, sizeof (Sample) * first); - memcpy (chaninfo->capture_wrap_buffer+first, buf + first, sizeof (Sample) * (rec_nframes - first)); - memcpy (chaninfo->capture_vector.buf[1], buf + first, sizeof (Sample) * (rec_nframes - first)); - - chaninfo->current_capture_buffer = chaninfo->capture_wrap_buffer; - } - } - - } else { - - if (was_recording) { - finish_capture (c); - } - - } - - if (rec_nframes) { - - /* data will be written to disk */ - - if (rec_nframes == nframes && rec_offset == 0) { - - for (chan = c->begin(); chan != c->end(); ++chan) { - (*chan)->current_playback_buffer = (*chan)->current_capture_buffer; - } - - playback_distance = nframes; - - } else { - - - /* we can't use the capture buffer as the playback buffer, because - we recorded only a part of the current process' cycle data - for capture. - */ - - collect_playback = true; - } - - adjust_capture_position = rec_nframes; - - } else if (can_record && record_enabled()) { - - /* can't do actual capture yet - waiting for latency effects to finish before we start*/ - - for (chan = c->begin(); chan != c->end(); ++chan) { - (*chan)->current_playback_buffer = (*chan)->current_capture_buffer; - } - - playback_distance = nframes; - - } else { - - collect_playback = true; - } - - if ((_track->monitoring_state () & MonitoringDisk) || collect_playback) { - - /* we're doing playback */ - - framecnt_t necessary_samples; - - /* no varispeed playback if we're recording, because the output .... TBD */ - - if (rec_nframes == 0 && _actual_speed != 1.0) { - necessary_samples = (framecnt_t) ceil ((nframes * fabs (_actual_speed))) + 2; - } else { - necessary_samples = nframes; - } - - for (chan = c->begin(); chan != c->end(); ++chan) { - (*chan)->playback_buf->get_read_vector (&(*chan)->playback_vector); - } - - n = 0; - - /* Setup current_playback_buffer in each ChannelInfo to point to data that someone - can read necessary_samples (== nframes at a transport speed of 1) worth of data - from right now. - */ - - for (chan = c->begin(); chan != c->end(); ++chan, ++n) { - - ChannelInfo* chaninfo (*chan); - - if (necessary_samples <= (framecnt_t) chaninfo->playback_vector.len[0]) { - /* There are enough samples in the first part of the ringbuffer */ - chaninfo->current_playback_buffer = chaninfo->playback_vector.buf[0]; - - } else { - framecnt_t total = chaninfo->playback_vector.len[0] + chaninfo->playback_vector.len[1]; - - if (necessary_samples > total) { - cerr << _name << " Need " << necessary_samples << " total = " << total << endl; - cerr << "underrun for " << _name << endl; - DEBUG_TRACE (DEBUG::Butler, string_compose ("%1 underrun in %2, rec_nframes = %3 total space = %4\n", - DEBUG_THREAD_SELF, name(), rec_nframes, total)); - DiskUnderrun (); - return -1; - - } else { - - /* We have enough samples, but not in one lump. Coalesce the two parts - into one in playback_wrap_buffer in our ChannelInfo, and specify that - as our current_playback_buffer. - */ - - assert(wrap_buffer_size >= necessary_samples); - - /* Copy buf[0] from playback_buf */ - memcpy ((char *) chaninfo->playback_wrap_buffer, - chaninfo->playback_vector.buf[0], - chaninfo->playback_vector.len[0] * sizeof (Sample)); - - /* Copy buf[1] from playback_buf */ - memcpy (chaninfo->playback_wrap_buffer + chaninfo->playback_vector.len[0], - chaninfo->playback_vector.buf[1], - (necessary_samples - chaninfo->playback_vector.len[0]) - * sizeof (Sample)); - - chaninfo->current_playback_buffer = chaninfo->playback_wrap_buffer; - } - } - } - - if (rec_nframes == 0 && _actual_speed != 1.0f && _actual_speed != -1.0f) { - - interpolation.set_speed (_target_speed); - - int channel = 0; - for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan, ++channel) { - ChannelInfo* chaninfo (*chan); - - playback_distance = interpolation.interpolate ( - channel, nframes, chaninfo->current_playback_buffer, chaninfo->speed_buffer); - - chaninfo->current_playback_buffer = chaninfo->speed_buffer; - } - - } else { - playback_distance = nframes; - } - - _speed = _target_speed; - } - - if (need_disk_signal) { - - /* copy data over to buffer set */ - - size_t n_buffers = bufs.count().n_audio(); - size_t n_chans = c->size(); - gain_t scaling = 1.0f; - - if (n_chans > n_buffers) { - scaling = ((float) n_buffers)/n_chans; - } - - for (n = 0, chan = c->begin(); chan != c->end(); ++chan, ++n) { - - AudioBuffer& buf (bufs.get_audio (n%n_buffers)); - ChannelInfo* chaninfo (*chan); - - if (n < n_chans) { - if (scaling != 1.0f) { - buf.read_from_with_gain (chaninfo->current_playback_buffer, nframes, scaling); - } else { - buf.read_from (chaninfo->current_playback_buffer, nframes); - } - } else { - if (scaling != 1.0f) { - buf.accumulate_with_gain_from (chaninfo->current_playback_buffer, nframes, scaling); - } else { - buf.accumulate_from (chaninfo->current_playback_buffer, nframes); - } - } - } - - /* leave the MIDI count alone */ - ChanCount cnt (DataType::AUDIO, n_chans); - cnt.set (DataType::MIDI, bufs.count().n_midi()); - bufs.set_count (cnt); - - /* extra buffers will already be silent, so leave them alone */ - } - - return 0; -} - -frameoffset_t -AudioDiskstream::calculate_playback_distance (pframes_t nframes) -{ - frameoffset_t playback_distance = nframes; - - if (record_enabled()) { - playback_distance = nframes; - } else if (_actual_speed != 1.0f && _actual_speed != -1.0f) { - interpolation.set_speed (_target_speed); - boost::shared_ptr c = channels.reader(); - int channel = 0; - for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan, ++channel) { - playback_distance = interpolation.interpolate (channel, nframes, NULL, NULL); - } - } else { - playback_distance = nframes; - } - - if (_actual_speed < 0.0) { - return -playback_distance; - } else { - return playback_distance; - } -} - -/** Update various things including playback_sample, read pointer on each channel's playback_buf - * and write pointer on each channel's capture_buf. Also wout whether the butler is needed. - * @return true if the butler is required. - */ -bool -AudioDiskstream::commit (framecnt_t playback_distance) -{ - bool need_butler = false; - - if (!_io || !_io->active()) { - return false; - } - - if (_actual_speed < 0.0) { - playback_sample -= playback_distance; - } else { - playback_sample += playback_distance; - } - - boost::shared_ptr c = channels.reader(); - for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) { - - (*chan)->playback_buf->increment_read_ptr (playback_distance); - - if (adjust_capture_position) { - (*chan)->capture_buf->increment_write_ptr (adjust_capture_position); - } - } - - if (adjust_capture_position != 0) { - capture_captured += adjust_capture_position; - DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1 now captured %2 (by %3)\n", name(), capture_captured, adjust_capture_position)); - adjust_capture_position = 0; - } - - if (c->empty()) { - return false; - } - - if (_slaved) { - if (_io && _io->active()) { - need_butler = c->front()->playback_buf->write_space() >= c->front()->playback_buf->bufsize() / 2; - } else { - need_butler = false; - } - } else { - if (_io && _io->active()) { - need_butler = ((framecnt_t) c->front()->playback_buf->write_space() >= disk_read_chunk_frames) - || ((framecnt_t) c->front()->capture_buf->read_space() >= disk_write_chunk_frames); - } else { - need_butler = ((framecnt_t) c->front()->capture_buf->read_space() >= disk_write_chunk_frames); - } - } - - return need_butler; -} - -void -AudioDiskstream::set_pending_overwrite (bool yn) -{ - /* called from audio thread, so we can use the read ptr and playback sample as we wish */ - - _pending_overwrite = yn; - - overwrite_frame = playback_sample; - - boost::shared_ptr c = channels.reader (); - if (!c->empty ()) { - overwrite_offset = c->front()->playback_buf->get_read_ptr(); - } -} - -int -AudioDiskstream::overwrite_existing_buffers () -{ - boost::shared_ptr c = channels.reader(); - if (c->empty ()) { - _pending_overwrite = false; - return 0; - } - - Sample* mixdown_buffer; - float* gain_buffer; - int ret = -1; - bool reversed = (_visible_speed * _session.transport_speed()) < 0.0f; - - overwrite_queued = false; - - /* assume all are the same size */ - framecnt_t size = c->front()->playback_buf->bufsize(); - - mixdown_buffer = new Sample[size]; - gain_buffer = new float[size]; - - /* reduce size so that we can fill the buffer correctly (ringbuffers - can only handle size-1, otherwise they appear to be empty) - */ - size--; - - uint32_t n=0; - framepos_t start; - - for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan, ++n) { - - start = overwrite_frame; - framecnt_t cnt = size; - - /* to fill the buffer without resetting the playback sample, we need to - do it one or two chunks (normally two). - - |----------------------------------------------------------------------| - - ^ - overwrite_offset - |<- second chunk->||<----------------- first chunk ------------------>| - - */ - - framecnt_t to_read = size - overwrite_offset; - - if (read ((*chan)->playback_buf->buffer() + overwrite_offset, mixdown_buffer, gain_buffer, start, to_read, n, reversed)) { - error << string_compose(_("AudioDiskstream %1: when refilling, cannot read %2 from playlist at frame %3"), - id(), size, playback_sample) << endmsg; - goto out; - } - - if (cnt > to_read) { - - cnt -= to_read; - - if (read ((*chan)->playback_buf->buffer(), mixdown_buffer, gain_buffer, start, cnt, n, reversed)) { - error << string_compose(_("AudioDiskstream %1: when refilling, cannot read %2 from playlist at frame %3"), - id(), size, playback_sample) << endmsg; - goto out; - } - } - } - - ret = 0; - - out: - _pending_overwrite = false; - delete [] gain_buffer; - delete [] mixdown_buffer; - return ret; -} - -int -AudioDiskstream::seek (framepos_t frame, bool complete_refill) -{ - uint32_t n; - int ret = -1; - ChannelList::iterator chan; - boost::shared_ptr c = channels.reader(); - - Glib::Threads::Mutex::Lock lm (state_lock); - - for (n = 0, chan = c->begin(); chan != c->end(); ++chan, ++n) { - (*chan)->playback_buf->reset (); - (*chan)->capture_buf->reset (); - } - - /* can't rec-enable in destructive mode if transport is before start */ - - if (destructive() && record_enabled() && frame < _session.current_start_frame()) { - disengage_record_enable (); - } - - playback_sample = frame; - file_frame = frame; - - if (complete_refill) { - /* call _do_refill() to refill the entire buffer, using - the largest reads possible. - */ - while ((ret = do_refill_with_alloc (false)) > 0) ; - } else { - /* call _do_refill() to refill just one chunk, and then - return. - */ - ret = do_refill_with_alloc (true); - } - - return ret; -} - -int -AudioDiskstream::can_internal_playback_seek (framecnt_t distance) -{ - ChannelList::iterator chan; - boost::shared_ptr c = channels.reader(); - - for (chan = c->begin(); chan != c->end(); ++chan) { - if ((*chan)->playback_buf->read_space() < (size_t) distance) { - return false; - } - } - return true; -} - -int -AudioDiskstream::internal_playback_seek (framecnt_t distance) -{ - ChannelList::iterator chan; - boost::shared_ptr c = channels.reader(); - - for (chan = c->begin(); chan != c->end(); ++chan) { - (*chan)->playback_buf->increment_read_ptr (::llabs(distance)); - } - - if (first_recordable_frame < max_framepos) { - first_recordable_frame += distance; - } - playback_sample += distance; - - return 0; -} - -/** Read some data for 1 channel from our playlist into a buffer. - * @param buf Buffer to write to. - * @param start Session frame to start reading from; updated to where we end up - * after the read. - * @param cnt Count of samples to read. - * @param reversed true if we are running backwards, otherwise false. - */ -int -AudioDiskstream::read (Sample* buf, Sample* mixdown_buffer, float* gain_buffer, - framepos_t& start, framecnt_t cnt, - int channel, bool reversed) -{ - framecnt_t this_read = 0; - bool reloop = false; - framepos_t loop_end = 0; - framepos_t loop_start = 0; - framecnt_t offset = 0; - Location *loc = 0; - - /* XXX we don't currently play loops in reverse. not sure why */ - - if (!reversed) { - - framecnt_t loop_length = 0; - - /* Make the use of a Location atomic for this read operation. - - Note: Locations don't get deleted, so all we care about - when I say "atomic" is that we are always pointing to - the same one and using a start/length values obtained - just once. - */ - - if ((loc = loop_location) != 0) { - loop_start = loc->start(); - loop_end = loc->end(); - loop_length = loop_end - loop_start; - } - - /* if we are looping, ensure that the first frame we read is at the correct - position within the loop. - */ - - if (loc && start >= loop_end) { - start = loop_start + ((start - loop_start) % loop_length); - } - - } - - if (reversed) { - start -= cnt; - } - - /* We need this while loop in case we hit a loop boundary, in which case our read from - the playlist must be split into more than one section. - */ - - while (cnt) { - - /* take any loop into account. we can't read past the end of the loop. */ - - if (loc && (loop_end - start < cnt)) { - this_read = loop_end - start; - reloop = true; - } else { - reloop = false; - this_read = cnt; - } - - if (this_read == 0) { - break; - } - - this_read = min(cnt,this_read); - - if (audio_playlist()->read (buf+offset, mixdown_buffer, gain_buffer, start, this_read, channel) != this_read) { - error << string_compose(_("AudioDiskstream %1: cannot read %2 from playlist at frame %3"), id(), this_read, - start) << endmsg; - return -1; - } - - if (reversed) { - - swap_by_ptr (buf, buf + this_read - 1); - - } else { - - /* if we read to the end of the loop, go back to the beginning */ - - if (reloop) { - start = loop_start; - } else { - start += this_read; - } - } - - cnt -= this_read; - offset += this_read; - } - - return 0; -} - -int -AudioDiskstream::_do_refill_with_alloc (bool partial_fill) -{ - /* We limit disk reads to at most 4MB chunks, which with floating point - samples would be 1M samples. But we might use 16 or 14 bit samples, - in which case 4MB is more samples than that. Therefore size this for - the smallest sample value .. 4MB = 2M samples (16 bit). - */ - - Sample* mix_buf = new Sample[2*1048576]; - float* gain_buf = new float[2*1048576]; - - int ret = _do_refill (mix_buf, gain_buf, (partial_fill ? disk_read_chunk_frames : 0)); - - delete [] mix_buf; - delete [] gain_buf; - - return ret; -} - -/** Get some more data from disk and put it in our channels' playback_bufs, - * if there is suitable space in them. - * - * If fill_level is non-zero, then we will refill the buffer so that there is - * still at least fill_level samples of space left to be filled. This is used - * after locates so that we do not need to wait to fill the entire buffer. - * - */ - -int -AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer, framecnt_t fill_level) -{ - int32_t ret = 0; - framecnt_t to_read; - RingBufferNPT::rw_vector vector; - bool const reversed = (_visible_speed * _session.transport_speed()) < 0.0f; - framecnt_t total_space; - framecnt_t zero_fill; - uint32_t chan_n; - ChannelList::iterator i; - boost::shared_ptr c = channels.reader(); - framecnt_t ts; - - /* do not read from disk while session is marked as Loading, to avoid - useless redundant I/O. - */ - - if (_session.state_of_the_state() & Session::Loading) { - return 0; - } - - if (c->empty()) { - return 0; - } - - assert(mixdown_buffer); - assert(gain_buffer); - - vector.buf[0] = 0; - vector.len[0] = 0; - vector.buf[1] = 0; - vector.len[1] = 0; - - c->front()->playback_buf->get_write_vector (&vector); - - if ((total_space = vector.len[0] + vector.len[1]) == 0) { - /* nowhere to write to */ - return 0; - } - - if (fill_level) { - if (fill_level < total_space) { - total_space -= fill_level; - } else { - /* we can't do anything with it */ - fill_level = 0; - } - } - - /* if we're running close to normal speed and there isn't enough - space to do disk_read_chunk_frames of I/O, then don't bother. - - at higher speeds, just do it because the sync between butler - and audio thread may not be good enough. - - Note: it is a design assumption that disk_read_chunk_frames is smaller - than the playback buffer size, so this check should never trip when - the playback buffer is empty. - */ - - if ((total_space < disk_read_chunk_frames) && fabs (_actual_speed) < 2.0f) { - return 0; - } - - /* when slaved, don't try to get too close to the read pointer. this - leaves space for the buffer reversal to have something useful to - work with. - */ - - if (_slaved && total_space < (framecnt_t) (c->front()->playback_buf->bufsize() / 2)) { - return 0; - } - - if (reversed) { - - if (file_frame == 0) { - - /* at start: nothing to do but fill with silence */ - - for (chan_n = 0, i = c->begin(); i != c->end(); ++i, ++chan_n) { - - ChannelInfo* chan (*i); - chan->playback_buf->get_write_vector (&vector); - memset (vector.buf[0], 0, sizeof(Sample) * vector.len[0]); - if (vector.len[1]) { - memset (vector.buf[1], 0, sizeof(Sample) * vector.len[1]); - } - chan->playback_buf->increment_write_ptr (vector.len[0] + vector.len[1]); - } - return 0; - } - - if (file_frame < total_space) { - - /* too close to the start: read what we can, - and then zero fill the rest - */ - - zero_fill = total_space - file_frame; - total_space = file_frame; - - } else { - - zero_fill = 0; - } - - } else { - - if (file_frame == max_framepos) { - - /* at end: nothing to do but fill with silence */ - - for (chan_n = 0, i = c->begin(); i != c->end(); ++i, ++chan_n) { - - ChannelInfo* chan (*i); - chan->playback_buf->get_write_vector (&vector); - memset (vector.buf[0], 0, sizeof(Sample) * vector.len[0]); - if (vector.len[1]) { - memset (vector.buf[1], 0, sizeof(Sample) * vector.len[1]); - } - chan->playback_buf->increment_write_ptr (vector.len[0] + vector.len[1]); - } - return 0; - } - - if (file_frame > max_framepos - total_space) { - - /* to close to the end: read what we can, and zero fill the rest */ - - zero_fill = total_space - (max_framepos - file_frame); - total_space = max_framepos - file_frame; - - } else { - zero_fill = 0; - } - } - - framepos_t file_frame_tmp = 0; - - /* total_space is in samples. We want to optimize read sizes in various sizes using bytes */ - - const size_t bits_per_sample = format_data_width (_session.config.get_native_file_data_format()); - size_t total_bytes = total_space * bits_per_sample / 8; - - /* chunk size range is 256kB to 4MB. Bigger is faster in terms of MB/sec, but bigger chunk size always takes longer - */ - size_t byte_size_for_read = max ((size_t) (256 * 1024), min ((size_t) (4 * 1048576), total_bytes)); - - /* find nearest (lower) multiple of 16384 */ - - byte_size_for_read = (byte_size_for_read / 16384) * 16384; - - /* now back to samples */ - - framecnt_t samples_to_read = byte_size_for_read / (bits_per_sample / 8); - - //cerr << name() << " will read " << byte_size_for_read << " out of total bytes " << total_bytes << " in buffer of " - // << c->front()->playback_buf->bufsize() * bits_per_sample / 8 << " bps = " << bits_per_sample << endl; - // cerr << name () << " read samples = " << samples_to_read << " out of total space " << total_space << " in buffer of " << c->front()->playback_buf->bufsize() << " samples\n"; - - // uint64_t before = g_get_monotonic_time (); - // uint64_t elapsed; - - for (chan_n = 0, i = c->begin(); i != c->end(); ++i, ++chan_n) { - - ChannelInfo* chan (*i); - Sample* buf1; - Sample* buf2; - framecnt_t len1, len2; - - chan->playback_buf->get_write_vector (&vector); - - if ((framecnt_t) vector.len[0] > samples_to_read) { - - /* we're not going to fill the first chunk, so certainly do not bother with the - other part. it won't be connected with the part we do fill, as in: - - .... => writable space - ++++ => readable space - ^^^^ => 1 x disk_read_chunk_frames that would be filled - - |......|+++++++++++++|...............................| - buf1 buf0 - ^^^^^^^^^^^^^^^ - - - So, just pretend that the buf1 part isn't there. - - */ - - vector.buf[1] = 0; - vector.len[1] = 0; - - } - - ts = total_space; - file_frame_tmp = file_frame; - - buf1 = vector.buf[0]; - len1 = vector.len[0]; - buf2 = vector.buf[1]; - len2 = vector.len[1]; - - to_read = min (ts, len1); - to_read = min (to_read, (framecnt_t) samples_to_read); - - assert (to_read >= 0); - - if (to_read) { - - if (read (buf1, mixdown_buffer, gain_buffer, file_frame_tmp, to_read, chan_n, reversed)) { - ret = -1; - goto out; - } - - chan->playback_buf->increment_write_ptr (to_read); - ts -= to_read; - } - - to_read = min (ts, len2); - - if (to_read) { - - /* we read all of vector.len[0], but it wasn't the - entire samples_to_read of data, so read some or - all of vector.len[1] as well. - */ - - if (read (buf2, mixdown_buffer, gain_buffer, file_frame_tmp, to_read, chan_n, reversed)) { - ret = -1; - goto out; - } - - chan->playback_buf->increment_write_ptr (to_read); - } - - if (zero_fill) { - /* XXX: do something */ - } - - } - - // elapsed = g_get_monotonic_time () - before; - // cerr << "\tbandwidth = " << (byte_size_for_read / 1048576.0) / (elapsed/1000000.0) << "MB/sec\n"; - - file_frame = file_frame_tmp; - assert (file_frame >= 0); - - ret = ((total_space - samples_to_read) > disk_read_chunk_frames); - - c->front()->playback_buf->get_write_vector (&vector); - - out: - return ret; -} - -/** Flush pending data to disk. - * - * Important note: this function will write *AT MOST* disk_write_chunk_frames - * of data to disk. it will never write more than that. If it writes that - * much and there is more than that waiting to be written, it will return 1, - * otherwise 0 on success or -1 on failure. - * - * If there is less than disk_write_chunk_frames to be written, no data will be - * written at all unless @a force_flush is true. - */ -int -AudioDiskstream::do_flush (RunContext /*context*/, bool force_flush) -{ - uint32_t to_write; - int32_t ret = 0; - RingBufferNPT::rw_vector vector; - RingBufferNPT::rw_vector transvec; - framecnt_t total; - - transvec.buf[0] = 0; - transvec.buf[1] = 0; - vector.buf[0] = 0; - vector.buf[1] = 0; - - boost::shared_ptr c = channels.reader(); - for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) { - - (*chan)->capture_buf->get_read_vector (&vector); - - total = vector.len[0] + vector.len[1]; - - if (total == 0 || (total < disk_write_chunk_frames && !force_flush && was_recording)) { - goto out; - } - - /* if there are 2+ chunks of disk i/o possible for - this track, let the caller know so that it can arrange - for us to be called again, ASAP. - - if we are forcing a flush, then if there is* any* extra - work, let the caller know. - - if we are no longer recording and there is any extra work, - let the caller know too. - */ - - if (total >= 2 * disk_write_chunk_frames || ((force_flush || !was_recording) && total > disk_write_chunk_frames)) { - ret = 1; - } - - to_write = min (disk_write_chunk_frames, (framecnt_t) vector.len[0]); - - // check the transition buffer when recording destructive - // important that we get this after the capture buf - - if (destructive()) { - (*chan)->capture_transition_buf->get_read_vector(&transvec); - size_t transcount = transvec.len[0] + transvec.len[1]; - size_t ti; - - for (ti=0; ti < transcount; ++ti) { - CaptureTransition & captrans = (ti < transvec.len[0]) ? transvec.buf[0][ti] : transvec.buf[1][ti-transvec.len[0]]; - - if (captrans.type == CaptureStart) { - // by definition, the first data we got above represents the given capture pos - - (*chan)->write_source->mark_capture_start (captrans.capture_val); - (*chan)->curr_capture_cnt = 0; - - } else if (captrans.type == CaptureEnd) { - - // capture end, the capture_val represents total frames in capture - - if (captrans.capture_val <= (*chan)->curr_capture_cnt + to_write) { - - // shorten to make the write a perfect fit - uint32_t nto_write = (captrans.capture_val - (*chan)->curr_capture_cnt); - - if (nto_write < to_write) { - ret = 1; // should we? - } - to_write = nto_write; - - (*chan)->write_source->mark_capture_end (); - - // increment past this transition, but go no further - ++ti; - break; - } - else { - // actually ends just beyond this chunk, so force more work - ret = 1; - break; - } - } - } - - if (ti > 0) { - (*chan)->capture_transition_buf->increment_read_ptr(ti); - } - } - - if ((!(*chan)->write_source) || (*chan)->write_source->write (vector.buf[0], to_write) != to_write) { - error << string_compose(_("AudioDiskstream %1: cannot write to disk"), id()) << endmsg; - return -1; - } - - (*chan)->capture_buf->increment_read_ptr (to_write); - (*chan)->curr_capture_cnt += to_write; - - if ((to_write == vector.len[0]) && (total > to_write) && (to_write < disk_write_chunk_frames) && !destructive()) { - - /* we wrote all of vector.len[0] but it wasn't an entire - disk_write_chunk_frames of data, so arrange for some part - of vector.len[1] to be flushed to disk as well. - */ - - to_write = min ((framecnt_t)(disk_write_chunk_frames - to_write), (framecnt_t) vector.len[1]); - - DEBUG_TRACE (DEBUG::Butler, string_compose ("%1 additional write of %2\n", name(), to_write)); - - if ((*chan)->write_source->write (vector.buf[1], to_write) != to_write) { - error << string_compose(_("AudioDiskstream %1: cannot write to disk"), id()) << endmsg; - return -1; - } - - (*chan)->capture_buf->increment_read_ptr (to_write); - (*chan)->curr_capture_cnt += to_write; - } - } - - out: - return ret; -} - -void -AudioDiskstream::transport_stopped_wallclock (struct tm& when, time_t twhen, bool abort_capture) -{ - uint32_t buffer_position; - bool more_work = true; - int err = 0; - boost::shared_ptr region; - framecnt_t total_capture; - SourceList srcs; - SourceList::iterator src; - ChannelList::iterator chan; - vector::iterator ci; - boost::shared_ptr c = channels.reader(); - uint32_t n = 0; - bool mark_write_completed = false; - - finish_capture (c); - - /* butler is already stopped, but there may be work to do - to flush remaining data to disk. - */ - - while (more_work && !err) { - switch (do_flush (TransportContext, true)) { - case 0: - more_work = false; - break; - case 1: - break; - case -1: - error << string_compose(_("AudioDiskstream \"%1\": cannot flush captured data to disk!"), _name) << endmsg; - err++; - } - } - - /* XXX is there anything we can do if err != 0 ? */ - Glib::Threads::Mutex::Lock lm (capture_info_lock); - - if (capture_info.empty()) { - return; - } - - if (abort_capture) { - - if (destructive()) { - goto outout; - } - - for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) { - - if ((*chan)->write_source) { - - (*chan)->write_source->mark_for_remove (); - (*chan)->write_source->drop_references (); - (*chan)->write_source.reset (); - } - - /* new source set up in "out" below */ - } - - goto out; - } - - for (total_capture = 0, ci = capture_info.begin(); ci != capture_info.end(); ++ci) { - total_capture += (*ci)->frames; - } - - /* figure out the name for this take */ - - for (n = 0, chan = c->begin(); chan != c->end(); ++chan, ++n) { - - boost::shared_ptr s = (*chan)->write_source; - - if (s) { - srcs.push_back (s); - s->update_header (capture_info.front()->start, when, twhen); - s->set_captured_for (_name.val()); - s->mark_immutable (); - - if (Config->get_auto_analyse_audio()) { - Analyser::queue_source_for_analysis (s, true); - } - - DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("newly captured source %1 length %2\n", s->path(), s->length (0))); - } - } - - /* destructive tracks have a single, never changing region */ - - if (destructive()) { - - /* send a signal that any UI can pick up to do the right thing. there is - a small problem here in that a UI may need the peak data to be ready - for the data that was recorded and this isn't interlocked with that - process. this problem is deferred to the UI. - */ - - _playlist->LayeringChanged(); // XXX this may not get the UI to do the right thing - - } else { - - string whole_file_region_name; - whole_file_region_name = region_name_from_path (c->front()->write_source->name(), true); - - /* Register a new region with the Session that - describes the entire source. Do this first - so that any sub-regions will obviously be - children of this one (later!) - */ - - try { - PropertyList plist; - - plist.add (Properties::start, c->front()->write_source->last_capture_start_frame()); - plist.add (Properties::length, total_capture); - plist.add (Properties::name, whole_file_region_name); - boost::shared_ptr rx (RegionFactory::create (srcs, plist)); - rx->set_automatic (true); - rx->set_whole_file (true); - - region = boost::dynamic_pointer_cast (rx); - region->special_set_position (capture_info.front()->start); - } - - - catch (failed_constructor& err) { - error << string_compose(_("%1: could not create region for complete audio file"), _name) << endmsg; - /* XXX what now? */ - } - - _last_capture_sources.insert (_last_capture_sources.end(), srcs.begin(), srcs.end()); - - _playlist->clear_changes (); - _playlist->set_capture_insertion_in_progress (true); - _playlist->freeze (); - - const framepos_t preroll_off = _session.preroll_record_trim_len (); - for (buffer_position = c->front()->write_source->last_capture_start_frame(), ci = capture_info.begin(); ci != capture_info.end(); ++ci) { - - string region_name; - - RegionFactory::region_name (region_name, whole_file_region_name, false); - - DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1 capture bufpos %5 start @ %2 length %3 add new region %4\n", - _name, (*ci)->start, (*ci)->frames, region_name, buffer_position)); - - try { - - PropertyList plist; - - plist.add (Properties::start, buffer_position); - plist.add (Properties::length, (*ci)->frames); - plist.add (Properties::name, region_name); - - boost::shared_ptr rx (RegionFactory::create (srcs, plist)); - region = boost::dynamic_pointer_cast (rx); - if (preroll_off > 0) { - region->trim_front (buffer_position + preroll_off); - } - } - - catch (failed_constructor& err) { - error << _("AudioDiskstream: could not create region for captured audio!") << endmsg; - continue; /* XXX is this OK? */ - } - - i_am_the_modifier++; - - _playlist->add_region (region, (*ci)->start + preroll_off, 1, non_layered()); - _playlist->set_layer (region, DBL_MAX); - i_am_the_modifier--; - - buffer_position += (*ci)->frames; - } - - _playlist->thaw (); - _playlist->set_capture_insertion_in_progress (false); - _session.add_command (new StatefulDiffCommand (_playlist)); - } - - mark_write_completed = true; - - out: - reset_write_sources (mark_write_completed); - - outout: - - for (ci = capture_info.begin(); ci != capture_info.end(); ++ci) { - delete *ci; - } - - capture_info.clear (); - capture_start_frame = 0; -} - -void -AudioDiskstream::transport_looped (framepos_t transport_frame) -{ - if (was_recording) { - // all we need to do is finish this capture, with modified capture length - boost::shared_ptr c = channels.reader(); - - finish_capture (c); - - // the next region will start recording via the normal mechanism - // we'll set the start position to the current transport pos - // no latency adjustment or capture offset needs to be made, as that already happened the first time - capture_start_frame = transport_frame; - first_recordable_frame = transport_frame; // mild lie - last_recordable_frame = max_framepos; - was_recording = true; - - if (recordable() && destructive()) { - for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) { - - RingBufferNPT::rw_vector transvec; - (*chan)->capture_transition_buf->get_write_vector(&transvec); - - if (transvec.len[0] > 0) { - transvec.buf[0]->type = CaptureStart; - transvec.buf[0]->capture_val = capture_start_frame; - (*chan)->capture_transition_buf->increment_write_ptr(1); - } - else { - // bad! - fatal << X_("programming error: capture_transition_buf is full on rec loop! inconceivable!") - << endmsg; - } - } - } - - } -} - -void -AudioDiskstream::finish_capture (boost::shared_ptr c) -{ - was_recording = false; - first_recordable_frame = max_framepos; - last_recordable_frame = max_framepos; - - if (capture_captured == 0) { - return; - } - - if (recordable() && destructive()) { - for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) { - - RingBufferNPT::rw_vector transvec; - (*chan)->capture_transition_buf->get_write_vector(&transvec); - - if (transvec.len[0] > 0) { - transvec.buf[0]->type = CaptureEnd; - transvec.buf[0]->capture_val = capture_captured; - (*chan)->capture_transition_buf->increment_write_ptr(1); - } - else { - // bad! - fatal << string_compose (_("programmer error: %1"), X_("capture_transition_buf is full when stopping record! inconceivable!")) << endmsg; - } - } - } - - - CaptureInfo* ci = new CaptureInfo; - - ci->start = capture_start_frame; - ci->frames = capture_captured; - - /* XXX theoretical race condition here. Need atomic exchange ? - However, the circumstances when this is called right - now (either on record-disable or transport_stopped) - mean that no actual race exists. I think ... - We now have a capture_info_lock, but it is only to be used - to synchronize in the transport_stop and the capture info - accessors, so that invalidation will not occur (both non-realtime). - */ - - DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("Finish capture, add new CI, %1 + %2\n", ci->start, ci->frames)); - - capture_info.push_back (ci); - capture_captured = 0; - - /* now we've finished a capture, reset first_recordable_frame for next time */ - first_recordable_frame = max_framepos; -} - -void -AudioDiskstream::set_record_enabled (bool yn) -{ - if (!recordable() || !_session.record_enabling_legal() || _io->n_ports().n_audio() == 0 || record_safe ()) { - return; - } - - /* can't rec-enable in destructive mode if transport is before start */ - - if (destructive() && yn && _session.transport_frame() < _session.current_start_frame()) { - return; - } - - /* yes, i know that this not proof against race conditions, but its - good enough. i think. - */ - - if (record_enabled() != yn) { - if (yn) { - engage_record_enable (); - } else { - disengage_record_enable (); - } - - RecordEnableChanged (); /* EMIT SIGNAL */ - } -} - -void -AudioDiskstream::set_record_safe (bool yn) -{ - if (!recordable() || !_session.record_enabling_legal() || _io->n_ports().n_audio() == 0) { - return; - } - - /* can't rec-safe in destructive mode if transport is before start ???? - REQUIRES REVIEW */ - - if (destructive() && yn && _session.transport_frame() < _session.current_start_frame()) { - return; - } - - /* yes, i know that this not proof against race conditions, but its - good enough. i think. - */ - - if (record_safe () != yn) { - if (yn) { - engage_record_safe (); - } else { - disengage_record_safe (); - } - - RecordSafeChanged (); /* EMIT SIGNAL */ - } -} - -bool -AudioDiskstream::prep_record_enable () -{ - if (!recordable() || !_session.record_enabling_legal() || _io->n_ports().n_audio() == 0 || record_safe ()) { // REQUIRES REVIEW "|| record_safe ()" - return false; - } - - /* can't rec-enable in destructive mode if transport is before start */ - - if (destructive() && _session.transport_frame() < _session.current_start_frame()) { - return false; - } - - bool rolling = _session.transport_speed() != 0.0f; - boost::shared_ptr c = channels.reader(); - - capturing_sources.clear (); - - if (Config->get_monitoring_model() == HardwareMonitoring) { - - for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) { - (*chan)->source.request_input_monitoring (!(_session.config.get_auto_input() && rolling)); - capturing_sources.push_back ((*chan)->write_source); - Source::Lock lock((*chan)->write_source->mutex()); - (*chan)->write_source->mark_streaming_write_started (lock); - } - - } else { - for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) { - capturing_sources.push_back ((*chan)->write_source); - Source::Lock lock((*chan)->write_source->mutex()); - (*chan)->write_source->mark_streaming_write_started (lock); - } - } - - return true; -} - -bool -AudioDiskstream::prep_record_disable () -{ - boost::shared_ptr c = channels.reader(); - if (Config->get_monitoring_model() == HardwareMonitoring) { - for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) { - (*chan)->source.request_input_monitoring (false); - } - } - capturing_sources.clear (); - - return true; -} - -XMLNode& -AudioDiskstream::get_state () -{ - XMLNode& node (Diskstream::get_state()); - - boost::shared_ptr c = channels.reader(); - node.set_property ("channels", (uint32_t)c->size()); - - if (!capturing_sources.empty() && _session.get_record_enabled()) { - - XMLNode* cs_child = new XMLNode (X_("CapturingSources")); - XMLNode* cs_grandchild; - - for (vector >::iterator i = capturing_sources.begin(); i != capturing_sources.end(); ++i) { - cs_grandchild = new XMLNode (X_("file")); - cs_grandchild->set_property (X_("path"), (*i)->path()); - cs_child->add_child_nocopy (*cs_grandchild); - } - - /* store the location where capture will start */ - - Location* pi; - - if (_session.preroll_record_punch_enabled ()) { - cs_child->set_property (X_("at"), _session.preroll_record_punch_pos()); - } else if (_session.config.get_punch_in() && ((pi = _session.locations()->auto_punch_location()) != 0)) { - cs_child->set_property (X_("at"), pi->start()); - } else { - cs_child->set_property (X_("at"), _session.transport_frame()); - } - - node.add_child_nocopy (*cs_child); - } - - return node; -} - -int -AudioDiskstream::set_state (const XMLNode& node, int version) -{ - XMLNodeList nlist = node.children(); - XMLNodeIterator niter; - XMLNode* capture_pending_node = 0; - - /* prevent write sources from being created */ - - in_set_state = true; - - for (niter = nlist.begin(); niter != nlist.end(); ++niter) { - if ((*niter)->name() == IO::state_node_name) { - deprecated_io_node = new XMLNode (**niter); - } - - if ((*niter)->name() == X_("CapturingSources")) { - capture_pending_node = *niter; - } - } - - if (Diskstream::set_state (node, version)) { - return -1; - } - - uint32_t nchans = 1; - node.get_property ("channels", nchans); - - // create necessary extra channels - // we are always constructed with one and we always need one - - _n_channels.set(DataType::AUDIO, channels.reader()->size()); - - if (nchans > _n_channels.n_audio()) { - - add_channel (nchans - _n_channels.n_audio()); - IO::PortCountChanged(_n_channels); - - } else if (nchans < _n_channels.n_audio()) { - - remove_channel (_n_channels.n_audio() - nchans); - } - - if (!destructive() && capture_pending_node) { - /* destructive streams have one and only one source per channel, - and so they never end up in pending capture in any useful - sense. - */ - use_pending_capture_data (*capture_pending_node); - } - - in_set_state = false; - - /* make sure this is clear before we do anything else */ - - capturing_sources.clear (); - - /* write sources are handled when we handle the input set - up of the IO that owns this DS (::non_realtime_input_change()) - */ - - return 0; -} - -int -AudioDiskstream::use_new_write_source (uint32_t n) -{ - boost::shared_ptr c = channels.reader(); - - if (!recordable()) { - return 1; - } - - if (n >= c->size()) { - error << string_compose (_("AudioDiskstream: channel %1 out of range"), n) << endmsg; - return -1; - } - - ChannelInfo* chan = (*c)[n]; - - try { - if ((chan->write_source = _session.create_audio_source_for_session ( - n_channels().n_audio(), write_source_name(), n, destructive())) == 0) { - throw failed_constructor(); - } - } - - catch (failed_constructor &err) { - error << string_compose (_("%1:%2 new capture file not initialized correctly"), _name, n) << endmsg; - chan->write_source.reset (); - return -1; - } - - /* do not remove destructive files even if they are empty */ - - chan->write_source->set_allow_remove_if_empty (!destructive()); - - return 0; -} - -void -AudioDiskstream::reset_write_sources (bool mark_write_complete, bool /*force*/) -{ - ChannelList::iterator chan; - boost::shared_ptr c = channels.reader(); - uint32_t n; - - if (!_session.writable() || !recordable()) { - return; - } - - capturing_sources.clear (); - - for (chan = c->begin(), n = 0; chan != c->end(); ++chan, ++n) { - - if (!destructive()) { - - if ((*chan)->write_source) { - - if (mark_write_complete) { - Source::Lock lock((*chan)->write_source->mutex()); - (*chan)->write_source->mark_streaming_write_completed (lock); - (*chan)->write_source->done_with_peakfile_writes (); - } - - if ((*chan)->write_source->removable()) { - (*chan)->write_source->mark_for_remove (); - (*chan)->write_source->drop_references (); - } - - (*chan)->write_source.reset (); - } - - use_new_write_source (n); - - if (record_enabled()) { - capturing_sources.push_back ((*chan)->write_source); - } - - } else { - - if ((*chan)->write_source == 0) { - use_new_write_source (n); - } - } - } - - if (destructive() && !c->empty ()) { - - /* we now have all our write sources set up, so create the - playlist's single region. - */ - - if (_playlist->empty()) { - setup_destructive_playlist (); - } - } -} - -void -AudioDiskstream::set_block_size (pframes_t /*nframes*/) -{ - if (_session.get_block_size() > speed_buffer_size) { - speed_buffer_size = _session.get_block_size(); - boost::shared_ptr c = channels.reader(); - - for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) { - if ((*chan)->speed_buffer) { - delete [] (*chan)->speed_buffer; - } - (*chan)->speed_buffer = new Sample[speed_buffer_size]; - } - } - allocate_temporary_buffers (); -} - -void -AudioDiskstream::allocate_temporary_buffers () -{ - /* make sure the wrap buffer is at least large enough to deal - with the speeds up to 1.2, to allow for micro-variation - when slaving to MTC, Timecode etc. - */ - - double const sp = max (fabs (_actual_speed), 1.2); - framecnt_t required_wrap_size = (framecnt_t) ceil (_session.get_block_size() * sp) + 2; - - if (required_wrap_size > wrap_buffer_size) { - - boost::shared_ptr c = channels.reader(); - - for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) { - if ((*chan)->playback_wrap_buffer) { - delete [] (*chan)->playback_wrap_buffer; - } - (*chan)->playback_wrap_buffer = new Sample[required_wrap_size]; - if ((*chan)->capture_wrap_buffer) { - delete [] (*chan)->capture_wrap_buffer; - } - (*chan)->capture_wrap_buffer = new Sample[required_wrap_size]; - } - - wrap_buffer_size = required_wrap_size; - } -} - -void -AudioDiskstream::request_input_monitoring (bool yn) -{ - boost::shared_ptr c = channels.reader(); - - for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) { - (*chan)->source.request_input_monitoring (yn); - } -} - -void -AudioDiskstream::set_align_style_from_io () -{ - bool have_physical = false; - - if (_alignment_choice != Automatic) { - return; - } - - if (_io == 0) { - return; - } - - get_input_sources (); - - boost::shared_ptr c = channels.reader(); - - for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) { - if ((*chan)->source.is_physical ()) { - have_physical = true; - break; - } - } - -#ifdef MIXBUS - // compensate for latency when bouncing from master or mixbus. - // we need to use "ExistingMaterial" to pick up the master bus' latency - // see also Route::direct_feeds_according_to_reality - IOVector ios; - ios.push_back (_io); - if (_session.master_out() && ios.fed_by (_session.master_out()->output())) { - have_physical = true; - } - for (uint32_t n = 0; n < NUM_MIXBUSES && !have_physical; ++n) { - if (_session.get_mixbus (n) && ios.fed_by (_session.get_mixbus(n)->output())) { - have_physical = true; - } - } -#endif - - if (have_physical) { - set_align_style (ExistingMaterial); - } else { - set_align_style (CaptureTime); - } -} - -int -AudioDiskstream::add_channel_to (boost::shared_ptr c, uint32_t how_many) -{ - while (how_many--) { - c->push_back (new ChannelInfo( - _session.butler()->audio_diskstream_playback_buffer_size(), - _session.butler()->audio_diskstream_capture_buffer_size(), - speed_buffer_size, wrap_buffer_size)); - interpolation.add_channel_to ( - _session.butler()->audio_diskstream_playback_buffer_size(), - speed_buffer_size); - } - - _n_channels.set(DataType::AUDIO, c->size()); - - return 0; -} - -int -AudioDiskstream::add_channel (uint32_t how_many) -{ - RCUWriter writer (channels); - boost::shared_ptr c = writer.get_copy(); - - return add_channel_to (c, how_many); -} - -int -AudioDiskstream::remove_channel_from (boost::shared_ptr c, uint32_t how_many) -{ - while (how_many-- && !c->empty()) { - delete c->back(); - c->pop_back(); - interpolation.remove_channel_from (); - } - - _n_channels.set(DataType::AUDIO, c->size()); - - return 0; -} - -int -AudioDiskstream::remove_channel (uint32_t how_many) -{ - RCUWriter writer (channels); - boost::shared_ptr c = writer.get_copy(); - - return remove_channel_from (c, how_many); -} - -float -AudioDiskstream::playback_buffer_load () const -{ - boost::shared_ptr c = channels.reader(); - - if (c->empty ()) { - return 1.0; - } - - return (float) ((double) c->front()->playback_buf->read_space()/ - (double) c->front()->playback_buf->bufsize()); -} - -float -AudioDiskstream::capture_buffer_load () const -{ - boost::shared_ptr c = channels.reader(); - - if (c->empty ()) { - return 1.0; - } - - return (float) ((double) c->front()->capture_buf->write_space()/ - (double) c->front()->capture_buf->bufsize()); -} - -int -AudioDiskstream::use_pending_capture_data (XMLNode& node) -{ - XMLProperty const * prop; - XMLNodeList nlist = node.children(); - XMLNodeIterator niter; - boost::shared_ptr fs; - boost::shared_ptr first_fs; - SourceList pending_sources; - framepos_t position; - - if ((prop = node.property (X_("at"))) == 0) { - return -1; - } - - if (sscanf (prop->value().c_str(), "%" PRIu64, &position) != 1) { - return -1; - } - - for (niter = nlist.begin(); niter != nlist.end(); ++niter) { - if ((*niter)->name() == X_("file")) { - - if ((prop = (*niter)->property (X_("path"))) == 0) { - continue; - } - - // This protects sessions from errant CapturingSources in stored sessions - GStatBuf sbuf; - if (g_stat (prop->value().c_str(), &sbuf)) { - continue; - } - - /* XXX as of June 2014, we always record to mono - files. Since this Source is being created as part of - crash recovery, we know that we need the first - channel (the final argument to the SourceFactory - call below). If we ever support non-mono files for - capture, this will need rethinking. - */ - - try { - fs = boost::dynamic_pointer_cast (SourceFactory::createForRecovery (DataType::AUDIO, _session, prop->value(), 0)); - } - - catch (failed_constructor& err) { - error << string_compose (_("%1: cannot restore pending capture source file %2"), - _name, prop->value()) - << endmsg; - return -1; - } - - pending_sources.push_back (fs); - - if (first_fs == 0) { - first_fs = fs; - } - - fs->set_captured_for (_name.val()); - } - } - - if (pending_sources.size() == 0) { - /* nothing can be done */ - return 1; - } - - if (pending_sources.size() != _n_channels.n_audio()) { - error << string_compose (_("%1: incorrect number of pending sources listed - ignoring them all"), _name) - << endmsg; - return -1; - } - - try { - - boost::shared_ptr wf_region; - boost::shared_ptr region; - - /* First create the whole file region */ - - PropertyList plist; - - plist.add (Properties::start, 0); - plist.add (Properties::length, first_fs->length (first_fs->timeline_position())); - plist.add (Properties::name, region_name_from_path (first_fs->name(), true)); - - wf_region = boost::dynamic_pointer_cast (RegionFactory::create (pending_sources, plist)); - - wf_region->set_automatic (true); - wf_region->set_whole_file (true); - wf_region->special_set_position (position); - - /* Now create a region that isn't the whole file for adding to - * the playlist */ - - region = boost::dynamic_pointer_cast (RegionFactory::create (pending_sources, plist)); - - _playlist->add_region (region, position); - } - - catch (failed_constructor& err) { - error << string_compose ( - _("%1: cannot create whole-file region from pending capture sources"), - _name) << endmsg; - - return -1; - } - - - return 0; -} - -void -AudioDiskstream::adjust_playback_buffering () -{ - boost::shared_ptr c = channels.reader(); - - for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) { - (*chan)->resize_playback (_session.butler()->audio_diskstream_playback_buffer_size()); - } -} - -void -AudioDiskstream::adjust_capture_buffering () -{ - boost::shared_ptr c = channels.reader(); - - for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) { - (*chan)->resize_capture (_session.butler()->audio_diskstream_capture_buffer_size()); - } -} - -bool -AudioDiskstream::ChannelSource::is_physical () const -{ - if (name.empty()) { - return false; - } - - return AudioEngine::instance()->port_is_physical (name); -} - -void -AudioDiskstream::ChannelSource::request_input_monitoring (bool yn) const -{ - if (name.empty()) { - return; - } - - return AudioEngine::instance()->request_input_monitoring (name, yn); -} - -AudioDiskstream::ChannelInfo::ChannelInfo (framecnt_t playback_bufsize, framecnt_t capture_bufsize, framecnt_t speed_size, framecnt_t wrap_size) -{ - current_capture_buffer = 0; - current_playback_buffer = 0; - curr_capture_cnt = 0; - - speed_buffer = new Sample[speed_size]; - playback_wrap_buffer = new Sample[wrap_size]; - capture_wrap_buffer = new Sample[wrap_size]; - - playback_buf = new RingBufferNPT (playback_bufsize); - capture_buf = new RingBufferNPT (capture_bufsize); - capture_transition_buf = new RingBufferNPT (256); - - /* touch the ringbuffer buffers, which will cause - them to be mapped into locked physical RAM if - we're running with mlockall(). this doesn't do - much if we're not. - */ - - memset (playback_buf->buffer(), 0, sizeof (Sample) * playback_buf->bufsize()); - memset (capture_buf->buffer(), 0, sizeof (Sample) * capture_buf->bufsize()); - memset (capture_transition_buf->buffer(), 0, sizeof (CaptureTransition) * capture_transition_buf->bufsize()); -} - -void -AudioDiskstream::ChannelInfo::resize_playback (framecnt_t playback_bufsize) -{ - delete playback_buf; - playback_buf = new RingBufferNPT (playback_bufsize); - memset (playback_buf->buffer(), 0, sizeof (Sample) * playback_buf->bufsize()); -} - -void -AudioDiskstream::ChannelInfo::resize_capture (framecnt_t capture_bufsize) -{ - delete capture_buf; - - capture_buf = new RingBufferNPT (capture_bufsize); - memset (capture_buf->buffer(), 0, sizeof (Sample) * capture_buf->bufsize()); -} - -AudioDiskstream::ChannelInfo::~ChannelInfo () -{ - if (write_source) { - if (write_source->removable()) { - /* this is a "stub" write source which exists in the - Session source list, but is removable. We must emit - a drop references call because it should not - continue to exist. If we do not do this, then the - Session retains a reference to it, it is not - deleted, and later attempts to create a new source - file will use wierd naming because it already - exists. - - XXX longer term TO-DO: do not add to session source - list until we write to the source. - */ - write_source->drop_references (); - } - } - - write_source.reset (); - - delete [] speed_buffer; - speed_buffer = 0; - - delete [] playback_wrap_buffer; - playback_wrap_buffer = 0; - - delete [] capture_wrap_buffer; - capture_wrap_buffer = 0; - - delete playback_buf; - playback_buf = 0; - - delete capture_buf; - capture_buf = 0; - - delete capture_transition_buf; - capture_transition_buf = 0; -} - - -bool -AudioDiskstream::set_name (string const & name) -{ - if (_name == name) { - return true; - } - Diskstream::set_name (name); - - /* get a new write source so that its name reflects the new diskstream name */ - - boost::shared_ptr c = channels.reader(); - ChannelList::iterator i; - int n = 0; - - for (n = 0, i = c->begin(); i != c->end(); ++i, ++n) { - use_new_write_source (n); - } - - return true; -} - -bool -AudioDiskstream::set_write_source_name (const std::string& str) { - if (_write_source_name == str) { - return true; - } - - Diskstream::set_write_source_name (str); - - if (_write_source_name == name()) { - return true; - } - boost::shared_ptr c = channels.reader(); - ChannelList::iterator i; - int n = 0; - - for (n = 0, i = c->begin(); i != c->end(); ++i, ++n) { - use_new_write_source (n); - } - return true; -} diff --git a/libs/ardour/audio_track.cc b/libs/ardour/audio_track.cc index 56d82c3a19..e17babaff2 100644 --- a/libs/ardour/audio_track.cc +++ b/libs/ardour/audio_track.cc @@ -26,7 +26,6 @@ #include "ardour/amp.h" #include "ardour/audio_buffer.h" -#include "ardour/audio_diskstream.h" #include "ardour/audio_track.h" #include "ardour/audioplaylist.h" #include "ardour/boost_debug.h" diff --git a/libs/ardour/audio_track_importer.cc b/libs/ardour/audio_track_importer.cc index e42c804f66..500f1c1af7 100644 --- a/libs/ardour/audio_track_importer.cc +++ b/libs/ardour/audio_track_importer.cc @@ -21,7 +21,7 @@ #include "ardour/audio_track_importer.h" #include "ardour/audio_playlist_importer.h" -#include "ardour/audio_diskstream.h" +#include "ardour/disk_reader.h" #include "ardour/session.h" #include "pbd/controllable.h" @@ -277,6 +277,8 @@ AudioTrackImporter::_cancel_move () void AudioTrackImporter::_move () { + /* XXX DISK */ +#if 0 /* Add diskstream */ boost::shared_ptr ds_node_list; @@ -293,7 +295,7 @@ AudioTrackImporter::_move () assert (p); p->set_value (new_ds_id.to_s()); - boost::shared_ptr new_ds (new AudioDiskstream (session, *ds_node)); + boost::shared_ptr new_ds (new DiskReader (session, *ds_node)); new_ds->set_name (name); new_ds->do_refill_with_alloc (); new_ds->set_block_size (session.get_block_size ()); @@ -309,6 +311,7 @@ AudioTrackImporter::_move () XMLNode routes ("Routes"); routes.add_child_copy (xml_track); session.load_routes (routes, 3000); +#endif } bool diff --git a/libs/ardour/diskstream.cc b/libs/ardour/diskstream.cc deleted file mode 100644 index 58abf7803d..0000000000 --- a/libs/ardour/diskstream.cc +++ /dev/null @@ -1,902 +0,0 @@ -/* - 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 - 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 "pbd/error.h" -#include "pbd/basename.h" -#include "pbd/memento_command.h" -#include "pbd/xml++.h" -#include "pbd/stacktrace.h" -#include "pbd/enum_convert.h" -#include "pbd/types_convert.h" - -#include "ardour/debug.h" -#include "ardour/diskstream.h" -#include "ardour/io.h" -#include "ardour/pannable.h" -#include "ardour/profile.h" -#include "ardour/playlist.h" -#include "ardour/session.h" -#include "ardour/session_playlists.h" -#include "ardour/track.h" -#include "ardour/types_convert.h" - -#include "pbd/i18n.h" -#include - -using namespace std; -using namespace ARDOUR; -using namespace PBD; - -namespace PBD { - DEFINE_ENUM_CONVERT(Diskstream::Flag); -} - -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; - -Diskstream::Diskstream (Session &sess, const string &name, Flag flag) - : SessionObject(sess, name) - , i_am_the_modifier (0) - , _track (0) - , _record_enabled (0) - , _record_safe (0) - , _visible_speed (1.0f) - , _actual_speed (1.0f) - , _buffer_reallocation_required (false) - , _seek_required (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_framepos) - , last_recordable_frame (max_framepos) - , last_possibly_recording (0) - , _alignment_style (ExistingMaterial) - , _alignment_choice (Automatic) - , _slaved (false) - , loop_location (0) - , overwrite_frame (0) - , overwrite_offset (0) - , _pending_overwrite (false) - , overwrite_queued (false) - , wrap_buffer_size (0) - , speed_buffer_size (0) - , _speed (1.0) - , _target_speed (_speed) - , file_frame (0) - , playback_sample (0) - , in_set_state (false) - , _flags (flag) - , deprecated_io_node (0) -{ -} - -Diskstream::Diskstream (Session& sess, const XMLNode& /*node*/) - : SessionObject(sess, "unnamed diskstream") - , i_am_the_modifier (0) - , _track (0) - , _record_enabled (0) - , _record_safe (0) - , _visible_speed (1.0f) - , _actual_speed (1.0f) - , _buffer_reallocation_required (false) - , _seek_required (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_framepos) - , last_recordable_frame (max_framepos) - , last_possibly_recording (0) - , _alignment_style (ExistingMaterial) - , _alignment_choice (Automatic) - , _slaved (false) - , loop_location (0) - , overwrite_frame (0) - , overwrite_offset (0) - , _pending_overwrite (false) - , overwrite_queued (false) - , wrap_buffer_size (0) - , speed_buffer_size (0) - , _speed (1.0) - , _target_speed (_speed) - , file_frame (0) - , playback_sample (0) - , in_set_state (false) - , _flags (Recordable) - , deprecated_io_node (0) -{ -} - -Diskstream::~Diskstream () -{ - DEBUG_TRACE (DEBUG::Destruction, string_compose ("Diskstream %1 deleted\n", _name)); - - if (_playlist) { - _playlist->release (); - } - - delete deprecated_io_node; -} - -bool -Diskstream::non_layered () const -{ - return _session.config.get_layered_record_mode(); -} - -void -Diskstream::set_track (Track* t) -{ - _track = t; - _io = _track->input(); - - ic_connection.disconnect(); - _io->changed.connect_same_thread (ic_connection, boost::bind (&Diskstream::handle_input_change, this, _1, _2)); - - if (_io->n_ports() != ChanCount::ZERO) { - input_change_pending.type = IOChange::Type (IOChange::ConfigurationChanged|IOChange::ConnectionsChanged); - non_realtime_input_change (); - } - - _track->Destroyed.connect_same_thread (*this, boost::bind (&Diskstream::route_going_away, this)); -} - -void -Diskstream::handle_input_change (IOChange change, void * /*src*/) -{ - Glib::Threads::Mutex::Lock lm (state_lock); - - 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 -Diskstream::non_realtime_set_speed () -{ - if (_buffer_reallocation_required) - { - Glib::Threads::Mutex::Lock lm (state_lock); - allocate_temporary_buffers (); - - _buffer_reallocation_required = false; - } - - if (_seek_required) { - if (speed() != 1.0f || speed() != -1.0f) { - seek ((framepos_t) (_session.transport_frame() * (double) speed()), true); - } - else { - seek (_session.transport_frame(), true); - } - - _seek_required = false; - } -} - -bool -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) { - - 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; - } - - _actual_speed = new_speed; - _target_speed = fabs(_actual_speed); - } - - if (changed) { - if (!global) { - _seek_required = true; - } - SpeedChanged (); /* EMIT SIGNAL */ - } - - return _buffer_reallocation_required || _seek_required; -} - -void -Diskstream::set_capture_offset () -{ - if (_io == 0) { - /* can't capture, so forget it */ - return; - } - - switch (_alignment_style) { - case ExistingMaterial: - _capture_offset = _io->latency(); -#ifdef MIXBUS - /* add additional latency, delayline inside the channelstrip + master-bus - * in MB the master-bus has no input-ports, so its latency does not propagate - */ - if (_session.master_out()) { - _capture_offset += _session.master_out()->signal_latency(); - } -#endif - - break; - - case CaptureTime: - default: - _capture_offset = 0; - break; - } -#ifdef MIXBUS - framecnt_t port_offset; - if (_track->mixbus_internal_bounce (port_offset)) { - /* _capture_offset may become negative, but the sum - * _capture_offset + existing_material_offset - * will be postive. - */ - _capture_offset -= port_offset; - } -#endif - - 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, bool force) -{ - if (record_enabled() && _session.actively_recording()) { - return; - } - - 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) -{ - if (location) { - if (location->start() >= location->end()) { - error << string_compose(_("Location \"%1\" not valid for track loop (start >= end)"), location->name()) << endl; - return -1; - } - } - - loop_location = location; - - LoopSet (location); /* EMIT SIGNAL */ - return 0; -} - -/** 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::Threads::Mutex::Lock lm (capture_info_lock); - - if (capture_info.size() > n) { - /* this is a completed capture */ - return capture_info[n]->start; - } else { - /* this is the currently in-progress capture */ - return capture_start_frame; - } -} - -ARDOUR::framecnt_t -Diskstream::get_captured_frames (uint32_t n) const -{ - Glib::Threads::Mutex::Lock lm (capture_info_lock); - - if (capture_info.size() > n) { - /* this is a completed capture */ - return capture_info[n]->frames; - } else { - /* this is the currently in-progress capture */ - return capture_captured; - } -} - -void -Diskstream::set_roll_delay (ARDOUR::framecnt_t nframes) -{ - _roll_delay = nframes; -} - -int -Diskstream::use_playlist (boost::shared_ptr playlist) -{ - if (!playlist) { - return 0; - } - - bool prior_playlist = false; - - { - Glib::Threads::Mutex::Lock lm (state_lock); - - if (playlist == _playlist) { - return 0; - } - - playlist_connections.drop_connections (); - - if (_playlist) { - _playlist->release(); - prior_playlist = true; - } - - _playlist = playlist; - _playlist->use(); - - if (!in_set_state && destructive() && recordable()) { - reset_write_sources (false); - } - - _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, _2)); - } - - /* 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 && prior_playlist) { - _session.request_overwrite_buffer (_track); - overwrite_queued = true; - } - - PlaylistChanged (); /* EMIT SIGNAL */ - _session.set_dirty (); - - return 0; -} - -void -Diskstream::playlist_changed (const PropertyChange&) -{ - playlist_modified (); -} - -void -Diskstream::playlist_modified () -{ - if (!i_am_the_modifier && !overwrite_queued) { - _session.request_overwrite_buffer (_track); - overwrite_queued = true; - } -} - -void -Diskstream::playlist_deleted (boost::weak_ptr wpl) -{ - boost::shared_ptr pl (wpl.lock()); - - 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 (); - } - } -} - -bool -Diskstream::set_name (const string& str) -{ - if (_name != str) { - assert(playlist()); - std::string name (str); - while (_session.playlists->by_name (name)) { - name = Playlist::bump_name (name, _session); - } - playlist()->set_name (name); - SessionObject::set_name(name); - } - return true; -} - -bool -Diskstream::set_write_source_name (const std::string& str) { - _write_source_name = str; - return true; -} - -XMLNode& -Diskstream::get_state () -{ - XMLNode* node = new XMLNode ("Diskstream"); - - node->set_property ("flags", _flags); - node->set_property ("playlist", _playlist->name()); - node->set_property ("name", name()); - node->set_property ("id", id ()); - node->set_property ("speed", _visible_speed); - node->set_property ("capture-alignment", _alignment_choice); - node->set_property ("record-safe", _record_safe); - - if (_extra_xml) { - node->add_child_copy (*_extra_xml); - } - return *node; -} - -int -Diskstream::set_state (const XMLNode& node, int /*version*/) -{ - std::string name; - if (node.get_property ("name", name)) { - _name = name; - } - - if (deprecated_io_node) { - set_id (*deprecated_io_node); - } else { - set_id (node); - } - - node.get_property ("flags", _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; - } - - AlignChoice achoice = Automatic; - node.get_property (X_("capture-alignment"), achoice); - set_align_choice (achoice, true); - - XMLProperty const * prop; - - if ((prop = node.property ("playlist")) == 0) { - return -1; - } - - if (find_and_use_playlist (prop->value())) { - return -1; - } - - double sp; - if (node.get_property ("speed", sp)) { - if (realtime_set_speed (sp, false)) { - non_realtime_set_speed (); - } - } - - bool record_safe; - if (node.get_property ("record-safe", record_safe)) { - _record_safe = record_safe ? 1 : 0; - } - - return 0; -} - -void -Diskstream::playlist_ranges_moved (list< Evoral::RangeMove > const & movements_frames, bool from_undo) -{ - /* 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; - } - - 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 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(); - if (!alist->size()) { - continue; - } - 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 */ - _track->foreach_processor (boost::bind (&Diskstream::move_processor_automation, this, _1, 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::const_iterator i = a.begin (); i != a.end (); ++i) { - boost::shared_ptr al = processor->automation_control(*i)->alist(); - if (!al->size()) { - continue; - } - XMLNode & 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 (framepos_t transport_frame, 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; - 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. - */ - - rolling = _session.transport_speed() != 0.0f; - possibly_recording = (rolling << 2) | ((int)record_enabled() << 1) | (int)can_record; - change = possibly_recording ^ last_possibly_recording; - - if (possibly_recording == last_possibly_recording) { - return; - } - - const framecnt_t existing_material_offset = _session.worst_playback_latency(); - - if (possibly_recording == fully_rec_enabled) { - - if (last_possibly_recording == fully_rec_enabled) { - return; - } - - capture_start_frame = _session.transport_frame(); - first_recordable_frame = capture_start_frame + _capture_offset; - last_recordable_frame = max_framepos; - - 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 (_alignment_style == ExistingMaterial) { - first_recordable_frame += existing_material_offset; - DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("\tshift FRF by EMO %1\n", - first_recordable_frame)); - } - - prepare_record_status (capture_start_frame); - - } else { - - if (last_possibly_recording == fully_rec_enabled) { - - /* we were recording last time */ - - if (change & transport_rolling) { - - /* 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. - */ - - } else { - /* punch out */ - - last_recordable_frame = _session.transport_frame() + _capture_offset; - - if (_alignment_style == ExistingMaterial) { - last_recordable_frame += existing_material_offset; - } - } - } - } - - last_possibly_recording = possibly_recording; -} - -void -Diskstream::route_going_away () -{ - _io.reset (); -} - -void -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 Evoral::OverlapNone: - rec_nframes = 0; - break; - - case Evoral::OverlapInternal: - /* ---------- recrange - * |---| transrange - */ - rec_nframes = nframes; - rec_offset = 0; - break; - - case Evoral::OverlapStart: - /* |--------| recrange - * -----| transrange - */ - rec_nframes = transport_frame + nframes - first_recordable_frame; - if (rec_nframes) { - rec_offset = first_recordable_frame - transport_frame; - } - break; - - case Evoral::OverlapEnd: - /* |--------| recrange - * |-------- transrange - */ - rec_nframes = last_recordable_frame - transport_frame; - rec_offset = 0; - break; - - case Evoral::OverlapExternal: - /* |--------| recrange - * -------------- 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); - -#ifndef NDEBUG - 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; -#endif -} - -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; -} diff --git a/libs/ardour/enums.cc b/libs/ardour/enums.cc index eee20f811d..8fa43508a9 100644 --- a/libs/ardour/enums.cc +++ b/libs/ardour/enums.cc @@ -23,7 +23,7 @@ #include "evoral/Range.hpp" // shouldn't Evoral have its own enum registration? #include "ardour/delivery.h" -#include "ardour/diskstream.h" +#include "ardour/disk_io.h" #include "ardour/export_channel.h" #include "ardour/export_filename.h" #include "ardour/export_format_base.h" @@ -100,7 +100,7 @@ setup_enum_writer () TracksAutoNamingRule _TracksAutoNamingRule; Session::StateOfTheState _Session_StateOfTheState; Source::Flag _Source_Flag; - Diskstream::Flag _Diskstream_Flag; + DiskIOProcessor::Flag _DiskIOProcessor_Flag; Location::Flags _Location_Flags; PositionLockStyle _PositionLockStyle; TempoSection::Type _TempoSection_Type; @@ -507,10 +507,10 @@ setup_enum_writer () REGISTER_ENUM(ExistingNewlyCreatedBoth); REGISTER (_RegionSelectionAfterSplit); - REGISTER_CLASS_ENUM (Diskstream, Recordable); - REGISTER_CLASS_ENUM (Diskstream, Hidden); - REGISTER_CLASS_ENUM (Diskstream, Destructive); - REGISTER_BITS (_Diskstream_Flag); + REGISTER_CLASS_ENUM (DiskIOProcessor, Recordable); + REGISTER_CLASS_ENUM (DiskIOProcessor, Hidden); + REGISTER_CLASS_ENUM (DiskIOProcessor, Destructive); + REGISTER_BITS (_DiskIOProcessor_Flag); REGISTER_CLASS_ENUM (Location, IsMark); REGISTER_CLASS_ENUM (Location, IsAutoPunch); diff --git a/libs/ardour/import.cc b/libs/ardour/import.cc index 431b1d75e7..8a2c3ad9bb 100644 --- a/libs/ardour/import.cc +++ b/libs/ardour/import.cc @@ -47,7 +47,6 @@ #include "ardour/analyser.h" #include "ardour/ardour.h" -#include "ardour/audio_diskstream.h" #include "ardour/audioengine.h" #include "ardour/audioregion.h" #include "ardour/import_status.h" diff --git a/libs/ardour/midi_diskstream.cc b/libs/ardour/midi_diskstream.cc deleted file mode 100644 index 3263fb02db..0000000000 --- a/libs/ardour/midi_diskstream.cc +++ /dev/null @@ -1,1582 +0,0 @@ -/* - Copyright (C) 2000-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 - 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 "pbd/error.h" -#include "pbd/ffs.h" -#include "pbd/basename.h" -#include -#include "pbd/xml++.h" -#include "pbd/memento_command.h" -#include "pbd/enumwriter.h" -#include "pbd/stateful_diff_command.h" -#include "pbd/stacktrace.h" - -#include "ardour/audioengine.h" -#include "ardour/butler.h" -#include "ardour/debug.h" -#include "ardour/evoral_types_convert.h" -#include "ardour/io.h" -#include "ardour/midi_diskstream.h" -#include "ardour/midi_model.h" -#include "ardour/midi_playlist.h" -#include "ardour/midi_port.h" -#include "ardour/midi_region.h" -#include "ardour/midi_ring_buffer.h" -#include "ardour/midi_track.h" -#include "ardour/playlist_factory.h" -#include "ardour/region_factory.h" -#include "ardour/session.h" -#include "ardour/session_playlists.h" -#include "ardour/smf_source.h" -#include "ardour/types.h" -#include "ardour/utils.h" - -#include "midi++/types.h" - -#include "pbd/i18n.h" -#include - -using namespace std; -using namespace ARDOUR; -using namespace PBD; - -framecnt_t MidiDiskstream::midi_readahead = 4096; - -MidiDiskstream::MidiDiskstream (Session &sess, const string &name, Diskstream::Flag flag) - : Diskstream(sess, name, flag) - , _playback_buf(0) - , _capture_buf(0) - , _note_mode(Sustained) - , _frames_written_to_ringbuffer(0) - , _frames_read_from_ringbuffer(0) - , _frames_pending_write(0) - , _num_captured_loops(0) - , _accumulated_capture_offset(0) - , _gui_feed_buffer(AudioEngine::instance()->raw_buffer_size (DataType::MIDI)) -{ - in_set_state = true; - - init (); - use_new_playlist (); - use_new_write_source (0); - - in_set_state = false; - - if (destructive()) { - throw failed_constructor(); - } -} - -MidiDiskstream::MidiDiskstream (Session& sess, const XMLNode& node) - : Diskstream(sess, node) - , _playback_buf(0) - , _capture_buf(0) - , _note_mode(Sustained) - , _frames_written_to_ringbuffer(0) - , _frames_read_from_ringbuffer(0) - , _frames_pending_write(0) - , _num_captured_loops(0) - , _accumulated_capture_offset(0) - , _gui_feed_buffer(AudioEngine::instance()->raw_buffer_size (DataType::MIDI)) -{ - in_set_state = true; - - init (); - - if (set_state (node, Stateful::loading_state_version)) { - in_set_state = false; - throw failed_constructor(); - } - - use_new_write_source (0); - - in_set_state = false; -} - -void -MidiDiskstream::init () -{ - /* there are no channels at this point, so these - two calls just get speed_buffer_size and wrap_buffer - size setup without duplicating their code. - */ - - set_block_size (_session.get_block_size()); - allocate_temporary_buffers (); - - const size_t size = _session.butler()->midi_diskstream_buffer_size(); - _playback_buf = new MidiRingBuffer(size); - _capture_buf = new MidiRingBuffer(size); - - _n_channels = ChanCount(DataType::MIDI, 1); - interpolation.add_channel_to (0,0); -} - -MidiDiskstream::~MidiDiskstream () -{ - Glib::Threads::Mutex::Lock lm (state_lock); - delete _playback_buf; - delete _capture_buf; -} - - -void -MidiDiskstream::non_realtime_locate (framepos_t position) -{ - if (_write_source) { - _write_source->set_timeline_position (position); - } - seek (position, false); -} - - -void -MidiDiskstream::non_realtime_input_change () -{ - { - Glib::Threads::Mutex::Lock lm (state_lock); - - if (input_change_pending.type == IOChange::NoChange) { - return; - } - - if (input_change_pending.type & IOChange::ConfigurationChanged) { - uint32_t ni = _io->n_ports().n_midi(); - - if (ni != _n_channels.n_midi()) { - error << string_compose (_("%1: I/O configuration change %4 requested to use %2, but channel setup is %3"), - name(), - _io->n_ports(), - _n_channels, input_change_pending.type) - << endmsg; - } - - if (ni == 0) { - _source_port.reset (); - } else { - _source_port = _io->midi(0); - } - } - - if (input_change_pending.type & IOChange::ConnectionsChanged) { - set_capture_offset (); - set_align_style_from_io (); - } - - input_change_pending.type = IOChange::NoChange; - - /* implicit unlock */ - } - - /* unlike with audio, there is never any need to reset write sources - based on input configuration changes because ... a MIDI track - has just 1 MIDI port as input, always. - */ - - /* now refill channel buffers */ - - if (speed() != 1.0f || speed() != -1.0f) { - seek ((framepos_t) (_session.transport_frame() * (double) speed())); - } - else { - seek (_session.transport_frame()); - } - - g_atomic_int_set(const_cast (&_frames_pending_write), 0); - g_atomic_int_set(const_cast (&_num_captured_loops), 0); -} - -int -MidiDiskstream::find_and_use_playlist (const string& name) -{ - boost::shared_ptr playlist; - - if ((playlist = boost::dynamic_pointer_cast (_session.playlists->by_name (name))) == 0) { - playlist = boost::dynamic_pointer_cast (PlaylistFactory::create (DataType::MIDI, _session, name)); - } - - if (!playlist) { - error << string_compose(_("MidiDiskstream: Playlist \"%1\" isn't a midi playlist"), name) << endmsg; - return -1; - } - - return use_playlist (playlist); -} - -int -MidiDiskstream::use_playlist (boost::shared_ptr playlist) -{ - if (boost::dynamic_pointer_cast(playlist)) { - Diskstream::use_playlist(playlist); - } - - return 0; -} - -int -MidiDiskstream::use_new_playlist () -{ - string newname; - boost::shared_ptr playlist; - - if (!in_set_state && destructive()) { - return 0; - } - - if (_playlist) { - newname = Playlist::bump_name (_playlist->name(), _session); - } else { - newname = Playlist::bump_name (_name, _session); - } - - if ((playlist = boost::dynamic_pointer_cast (PlaylistFactory::create ( - DataType::MIDI, _session, newname, hidden()))) != 0) { - - return use_playlist (playlist); - - } else { - return -1; - } -} - -int -MidiDiskstream::use_copy_playlist () -{ - if (destructive()) { - return 0; - } - - if (_playlist == 0) { - error << string_compose(_("MidiDiskstream %1: there is no existing playlist to make a copy of!"), _name) << endmsg; - return -1; - } - - string newname; - boost::shared_ptr playlist; - - newname = Playlist::bump_name (_playlist->name(), _session); - - if ((playlist = boost::dynamic_pointer_cast(PlaylistFactory::create (midi_playlist(), newname))) != 0) { - return use_playlist (playlist); - } else { - return -1; - } -} - -void -MidiDiskstream::set_note_mode (NoteMode m) -{ - _note_mode = m; - midi_playlist()->set_note_mode(m); - if (_write_source && _write_source->model()) - _write_source->model()->set_note_mode(m); -} - -/** Get the start, end, and length of a location "atomically". - * - * Note: Locations don't get deleted, so all we care about when I say "atomic" - * is that we are always pointing to the same one and using start/length values - * obtained just once. Use this function to achieve this since location being - * a parameter achieves this. - */ -static void -get_location_times(const Location* location, - framepos_t* start, - framepos_t* end, - framepos_t* length) -{ - if (location) { - *start = location->start(); - *end = location->end(); - *length = *end - *start; - } -} - -int -MidiDiskstream::process (BufferSet& bufs, framepos_t transport_frame, pframes_t nframes, framecnt_t& playback_distance, bool need_disk_signal) -{ - framecnt_t rec_offset = 0; - framecnt_t rec_nframes = 0; - bool nominally_recording; - bool re = record_enabled (); - bool can_record = _session.actively_recording (); - - playback_distance = 0; - - check_record_status (transport_frame, can_record); - - nominally_recording = (can_record && re); - - if (nframes == 0) { - return 0; - } - - boost::shared_ptr sp = _source_port.lock (); - - if (sp == 0) { - return 1; - } - - Glib::Threads::Mutex::Lock sm (state_lock, Glib::Threads::TRY_LOCK); - - if (!sm.locked()) { - return 1; - } - - const Location* const loop_loc = loop_location; - framepos_t loop_start = 0; - framepos_t loop_end = 0; - framepos_t loop_length = 0; - - get_location_times (loop_loc, &loop_start, &loop_end, &loop_length); - - adjust_capture_position = 0; - - if (nominally_recording || (re && was_recording && _session.get_record_enabled() && (_session.config.get_punch_in() || _session.preroll_record_punch_enabled()))) { - Evoral::OverlapType ot = Evoral::coverage (first_recordable_frame, last_recordable_frame, transport_frame, transport_frame + nframes); - // XXX should this be transport_frame + nframes - 1 ? coverage() expects its parameter ranges to include their end points - - calculate_record_range(ot, transport_frame, nframes, rec_nframes, rec_offset); - /* For audio: not writing frames to the capture ringbuffer offsets - * the recording. For midi: we need to keep track of the record range - * and subtract the accumulated difference from the event time. - */ - if (rec_nframes) { - _accumulated_capture_offset += rec_offset; - } else { - _accumulated_capture_offset += nframes; - } - - if (rec_nframes && !was_recording) { - if (loop_loc) { - /* Loop recording, so pretend the capture started at the loop - start rgardless of what time it is now, so the source starts - at the loop start and can handle time wrapping around. - Otherwise, start the source right now as usual. - */ - capture_captured = transport_frame - loop_start; - capture_start_frame = loop_start; - } - _write_source->mark_write_starting_now( - capture_start_frame, capture_captured, loop_length); - g_atomic_int_set(const_cast (&_frames_pending_write), 0); - g_atomic_int_set(const_cast (&_num_captured_loops), 0); - was_recording = true; - } - } - - if (can_record && !_last_capture_sources.empty()) { - _last_capture_sources.clear (); - } - - if (nominally_recording || rec_nframes) { - // Pump entire port buffer into the ring buffer (TODO: split cycles?) - MidiBuffer& buf = sp->get_midi_buffer(nframes); - MidiTrack* mt = dynamic_cast(_track); - MidiChannelFilter* filter = mt ? &mt->capture_filter() : NULL; - - for (MidiBuffer::iterator i = buf.begin(); i != buf.end(); ++i) { - Evoral::Event ev(*i, false); - if (ev.time() + rec_offset > rec_nframes) { - break; - } -#ifndef NDEBUG - if (DEBUG_ENABLED(DEBUG::MidiIO)) { - const uint8_t* __data = ev.buffer(); - DEBUG_STR_DECL(a); - DEBUG_STR_APPEND(a, string_compose ("mididiskstream %1 capture event @ %2 + %3 sz %4 ", this, ev.time(), transport_frame, ev.size())); - for (size_t i=0; i < ev.size(); ++i) { - DEBUG_STR_APPEND(a,hex); - DEBUG_STR_APPEND(a,"0x"); - DEBUG_STR_APPEND(a,(int)__data[i]); - DEBUG_STR_APPEND(a,' '); - } - DEBUG_STR_APPEND(a,'\n'); - DEBUG_TRACE (DEBUG::MidiIO, DEBUG_STR(a).str()); - } -#endif - /* Write events to the capture buffer in frames from session start, - but ignoring looping so event time progresses monotonically. - The source knows the loop length so it knows exactly where the - event occurs in the series of recorded loops and can implement - any desirable behaviour. We don't want to send event with - transport time here since that way the source can not - reconstruct their actual time; future clever MIDI looping should - probably be implemented in the source instead of here. - */ - const framecnt_t loop_offset = _num_captured_loops * loop_length; - const framepos_t event_time = transport_frame + loop_offset - _accumulated_capture_offset + ev.time(); - if (event_time < 0 || event_time < first_recordable_frame) { - /* Event out of range, skip */ - continue; - } - - if (!filter || !filter->filter(ev.buffer(), ev.size())) { - _capture_buf->write(event_time, ev.event_type(), ev.size(), ev.buffer()); - } - } - g_atomic_int_add(const_cast(&_frames_pending_write), nframes); - - if (buf.size() != 0) { - Glib::Threads::Mutex::Lock lm (_gui_feed_buffer_mutex, Glib::Threads::TRY_LOCK); - - if (lm.locked ()) { - /* Copy this data into our GUI feed buffer and tell the GUI - that it can read it if it likes. - */ - _gui_feed_buffer.clear (); - - for (MidiBuffer::iterator i = buf.begin(); i != buf.end(); ++i) { - /* This may fail if buf is larger than _gui_feed_buffer, but it's not really - the end of the world if it does. - */ - _gui_feed_buffer.push_back ((*i).time() + transport_frame, (*i).size(), (*i).buffer()); - } - } - - DataRecorded (_write_source); /* EMIT SIGNAL */ - } - - } else { - - if (was_recording) { - finish_capture (); - } - _accumulated_capture_offset = 0; - - } - - if (rec_nframes) { - - /* data will be written to disk */ - - if (rec_nframes == nframes && rec_offset == 0) { - playback_distance = nframes; - } - - adjust_capture_position = rec_nframes; - - } else if (nominally_recording) { - - /* XXXX do this for MIDI !!! - can't do actual capture yet - waiting for latency effects to finish before we start - */ - - playback_distance = nframes; - - } else if (_actual_speed != 1.0f && _target_speed > 0) { - - interpolation.set_speed (_target_speed); - - playback_distance = interpolation.distance (nframes); - - } else { - playback_distance = nframes; - } - - if (need_disk_signal && !_session.declick_out_pending()) { - /* copy the diskstream data to all output buffers */ - - MidiBuffer& mbuf (bufs.get_midi (0)); - get_playback (mbuf, playback_distance); - - /* leave the audio count alone */ - ChanCount cnt (DataType::MIDI, 1); - cnt.set (DataType::AUDIO, bufs.count().n_audio()); - bufs.set_count (cnt); - - /* vari-speed */ - if (_target_speed > 0 && _actual_speed != 1.0f) { - MidiBuffer& mbuf (bufs.get_midi (0)); - for (MidiBuffer::iterator i = mbuf.begin(); i != mbuf.end(); ++i) { - MidiBuffer::TimeType *tme = i.timeptr(); - *tme = (*tme) * nframes / playback_distance; - } - } - } - - return 0; -} - -frameoffset_t -MidiDiskstream::calculate_playback_distance (pframes_t nframes) -{ - frameoffset_t playback_distance = nframes; - - if (!record_enabled() && _actual_speed != 1.0f && _actual_speed > 0.f) { - interpolation.set_speed (_target_speed); - playback_distance = interpolation.distance (nframes, false); - } - - if (_actual_speed < 0.0) { - return -playback_distance; - } else { - return playback_distance; - } -} - -bool -MidiDiskstream::commit (framecnt_t playback_distance) -{ - bool need_butler = false; - - if (!_io || !_io->active()) { - return false; - } - - if (_actual_speed < 0.0) { - playback_sample -= playback_distance; - } else { - playback_sample += playback_distance; - } - - if (adjust_capture_position != 0) { - capture_captured += adjust_capture_position; - adjust_capture_position = 0; - } - - uint32_t frames_read = g_atomic_int_get(const_cast(&_frames_read_from_ringbuffer)); - uint32_t frames_written = g_atomic_int_get(const_cast(&_frames_written_to_ringbuffer)); - - /* - cerr << name() << " MDS written: " << frames_written << " - read: " << frames_read << - " = " << frames_written - frames_read - << " + " << playback_distance << " < " << midi_readahead << " = " << need_butler << ")" << endl; - */ - - /* frames_read will generally be less than frames_written, but - * immediately after an overwrite, we can end up having read some data - * before we've written any. we don't need to trip an assert() on this, - * but we do need to check so that the decision on whether or not we - * need the butler is done correctly. - */ - - /* furthermore.. - * - * Doing heavy GUI operations[1] can stall also the butler. - * The RT-thread meanwhile will happily continue and - * ‘frames_read’ (from buffer to output) will become larger - * than ‘frames_written’ (from disk to buffer). - * - * The disk-stream is now behind.. - * - * In those cases the butler needs to be summed to refill the buffer (done now) - * AND we need to skip (frames_read - frames_written). ie remove old events - * before playback_sample from the rinbuffer. - * - * [1] one way to do so is described at #6170. - * For me just popping up the context-menu on a MIDI-track header - * of a track with a large (think beethoven :) midi-region also did the - * trick. The playhead stalls for 2 or 3 sec, until the context-menu shows. - * - * In both cases the root cause is that redrawing MIDI regions on the GUI is still very slow - * and can stall - */ - if (frames_read <= frames_written) { - if ((frames_written - frames_read) + playback_distance < midi_readahead) { - need_butler = true; - } - } else { - need_butler = true; - } - - - return need_butler; -} - -void -MidiDiskstream::set_pending_overwrite (bool yn) -{ - /* called from audio thread, so we can use the read ptr and playback sample as we wish */ - - _pending_overwrite = yn; - overwrite_frame = playback_sample; -} - -int -MidiDiskstream::overwrite_existing_buffers () -{ - /* Clear the playback buffer contents. This is safe as long as the butler - thread is suspended, which it should be. */ - _playback_buf->reset (); - _playback_buf->reset_tracker (); - - g_atomic_int_set (&_frames_read_from_ringbuffer, 0); - g_atomic_int_set (&_frames_written_to_ringbuffer, 0); - - /* Resolve all currently active notes in the playlist. This is more - aggressive than it needs to be: ideally we would only resolve what is - absolutely necessary, but this seems difficult and/or impossible without - having the old data or knowing what change caused the overwrite. */ - midi_playlist()->resolve_note_trackers (*_playback_buf, overwrite_frame); - - read (overwrite_frame, disk_read_chunk_frames, false); - file_frame = overwrite_frame; // it was adjusted by ::read() - overwrite_queued = false; - _pending_overwrite = false; - - return 0; -} - -int -MidiDiskstream::seek (framepos_t frame, bool complete_refill) -{ - Glib::Threads::Mutex::Lock lm (state_lock); - int ret = -1; - - if (g_atomic_int_get (&_frames_read_from_ringbuffer) == 0) { - /* we haven't read anything since the last seek, - so flush all note trackers to prevent - wierdness - */ - reset_tracker (); - } - - _playback_buf->reset(); - _capture_buf->reset(); - g_atomic_int_set(&_frames_read_from_ringbuffer, 0); - g_atomic_int_set(&_frames_written_to_ringbuffer, 0); - - playback_sample = frame; - file_frame = frame; - - if (complete_refill) { - while ((ret = do_refill_with_alloc ()) > 0) ; - } else { - ret = do_refill_with_alloc (); - } - - return ret; -} - -int -MidiDiskstream::can_internal_playback_seek (framecnt_t distance) -{ - uint32_t frames_read = g_atomic_int_get(&_frames_read_from_ringbuffer); - uint32_t frames_written = g_atomic_int_get(&_frames_written_to_ringbuffer); - return ((frames_written - frames_read) < distance); -} - -int -MidiDiskstream::internal_playback_seek (framecnt_t distance) -{ - first_recordable_frame += distance; - playback_sample += distance; - - return 0; -} - -/** @a start is set to the new frame position (TIME) read up to */ -int -MidiDiskstream::read (framepos_t& start, framecnt_t dur, bool reversed) -{ - framecnt_t this_read = 0; - framepos_t loop_end = 0; - framepos_t loop_start = 0; - framecnt_t loop_length = 0; - Location* loc = loop_location; - framepos_t effective_start = start; - Evoral::Range* loop_range (0); - - MidiTrack* mt = dynamic_cast(_track); - MidiChannelFilter* filter = mt ? &mt->playback_filter() : NULL; - - frameoffset_t loop_offset = 0; - - if (!reversed && loc) { - get_location_times (loc, &loop_start, &loop_end, &loop_length); - } - - while (dur) { - - /* take any loop into account. we can't read past the end of the loop. */ - - if (loc && !reversed) { - - if (!loop_range) { - loop_range = new Evoral::Range (loop_start, loop_end-1); // inclusive semantics require -1 - } - - /* if we are (seamlessly) looping, ensure that the first frame we read is at the correct - position within the loop. - */ - - effective_start = loop_range->squish (effective_start); - - if ((loop_end - effective_start) <= dur) { - /* too close to end of loop to read "dur", so - shorten it. - */ - this_read = loop_end - effective_start; - } else { - this_read = dur; - } - - } else { - this_read = dur; - } - - if (this_read == 0) { - break; - } - - this_read = min (dur,this_read); - - DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("MDS ::read at %1 for %2 loffset %3\n", effective_start, this_read, loop_offset)); - - if (midi_playlist()->read (*_playback_buf, effective_start, this_read, loop_range, 0, filter) != this_read) { - error << string_compose( - _("MidiDiskstream %1: cannot read %2 from playlist at frame %3"), - id(), this_read, start) << endmsg; - return -1; - } - - g_atomic_int_add (&_frames_written_to_ringbuffer, this_read); - - if (reversed) { - - // Swap note ons with note offs here. etc? - // Fully reversing MIDI requires look-ahead (well, behind) to find previous - // CC values etc. hard. - - } else { - - /* adjust passed-by-reference argument (note: this is - monotonic and does not reflect looping. - */ - start += this_read; - - /* similarly adjust effective_start, but this may be - readjusted for seamless looping as we continue around - the loop. - */ - effective_start += this_read; - } - - dur -= this_read; - //offset += this_read; - } - - return 0; -} - -int -MidiDiskstream::_do_refill_with_alloc (bool /* partial_fill */) -{ - return do_refill(); -} - -int -MidiDiskstream::do_refill () -{ - int ret = 0; - size_t write_space = _playback_buf->write_space(); - bool reversed = (_visible_speed * _session.transport_speed()) < 0.0f; - - DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("MDS refill, write space = %1 file frame = %2\n", - write_space, file_frame)); - - /* no space to write */ - if (write_space == 0) { - return 0; - } - - if (reversed) { - return 0; - } - - /* at end: nothing to do */ - if (file_frame == max_framepos) { - return 0; - } - - uint32_t frames_read = g_atomic_int_get(&_frames_read_from_ringbuffer); - uint32_t frames_written = g_atomic_int_get(&_frames_written_to_ringbuffer); - - if ((frames_read < frames_written) && (frames_written - frames_read) >= midi_readahead) { - return 0; - } - - framecnt_t to_read = midi_readahead - ((framecnt_t)frames_written - (framecnt_t)frames_read); - - to_read = min (to_read, (framecnt_t) (max_framepos - file_frame)); - to_read = min (to_read, (framecnt_t) write_space); - - if (read (file_frame, to_read, reversed)) { - ret = -1; - } - - return ret; -} - -/** Flush pending data to disk. - * - * Important note: this function will write *AT MOST* disk_write_chunk_frames - * of data to disk. it will never write more than that. If it writes that - * much and there is more than that waiting to be written, it will return 1, - * otherwise 0 on success or -1 on failure. - * - * If there is less than disk_write_chunk_frames to be written, no data will be - * written at all unless @a force_flush is true. - */ -int -MidiDiskstream::do_flush (RunContext /*context*/, bool force_flush) -{ - framecnt_t to_write; - int32_t ret = 0; - - if (!_write_source) { - return 0; - } - - const framecnt_t total = g_atomic_int_get(const_cast (&_frames_pending_write)); - - if (total == 0 || - _capture_buf->read_space() == 0 || - (!force_flush && (total < disk_write_chunk_frames) && was_recording)) { - goto out; - } - - /* if there are 2+ chunks of disk i/o possible for - this track), let the caller know so that it can arrange - for us to be called again, ASAP. - - if we are forcing a flush, then if there is* any* extra - work, let the caller know. - - if we are no longer recording and there is any extra work, - let the caller know too. - */ - - if (total >= 2 * disk_write_chunk_frames || ((force_flush || !was_recording) && total > disk_write_chunk_frames)) { - ret = 1; - } - - if (force_flush) { - /* push out everything we have, right now */ - to_write = max_framecnt; - } else { - to_write = disk_write_chunk_frames; - } - - if (record_enabled() && ((total > disk_write_chunk_frames) || force_flush)) { - Source::Lock lm(_write_source->mutex()); - if (_write_source->midi_write (lm, *_capture_buf, get_capture_start_frame (0), to_write) != to_write) { - error << string_compose(_("MidiDiskstream %1: cannot write to disk"), id()) << endmsg; - return -1; - } - g_atomic_int_add(const_cast (&_frames_pending_write), -to_write); - } - -out: - return ret; -} - -void -MidiDiskstream::transport_stopped_wallclock (struct tm& /*when*/, time_t /*twhen*/, bool abort_capture) -{ - bool more_work = true; - int err = 0; - boost::shared_ptr region; - MidiRegion::SourceList srcs; - MidiRegion::SourceList::iterator src; - vector::iterator ci; - - finish_capture (); - - /* butler is already stopped, but there may be work to do - to flush remaining data to disk. - */ - - while (more_work && !err) { - switch (do_flush (TransportContext, true)) { - case 0: - more_work = false; - break; - case 1: - break; - case -1: - error << string_compose(_("MidiDiskstream \"%1\": cannot flush captured data to disk!"), _name) << endmsg; - err++; - } - } - - /* XXX is there anything we can do if err != 0 ? */ - Glib::Threads::Mutex::Lock lm (capture_info_lock); - - if (capture_info.empty()) { - goto no_capture_stuff_to_do; - } - - if (abort_capture) { - - if (_write_source) { - _write_source->mark_for_remove (); - _write_source->drop_references (); - _write_source.reset(); - } - - /* new source set up in "out" below */ - - } else { - - framecnt_t total_capture = 0; - for (ci = capture_info.begin(); ci != capture_info.end(); ++ci) { - total_capture += (*ci)->frames; - } - - if (_write_source->length (capture_info.front()->start) != 0) { - - /* phew, we have data */ - - Source::Lock source_lock(_write_source->mutex()); - - /* figure out the name for this take */ - - srcs.push_back (_write_source); - - _write_source->set_timeline_position (capture_info.front()->start); - _write_source->set_captured_for (_name); - - /* set length in beats to entire capture length */ - - BeatsFramesConverter converter (_session.tempo_map(), capture_info.front()->start); - const Evoral::Beats total_capture_beats = converter.from (total_capture); - _write_source->set_length_beats (total_capture_beats); - - /* flush to disk: this step differs from the audio path, - where all the data is already on disk. - */ - - _write_source->mark_midi_streaming_write_completed (source_lock, Evoral::Sequence::ResolveStuckNotes, total_capture_beats); - - /* we will want to be able to keep (over)writing the source - but we don't want it to be removable. this also differs - from the audio situation, where the source at this point - must be considered immutable. luckily, we can rely on - MidiSource::mark_streaming_write_completed() to have - already done the necessary work for that. - */ - - string whole_file_region_name; - whole_file_region_name = region_name_from_path (_write_source->name(), true); - - /* Register a new region with the Session that - describes the entire source. Do this first - so that any sub-regions will obviously be - children of this one (later!) - */ - - try { - PropertyList plist; - - plist.add (Properties::name, whole_file_region_name); - plist.add (Properties::whole_file, true); - plist.add (Properties::automatic, true); - plist.add (Properties::start, 0); - plist.add (Properties::length, total_capture); - plist.add (Properties::layer, 0); - - boost::shared_ptr rx (RegionFactory::create (srcs, plist)); - - region = boost::dynamic_pointer_cast (rx); - region->special_set_position (capture_info.front()->start); - } - - - catch (failed_constructor& err) { - error << string_compose(_("%1: could not create region for complete midi file"), _name) << endmsg; - /* XXX what now? */ - } - - _last_capture_sources.insert (_last_capture_sources.end(), srcs.begin(), srcs.end()); - - _playlist->clear_changes (); - _playlist->freeze (); - - /* Session frame time of the initial capture in this pass, which is where the source starts */ - framepos_t initial_capture = 0; - if (!capture_info.empty()) { - initial_capture = capture_info.front()->start; - } - - const framepos_t preroll_off = _session.preroll_record_trim_len (); - for (ci = capture_info.begin(); ci != capture_info.end(); ++ci) { - - string region_name; - - RegionFactory::region_name (region_name, _write_source->name(), false); - - DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1 capture start @ %2 length %3 add new region %4\n", - _name, (*ci)->start, (*ci)->frames, region_name)); - - - // cerr << _name << ": based on ci of " << (*ci)->start << " for " << (*ci)->frames << " add a region\n"; - - try { - PropertyList plist; - - /* start of this region is the offset between the start of its capture and the start of the whole pass */ - plist.add (Properties::start, (*ci)->start - initial_capture); - plist.add (Properties::length, (*ci)->frames); - plist.add (Properties::length_beats, converter.from((*ci)->frames).to_double()); - plist.add (Properties::name, region_name); - - boost::shared_ptr rx (RegionFactory::create (srcs, plist)); - region = boost::dynamic_pointer_cast (rx); - if (preroll_off > 0) { - region->trim_front ((*ci)->start - initial_capture + preroll_off); - } - } - - catch (failed_constructor& err) { - error << _("MidiDiskstream: could not create region for captured midi!") << endmsg; - continue; /* XXX is this OK? */ - } - - // cerr << "add new region, buffer position = " << buffer_position << " @ " << (*ci)->start << endl; - - i_am_the_modifier++; - _playlist->add_region (region, (*ci)->start + preroll_off, 1, non_layered()); - i_am_the_modifier--; - } - - _playlist->thaw (); - _session.add_command (new StatefulDiffCommand(_playlist)); - - } else { - - /* No data was recorded, so this capture will - effectively be aborted; do the same as we - do for an explicit abort. - */ - - if (_write_source) { - _write_source->mark_for_remove (); - _write_source->drop_references (); - _write_source.reset(); - } - } - - } - - use_new_write_source (0); - - for (ci = capture_info.begin(); ci != capture_info.end(); ++ci) { - delete *ci; - } - - capture_info.clear (); - capture_start_frame = 0; - - no_capture_stuff_to_do: - - reset_tracker (); -} - -void -MidiDiskstream::transport_looped (framepos_t) -{ - /* Here we only keep track of the number of captured loops so monotonic - event times can be delivered to the write source in process(). Trying - to be clever here is a world of trouble, it is better to simply record - the input in a straightforward non-destructive way. In the future when - we want to implement more clever MIDI looping modes it should be done in - the Source and/or entirely after the capture is finished. - */ - if (was_recording) { - g_atomic_int_add(const_cast (&_num_captured_loops), 1); - } -} - -void -MidiDiskstream::finish_capture () -{ - was_recording = false; - - if (capture_captured == 0) { - return; - } - - CaptureInfo* ci = new CaptureInfo; - - ci->start = capture_start_frame; - ci->frames = capture_captured; - - /* XXX theoretical race condition here. Need atomic exchange ? - However, the circumstances when this is called right - now (either on record-disable or transport_stopped) - mean that no actual race exists. I think ... - We now have a capture_info_lock, but it is only to be used - to synchronize in the transport_stop and the capture info - accessors, so that invalidation will not occur (both non-realtime). - */ - - // cerr << "Finish capture, add new CI, " << ci->start << '+' << ci->frames << endl; - - capture_info.push_back (ci); - capture_captured = 0; -} - -void -MidiDiskstream::set_record_enabled (bool yn) -{ - if (!recordable() || !_session.record_enabling_legal() || _io->n_ports().n_midi() == 0 || record_safe ()) { - return; - } - - /* yes, i know that this not proof against race conditions, but its - good enough. i think. - */ - - if (record_enabled() != yn) { - if (yn) { - engage_record_enable (); - } else { - disengage_record_enable (); - } - - RecordEnableChanged (); /* EMIT SIGNAL */ - } -} - -void -MidiDiskstream::set_record_safe (bool yn) -{ - if (!recordable() || !_session.record_enabling_legal() || _io->n_ports().n_midi() == 0) { // REQUIRES REVIEW - return; - } - - /* yes, i know that this not proof against race conditions, but its - good enough. i think. - */ - - if (record_safe () != yn) { - if (yn) { - engage_record_safe (); - } else { - disengage_record_safe (); - } - - RecordSafeChanged (); /* EMIT SIGNAL */ - } -} - -bool -MidiDiskstream::prep_record_enable () -{ - if (!recordable() || !_session.record_enabling_legal() || _io->n_ports().n_midi() == 0 || record_safe ()) { // REQUIRES REVIEW "|| record_safe ()" - return false; - } - - bool const rolling = _session.transport_speed() != 0.0f; - - boost::shared_ptr sp = _source_port.lock (); - - if (sp && Config->get_monitoring_model() == HardwareMonitoring) { - sp->request_input_monitoring (!(_session.config.get_auto_input() && rolling)); - } - - return true; -} - -bool -MidiDiskstream::prep_record_disable () -{ - - return true; -} - -XMLNode& -MidiDiskstream::get_state () -{ - XMLNode& node (Diskstream::get_state()); - - if (_write_source && _session.get_record_enabled()) { - - XMLNode* cs_child = new XMLNode (X_("CapturingSources")); - XMLNode* cs_grandchild; - - cs_grandchild = new XMLNode (X_("file")); - cs_grandchild->set_property (X_("path"), _write_source->path()); - cs_child->add_child_nocopy (*cs_grandchild); - - /* store the location where capture will start */ - - Location* pi; - - if (_session.preroll_record_punch_enabled ()) { - cs_child->set_property (X_("at"), _session.preroll_record_punch_pos()); - } else if (_session.config.get_punch_in() && ((pi = _session.locations()->auto_punch_location()) != 0)) { - cs_child->set_property (X_("at"), pi->start()); - } else { - cs_child->set_property (X_("at"), _session.transport_frame()); - } - - node.add_child_nocopy (*cs_child); - } - - return node; -} - -int -MidiDiskstream::set_state (const XMLNode& node, int version) -{ - XMLNodeList nlist = node.children(); - XMLNodeIterator niter; - XMLNode* capture_pending_node = 0; - - /* prevent write sources from being created */ - - in_set_state = true; - - for (niter = nlist.begin(); niter != nlist.end(); ++niter) { - if ((*niter)->name() == X_("CapturingSources")) { - capture_pending_node = *niter; - } - } - - if (Diskstream::set_state (node, version)) { - return -1; - } - - if (capture_pending_node) { - use_pending_capture_data (*capture_pending_node); - } - - in_set_state = false; - - return 0; -} - -int -MidiDiskstream::use_new_write_source (uint32_t n) -{ - if (!_session.writable() || !recordable()) { - return 1; - } - - _accumulated_capture_offset = 0; - _write_source.reset(); - - try { - _write_source = boost::dynamic_pointer_cast( - _session.create_midi_source_for_session (write_source_name ())); - - if (!_write_source) { - throw failed_constructor(); - } - } - - catch (failed_constructor &err) { - error << string_compose (_("%1:%2 new capture file not initialized correctly"), _name, n) << endmsg; - _write_source.reset(); - return -1; - } - - return 0; -} -/** - * We want to use the name of the existing write source (the one that will be - * used by the next capture) for another purpose. So change the name of the - * current source, and return its current name. - * - * Return an empty string if the change cannot be accomplished. - */ -std::string -MidiDiskstream::steal_write_source_name () -{ - string our_old_name = _write_source->name(); - - /* this will bump the name of the current write source to the next one - * (e.g. "MIDI 1-1" gets renamed to "MIDI 1-2"), thus leaving the - * current write source name (e.g. "MIDI 1-1" available). See the - * comments in Session::create_midi_source_by_stealing_name() about why - * we do this. - */ - - try { - string new_path = _session.new_midi_source_path (name()); - - if (_write_source->rename (new_path)) { - return string(); - } - } catch (...) { - return string (); - } - - return our_old_name; -} - -void -MidiDiskstream::reset_write_sources (bool mark_write_complete, bool /*force*/) -{ - if (!_session.writable() || !recordable()) { - return; - } - - if (_write_source && mark_write_complete) { - Source::Lock lm(_write_source->mutex()); - _write_source->mark_streaming_write_completed (lm); - } - use_new_write_source (0); -} - -void -MidiDiskstream::set_block_size (pframes_t /*nframes*/) -{ -} - -void -MidiDiskstream::allocate_temporary_buffers () -{ -} - -void -MidiDiskstream::ensure_input_monitoring (bool yn) -{ - boost::shared_ptr sp = _source_port.lock (); - - if (sp) { - sp->ensure_input_monitoring (yn); - } -} - -void -MidiDiskstream::set_align_style_from_io () -{ - if (_alignment_choice != Automatic) { - return; - } - - /* XXX Not sure what, if anything we can do with MIDI - as far as capture alignment etc. - */ - - set_align_style (ExistingMaterial); -} - - -float -MidiDiskstream::playback_buffer_load () const -{ - /* For MIDI it's not trivial to differentiate the following two cases: - - 1. The playback buffer is empty because the system has run out of time to fill it. - 2. The playback buffer is empty because there is no more data on the playlist. - - If we use a simple buffer load computation, we will report that the MIDI diskstream - cannot keep up when #2 happens, when in fact it can. Since MIDI data rates - are so low compared to audio, just give a pretend answer here. - */ - - return 1; -} - -float -MidiDiskstream::capture_buffer_load () const -{ - /* We don't report playback buffer load, so don't report capture load either */ - - return 1; -} - -int -MidiDiskstream::use_pending_capture_data (XMLNode& /*node*/) -{ - return 0; -} - -void -MidiDiskstream::flush_playback (framepos_t start, framepos_t end) -{ - _playback_buf->flush (start, end); - g_atomic_int_add (&_frames_read_from_ringbuffer, end - start); -} - -/** Writes playback events from playback_sample for nframes to dst, translating time stamps - * so that an event at playback_sample has time = 0 - */ -void -MidiDiskstream::get_playback (MidiBuffer& dst, framecnt_t nframes) -{ - dst.clear(); - - Location* loc = loop_location; - - DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ( - "%1 MDS pre-read read %8 offset = %9 @ %4..%5 from %2 write to %3, LOOPED ? %6 .. %7\n", _name, - _playback_buf->get_read_ptr(), _playback_buf->get_write_ptr(), playback_sample, playback_sample + nframes, - (loc ? loc->start() : -1), (loc ? loc->end() : -1), nframes, Port::port_offset())); - - //cerr << "======== PRE ========\n"; - //_playback_buf->dump (cerr); - //cerr << "----------------\n"; - - size_t events_read = 0; - - if (loc) { - framepos_t effective_start; - - Evoral::Range loop_range (loc->start(), loc->end() - 1); - effective_start = loop_range.squish (playback_sample); - - DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("looped, effective start adjusted to %1\n", effective_start)); - - if (effective_start == loc->start()) { - /* We need to turn off notes that may extend - beyond the loop end. - */ - - _playback_buf->resolve_tracker (dst, 0); - } - - /* for split-cycles we need to offset the events */ - - if (loc->end() >= effective_start && loc->end() < effective_start + nframes) { - - /* end of loop is within the range we are reading, so - split the read in two, and lie about the location - for the 2nd read - */ - - framecnt_t first, second; - - first = loc->end() - effective_start; - second = nframes - first; - - DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("loop read for eff %1 end %2: %3 and %4, cycle offset %5\n", - effective_start, loc->end(), first, second)); - - if (first) { - DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("loop read #1, from %1 for %2\n", - effective_start, first)); - events_read = _playback_buf->read (dst, effective_start, first); - } - - if (second) { - DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("loop read #2, from %1 for %2\n", - loc->start(), second)); - events_read += _playback_buf->read (dst, loc->start(), second); - } - - } else { - DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("loop read #3, adjusted start as %1 for %2\n", - effective_start, nframes)); - events_read = _playback_buf->read (dst, effective_start, effective_start + nframes); - } - } else { - const size_t n_skipped = _playback_buf->skip_to (playback_sample); - if (n_skipped > 0) { - warning << string_compose(_("MidiDiskstream %1: skipped %2 events, possible underflow"), id(), n_skipped) << endmsg; - } - DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("playback buffer read, from %1 to %2 (%3)", playback_sample, playback_sample + nframes, nframes)); - events_read = _playback_buf->read (dst, playback_sample, playback_sample + nframes); - } - - DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ( - "%1 MDS events read %2 range %3 .. %4 rspace %5 wspace %6 r@%7 w@%8\n", - _name, events_read, playback_sample, playback_sample + nframes, - _playback_buf->read_space(), _playback_buf->write_space(), - _playback_buf->get_read_ptr(), _playback_buf->get_write_ptr())); - - g_atomic_int_add (&_frames_read_from_ringbuffer, nframes); - - //cerr << "======== POST ========\n"; - //_playback_buf->dump (cerr); - //cerr << "----------------\n"; -} - -bool -MidiDiskstream::set_name (string const & name) -{ - if (_name == name) { - return true; - } - Diskstream::set_name (name); - - /* get a new write source so that its name reflects the new diskstream name */ - use_new_write_source (0); - - return true; -} - -bool -MidiDiskstream::set_write_source_name (const std::string& str) { - if (_write_source_name == str) { - return true; - } - Diskstream::set_write_source_name (str); - if (_write_source_name == name()) { - return true; - } - use_new_write_source (0); - return true; -} - -boost::shared_ptr -MidiDiskstream::get_gui_feed_buffer () const -{ - boost::shared_ptr b (new MidiBuffer (AudioEngine::instance()->raw_buffer_size (DataType::MIDI))); - - Glib::Threads::Mutex::Lock lm (_gui_feed_buffer_mutex); - b->copy (_gui_feed_buffer); - return b; -} - -void -MidiDiskstream::reset_tracker () -{ - _playback_buf->reset_tracker (); - - boost::shared_ptr mp (midi_playlist()); - - if (mp) { - mp->reset_note_trackers (); - } -} - -void -MidiDiskstream::resolve_tracker (Evoral::EventSink& buffer, framepos_t time) -{ - _playback_buf->resolve_tracker(buffer, time); - - boost::shared_ptr mp (midi_playlist()); - - if (mp) { - mp->reset_note_trackers (); - } -} - - -boost::shared_ptr -MidiDiskstream::midi_playlist () -{ - return boost::dynamic_pointer_cast(_playlist); -} diff --git a/libs/ardour/rc_configuration.cc b/libs/ardour/rc_configuration.cc index d6b6d02f1a..f5442f26e3 100644 --- a/libs/ardour/rc_configuration.cc +++ b/libs/ardour/rc_configuration.cc @@ -29,8 +29,9 @@ #include "pbd/replace_all.h" #include "ardour/audioengine.h" +#include "ardour/disk_reader.h" +#include "ardour/disk_writer.h" #include "ardour/control_protocol_manager.h" -#include "ardour/diskstream.h" #include "ardour/filesystem_paths.h" #include "ardour/port.h" #include "ardour/rc_configuration.h" @@ -235,8 +236,8 @@ RCConfiguration::set_state (const XMLNode& root, int version) } } - Diskstream::set_disk_read_chunk_frames (minimum_disk_read_bytes.get() / sizeof (Sample)); - Diskstream::set_disk_write_chunk_frames (minimum_disk_write_bytes.get() / sizeof (Sample)); + DiskReader::set_chunk_frames (minimum_disk_read_bytes.get() / sizeof (Sample)); + DiskWriter::set_chunk_frames (minimum_disk_write_bytes.get() / sizeof (Sample)); return 0; } diff --git a/libs/ardour/track.cc b/libs/ardour/track.cc index a1b2b8e56c..2673341f1f 100644 --- a/libs/ardour/track.cc +++ b/libs/ardour/track.cc @@ -20,7 +20,6 @@ #include "ardour/amp.h" #include "ardour/debug.h" #include "ardour/delivery.h" -#include "ardour/diskstream.h" #include "ardour/disk_reader.h" #include "ardour/disk_writer.h" #include "ardour/event_type_map.h" diff --git a/libs/ardour/wscript b/libs/ardour/wscript index 721a22b2a5..bca5dab8ce 100644 --- a/libs/ardour/wscript +++ b/libs/ardour/wscript @@ -25,7 +25,6 @@ libardour_sources = [ 'async_midi_port.cc', 'audio_backend.cc', 'audio_buffer.cc', - 'audio_diskstream.cc', 'audio_library.cc', 'audio_playlist.cc', 'audio_playlist_importer.cc', @@ -70,7 +69,6 @@ libardour_sources = [ 'disk_io.cc', 'disk_reader.cc', 'disk_writer.cc', - 'diskstream.cc', 'dsp_filter.cc', 'ebur128_analysis.cc', 'element_import_handler.cc', @@ -128,7 +126,6 @@ libardour_sources = [ 'midi_buffer.cc', 'midi_channel_filter.cc', 'midi_clock_slave.cc', - 'midi_diskstream.cc', 'midi_model.cc', 'midi_patch_manager.cc', 'midi_playlist.cc', -- 2.30.2