X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Faudio_diskstream.cc;h=bc42cb0b5b5849c6c07c023ed6e8ac0a6fbf6541;hb=badc087263990ecf360792c10e4d9f2d60828d43;hp=9619f404fabd466b975ac748fbe9f87f900178ea;hpb=1a03f9440d11d74d291737d5b98e5624023860ac;p=ardour.git diff --git a/libs/ardour/audio_diskstream.cc b/libs/ardour/audio_diskstream.cc index 9619f404fa..bc42cb0b5b 100644 --- a/libs/ardour/audio_diskstream.cc +++ b/libs/ardour/audio_diskstream.cc @@ -31,10 +31,11 @@ #include #include -#include #include #include #include +#include +#include #include #include @@ -42,11 +43,14 @@ #include #include #include -#include #include +#include #include +#include #include #include +#include +#include #include "i18n.h" #include @@ -62,6 +66,7 @@ gain_t* AudioDiskstream::_gain_buffer = 0; AudioDiskstream::AudioDiskstream (Session &sess, const string &name, Diskstream::Flag flag) : Diskstream(sess, name, flag) , deprecated_io_node(NULL) + , channels (new ChannelList) { /* prevent any write sources from being created */ @@ -71,13 +76,12 @@ AudioDiskstream::AudioDiskstream (Session &sess, const string &name, Diskstream: use_new_playlist (); in_set_state = false; - - DiskstreamCreated (this); /* EMIT SIGNAL */ } AudioDiskstream::AudioDiskstream (Session& sess, const XMLNode& node) : Diskstream(sess, node) , deprecated_io_node(NULL) + , channels (new ChannelList) { in_set_state = true; init (Recordable); @@ -92,39 +96,8 @@ AudioDiskstream::AudioDiskstream (Session& sess, const XMLNode& node) if (destructive()) { use_destructive_playlist (); } - - DiskstreamCreated (this); /* EMIT SIGNAL */ } -void -AudioDiskstream::init_channel (ChannelInfo &chan) -{ - chan.playback_wrap_buffer = 0; - chan.capture_wrap_buffer = 0; - chan.speed_buffer = 0; - chan.peak_power = 0.0f; - chan.write_source = 0; - chan.source = 0; - chan.current_capture_buffer = 0; - chan.current_playback_buffer = 0; - chan.curr_capture_cnt = 0; - - chan.playback_buf = new RingBufferNPT (_session.diskstream_buffer_size()); - chan.capture_buf = new RingBufferNPT (_session.diskstream_buffer_size()); - chan.capture_transition_buf = new RingBufferNPT (128); - - - /* 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 (chan.playback_buf->buffer(), 0, sizeof (Sample) * chan.playback_buf->bufsize()); - memset (chan.capture_buf->buffer(), 0, sizeof (Sample) * chan.capture_buf->bufsize()); - memset (chan.capture_transition_buf->buffer(), 0, sizeof (CaptureTransition) * chan.capture_transition_buf->bufsize()); -} - - void AudioDiskstream::init (Diskstream::Flag f) { @@ -138,45 +111,26 @@ AudioDiskstream::init (Diskstream::Flag f) set_block_size (_session.get_block_size()); allocate_temporary_buffers (); - add_channel (); - assert(_n_channels == 1); + add_channel (1); + assert(_n_channels == ChanCount(DataType::AUDIO, 1)); } -void -AudioDiskstream::destroy_channel (ChannelInfo &chan) +AudioDiskstream::~AudioDiskstream () { - if (chan.write_source) { - chan.write_source->release (); - chan.write_source = 0; - } + notify_callbacks (); + + { + RCUWriter writer (channels); + boost::shared_ptr c = writer.get_copy(); - if (chan.speed_buffer) { - delete [] chan.speed_buffer; - } + for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) { + delete *chan; + } - if (chan.playback_wrap_buffer) { - delete [] chan.playback_wrap_buffer; + c->clear(); } - if (chan.capture_wrap_buffer) { - delete [] chan.capture_wrap_buffer; - } - - delete chan.playback_buf; - delete chan.capture_buf; - delete chan.capture_transition_buf; - - chan.playback_buf = 0; - chan.capture_buf = 0; -} -AudioDiskstream::~AudioDiskstream () -{ - Glib::Mutex::Lock lm (state_lock); - - for (ChannelList::iterator chan = channels.begin(); chan != channels.end(); ++chan) - destroy_channel((*chan)); - - channels.clear(); + channels.flush (); } void @@ -192,8 +146,8 @@ AudioDiskstream::allocate_working_buffers() void AudioDiskstream::free_working_buffers() { - delete _mixdown_buffer; - delete _gain_buffer; + delete [] _mixdown_buffer; + delete [] _gain_buffer; _working_buffers_size = 0; _mixdown_buffer = 0; _gain_buffer = 0; @@ -202,37 +156,26 @@ AudioDiskstream::free_working_buffers() void AudioDiskstream::non_realtime_input_change () { - { + { Glib::Mutex::Lock lm (state_lock); if (input_change_pending == NoChange) { return; } - if (input_change_pending & ConfigurationChanged) { - - if (_io->n_inputs() > _n_channels) { - - // we need to add new channel infos - - int diff = _io->n_inputs() - channels.size(); - - for (int i = 0; i < diff; ++i) { - add_channel (); - } - - } else if (_io->n_inputs() < _n_channels) { - - // we need to get rid of channels - - int diff = channels.size() - _io->n_inputs(); - - for (int i = 0; i < diff; ++i) { - remove_channel (); - } + { + RCUWriter writer (channels); + boost::shared_ptr c = writer.get_copy(); + + _n_channels.set(DataType::AUDIO, c->size()); + + if (_io->n_inputs().n_audio() > _n_channels.n_audio()) { + add_channel_to (c, _io->n_inputs().n_audio() - _n_channels.n_audio()); + } else if (_io->n_inputs().n_audio() < _n_channels.n_audio()) { + remove_channel_from (c, _n_channels.n_audio() - _io->n_inputs().n_audio()); } - } - + } + get_input_sources (); set_capture_offset (); @@ -242,10 +185,12 @@ AudioDiskstream::non_realtime_input_change () } else { set_align_style_from_io (); } - + input_change_pending = NoChange; - } + /* implicit unlock */ + } + /* reset capture files */ reset_write_sources (false); @@ -253,9 +198,8 @@ AudioDiskstream::non_realtime_input_change () /* now refill channel buffers */ if (speed() != 1.0f || speed() != -1.0f) { - seek ((jack_nframes_t) (_session.transport_frame() * (double) speed())); - } - else { + seek ((nframes_t) (_session.transport_frame() * (double) speed())); + } else { seek (_session.transport_frame()); } } @@ -263,27 +207,27 @@ AudioDiskstream::non_realtime_input_change () void AudioDiskstream::get_input_sources () { - uint32_t ni = _io->n_inputs(); - - for (uint32_t n = 0; n < ni; ++n) { + boost::shared_ptr c = channels.reader(); + + uint32_t n; + ChannelList::iterator chan; + uint32_t ni = _io->n_inputs().n_audio(); + vector connections; + + for (n = 0, chan = c->begin(); chan != c->end() && n < ni; ++chan, ++n) { - const char **connections = _io->input(n)->get_connections (); - ChannelInfo& chan = channels[n]; + connections.clear (); + + if (_io->input(n)->get_connections (connections) == 0) { - if (connections == 0 || connections[0] == 0) { - - if (chan.source) { + if ((*chan)->source) { // _source->disable_metering (); } - chan.source = 0; + (*chan)->source = 0; } else { - chan.source = _session.engine().get_port_by_name (connections[0]); - } - - if (connections) { - free (connections); + (*chan)->source = dynamic_cast(_session.engine().get_port_by_name (connections[0]) ); } } } @@ -291,15 +235,13 @@ AudioDiskstream::get_input_sources () int AudioDiskstream::find_and_use_playlist (const string& name) { - Playlist* pl; - AudioPlaylist* playlist; + boost::shared_ptr playlist; - if ((pl = _session.playlist_by_name (name)) == 0) { - playlist = new AudioPlaylist(_session, name); - pl = playlist; + if ((playlist = boost::dynamic_pointer_cast (_session.playlist_by_name (name))) == 0) { + playlist = boost::dynamic_pointer_cast (PlaylistFactory::create (DataType::AUDIO, _session, name)); } - if ((playlist = dynamic_cast (pl)) == 0) { + if (!playlist) { error << string_compose(_("AudioDiskstream: Playlist \"%1\" isn't an audio playlist"), name) << endmsg; return -1; } @@ -308,9 +250,9 @@ AudioDiskstream::find_and_use_playlist (const string& name) } int -AudioDiskstream::use_playlist (Playlist* playlist) +AudioDiskstream::use_playlist (boost::shared_ptr playlist) { - assert(dynamic_cast(playlist)); + assert(boost::dynamic_pointer_cast(playlist)); Diskstream::use_playlist(playlist); @@ -321,7 +263,7 @@ int AudioDiskstream::use_new_playlist () { string newname; - AudioPlaylist* playlist; + boost::shared_ptr playlist; if (!in_set_state && destructive()) { return 0; @@ -333,9 +275,11 @@ AudioDiskstream::use_new_playlist () newname = Playlist::bump_name (_name, _session); } - if ((playlist = new AudioPlaylist (_session, newname, hidden())) != 0) { + if ((playlist = boost::dynamic_pointer_cast (PlaylistFactory::create (DataType::AUDIO, _session, newname, hidden()))) != 0) { + playlist->set_orig_diskstream_id (id()); return use_playlist (playlist); + } else { return -1; } @@ -356,11 +300,11 @@ AudioDiskstream::use_copy_playlist () } string newname; - AudioPlaylist* playlist; + boost::shared_ptr playlist; newname = Playlist::bump_name (_playlist->name(), _session); - if ((playlist = new AudioPlaylist (*audio_playlist(), newname)) != 0) { + if ((playlist = boost::dynamic_pointer_cast(PlaylistFactory::create (audio_playlist(), newname))) != 0) { playlist->set_orig_diskstream_id (id()); return use_playlist (playlist); } else { @@ -371,54 +315,64 @@ AudioDiskstream::use_copy_playlist () void AudioDiskstream::setup_destructive_playlist () { - AudioRegion::SourceList srcs; - - for (ChannelList::iterator chan = channels.begin(); chan != channels.end(); ++chan) { - srcs.push_back ((*chan).write_source); + 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 */ - cerr << "setup DS using " << srcs.front()->natural_position () << endl; - - AudioRegion* region = new AudioRegion (srcs, 0, max_frames, _name); - _playlist->add_region (*region, srcs.front()->natural_position()); + boost::shared_ptr region (RegionFactory::create (srcs, 0, max_frames - srcs.front()->natural_position(), _name)); + _playlist->add_region (region, srcs.front()->natural_position()); } void AudioDiskstream::use_destructive_playlist () { - /* use the sources associated with the single full-extent region */ - - Playlist::RegionList* rl = _playlist->regions_at (0); + /* 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 = _playlist->find_next_region (_session.current_start_frame(), Start, 1); - if (rl->empty()) { + if (!rp) { reset_write_sources (false, true); return; } - AudioRegion* region = dynamic_cast (rl->front()); + boost::shared_ptr region = boost::dynamic_pointer_cast (rp); if (region == 0) { throw failed_constructor(); } - delete rl; + /* be sure to stretch the region out to the maximum length */ + + region->set_length (max_frames - region->position(), this); 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); - for (n = 0, chan = channels.begin(); chan != channels.end(); ++chan, ++n) { - (*chan).write_source = dynamic_cast(®ion->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 */ + + (*chan)->write_source->set_destructive (true); } /* the source list will never be reset for a destructive track */ } void -AudioDiskstream::check_record_status (jack_nframes_t transport_frame, jack_nframes_t nframes, bool can_record) +AudioDiskstream::check_record_status (nframes_t transport_frame, nframes_t nframes, bool can_record) { int possibly_recording; int rolling; @@ -468,8 +422,7 @@ AudioDiskstream::check_record_status (jack_nframes_t transport_frame, jack_nfram if (_alignment_style == ExistingMaterial) { - - if (!_session.get_punch_in()) { + if (!Config->get_punch_in()) { /* manual punch in happens at the correct transport frame because the user hit a button. but to get alignment correct @@ -498,7 +451,7 @@ AudioDiskstream::check_record_status (jack_nframes_t transport_frame, jack_nfram } else { - if (_session.get_punch_in()) { + if (Config->get_punch_in()) { first_recordable_frame += _roll_delay; } else { capture_start_frame -= _roll_delay; @@ -507,16 +460,17 @@ AudioDiskstream::check_record_status (jack_nframes_t transport_frame, jack_nfram } - if (_flags & Recordable) { - for (ChannelList::iterator chan = channels.begin(); chan != channels.end(); ++chan) { + if (recordable() && destructive()) { + boost::shared_ptr c = channels.reader(); + for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) { RingBufferNPT::rw_vector transvec; - (*chan).capture_transition_buf->get_write_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); + (*chan)->capture_transition_buf->increment_write_ptr(1); } else { // bad! @@ -543,13 +497,14 @@ AudioDiskstream::check_record_status (jack_nframes_t transport_frame, jack_nfram } int -AudioDiskstream::process (jack_nframes_t transport_frame, jack_nframes_t nframes, jack_nframes_t offset, bool can_record, bool rec_monitors_input) +AudioDiskstream::process (nframes_t transport_frame, nframes_t nframes, nframes_t offset, bool can_record, bool rec_monitors_input) { uint32_t n; - ChannelList::iterator c; + boost::shared_ptr c = channels.reader(); + ChannelList::iterator chan; int ret = -1; - jack_nframes_t rec_offset = 0; - jack_nframes_t rec_nframes = 0; + nframes_t rec_offset = 0; + nframes_t rec_nframes = 0; bool nominally_recording; bool re = record_enabled (); bool collect_playback = false; @@ -566,6 +521,8 @@ AudioDiskstream::process (jack_nframes_t transport_frame, jack_nframes_t nframes return 0; } + commit_should_unlock = false; + check_record_status (transport_frame, nframes, can_record); nominally_recording = (can_record && re); @@ -583,18 +540,23 @@ AudioDiskstream::process (jack_nframes_t transport_frame, jack_nframes_t nframes // If we can't take the state lock return. if (!state_lock.trylock()) { return 1; - } - + } + commit_should_unlock = true; adjust_capture_position = 0; - for (c = channels.begin(); c != channels.end(); ++c) { - (*c).current_capture_buffer = 0; - (*c).current_playback_buffer = 0; + for (chan = c->begin(); chan != c->end(); ++chan) { + (*chan)->current_capture_buffer = 0; + (*chan)->current_playback_buffer = 0; } - if (nominally_recording || (_session.get_record_enabled() && _session.get_punch_in())) { + if (nominally_recording || (_session.get_record_enabled() && Config->get_punch_in())) { OverlapType ot; + // 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_frames; + } + ot = coverage (first_recordable_frame, last_recordable_frame, transport_frame, transport_frame + nframes); switch (ot) { @@ -632,7 +594,7 @@ AudioDiskstream::process (jack_nframes_t transport_frame, jack_nframes_t nframes /* |--------| recrange -------------- transrange */ - rec_nframes = last_recordable_frame - last_recordable_frame; + rec_nframes = last_recordable_frame - first_recordable_frame; rec_offset = first_recordable_frame - transport_frame; break; } @@ -650,47 +612,62 @@ AudioDiskstream::process (jack_nframes_t transport_frame, jack_nframes_t nframes if (nominally_recording || rec_nframes) { - for (n = 0, c = channels.begin(); c != channels.end(); ++c, ++n) { + uint32_t limit = _io->n_inputs ().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& chan (*c); - - chan.capture_buf->get_write_vector (&chan.capture_vector); + ChannelInfo* chaninfo (*chan); + + chaninfo->capture_buf->get_write_vector (&chaninfo->capture_vector); - if (rec_nframes <= chan.capture_vector.len[0]) { + if (rec_nframes <= chaninfo->capture_vector.len[0]) { - chan.current_capture_buffer = chan.capture_vector.buf[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 */ - memcpy (chan.current_capture_buffer, _io->input(n)->get_buffer (rec_nframes) + offset + rec_offset, sizeof (Sample) * rec_nframes); + AudioPort* const ap = _io->audio_input(n); + assert(ap); + assert(rec_nframes <= ap->get_audio_buffer().capacity()); + memcpy (chaninfo->current_capture_buffer, ap->get_audio_buffer().data(rec_nframes, offset + rec_offset), sizeof (Sample) * rec_nframes); } else { - jack_nframes_t total = chan.capture_vector.len[0] + chan.capture_vector.len[1]; + nframes_t total = chaninfo->capture_vector.len[0] + chaninfo->capture_vector.len[1]; if (rec_nframes > total) { DiskOverrun (); goto out; } - Sample* buf = _io->input (n)->get_buffer (nframes) + offset; - jack_nframes_t first = chan.capture_vector.len[0]; + AudioPort* const ap = _io->audio_input(n); + assert(ap); + + Sample* buf = ap->get_audio_buffer().data(nframes, offset); + nframes_t first = chaninfo->capture_vector.len[0]; - memcpy (chan.capture_wrap_buffer, buf, sizeof (Sample) * first); - memcpy (chan.capture_vector.buf[0], buf, sizeof (Sample) * first); - memcpy (chan.capture_wrap_buffer+first, buf + first, sizeof (Sample) * (rec_nframes - first)); - memcpy (chan.capture_vector.buf[1], buf + first, sizeof (Sample) * (rec_nframes - first)); + 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)); - chan.current_capture_buffer = chan.capture_wrap_buffer; + chaninfo->current_capture_buffer = chaninfo->capture_wrap_buffer; } } } else { if (was_recording) { - finish_capture (rec_monitors_input); + finish_capture (rec_monitors_input, c); } } @@ -701,8 +678,8 @@ AudioDiskstream::process (jack_nframes_t transport_frame, jack_nframes_t nframes if (rec_nframes == nframes && rec_offset == 0) { - for (c = channels.begin(); c != channels.end(); ++c) { - (*c).current_playback_buffer = (*c).current_capture_buffer; + for (chan = c->begin(); chan != c->end(); ++chan) { + (*chan)->current_playback_buffer = (*chan)->current_capture_buffer; } playback_distance = nframes; @@ -724,8 +701,8 @@ AudioDiskstream::process (jack_nframes_t transport_frame, jack_nframes_t nframes /* can't do actual capture yet - waiting for latency effects to finish before we start*/ - for (c = channels.begin(); c != channels.end(); ++c) { - (*c).current_playback_buffer = (*c).current_capture_buffer; + for (chan = c->begin(); chan != c->end(); ++chan) { + (*chan)->current_playback_buffer = (*chan)->current_capture_buffer; } playback_distance = nframes; @@ -739,32 +716,32 @@ AudioDiskstream::process (jack_nframes_t transport_frame, jack_nframes_t nframes /* we're doing playback */ - jack_nframes_t necessary_samples; + nframes_t necessary_samples; /* no varispeed playback if we're recording, because the output .... TBD */ if (rec_nframes == 0 && _actual_speed != 1.0f) { - necessary_samples = (jack_nframes_t) floor ((nframes * fabs (_actual_speed))) + 1; + necessary_samples = (nframes_t) floor ((nframes * fabs (_actual_speed))) + 1; } else { necessary_samples = nframes; } - for (c = channels.begin(); c != channels.end(); ++c) { - (*c).playback_buf->get_read_vector (&(*c).playback_vector); + for (chan = c->begin(); chan != c->end(); ++chan) { + (*chan)->playback_buf->get_read_vector (&(*chan)->playback_vector); } n = 0; - for (c = channels.begin(); c != channels.end(); ++c, ++n) { - - ChannelInfo& chan (*c); + for (chan = c->begin(); chan != c->end(); ++chan, ++n) { + + ChannelInfo* chaninfo (*chan); - if (necessary_samples <= chan.playback_vector.len[0]) { + if (necessary_samples <= chaninfo->playback_vector.len[0]) { - chan.current_playback_buffer = chan.playback_vector.buf[0]; + chaninfo->current_playback_buffer = chaninfo->playback_vector.buf[0]; } else { - jack_nframes_t total = chan.playback_vector.len[0] + chan.playback_vector.len[1]; + nframes_t total = chaninfo->playback_vector.len[0] + chaninfo->playback_vector.len[1]; if (necessary_samples > total) { DiskUnderrun (); @@ -772,12 +749,12 @@ AudioDiskstream::process (jack_nframes_t transport_frame, jack_nframes_t nframes } else { - memcpy ((char *) chan.playback_wrap_buffer, chan.playback_vector.buf[0], - chan.playback_vector.len[0] * sizeof (Sample)); - memcpy (chan.playback_wrap_buffer + chan.playback_vector.len[0], chan.playback_vector.buf[1], - (necessary_samples - chan.playback_vector.len[0]) * sizeof (Sample)); + memcpy ((char *) chaninfo->playback_wrap_buffer, chaninfo->playback_vector.buf[0], + chaninfo->playback_vector.len[0] * sizeof (Sample)); + memcpy (chaninfo->playback_wrap_buffer + chaninfo->playback_vector.len[0], chaninfo->playback_vector.buf[1], + (necessary_samples - chaninfo->playback_vector.len[0]) * sizeof (Sample)); - chan.current_playback_buffer = chan.playback_wrap_buffer; + chaninfo->current_playback_buffer = chaninfo->playback_wrap_buffer; } } } @@ -785,38 +762,47 @@ AudioDiskstream::process (jack_nframes_t transport_frame, jack_nframes_t nframes if (rec_nframes == 0 && _actual_speed != 1.0f && _actual_speed != -1.0f) { uint64_t phase = last_phase; - jack_nframes_t i = 0; + int64_t phi_delta; + nframes_t i = 0; // Linearly interpolate into the alt buffer // using 40.24 fixp maths (swh) - for (c = channels.begin(); c != channels.end(); ++c) { + if (phi != target_phi) { + phi_delta = ((int64_t)(target_phi - phi)) / nframes; + } else { + phi_delta = 0; + } + + for (chan = c->begin(); chan != c->end(); ++chan) { float fr; - ChannelInfo& chan (*c); + ChannelInfo* chaninfo (*chan); i = 0; phase = last_phase; - for (jack_nframes_t outsample = 0; outsample < nframes; ++outsample) { + for (nframes_t outsample = 0; outsample < nframes; ++outsample) { i = phase >> 24; fr = (phase & 0xFFFFFF) / 16777216.0f; - chan.speed_buffer[outsample] = - chan.current_playback_buffer[i] * (1.0f - fr) + - chan.current_playback_buffer[i+1] * fr; - phase += phi; + chaninfo->speed_buffer[outsample] = + chaninfo->current_playback_buffer[i] * (1.0f - fr) + + chaninfo->current_playback_buffer[i+1] * fr; + phase += phi + phi_delta; } - chan.current_playback_buffer = chan.speed_buffer; + chaninfo->current_playback_buffer = chaninfo->speed_buffer; } - playback_distance = i + 1; + playback_distance = i; // + 1; last_phase = (phase & 0xFFFFFF); } else { playback_distance = nframes; } + phi = target_phi; + } ret = 0; @@ -830,6 +816,7 @@ AudioDiskstream::process (jack_nframes_t transport_frame, jack_nframes_t nframes be called. unlock the state lock. */ + commit_should_unlock = false; state_lock.unlock(); } @@ -837,7 +824,7 @@ AudioDiskstream::process (jack_nframes_t transport_frame, jack_nframes_t nframes } bool -AudioDiskstream::commit (jack_nframes_t nframes) +AudioDiskstream::commit (nframes_t nframes) { bool need_butler = false; @@ -847,12 +834,13 @@ AudioDiskstream::commit (jack_nframes_t nframes) playback_sample += playback_distance; } - for (ChannelList::iterator chan = channels.begin(); chan != channels.end(); ++chan) { + boost::shared_ptr c = channels.reader(); + for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) { - (*chan).playback_buf->increment_read_ptr (playback_distance); + (*chan)->playback_buf->increment_read_ptr (playback_distance); if (adjust_capture_position) { - (*chan).capture_buf->increment_write_ptr (adjust_capture_position); + (*chan)->capture_buf->increment_write_ptr (adjust_capture_position); } } @@ -862,13 +850,15 @@ AudioDiskstream::commit (jack_nframes_t nframes) } if (_slaved) { - need_butler = channels[0].playback_buf->write_space() >= channels[0].playback_buf->bufsize() / 2; + need_butler = c->front()->playback_buf->write_space() >= c->front()->playback_buf->bufsize() / 2; } else { - need_butler = channels[0].playback_buf->write_space() >= disk_io_chunk_frames - || channels[0].capture_buf->read_space() >= disk_io_chunk_frames; + need_butler = c->front()->playback_buf->write_space() >= disk_io_chunk_frames + || c->front()->capture_buf->read_space() >= disk_io_chunk_frames; } - state_lock.unlock(); + if (commit_should_unlock) { + state_lock.unlock(); + } _processed = false; @@ -883,12 +873,13 @@ AudioDiskstream::set_pending_overwrite (bool yn) pending_overwrite = yn; overwrite_frame = playback_sample; - overwrite_offset = channels.front().playback_buf->get_read_ptr(); + overwrite_offset = channels.reader()->front()->playback_buf->get_read_ptr(); } int AudioDiskstream::overwrite_existing_buffers () { + boost::shared_ptr c = channels.reader(); Sample* mixdown_buffer; float* gain_buffer; int ret = -1; @@ -897,7 +888,7 @@ AudioDiskstream::overwrite_existing_buffers () overwrite_queued = false; /* assume all are the same size */ - jack_nframes_t size = channels[0].playback_buf->bufsize(); + nframes_t size = c->front()->playback_buf->bufsize(); mixdown_buffer = new Sample[size]; gain_buffer = new float[size]; @@ -906,12 +897,12 @@ AudioDiskstream::overwrite_existing_buffers () size--; uint32_t n=0; - jack_nframes_t start; + nframes_t start; - for (ChannelList::iterator chan = channels.begin(); chan != channels.end(); ++chan, ++n) { + for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan, ++n) { start = overwrite_frame; - jack_nframes_t cnt = size; + nframes_t cnt = size; /* to fill the buffer without resetting the playback sample, we need to do it one or two chunks (normally two). @@ -924,9 +915,9 @@ AudioDiskstream::overwrite_existing_buffers () */ - jack_nframes_t to_read = size - overwrite_offset; + nframes_t to_read = size - overwrite_offset; - if (read ((*chan).playback_buf->buffer() + overwrite_offset, mixdown_buffer, gain_buffer, start, to_read, *chan, n, reversed)) { + if (read ((*chan)->playback_buf->buffer() + overwrite_offset, mixdown_buffer, gain_buffer, start, to_read, *chan, n, reversed)) { error << string_compose(_("AudioDiskstream %1: when refilling, cannot read %2 from playlist at frame %3"), _id, size, playback_sample) << endmsg; goto out; @@ -936,7 +927,7 @@ AudioDiskstream::overwrite_existing_buffers () cnt -= to_read; - if (read ((*chan).playback_buf->buffer(), mixdown_buffer, gain_buffer, + if (read ((*chan)->playback_buf->buffer(), mixdown_buffer, gain_buffer, start, cnt, *chan, n, reversed)) { error << string_compose(_("AudioDiskstream %1: when refilling, cannot read %2 from playlist at frame %3"), _id, size, playback_sample) << endmsg; @@ -955,27 +946,29 @@ AudioDiskstream::overwrite_existing_buffers () } int -AudioDiskstream::seek (jack_nframes_t frame, bool complete_refill) +AudioDiskstream::seek (nframes_t frame, bool complete_refill) { - Glib::Mutex::Lock lm (state_lock); uint32_t n; - int ret; + int ret = -1; ChannelList::iterator chan; + boost::shared_ptr c = channels.reader(); - for (n = 0, chan = channels.begin(); chan != channels.end(); ++chan, ++n) { - (*chan).playback_buf->reset (); - (*chan).capture_buf->reset (); + Glib::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) { while ((ret = do_refill_with_alloc ()) > 0) ; } else { @@ -986,12 +979,13 @@ AudioDiskstream::seek (jack_nframes_t frame, bool complete_refill) } int -AudioDiskstream::can_internal_playback_seek (jack_nframes_t distance) +AudioDiskstream::can_internal_playback_seek (nframes_t distance) { ChannelList::iterator chan; + boost::shared_ptr c = channels.reader(); - for (chan = channels.begin(); chan != channels.end(); ++chan) { - if ((*chan).playback_buf->read_space() < distance) { + for (chan = c->begin(); chan != c->end(); ++chan) { + if ((*chan)->playback_buf->read_space() < distance) { return false; } } @@ -999,12 +993,13 @@ AudioDiskstream::can_internal_playback_seek (jack_nframes_t distance) } int -AudioDiskstream::internal_playback_seek (jack_nframes_t distance) +AudioDiskstream::internal_playback_seek (nframes_t distance) { ChannelList::iterator chan; + boost::shared_ptr c = channels.reader(); - for (chan = channels.begin(); chan != channels.end(); ++chan) { - (*chan).playback_buf->increment_read_ptr (distance); + for (chan = c->begin(); chan != c->end(); ++chan) { + (*chan)->playback_buf->increment_read_ptr (distance); } first_recordable_frame += distance; @@ -1014,18 +1009,21 @@ AudioDiskstream::internal_playback_seek (jack_nframes_t distance) } int -AudioDiskstream::read (Sample* buf, Sample* mixdown_buffer, float* gain_buffer, jack_nframes_t& start, jack_nframes_t cnt, - ChannelInfo& channel_info, int channel, bool reversed) +AudioDiskstream::read (Sample* buf, Sample* mixdown_buffer, float* gain_buffer, nframes_t& start, nframes_t cnt, + ChannelInfo* channel_info, int channel, bool reversed) { - jack_nframes_t this_read = 0; + nframes_t this_read = 0; bool reloop = false; - jack_nframes_t loop_end = 0; - jack_nframes_t loop_start = 0; - jack_nframes_t loop_length = 0; - jack_nframes_t offset = 0; + nframes_t loop_end = 0; + nframes_t loop_start = 0; + nframes_t loop_length = 0; + nframes_t offset = 0; Location *loc = 0; + /* XXX we don't currently play loops in reverse. not sure why */ + if (!reversed) { + /* Make the use of a Location atomic for this read operation. Note: Locations don't get deleted, so all we care about @@ -1049,11 +1047,16 @@ AudioDiskstream::read (Sample* buf, Sample* mixdown_buffer, float* gain_buffer, start = loop_start + ((start - loop_start) % loop_length); //cerr << "to " << start << endl; } + //cerr << "start is " << start << " loopstart: " << loop_start << " loopend: " << loop_end << endl; } while (cnt) { + if (reversed) { + start -= cnt; + } + /* take any loop into account. we can't read past the end of the loop. */ if (loc && (loop_end - start < cnt)) { @@ -1081,9 +1084,6 @@ AudioDiskstream::read (Sample* buf, Sample* mixdown_buffer, float* gain_buffer, if (reversed) { - /* don't adjust start, since caller has already done that - */ - swap_by_ptr (buf, buf + this_read - 1); } else { @@ -1105,7 +1105,7 @@ AudioDiskstream::read (Sample* buf, Sample* mixdown_buffer, float* gain_buffer, } int -AudioDiskstream::do_refill_with_alloc() +AudioDiskstream::do_refill_with_alloc () { Sample* mix_buf = new Sample[disk_io_chunk_frames]; float* gain_buf = new float[disk_io_chunk_frames]; @@ -1122,24 +1122,34 @@ int AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer) { int32_t ret = 0; - jack_nframes_t to_read; + nframes_t to_read; RingBufferNPT::rw_vector vector; bool reversed = (_visible_speed * _session.transport_speed()) < 0.0f; - jack_nframes_t total_space; - jack_nframes_t zero_fill; + nframes_t total_space; + nframes_t zero_fill; uint32_t chan_n; ChannelList::iterator i; - jack_nframes_t ts; + boost::shared_ptr c = channels.reader(); + nframes_t ts; + + if (c->empty()) { + return 0; + } assert(mixdown_buffer); assert(gain_buffer); - channels.front().playback_buf->get_write_vector (&vector); + 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) { return 0; } - + /* 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. @@ -1165,10 +1175,12 @@ AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer) work with. */ - if (_slaved && total_space < (channels.front().playback_buf->bufsize() / 2)) { + if (_slaved && total_space < (c->front()->playback_buf->bufsize() / 2)) { return 0; } + /* never do more than disk_io_chunk_frames worth of disk input per call (limit doesn't apply for memset) */ + total_space = min (disk_io_chunk_frames, total_space); if (reversed) { @@ -1177,15 +1189,15 @@ AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer) /* at start: nothing to do but fill with silence */ - for (chan_n = 0, i = channels.begin(); i != channels.end(); ++i, ++chan_n) { + for (chan_n = 0, i = c->begin(); i != c->end(); ++i, ++chan_n) { - ChannelInfo& chan (*i); - chan.playback_buf->get_write_vector (&vector); + 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]); + chan->playback_buf->increment_write_ptr (vector.len[0] + vector.len[1]); } return 0; } @@ -1202,11 +1214,6 @@ AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer) } else { - /* move read position backwards because we are going - to reverse the data. - */ - - file_frame -= total_space; zero_fill = 0; } @@ -1216,15 +1223,15 @@ AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer) /* at end: nothing to do but fill with silence */ - for (chan_n = 0, i = channels.begin(); i != channels.end(); ++i, ++chan_n) { + for (chan_n = 0, i = c->begin(); i != c->end(); ++i, ++chan_n) { - ChannelInfo& chan (*i); - chan.playback_buf->get_write_vector (&vector); + 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]); + chan->playback_buf->increment_write_ptr (vector.len[0] + vector.len[1]); } return 0; } @@ -1241,32 +1248,47 @@ AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer) } } - jack_nframes_t file_frame_tmp = 0; + nframes_t file_frame_tmp = 0; - for (chan_n = 0, i = channels.begin(); i != channels.end(); ++i, ++chan_n) { + for (chan_n = 0, i = c->begin(); i != c->end(); ++i, ++chan_n) { - ChannelInfo& chan (*i); + ChannelInfo* chan (*i); Sample* buf1; Sample* buf2; - jack_nframes_t len1, len2; + nframes_t len1, len2; - chan.playback_buf->get_write_vector (&vector); + chan->playback_buf->get_write_vector (&vector); + + if (vector.len[0] > disk_io_chunk_frames) { + + /* 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_io_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; - if (reversed) { - buf1 = vector.buf[1]; - len1 = vector.len[1]; - buf2 = vector.buf[0]; - len2 = vector.len[0]; - } else { - buf1 = vector.buf[0]; - len1 = vector.len[0]; - buf2 = vector.buf[1]; - len2 = vector.len[1]; - } - + 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, disk_io_chunk_frames); @@ -1277,8 +1299,8 @@ AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer) ret = -1; goto out; } - - chan.playback_buf->increment_write_ptr (to_read); + + chan->playback_buf->increment_write_ptr (to_read); ts -= to_read; } @@ -1286,7 +1308,6 @@ AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer) if (to_read) { - /* we read all of vector.len[0], but it wasn't an entire disk_io_chunk_frames of data, so read some or all of vector.len[1] as well. */ @@ -1296,7 +1317,7 @@ AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer) goto out; } - chan.playback_buf->increment_write_ptr (to_read); + chan->playback_buf->increment_write_ptr (to_read); } if (zero_fill) { @@ -1329,17 +1350,22 @@ AudioDiskstream::do_flush (Session::RunContext context, bool force_flush) int32_t ret = 0; RingBufferNPT::rw_vector vector; RingBufferNPT::rw_vector transvec; - jack_nframes_t total; + nframes_t total; _write_data_count = 0; - for (ChannelList::iterator chan = channels.begin(); chan != channels.end(); ++chan) { + 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); + (*chan)->capture_buf->get_read_vector (&vector); total = vector.len[0] + vector.len[1]; - if (total == 0 || (total < disk_io_chunk_frames && !force_flush && was_recording)) { goto out; } @@ -1359,13 +1385,13 @@ AudioDiskstream::do_flush (Session::RunContext context, bool force_flush) ret = 1; } - to_write = min (disk_io_chunk_frames, (jack_nframes_t) vector.len[0]); + to_write = min (disk_io_chunk_frames, (nframes_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); + (*chan)->capture_transition_buf->get_read_vector(&transvec); size_t transcount = transvec.len[0] + transvec.len[1]; bool have_start = false; size_t ti; @@ -1376,8 +1402,8 @@ AudioDiskstream::do_flush (Session::RunContext context, bool force_flush) 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; + (*chan)->write_source->mark_capture_start (captrans.capture_val); + (*chan)->curr_capture_cnt = 0; have_start = true; } @@ -1385,17 +1411,17 @@ AudioDiskstream::do_flush (Session::RunContext context, bool force_flush) // capture end, the capture_val represents total frames in capture - if (captrans.capture_val <= (*chan).curr_capture_cnt + to_write) { + 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); + 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 (); + (*chan)->write_source->mark_capture_end (); // increment past this transition, but go no further ++ti; @@ -1410,17 +1436,17 @@ AudioDiskstream::do_flush (Session::RunContext context, bool force_flush) } if (ti > 0) { - (*chan).capture_transition_buf->increment_read_ptr(ti); + (*chan)->capture_transition_buf->increment_read_ptr(ti); } } - if ((!(*chan).write_source) || (*chan).write_source->write (vector.buf[0], to_write) != to_write) { + 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; + (*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_io_chunk_frames) && !destructive()) { @@ -1428,18 +1454,18 @@ AudioDiskstream::do_flush (Session::RunContext context, bool force_flush) disk_io_chunk_frames of data, so arrange for some part of vector.len[1] to be flushed to disk as well. */ - - to_write = min ((jack_nframes_t)(disk_io_chunk_frames - to_write), (jack_nframes_t) vector.len[1]); - - if ((*chan).write_source->write (vector.buf[1], to_write) != to_write) { + + to_write = min ((nframes_t)(disk_io_chunk_frames - to_write), (nframes_t) vector.len[1]); + + 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; } - _write_data_count += (*chan).write_source->write_data_count(); + _write_data_count += (*chan)->write_source->write_data_count(); - (*chan).capture_buf->increment_read_ptr (to_write); - (*chan).curr_capture_cnt += to_write; + (*chan)->capture_buf->increment_read_ptr (to_write); + (*chan)->curr_capture_cnt += to_write; } } @@ -1453,16 +1479,17 @@ AudioDiskstream::transport_stopped (struct tm& when, time_t twhen, bool abort_ca uint32_t buffer_position; bool more_work = true; int err = 0; - AudioRegion* region = 0; - jack_nframes_t total_capture; - AudioRegion::SourceList srcs; - AudioRegion::SourceList::iterator src; + boost::shared_ptr region; + nframes_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 (true); + finish_capture (true, c); /* butler is already stopped, but there may be work to do to flush remaining data to disk. @@ -1490,30 +1517,21 @@ AudioDiskstream::transport_stopped (struct tm& when, time_t twhen, bool abort_ca if (abort_capture) { - ChannelList::iterator chan; - - list* deletion_list = new list; + if (destructive()) { + goto outout; + } - for ( chan = channels.begin(); chan != channels.end(); ++chan) { + for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) { - if ((*chan).write_source) { + if ((*chan)->write_source) { - (*chan).write_source->mark_for_remove (); - (*chan).write_source->release (); - - deletion_list->push_back ((*chan).write_source); - - (*chan).write_source = 0; + (*chan)->write_source->mark_for_remove (); + (*chan)->write_source->drop_references (); + (*chan)->write_source.reset (); } /* new source set up in "out" below */ } - - if (!deletion_list->empty()) { - DeleteSources (deletion_list); - } else { - delete deletion_list; - } goto out; } @@ -1524,23 +1542,15 @@ AudioDiskstream::transport_stopped (struct tm& when, time_t twhen, bool abort_ca /* figure out the name for this take */ - for (n = 0, chan = channels.begin(); chan != channels.end(); ++chan, ++n) { + for (n = 0, chan = c->begin(); chan != c->end(); ++chan, ++n) { - AudioFileSource* s = (*chan).write_source; + boost::shared_ptr s = (*chan)->write_source; if (s) { - - AudioFileSource* fsrc; - srcs.push_back (s); - - if ((fsrc = dynamic_cast(s)) != 0) { - cerr << "updating source after capture\n"; - fsrc->update_header (capture_info.front()->start, when, twhen); - } - + s->update_header (capture_info.front()->start, when, twhen); s->set_captured_for (_name); - + s->mark_immutable (); } } @@ -1558,6 +1568,9 @@ AudioDiskstream::transport_stopped (struct tm& when, time_t twhen, bool abort_ca } 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 @@ -1565,10 +1578,11 @@ AudioDiskstream::transport_stopped (struct tm& when, time_t twhen, bool abort_ca */ try { - region = new AudioRegion (srcs, channels[0].write_source->last_capture_start_frame(), total_capture, - region_name_from_path (channels[0].write_source->name()), - 0, AudioRegion::Flag (AudioRegion::DefaultFlags|AudioRegion::Automatic|AudioRegion::WholeFile)); - + boost::shared_ptr rx (RegionFactory::create (srcs, c->front()->write_source->last_capture_start_frame(), total_capture, + whole_file_region_name, + 0, AudioRegion::Flag (AudioRegion::DefaultFlags|AudioRegion::Automatic|AudioRegion::WholeFile))); + + region = boost::dynamic_pointer_cast (rx); region->special_set_position (capture_info.front()->start); } @@ -1582,18 +1596,20 @@ AudioDiskstream::transport_stopped (struct tm& when, time_t twhen, bool abort_ca // cerr << _name << ": there are " << capture_info.size() << " capture_info records\n"; - XMLNode &before = _playlist->get_state(); + XMLNode &before = _playlist->get_state(); _playlist->freeze (); - for (buffer_position = channels[0].write_source->last_capture_start_frame(), ci = capture_info.begin(); ci != capture_info.end(); ++ci) { + for (buffer_position = c->front()->write_source->last_capture_start_frame(), ci = capture_info.begin(); ci != capture_info.end(); ++ci) { string region_name; - _session.region_name (region_name, channels[0].write_source->name(), false); + + _session.region_name (region_name, whole_file_region_name, false); - // cerr << _name << ": based on ci of " << (*ci)->start << " for " << (*ci)->frames << " add a region\n"; + // cerr << _name << ": based on ci of " << (*ci)->start << " for " << (*ci)->frames << " add region " << region_name << endl; try { - region = new AudioRegion (srcs, buffer_position, (*ci)->frames, region_name); + boost::shared_ptr rx (RegionFactory::create (srcs, buffer_position, (*ci)->frames, region_name)); + region = boost::dynamic_pointer_cast (rx); } catch (failed_constructor& err) { @@ -1601,27 +1617,29 @@ AudioDiskstream::transport_stopped (struct tm& when, time_t twhen, bool abort_ca continue; /* XXX is this OK? */ } - _last_capture_regions.push_back (region); + region->GoingAway.connect (bind (mem_fun (*this, &Diskstream::remove_region_from_last_capture), boost::weak_ptr(region))); - // cerr << "add new region, buffer position = " << buffer_position << " @ " << (*ci)->start << endl; + _last_capture_regions.push_back (region); i_am_the_modifier++; - _playlist->add_region (*region, (*ci)->start); + _playlist->add_region (region, (*ci)->start); i_am_the_modifier--; buffer_position += (*ci)->frames; } _playlist->thaw (); - XMLNode &after = _playlist->get_state(); - _session.add_command (new MementoCommand(*_playlist, before, after)); + XMLNode &after = _playlist->get_state(); + _session.add_command (new MementoCommand(*_playlist, &before, &after)); } mark_write_completed = true; + out: reset_write_sources (mark_write_completed); - out: + outout: + for (ci = capture_info.begin(); ci != capture_info.end(); ++ci) { delete *ci; } @@ -1631,7 +1649,58 @@ AudioDiskstream::transport_stopped (struct tm& when, time_t twhen, bool abort_ca } void -AudioDiskstream::finish_capture (bool rec_monitors_input) +AudioDiskstream::transport_looped (nframes_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(); + + // adjust the capture length knowing that the data will be recorded to disk + // only necessary after the first loop where we're recording + if (capture_info.size() == 0) { + capture_captured += _capture_offset; + + if (_alignment_style == ExistingMaterial) { + capture_captured += _session.worst_output_latency(); + } else { + capture_captured += _roll_delay; + } + } + + finish_capture (true, 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_frames; + 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 (bool rec_monitors_input, boost::shared_ptr c) { was_recording = false; @@ -1640,16 +1709,15 @@ AudioDiskstream::finish_capture (bool rec_monitors_input) } if (recordable() && destructive()) { - for (ChannelList::iterator chan = channels.begin(); chan != channels.end(); ++chan) { + for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) { RingBufferNPT::rw_vector transvec; - (*chan).capture_transition_buf->get_write_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); + (*chan)->capture_transition_buf->increment_write_ptr(1); } else { // bad! @@ -1677,12 +1745,15 @@ AudioDiskstream::finish_capture (bool rec_monitors_input) 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_frames; } void AudioDiskstream::set_record_enabled (bool yn) { - if (!recordable() || !_session.record_enabling_legal()) { + if (!recordable() || !_session.record_enabling_legal() || _io->n_inputs().n_audio() == 0) { return; } @@ -1692,7 +1763,7 @@ AudioDiskstream::set_record_enabled (bool yn) return; } - if (yn && channels[0].source == 0) { + if (yn && channels.reader()->front()->source == 0) { /* pick up connections not initiated *from* the IO object we're associated with. @@ -1717,20 +1788,26 @@ AudioDiskstream::set_record_enabled (bool yn) void AudioDiskstream::engage_record_enable () { - bool rolling = _session.transport_speed() != 0.0f; + bool rolling = _session.transport_speed() != 0.0f; + boost::shared_ptr c = channels.reader(); g_atomic_int_set (&_record_enabled, 1); capturing_sources.clear (); - if (Config->get_use_hardware_monitoring()) { - for (ChannelList::iterator chan = channels.begin(); chan != channels.end(); ++chan) { - if ((*chan).source) { - (*chan).source->request_monitor_input (!(_session.get_auto_input() && rolling)); + + if (Config->get_monitoring_model() == HardwareMonitoring) { + + for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) { + if ((*chan)->source) { + (*chan)->source->ensure_monitor_input (!(Config->get_auto_input() && rolling)); } - capturing_sources.push_back ((*chan).write_source); + capturing_sources.push_back ((*chan)->write_source); + (*chan)->write_source->mark_streaming_write_started (); } + } else { - for (ChannelList::iterator chan = channels.begin(); chan != channels.end(); ++chan) { - capturing_sources.push_back ((*chan).write_source); + for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) { + capturing_sources.push_back ((*chan)->write_source); + (*chan)->write_source->mark_streaming_write_started (); } } @@ -1741,17 +1818,17 @@ void AudioDiskstream::disengage_record_enable () { g_atomic_int_set (&_record_enabled, 0); - if (Config->get_use_hardware_monitoring()) { - for (ChannelList::iterator chan = channels.begin(); chan != channels.end(); ++chan) { - if ((*chan).source) { - (*chan).source->request_monitor_input (false); + boost::shared_ptr c = channels.reader(); + for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) { + if (Config->get_monitoring_model() == HardwareMonitoring) { + if ((*chan)->source) { + (*chan)->source->ensure_monitor_input (false); } } } capturing_sources.clear (); RecordEnableChanged (); /* EMIT SIGNAL */ } - XMLNode& AudioDiskstream::get_state () @@ -1759,20 +1836,20 @@ AudioDiskstream::get_state () XMLNode* node = new XMLNode ("AudioDiskstream"); char buf[64] = ""; LocaleGuard lg (X_("POSIX")); + boost::shared_ptr c = channels.reader(); - snprintf (buf, sizeof(buf), "0x%x", _flags); - node->add_property ("flags", buf); + node->add_property ("flags", enum_2_string (_flags)); - snprintf (buf, sizeof(buf), "%zd", channels.size()); + snprintf (buf, sizeof(buf), "%zd", c->size()); node->add_property ("channels", buf); node->add_property ("playlist", _playlist->name()); - snprintf (buf, sizeof(buf), "%f", _visible_speed); + snprintf (buf, sizeof(buf), "%.12g", _visible_speed); node->add_property ("speed", buf); node->add_property("name", _name); - id().print (buf); + id().print (buf, sizeof (buf)); node->add_property("id", buf); if (!capturing_sources.empty() && _session.get_record_enabled()) { @@ -1780,7 +1857,7 @@ AudioDiskstream::get_state () XMLNode* cs_child = new XMLNode (X_("CapturingSources")); XMLNode* cs_grandchild; - for (vector::iterator i = capturing_sources.begin(); i != capturing_sources.end(); ++i) { + for (vector >::iterator i = capturing_sources.begin(); i != capturing_sources.end(); ++i) { cs_grandchild = new XMLNode (X_("file")); cs_grandchild->add_property (X_("path"), (*i)->path()); cs_child->add_child_nocopy (*cs_grandchild); @@ -1790,7 +1867,7 @@ AudioDiskstream::get_state () Location* pi; - if (_session.get_punch_in() && ((pi = _session.locations()->auto_punch_location()) != 0)) { + if (Config->get_punch_in() && ((pi = _session.locations()->auto_punch_location()) != 0)) { snprintf (buf, sizeof (buf), "%" PRIu32, pi->start()); } else { snprintf (buf, sizeof (buf), "%" PRIu32, _session.transport_frame()); @@ -1848,7 +1925,7 @@ AudioDiskstream::set_state (const XMLNode& node) } if ((prop = node.property ("flags")) != 0) { - _flags = strtol (prop->value().c_str(), 0, 0); + _flags = Flag (string_2_enum (prop->value(), _flags)); } if ((prop = node.property ("channels")) != 0) { @@ -1858,27 +1935,15 @@ AudioDiskstream::set_state (const XMLNode& node) // create necessary extra channels // we are always constructed with one and we always need one - if (nchans > _n_channels) { - - // we need to add new channel infos - //LockMonitor lm (state_lock, __LINE__, __FILE__); - - int diff = nchans - channels.size(); - - for (int i=0; i < diff; ++i) { - add_channel (); - } + _n_channels.set(DataType::AUDIO, channels.reader()->size()); + + if (nchans > _n_channels.n_audio()) { - } else if (nchans < _n_channels) { + add_channel (nchans - _n_channels.n_audio()); - // we need to get rid of channels - //LockMonitor lm (state_lock, __LINE__, __FILE__); + } else if (nchans < _n_channels.n_audio()) { - int diff = channels.size() - nchans; - - for (int i = 0; i < diff; ++i) { - remove_channel (); - } + remove_channel (_n_channels.n_audio() - nchans); } if ((prop = node.property ("playlist")) == 0) { @@ -1914,8 +1979,6 @@ AudioDiskstream::set_state (const XMLNode& node) } } - _n_channels = channels.size(); - in_set_state = false; /* make sure this is clear before we do anything else */ @@ -1934,46 +1997,40 @@ AudioDiskstream::set_state (const XMLNode& node) int AudioDiskstream::use_new_write_source (uint32_t n) { + boost::shared_ptr c = channels.reader(); + if (!recordable()) { return 1; } - if (n >= channels.size()) { + if (n >= c->size()) { error << string_compose (_("AudioDiskstream: channel %1 out of range"), n) << endmsg; return -1; } - ChannelInfo &chan = channels[n]; + ChannelInfo* chan = (*c)[n]; - if (chan.write_source) { - - if (AudioFileSource::is_empty (chan.write_source->path())) { - chan.write_source->mark_for_remove (); - chan.write_source->release(); - delete chan.write_source; - } else { - chan.write_source->release(); - chan.write_source = 0; - } + if (chan->write_source) { + chan->write_source->done_with_peakfile_writes (); + chan->write_source->set_allow_remove_if_empty (true); + chan->write_source.reset (); } try { - if ((chan.write_source = _session.create_audio_source_for_session (*this, n, destructive())) == 0) { + if ((chan->write_source = _session.create_audio_source_for_session (*this, 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 = 0; + chan->write_source.reset (); return -1; } - chan.write_source->use (); - /* do not remove destructive files even if they are empty */ - chan.write_source->set_allow_remove_if_empty (!destructive()); + chan->write_source->set_allow_remove_if_empty (!destructive()); return 0; } @@ -1982,6 +2039,7 @@ void AudioDiskstream::reset_write_sources (bool mark_write_complete, bool force) { ChannelList::iterator chan; + boost::shared_ptr c = channels.reader(); uint32_t n; if (!recordable()) { @@ -1989,21 +2047,21 @@ AudioDiskstream::reset_write_sources (bool mark_write_complete, bool force) } capturing_sources.clear (); - - for (chan = channels.begin(), n = 0; chan != channels.end(); ++chan, ++n) { + + for (chan = c->begin(), n = 0; chan != c->end(); ++chan, ++n) { if (!destructive()) { - if ((*chan).write_source && mark_write_complete) { - (*chan).write_source->mark_streaming_write_completed (); + if ((*chan)->write_source && mark_write_complete) { + (*chan)->write_source->mark_streaming_write_completed (); } use_new_write_source (n); if (record_enabled()) { - capturing_sources.push_back ((*chan).write_source); + capturing_sources.push_back ((*chan)->write_source); } } else { - if ((*chan).write_source == 0) { + if ((*chan)->write_source == 0) { use_new_write_source (n); } } @@ -2025,11 +2083,12 @@ int AudioDiskstream::rename_write_sources () { ChannelList::iterator chan; + boost::shared_ptr c = channels.reader(); uint32_t n; - for (chan = channels.begin(), n = 0; chan != channels.end(); ++chan, ++n) { - if ((*chan).write_source != 0) { - (*chan).write_source->set_name (_name, destructive()); + for (chan = c->begin(), n = 0; chan != c->end(); ++chan, ++n) { + if ((*chan)->write_source != 0) { + (*chan)->write_source->set_source_name (_name, destructive()); /* XXX what to do if one of them fails ? */ } } @@ -2038,14 +2097,15 @@ AudioDiskstream::rename_write_sources () } void -AudioDiskstream::set_block_size (jack_nframes_t nframes) +AudioDiskstream::set_block_size (nframes_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 = channels.begin(); chan != channels.end(); ++chan) { - if ((*chan).speed_buffer) delete [] (*chan).speed_buffer; - (*chan).speed_buffer = new Sample[speed_buffer_size]; + 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 (); @@ -2060,15 +2120,17 @@ AudioDiskstream::allocate_temporary_buffers () */ double sp = max (fabsf (_actual_speed), 1.2f); - jack_nframes_t required_wrap_size = (jack_nframes_t) floor (_session.get_block_size() * sp) + 1; + nframes_t required_wrap_size = (nframes_t) floor (_session.get_block_size() * sp) + 1; if (required_wrap_size > wrap_buffer_size) { - for (ChannelList::iterator chan = channels.begin(); chan != channels.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]; + 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; @@ -2078,10 +2140,12 @@ AudioDiskstream::allocate_temporary_buffers () void AudioDiskstream::monitor_input (bool yn) { - for (ChannelList::iterator chan = channels.begin(); chan != channels.end(); ++chan) { + boost::shared_ptr c = channels.reader(); + + for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) { - if ((*chan).source) { - (*chan).source->request_monitor_input (yn); + if ((*chan)->source) { + (*chan)->source->ensure_monitor_input (yn); } } } @@ -2097,8 +2161,10 @@ AudioDiskstream::set_align_style_from_io () get_input_sources (); - for (ChannelList::iterator chan = channels.begin(); chan != channels.end(); ++chan) { - if ((*chan).source && (*chan).source->flags() & JackPortIsPhysical) { + boost::shared_ptr c = channels.reader(); + + for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) { + if ((*chan)->source && (*chan)->source->flags() & JackPortIsPhysical) { have_physical = true; break; } @@ -2112,53 +2178,64 @@ AudioDiskstream::set_align_style_from_io () } int -AudioDiskstream::add_channel () +AudioDiskstream::add_channel_to (boost::shared_ptr c, uint32_t how_many) { - /* XXX need to take lock??? */ + while (how_many--) { + c->push_back (new ChannelInfo(_session.diskstream_buffer_size(), speed_buffer_size, wrap_buffer_size)); + } - ChannelInfo chan; + _n_channels.set(DataType::AUDIO, c->size()); - init_channel (chan); + return 0; +} - chan.speed_buffer = new Sample[speed_buffer_size]; - chan.playback_wrap_buffer = new Sample[wrap_buffer_size]; - chan.capture_wrap_buffer = new Sample[wrap_buffer_size]; +int +AudioDiskstream::add_channel (uint32_t how_many) +{ + RCUWriter writer (channels); + boost::shared_ptr c = writer.get_copy(); - channels.push_back (chan); + return add_channel_to (c, how_many); +} - _n_channels = channels.size(); +int +AudioDiskstream::remove_channel_from (boost::shared_ptr c, uint32_t how_many) +{ + while (--how_many && !c->empty()) { + delete c->back(); + c->pop_back(); + } + + _n_channels.set(DataType::AUDIO, c->size()); return 0; } int -AudioDiskstream::remove_channel () +AudioDiskstream::remove_channel (uint32_t how_many) { - if (channels.size() > 1) { - /* XXX need to take lock??? */ - ChannelInfo & chan = channels.back(); - destroy_channel (chan); - channels.pop_back(); - - _n_channels = channels.size(); - return 0; - } - - return -1; + RCUWriter writer (channels); + boost::shared_ptr c = writer.get_copy(); + + return remove_channel_from (c, how_many); } float AudioDiskstream::playback_buffer_load () const { - return (float) ((double) channels.front().playback_buf->read_space()/ - (double) channels.front().playback_buf->bufsize()); + boost::shared_ptr c = channels.reader(); + + return (float) ((double) c->front()->playback_buf->read_space()/ + (double) c->front()->playback_buf->bufsize()); } float AudioDiskstream::capture_buffer_load () const { - return (float) ((double) channels.front().capture_buf->write_space()/ - (double) channels.front().capture_buf->bufsize()); + boost::shared_ptr c = channels.reader(); + + return (float) ((double) c->front()->capture_buf->write_space()/ + (double) c->front()->capture_buf->bufsize()); } int @@ -2167,10 +2244,10 @@ AudioDiskstream::use_pending_capture_data (XMLNode& node) const XMLProperty* prop; XMLNodeList nlist = node.children(); XMLNodeIterator niter; - AudioFileSource* fs; - AudioFileSource* first_fs = 0; - AudioRegion::SourceList pending_sources; - jack_nframes_t position; + boost::shared_ptr fs; + boost::shared_ptr first_fs; + SourceList pending_sources; + nframes_t position; if ((prop = node.property (X_("at"))) == 0) { return -1; @@ -2187,11 +2264,15 @@ AudioDiskstream::use_pending_capture_data (XMLNode& node) continue; } + // This protects sessions from errant CapturingSources in stored sessions + struct stat sbuf; + if (stat (prop->value().c_str(), &sbuf)) { + continue; + } + try { - fs = new SndFileSource (prop->value(), - Config->get_native_file_data_format(), - Config->get_native_file_header_format(), - _session.frame_rate()); + fs = boost::dynamic_pointer_cast ( + SourceFactory::createWritable (DataType::AUDIO, _session, prop->value(), false, _session.frame_rate())); } catch (failed_constructor& err) { @@ -2216,19 +2297,18 @@ AudioDiskstream::use_pending_capture_data (XMLNode& node) return 1; } - if (pending_sources.size() != _n_channels) { + 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; } - AudioRegion* region; + boost::shared_ptr region; try { - region = new AudioRegion (pending_sources, 0, first_fs->length(), - region_name_from_path (first_fs->name()), - 0, AudioRegion::Flag (AudioRegion::DefaultFlags|AudioRegion::Automatic|AudioRegion::WholeFile)); - + region = boost::dynamic_pointer_cast (RegionFactory::create (pending_sources, 0, first_fs->length(), + region_name_from_path (first_fs->name(), true), + 0, AudioRegion::Flag (AudioRegion::DefaultFlags|AudioRegion::Automatic|AudioRegion::WholeFile))); region->special_set_position (0); } @@ -2241,7 +2321,7 @@ AudioDiskstream::use_pending_capture_data (XMLNode& node) } try { - region = new AudioRegion (pending_sources, 0, first_fs->length(), region_name_from_path (first_fs->name())); + region = boost::dynamic_pointer_cast (RegionFactory::create (pending_sources, 0, first_fs->length(), region_name_from_path (first_fs->name(), true))); } catch (failed_constructor& err) { @@ -2252,7 +2332,138 @@ AudioDiskstream::use_pending_capture_data (XMLNode& node) return -1; } - _playlist->add_region (*region, position); + _playlist->add_region (region, position); + + return 0; +} + +int +AudioDiskstream::set_destructive (bool yn) +{ + bool bounce_ignored; + + if (yn != destructive()) { + + if (yn) { + /* requestor should already have checked this and + bounced if necessary and desired + */ + if (!can_become_destructive (bounce_ignored)) { + return -1; + } + _flags = Flag (_flags | Destructive); + use_destructive_playlist (); + } else { + _flags = Flag (_flags & ~Destructive); + reset_write_sources (true, true); + } + } return 0; } + +bool +AudioDiskstream::can_become_destructive (bool& requires_bounce) const +{ + if (!_playlist) { + requires_bounce = false; + return false; + } + + /* is there only one region ? */ + + if (_playlist->n_regions() != 1) { + requires_bounce = true; + return false; + } + + boost::shared_ptr first = _playlist->find_next_region (_session.current_start_frame(), Start, 1); + assert (first); + + /* do the source(s) for the region cover the session start position ? */ + + if (first->position() != _session.current_start_frame()) { + if (first->start() > _session.current_start_frame()) { + requires_bounce = true; + return false; + } + } + + /* is the source used by only 1 playlist ? */ + + boost::shared_ptr afirst = boost::dynamic_pointer_cast (first); + + assert (afirst); + + if (afirst->source()->used() > 1) { + requires_bounce = true; + return false; + } + + requires_bounce = false; + return true; +} + +AudioDiskstream::ChannelInfo::ChannelInfo (nframes_t bufsize, nframes_t speed_size, nframes_t wrap_size) +{ + peak_power = 0.0f; + source = 0; + 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 (bufsize); + capture_buf = new RingBufferNPT (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()); +} + +AudioDiskstream::ChannelInfo::~ChannelInfo () +{ + if (write_source) { + write_source.reset (); + } + + if (speed_buffer) { + delete [] speed_buffer; + speed_buffer = 0; + } + + if (playback_wrap_buffer) { + delete [] playback_wrap_buffer; + playback_wrap_buffer = 0; + } + + if (capture_wrap_buffer) { + delete [] capture_wrap_buffer; + capture_wrap_buffer = 0; + } + + if (playback_buf) { + delete playback_buf; + playback_buf = 0; + } + + if (capture_buf) { + delete capture_buf; + capture_buf = 0; + } + + if (capture_transition_buf) { + delete capture_transition_buf; + capture_transition_buf = 0; + } +}