*/
+#include "pbd/debug.h"
#include "pbd/error.h"
-#include "pbd/i18n.h"
-#include "ardour/rc_configuration.h"
+#include "ardour/audioplaylist.h"
+#include "ardour/butler.h"
+#include "ardour/debug.h"
#include "ardour/disk_io.h"
#include "ardour/disk_reader.h"
#include "ardour/disk_writer.h"
#include "ardour/location.h"
+#include "ardour/midi_ring_buffer.h"
+#include "ardour/midi_playlist.h"
+#include "ardour/playlist.h"
+#include "ardour/playlist_factory.h"
+#include "ardour/rc_configuration.h"
#include "ardour/session.h"
+#include "ardour/session_playlists.h"
+
+#include "pbd/i18n.h"
using namespace ARDOUR;
using namespace PBD;
: Processor (s, str)
, _flags (f)
, i_am_the_modifier (false)
- , _visible_speed (0.0)
- , _actual_speed (0.0)
- , _speed (0.0)
- , _target_speed (0.0)
- , _buffer_reallocation_required (false)
- , _seek_required (false)
, _slaved (false)
- , loop_location (0)
, in_set_state (false)
- , wrap_buffer_size (0)
- , speed_buffer_size (0)
+ , playback_sample (0)
+ , _need_butler (false)
+ , channels (new ChannelList)
+ , _midi_buf (new MidiRingBuffer<samplepos_t> (s.butler()->midi_diskstream_buffer_size()))
+ , _samples_written_to_ringbuffer (0)
+ , _samples_read_from_ringbuffer (0)
+{
+ set_display_to_user (false);
+}
+
+DiskIOProcessor::~DiskIOProcessor ()
+{
+ {
+ RCUWriter<ChannelList> writer (channels);
+ boost::shared_ptr<ChannelList> c = writer.get_copy();
+
+ for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
+ delete *chan;
+ }
+
+ c->clear();
+ }
+
+ channels.flush ();
+}
+
+
+void
+DiskIOProcessor::init ()
{
+ set_block_size (_session.get_block_size());
}
void
DiskIOProcessor::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;
+ samplecnt_t read_chunk_size;
+ samplecnt_t read_buffer_size;
+ samplecnt_t write_chunk_size;
+ samplecnt_t write_buffer_size;
if (!get_buffering_presets (bp, read_chunk_size, read_buffer_size, write_chunk_size, write_buffer_size)) {
return;
}
- DiskReader::set_chunk_frames (read_chunk_size);
- DiskWriter::set_chunk_frames (write_chunk_size);
+ DiskReader::set_chunk_samples (read_chunk_size);
+ DiskWriter::set_chunk_samples (write_chunk_size);
Config->set_audio_capture_buffer_seconds (write_buffer_size);
Config->set_audio_playback_buffer_seconds (read_buffer_size);
bool
DiskIOProcessor::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)
+ samplecnt_t& read_chunk_size,
+ samplecnt_t& read_buffer_size,
+ samplecnt_t& write_chunk_size,
+ samplecnt_t& write_buffer_size)
{
switch (bp) {
case Small:
return true;
}
+bool
+DiskIOProcessor::can_support_io_configuration (const ChanCount& in, ChanCount& out)
+{
+ if (in.n_midi() != 0 && in.n_midi() != 1) {
+ /* we only support zero or 1 MIDI stream */
+ return false;
+ }
-int
-DiskIOProcessor::set_loop (Location *location)
+ /* currently no way to deliver different channels that we receive */
+ out = in;
+
+ return true;
+}
+
+bool
+DiskIOProcessor::configure_io (ChanCount in, ChanCount out)
{
- if (location) {
- if (location->start() >= location->end()) {
- error << string_compose(_("Location \"%1\" not valid for track loop (start >= end)"), location->name()) << endl;
- return -1;
+ DEBUG_TRACE (DEBUG::DiskIO, string_compose ("Configuring %1 for in:%2 out:%3\n", name(), in, out));
+
+ bool changed = false;
+
+ {
+ RCUWriter<ChannelList> writer (channels);
+ boost::shared_ptr<ChannelList> c = writer.get_copy();
+
+ uint32_t n_audio = in.n_audio();
+
+ if (n_audio > c->size()) {
+ add_channel_to (c, n_audio - c->size());
+ changed = true;
+ } else if (n_audio < c->size()) {
+ remove_channel_from (c, c->size() - n_audio);
+ changed = true;
}
+
+ /* writer leaves scope, actual channel list is updated */
}
- loop_location = location;
+ if (in.n_midi() > 0 && !_midi_buf) {
+ const size_t size = _session.butler()->midi_diskstream_buffer_size();
+ _midi_buf = new MidiRingBuffer<samplepos_t>(size);
+ changed = true;
+ }
+
+ if (changed) {
+ seek (_session.transport_sample());
+ }
- LoopSet (location); /* EMIT SIGNAL */
+ return Processor::configure_io (in, out);
+}
+
+int
+DiskIOProcessor::set_block_size (pframes_t nframes)
+{
return 0;
}
void
-DiskIOProcessor::non_realtime_set_speed ()
+DiskIOProcessor::non_realtime_locate (samplepos_t location)
{
- if (_buffer_reallocation_required)
- {
- Glib::Threads::Mutex::Lock lm (state_lock);
- allocate_temporary_buffers ();
+ /* now refill channel buffers */
- _buffer_reallocation_required = false;
- }
+ seek (location, true);
+}
- 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);
- }
+int
+DiskIOProcessor::set_state (const XMLNode& node, int version)
+{
+ XMLProperty const * prop;
+
+ Processor::set_state (node, version);
- _seek_required = false;
+ if ((prop = node.property ("flags")) != 0) {
+ _flags = Flag (string_2_enum (prop->value(), _flags));
}
+
+ return 0;
}
-bool
-DiskIOProcessor::realtime_set_speed (double sp, bool global)
+int
+DiskIOProcessor::add_channel (uint32_t how_many)
{
- bool changed = false;
- double new_speed = sp * _session.transport_speed();
+ RCUWriter<ChannelList> writer (channels);
+ boost::shared_ptr<ChannelList> c = writer.get_copy();
- if (_visible_speed != sp) {
- _visible_speed = sp;
- changed = true;
+ return add_channel_to (c, how_many);
+}
+
+int
+DiskIOProcessor::remove_channel_from (boost::shared_ptr<ChannelList> c, uint32_t how_many)
+{
+ while (how_many-- && !c->empty()) {
+ delete c->back();
+ c->pop_back();
}
- if (new_speed != _actual_speed) {
+ return 0;
+}
- framecnt_t required_wrap_size = (framecnt_t) ceil (_session.get_block_size() *
- fabs (new_speed)) + 2;
+int
+DiskIOProcessor::remove_channel (uint32_t how_many)
+{
+ RCUWriter<ChannelList> writer (channels);
+ boost::shared_ptr<ChannelList> c = writer.get_copy();
- if (required_wrap_size > wrap_buffer_size) {
- _buffer_reallocation_required = true;
- }
+ return remove_channel_from (c, how_many);
+}
- _actual_speed = new_speed;
- _target_speed = fabs(_actual_speed);
+void
+DiskIOProcessor::playlist_deleted (boost::weak_ptr<Playlist> wpl)
+{
+ boost::shared_ptr<Playlist> pl (wpl.lock());
+
+ if (!pl) {
+ return;
}
- if (changed) {
- if (!global) {
- _seek_required = true;
+ for (uint32_t n = 0; n < DataType::num_types; ++n) {
+ if (pl == _playlists[n]) {
+
+ /* this catches an ordering issue with session destruction. playlists
+ are destroyed before disk readers. we have to invalidate any handles
+ we have to the playlist.
+ */
+ _playlists[n].reset ();
+ break;
}
- SpeedChanged (); /* EMIT SIGNAL */
}
+}
- return _buffer_reallocation_required || _seek_required;
+boost::shared_ptr<AudioPlaylist>
+DiskIOProcessor::audio_playlist () const
+{
+ return boost::dynamic_pointer_cast<AudioPlaylist> (_playlists[DataType::AUDIO]);
+}
+
+boost::shared_ptr<MidiPlaylist>
+DiskIOProcessor::midi_playlist () const
+{
+ return boost::dynamic_pointer_cast<MidiPlaylist> (_playlists[DataType::MIDI]);
}
int
-DiskIOProcessor::set_state (const XMLNode& node, int version)
+DiskIOProcessor::use_playlist (DataType dt, boost::shared_ptr<Playlist> playlist)
{
- XMLProperty const * prop;
+ if (!playlist) {
+ return 0;
+ }
- Processor::set_state (node, version);
+ DEBUG_TRACE (DEBUG::DiskIO, string_compose ("%1: set to use playlist %2 (%3)\n", name(), playlist->name(), dt.to_string()));
- if ((prop = node.property ("flags")) != 0) {
- _flags = Flag (string_2_enum (prop->value(), _flags));
+ if (playlist == _playlists[dt]) {
+ DEBUG_TRACE (DEBUG::DiskIO, string_compose ("%1: already using that playlist\n", name()));
+ return 0;
}
- if ((prop = node.property ("speed")) != 0) {
- double sp = atof (prop->value().c_str());
+ playlist_connections.drop_connections ();
- if (realtime_set_speed (sp, false)) {
- non_realtime_set_speed ();
- }
+ if (_playlists[dt]) {
+ _playlists[dt]->release();
}
+
+ _playlists[dt] = playlist;
+ playlist->use();
+
+ playlist->ContentsChanged.connect_same_thread (playlist_connections, boost::bind (&DiskIOProcessor::playlist_modified, this));
+ playlist->LayeringChanged.connect_same_thread (playlist_connections, boost::bind (&DiskIOProcessor::playlist_modified, this));
+ playlist->DropReferences.connect_same_thread (playlist_connections, boost::bind (&DiskIOProcessor::playlist_deleted, this, boost::weak_ptr<Playlist>(playlist)));
+ playlist->RangesMoved.connect_same_thread (playlist_connections, boost::bind (&DiskIOProcessor::playlist_ranges_moved, this, _1, _2));
+
+ DEBUG_TRACE (DEBUG::DiskIO, string_compose ("%1 now using playlist %1 (%2)\n", name(), playlist->name(), playlist->id()));
+
return 0;
}
+
+DiskIOProcessor::ChannelInfo::ChannelInfo (samplecnt_t bufsize)
+ : rbuf (0)
+ , wbuf (0)
+ , capture_transition_buf (0)
+ , curr_capture_cnt (0)
+{
+}
+
+DiskIOProcessor::ChannelInfo::~ChannelInfo ()
+{
+ delete rbuf;
+ delete wbuf;
+ delete capture_transition_buf;
+ rbuf = 0;
+ wbuf = 0;
+ capture_transition_buf = 0;
+}
+
+void
+DiskIOProcessor::drop_route ()
+{
+ _route.reset ();
+}
+
+void
+DiskIOProcessor::set_route (boost::shared_ptr<Route> r)
+{
+ _route = r;
+
+ if (_route) {
+ _route->DropReferences.connect_same_thread (*this, boost::bind (&DiskIOProcessor::drop_route, this));
+ }
+}
+
+/** 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.
+ */
+void
+DiskIOProcessor::get_location_times(const Location* location,
+ samplepos_t* start,
+ samplepos_t* end,
+ samplepos_t* length)
+{
+ if (location) {
+ *start = location->start();
+ *end = location->end();
+ *length = *end - *start;
+ }
+}
+