/* * Copyright (C) 2017-2018 Paul Davis * Copyright (C) 2017-2019 Robin Gareus * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include "pbd/enumwriter.h" #include "pbd/memento_command.h" #include "pbd/playback_buffer.h" #include "temporal/range.h" #include "ardour/amp.h" #include "ardour/audio_buffer.h" #include "ardour/audioengine.h" #include "ardour/audioplaylist.h" #include "ardour/butler.h" #include "ardour/debug.h" #include "ardour/disk_reader.h" #include "ardour/midi_playlist.h" #include "ardour/midi_ring_buffer.h" #include "ardour/midi_track.h" #include "ardour/pannable.h" #include "ardour/playlist.h" #include "ardour/playlist_factory.h" #include "ardour/session.h" #include "ardour/session_playlists.h" #include "pbd/i18n.h" using namespace ARDOUR; using namespace PBD; using namespace std; ARDOUR::samplecnt_t DiskReader::_chunk_samples = default_chunk_samples (); PBD::Signal0 DiskReader::Underrun; thread_local Sample* DiskReader::_sum_buffer = 0; thread_local Sample* DiskReader::_mixdown_buffer = 0; thread_local gain_t* DiskReader::_gain_buffer = 0; std::atomic DiskReader::_no_disk_output (0); DiskReader::Declicker DiskReader::loop_declick_in; DiskReader::Declicker DiskReader::loop_declick_out; samplecnt_t DiskReader::loop_fade_length (0); DiskReader::DiskReader (Session& s, Track& t, string const& str, Temporal::TimeDomainProvider const & tdp, DiskIOProcessor::Flag f) : DiskIOProcessor (s, t, X_("player:") + str, f, tdp) , overwrite_sample (0) , run_must_resolve (false) , _declick_amp (s.nominal_sample_rate ()) , _declick_offs (0) , _declick_enabled (false) , last_refill_loop_start (0) , _midi_catchup (false) , _need_midi_catchup (false) { file_sample[DataType::AUDIO] = 0; file_sample[DataType::MIDI] = 0; _pending_overwrite.store (OverwriteReason (0)); } DiskReader::~DiskReader () { DEBUG_TRACE (DEBUG::Destruction, string_compose ("DiskReader %1 @ %2 deleted\n", _name, this)); } std::string DiskReader::display_name () const { return std::string (_("Player")); } void DiskReader::ReaderChannelInfo::resize (samplecnt_t bufsize) { delete rbuf; rbuf = 0; rbuf = new PlaybackBuffer (bufsize); /* touch memory to lock it */ memset (rbuf->buffer (), 0, sizeof (Sample) * rbuf->bufsize ()); initialized = false; } void DiskReader::ReaderChannelInfo::resize_preloop (samplecnt_t bufsize) { if (bufsize == 0) { return; } if (bufsize > pre_loop_buffer_size) { delete[] pre_loop_buffer; pre_loop_buffer = new Sample[bufsize]; pre_loop_buffer_size = bufsize; } } int DiskReader::add_channel_to (std::shared_ptr c, uint32_t how_many) { samplecnt_t bufsz = std::max (_chunk_samples * 2, _session.butler ()->audio_playback_buffer_size ()); while (how_many--) { c->push_back (new ReaderChannelInfo (bufsz, loop_fade_length)); DEBUG_TRACE (DEBUG::DiskIO, string_compose ("'%1': new reader channel, write space = %2 read = %3\n", name (), c->back ()->rbuf->write_space (), c->back ()->rbuf->read_space ())); } return 0; } void DiskReader::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. */ _sum_buffer = new Sample[2 * 1048576]; _mixdown_buffer = new Sample[2 * 1048576]; _gain_buffer = new gain_t[2 * 1048576]; } void DiskReader::free_working_buffers () { delete[] _sum_buffer; delete[] _mixdown_buffer; delete[] _gain_buffer; _sum_buffer = 0; _mixdown_buffer = 0; _gain_buffer = 0; } samplecnt_t DiskReader::default_chunk_samples () { return 65536; } bool DiskReader::set_name (string const& str) { string my_name = X_("player:"); my_name += str; if (_name != my_name) { SessionObject::set_name (my_name); } return true; } XMLNode& DiskReader::state () const { XMLNode& node (DiskIOProcessor::state ()); node.set_property (X_("type"), X_("diskreader")); return node; } int DiskReader::set_state (const XMLNode& node, int version) { if (DiskIOProcessor::set_state (node, version)) { return -1; } return 0; } void DiskReader::realtime_handle_transport_stopped () { if (_session.exporting () && !_session.realtime_export ()) { _declick_amp.set_gain (0); } /* can't do the resolve here because we don't have a place to put the * note resolving data. Defer to * MidiTrack::realtime_handle_transport_stopped() which will call * ::resolve_tracker() and put the output in its _immediate_events store. */ } void DiskReader::realtime_locate (bool for_loop_end) { if (!for_loop_end) { MidiTrack* mt = dynamic_cast (&_track); _tracker.resolve_notes (mt->immediate_events (), 0); } } float DiskReader::buffer_load () const { /* Note: 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 use the audio value here. */ std::shared_ptr c = channels.reader (); if (c->empty ()) { /* no channels, so no buffers, so completely full and ready to playback, sir! */ return 1.0; } PBD::PlaybackBuffer* b = c->front ()->rbuf; return (float)((double)b->read_space () / (double)b->bufsize ()); } void DiskReader::adjust_buffering () { std::shared_ptr c = channels.reader (); samplecnt_t bufsz = std::max (_chunk_samples * 2, _session.butler ()->audio_playback_buffer_size ()); for (auto const& chan : *c) { chan->resize (bufsz); } } void DiskReader::playlist_modified () { _session.request_overwrite_buffer (_track.shared_ptr (), PlaylistModified); } int DiskReader::use_playlist (DataType dt, std::shared_ptr playlist) { bool prior_playlist = false; if (_playlists[dt]) { prior_playlist = true; } if (DiskIOProcessor::use_playlist (dt, playlist)) { return -1; } /* 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 (!(_pending_overwrite.load () & PlaylistChanged) || prior_playlist) { _session.request_overwrite_buffer (_track.shared_ptr (), PlaylistChanged); } return 0; } void DiskReader::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_sample, double speed, pframes_t nframes, bool result_required) { uint32_t n; std::shared_ptr c = channels.reader (); ChannelList::const_iterator chan; sampleoffset_t disk_samples_to_consume; MonitorState ms = _track.monitoring_state (); const bool midi_only = (c->empty () || !_playlists[DataType::AUDIO]); bool no_disk_output = _no_disk_output.load () != 0; if (!check_active ()) { return; } const gain_t target_gain = ((speed == 0.0) || ((ms & MonitoringDisk) == 0)) ? 0.0 : 1.0; bool declick_out = (_declick_amp.gain () != target_gain) && target_gain == 0.0; if (declick_out && _declick_amp.gain () == GAIN_COEFF_UNITY) { /* beginning a de-click, set de-click reason */ if (speed == 0) { _declick_enabled = _session.cfg ()->get_use_transport_fades (); } else { _declick_enabled = _session.cfg ()->get_use_monitor_fades (); } } else if (_declick_amp.gain () == GAIN_COEFF_ZERO && speed == 0) { /* fade in */ _declick_enabled = _session.cfg ()->get_use_transport_fades (); } if (!_declick_enabled || (_session.exporting () && !_session.realtime_export ())) { /* no transport fades or exporting - no declick out logic */ if (!midi_only) { _declick_amp.set_gain (target_gain); declick_out = false; } } else { /* using transport fades and not exporting - declick login in effect */ if (ms == MonitoringDisk) { /* Only monitoring from disk, so if we've finished a * declick (for stop/locate), do not accidentally pass * any data from disk to our outputs. */ if ((target_gain == 0.0) && (_declick_amp.gain () == target_gain)) { /* we were heading for zero (declick out for * stop), and we've reached there. Done. */ return; } } } BufferSet& scratch_bufs (_session.get_scratch_buffers (bufs.count ())); const bool still_locating = _session.global_locate_pending (); assert (speed == -1 || speed == 0 || speed == 1); if (speed == 0) { disk_samples_to_consume = 0; } else { disk_samples_to_consume = nframes; } if (midi_only) { /* do nothing with audio */ goto midi; } if (declick_out) { /* fade-out */ // printf ("DR fade-out speed=%.1f gain=%.3f off=%ld start=%ld playpos=%ld (%s)\n", speed, _declick_amp.gain (), _declick_offs, start_sample, playback_sample, owner()->name().c_str()); ms = MonitorState (ms | MonitoringDisk); assert (result_required); result_required = true; disk_samples_to_consume = 0; // non-committing read } else { _declick_offs = 0; } if (!result_required || ((ms & MonitoringDisk) == 0) || still_locating || no_disk_output) { /* no need for actual disk data, just advance read pointer */ if (!still_locating || no_disk_output) { for (auto const& chan : *c) { assert (chan->rbuf); chan->rbuf->increment_read_ptr (disk_samples_to_consume); } } /* if monitoring disk but locating put silence in the buffers */ if ((no_disk_output || still_locating) && (ms == MonitoringDisk)) { bufs.silence (nframes, 0); } } else { /* we need audio data from disk */ size_t n_buffers = bufs.count ().n_audio (); size_t n_chans = c->size (); gain_t scaling; if (n_chans > n_buffers) { scaling = ((float)n_buffers) / n_chans; } else { scaling = 1.0; } const float initial_declick_gain = _declick_amp.gain (); const sampleoffset_t declick_offs = _declick_offs; for (n = 0, chan = c->begin (); chan != c->end (); ++chan, ++n) { ReaderChannelInfo* chaninfo = dynamic_cast (*chan); AudioBuffer& output (bufs.get_audio (n % n_buffers)); AudioBuffer& disk_buf ((ms & MonitoringInput) ? scratch_bufs.get_audio (n) : output); if (start_sample != playback_sample && target_gain != 0) { samplepos_t ss = start_sample; Location* loc = _loop_location; if (loc) { Temporal::Range loop_range (loc->start (), loc->end ()); ss = loop_range.squish (timepos_t (playback_sample)).samples (); playback_sample = ss; } if (ss != playback_sample) { if (can_internal_playback_seek (ss - playback_sample)) { internal_playback_seek (ss - playback_sample); } else { disk_samples_to_consume = 0; /* will force an underrun below */ } } } /* reset _declick_amp to the correct gain before processing this channel. */ _declick_amp.set_gain (initial_declick_gain); if (!declick_out) { const samplecnt_t available = chaninfo->rbuf->read (disk_buf.data (), disk_samples_to_consume); if (available == 0 && !chaninfo->initialized) { disk_buf.silence (disk_samples_to_consume); } else if (disk_samples_to_consume > available) { #ifndef NDEBUG // not rt-safe to print here cerr << "underrun for " << _name << " Available samples: " << available << " required: " << disk_samples_to_consume << endl; #endif DEBUG_TRACE (DEBUG::DiskIO, string_compose ("'%1': underrun in thread %2, available = %3 need = %4\n", owner ()->name (), DEBUG_THREAD_SELF, available, disk_samples_to_consume)); DEBUG_TRACE (DEBUG::AudioCacheRefill, string_compose ("DR '%1' underrun have %2 need %3 samples at pos %4\n", name (), available, disk_samples_to_consume, std::setprecision (3), std::fixed, start_sample / (float)_session.sample_rate ())); Underrun (); return; } } else if (_declick_amp.gain () != target_gain) { assert (target_gain == 0); /* note that this is a non-committing read: it * retrieves data from the ringbuffer but does not * advance the read pointer. As a result, * subsequent calls (as we declick) need to * pass in an offset describing where to read * from. We maintain _declick_offs across calls * to ::run() */ const samplecnt_t total = chaninfo->rbuf->read (disk_buf.data (), nframes, false, declick_offs); if (n == 0) { _declick_offs += total; } } _declick_amp.apply_gain (disk_buf, nframes, target_gain); /* _declick_amp is now left with the correct gain after processing nframes */ Amp::apply_simple_gain (disk_buf, nframes, scaling); if (ms & MonitoringInput) { /* mix the disk signal into the input signal (already in bufs) */ mix_buffers_no_gain (output.data (), disk_buf.data (), nframes); } } } midi: /* MIDI data handling */ const bool no_playlist_modification_pending = !(pending_overwrite () & PlaylistModified); if (bufs.count ().n_midi ()) { MidiBuffer& dst (bufs.get_midi (0)); if (run_must_resolve) { resolve_tracker (dst, 0); run_must_resolve = false; } if (!no_disk_output && !declick_in_progress () && (ms & MonitoringDisk) && !still_locating && no_playlist_modification_pending && speed) { get_midi_playback (dst, start_sample, end_sample, ms, scratch_bufs, speed, disk_samples_to_consume); } } /* decide if we need the butler */ if (!still_locating && no_playlist_modification_pending) { bool butler_required = false; if (speed < 0.0) { playback_sample -= disk_samples_to_consume; } else { playback_sample += disk_samples_to_consume; } Location* loc = _loop_location; if (loc) { Temporal::Range loop_range (loc->start (), loc->end ()); playback_sample = loop_range.squish (timepos_t (playback_sample)).samples (); } if (_playlists[DataType::AUDIO]) { if (!c->empty ()) { if (_slaved) { if (c->front ()->rbuf->write_space () >= c->front ()->rbuf->bufsize () / 2) { DEBUG_TRACE (DEBUG::DiskIO, string_compose ("'%1': slaved, write space = %2 of %3\n", _owner->name (), c->front ()->rbuf->write_space (), c->front ()->rbuf->bufsize ())); butler_required = true; } } else { if ((samplecnt_t)c->front ()->rbuf->write_space () >= _chunk_samples) { DEBUG_TRACE (DEBUG::DiskIO, string_compose ("%1: write space = %2 chunk size = %3\n", _owner->name (), c->front ()->rbuf->write_space (), _chunk_samples)); butler_required = true; } } } } /* All of MIDI is in RAM, no need to call the butler unless we * have to overwrite buffers because of a playlist change. */ _need_butler = butler_required; } if (_need_butler) { DEBUG_TRACE (DEBUG::DiskIO, string_compose ("'%1' reader run, needs butler = %2\n", _owner->name (), _need_butler)); } } bool DiskReader::declick_in_progress () const { if (!_declick_enabled || (_session.exporting () && !_session.realtime_export ())) { return false; } return _declick_amp.gain () != 0; // declick-out } void DiskReader::configuration_changed () { std::shared_ptr c = channels.reader (); if (!c->empty ()) { ReaderChannelInfo* chaninfo = dynamic_cast (c->front ()); if (!chaninfo->initialized) { seek (_session.transport_sample (), true); return; } } _session.request_overwrite_buffer (_track.shared_ptr (), LoopDisabled); } bool DiskReader::pending_overwrite () const { return _pending_overwrite.load () != 0; } void DiskReader::set_pending_overwrite (OverwriteReason why) { DEBUG_TRACE (DEBUG::DiskIO, string_compose ("'%1': set_pending_overwrite because %2%3%4)\n", owner ()->name (), std::hex, why, std::dec)); std::shared_ptr c = channels.reader (); /* called from audio thread, so we can use the read ptr and playback sample as we wish */ if (!c->empty ()) { if (c->size () > 1) { /* Align newly added buffers. * * overwrite_sample and file_sample[] are are maintained * per DiskReader, not per channel. * ::refill_audio() and ::overwrite_existing_audio() expect * that read-pointers and fill_level of all buffers are in sync. */ ChannelList::const_iterator chan = c->begin (); for (++chan; chan != c->end (); ++chan) { ReaderChannelInfo* chaninfo = dynamic_cast (*chan); if (!chaninfo->initialized) { (*chan)->rbuf->align_to (*(c->front ()->rbuf)); } } } const samplecnt_t reserved_size = c->front ()->rbuf->reserved_size (); const samplecnt_t bufsize = c->front ()->rbuf->bufsize (); overwrite_offset = c->front ()->rbuf->read_ptr (); overwrite_sample = playback_sample - reserved_size; if (overwrite_offset > reserved_size) { /* * |----------------------------------------------------------------------| * ^ ^ * RRRRRRRRRRRRRRRRoverwrite_offset (old read_ptr) * |<- second ->|<------------------ first chunk ------------------------>| * * Fill the the end of the buffer ("first chunk"), above */ overwrite_offset -= reserved_size; } else { /* * |----------------------------------------------------------------------| * RRRRRRRRE^ RRRRRRRRR * overwrite_offset (old read_ptr) * |< second chunk >|| * * Fill the end of the buffer ("R1R1R1" aka "first" above) */ overwrite_offset = bufsize - (reserved_size - overwrite_offset); } } if (why & (LoopChanged | PlaylistModified | PlaylistChanged)) { run_must_resolve = true; } while (true) { OverwriteReason current = OverwriteReason (_pending_overwrite.load ()); OverwriteReason next = OverwriteReason (current | why); if (_pending_overwrite.compare_exchange_strong (current, next)) { break; } } } bool DiskReader::overwrite_existing_audio () { /* This is a tricky and/or clever little method. Let's try to describe * precisely what it does. * * Our goal is to completely overwrite the playback buffers for each * audio channel with new data. The wrinkle is that we want to preserve * the EXACT mapping between a given timeline position and buffer * offset that existed when we requested an overwrite. That is, if the * Nth position in the buffer contained the sample corresponding to * timeline position T, then once this is complete that condition * should still hold. The actual value of the sample (and even whether it * corresponds to any actual material on disk - it may just be silence) * may change, but this buffer_offset<->timeline_position mapping must * remain constant. * * Why do this? There are many reasons. A trivial example is that the * region gain level for one region has been changed, and the user * should be able to hear the result. * * In ::set_pending_overwrite() (above) we stored a sample and a buffer * offset. These corresponded to the next sample to be played and the * buffer position holding that sample. We were able to determine this * pair atomically because ::set_pending_overwrite() is called from * within process context, and thus neither playback_sample nor the * buffer read ptr can change while it runs. We computed the earliest * sample/timeline position in the buffer (at the start of the reserved * zone, if any) and its corresponding buffer offset. * * Here, we will refill the buffer, starting with the sample and buffer * offset computed by ::set_pending_overwrite(). Typically this will * take two reads from the playlist, because our read will be "split" * by the end of the buffer (i.e. we fill from some mid-buffer point to * the end, then fill from the start to the mid-buffer point, as is * common with ring buffers). * * Note that the process thread may indeed access the buffer while we * are doing this. There is a strong likelihood of colliding read/write * between this thread (the butler) and a process thread. But we don't * care: we know that the samples being read/written will correspond to * the same timeline position, and that the user has just done * something forcing us to update the value(s). Given that a Sample is * currently (and likely forever) a floating point value, and that on * many/most architectures, a store for a floating point value is * non-atomic, there is some chance of the process read reading a * sample value while it is being written. This could theoretically * cause a brief glitch, but no more or less than any other * "discontinuity" in the sample's value will. * * It goes without saying that this relies on being serialized within * the butler thread with respect any other buffer write operation * (e.g. via ::refill()). It should also be noted that it has no effect * at all on the write-related members of the playback buffer - we * simply replace the contents of the buffer. */ std::shared_ptr c = channels.reader (); if (c->empty ()) { return true; } const bool reversed = !_session.transport_will_roll_forwards (); sampleoffset_t chunk1_offset; size_t chunk1_cnt; size_t chunk2_cnt; const size_t to_overwrite = c->front ()->rbuf->overwritable_at (overwrite_offset); DEBUG_TRACE (DEBUG::DiskIO, string_compose ("'%1': overwrite_existing_audio at %2 offset = %2 to_ovrwrt = %3\n", owner ()->name (), overwrite_sample, overwrite_offset, to_overwrite)); chunk1_offset = overwrite_offset; chunk1_cnt = min (c->front ()->rbuf->bufsize () - (size_t)overwrite_offset, to_overwrite); /* note: because we are overwriting buffer contents but not moving the * write/read pointers, we actually want to fill all the way to the * write pointer (the value returned by PlaybackBuffer::overwritable_at(). * * This differs from what happens during ::refill_audio() where we are * careful not to allow the read pointer to catch the write pointer * (that indicates an empty buffer) */ if (chunk1_cnt == to_overwrite) { chunk2_cnt = 0; } else { chunk2_cnt = to_overwrite - chunk1_cnt; } boost::scoped_array sum_buffer (new Sample[to_overwrite]); boost::scoped_array mixdown_buffer (new Sample[to_overwrite]); boost::scoped_array gain_buffer (new float[to_overwrite]); uint32_t n = 0; bool ret = true; samplepos_t start = overwrite_sample; if (chunk1_cnt) { for (auto const& chan : *c) { Sample* buf = chan->rbuf->buffer (); ReaderChannelInfo* rci = dynamic_cast (chan); /* Note that @p start is passed by reference and will be * updated by the ::audio_read() call */ start = overwrite_sample; if (audio_read (sum_buffer.get (), mixdown_buffer.get (), gain_buffer.get (), start, chunk1_cnt, rci, n, reversed) != (samplecnt_t)chunk1_cnt) { error << string_compose (_("DiskReader %1: when overwriting(1), cannot read %2 from playlist at sample %3"), id (), chunk1_cnt, overwrite_sample) << endmsg; ret = false; ++n; continue; } memcpy (buf + chunk1_offset, sum_buffer.get (), sizeof (float) * chunk1_cnt); ++n; } } overwrite_sample = start; /* sequence read chunks. first read data at same position for all channels */ n = 0; for (auto const& chan : *c) { Sample* buf = chan->rbuf->buffer (); ReaderChannelInfo* rci = dynamic_cast (chan); if (chunk2_cnt) { start = overwrite_sample; if (audio_read (sum_buffer.get (), mixdown_buffer.get (), gain_buffer.get (), start, chunk2_cnt, rci, n, reversed) != (samplecnt_t)chunk2_cnt) { error << string_compose (_("DiskReader %1: when overwriting(2), cannot read %2 from playlist at sample %3"), id (), chunk2_cnt, overwrite_sample) << endmsg; ret = false; } memcpy (buf, sum_buffer.get (), sizeof (float) * chunk2_cnt); } if (!rci->initialized) { DEBUG_TRACE (DEBUG::DiskIO, string_compose ("'1': Init ReaderChannel overwriting at: %2, avail: %3\n", name (), overwrite_sample, chan->rbuf->read_space ())); if (chan->rbuf->read_space () > 0) { rci->initialized = true; } } ++n; } file_sample[DataType::AUDIO] = start; return ret; } bool DiskReader::overwrite_existing_midi () { RTMidiBuffer* mbuf = rt_midibuffer (); if (mbuf) { MidiTrack* mt = dynamic_cast (&_track); MidiChannelFilter* filter = mt ? &mt->playback_filter () : 0; #ifdef PROFILE_MIDI_IO PBD::Timing minsert; minsert.start (); #endif midi_playlist ()->render (filter); assert (midi_playlist ()->rendered ()); #ifdef PROFILE_MIDI_IO minsert.update (); cerr << "Reading " << name () << " took " << minsert.elapsed () << " microseconds, final size = " << midi_playlist ()->rendered ()->size () << endl; #endif } return true; } bool DiskReader::overwrite_existing_buffers () { /* called from butler thread */ DEBUG_TRACE (DEBUG::DiskIO, string_compose ("'%1': overwriting existing buffers at %2 (because %3%4%5)\n", owner ()->name (), overwrite_sample, std::hex, _pending_overwrite.load (), std::dec)); bool ret = true; if (_pending_overwrite.load () & (PlaylistModified | LoopDisabled | LoopChanged | PlaylistChanged)) { if (_playlists[DataType::AUDIO] && !overwrite_existing_audio ()) { ret = false; } } if (_pending_overwrite.load () & (PlaylistModified | PlaylistChanged)) { if (_playlists[DataType::MIDI] && !overwrite_existing_midi ()) { ret = false; } } DEBUG_TRACE (DEBUG::DiskIO, string_compose ("'%1': Clear _pending_overwrite\n", owner ()->name ())); _pending_overwrite.store (OverwriteReason (0)); return ret; } int DiskReader::seek (samplepos_t sample, bool complete_refill) { /* called via non_realtime_locate() from butler thread */ int ret = -1; const bool read_reversed = !_session.transport_will_roll_forwards (); const bool read_loop = (bool)_loop_location; std::shared_ptr c = channels.reader (); if (c->empty ()) { return 0; } /* There are two possible shortcuts we can take that will completely * skip reading from disk. However, they are invalid if we need to read * data in the opposite direction than we did last time, or if our need * for looped data has changed since the last read. Both of these change * the semantics of a read from disk, even if the position we are * reading from is the same. */ if ((_last_read_reversed.value_or (read_reversed) == read_reversed) && (_last_read_loop.value_or (read_loop) == read_loop)) { if (sample == playback_sample && !complete_refill) { return 0; } if ((size_t)abs (sample - playback_sample) < (c->front ()->rbuf->reserved_size () / 6)) { /* we're close enough. Note: this is a heuristic */ return 0; } } DEBUG_TRACE (DEBUG::DiskIO, string_compose ("'%1': seek %2 -> %3 refill = %4 pending_overwrite = %5\n", owner ()->name (), playback_sample, sample, complete_refill, _pending_overwrite.load ())); _pending_overwrite.store (OverwriteReason (0)); const samplecnt_t distance = sample - playback_sample; if (!complete_refill && can_internal_playback_seek (distance)) { internal_playback_seek (distance); return 0; } for (auto const& chan : *c) { chan->rbuf->reset (); assert (chan->rbuf->reserved_size () == 0); } /* move the intended read target, so that after the refill is done, * the intended read target is "reservation" from the start of the * playback buffer. Then increment the read ptr, so that we can * potentially do an internal seek backwards of up "reservation" * samples. */ const samplecnt_t rsize = (samplecnt_t)c->front ()->rbuf->reservation_size (); samplecnt_t shift = (sample > rsize ? rsize : sample); if (read_reversed) { /* reading in reverse, so start at a later sample, and read * "backwards" from there. */ shift = -shift; } /* start the read at an earlier position (or later if reversed) */ sample -= shift; playback_sample = sample; file_sample[DataType::AUDIO] = sample; file_sample[DataType::MIDI] = sample; if (complete_refill) { /* call _do_refill() to refill the entire buffer, using * the largest reads possible. */ while ((ret = do_refill_with_alloc (false, read_reversed)) > 0) ; } else { /* call _do_refill() to refill just one chunk, and then return. */ ret = do_refill_with_alloc (true, read_reversed); } if (shift) { /* now tell everyone where we really are, leaving the * "reserved" data represented by "shift" available in the * buffer for backwards-internal-seek */ playback_sample += shift; /* we always move the read-ptr forwards, since even when in * reverse, the data is placed in the buffer in normal read * (increment) order. */ shift = abs (shift); for (auto const& chan : *c) { chan->rbuf->increment_read_ptr (shift); } } return ret; } bool DiskReader::can_internal_playback_seek (sampleoffset_t distance) { /* 1. Audio */ std::shared_ptr c = channels.reader (); for (auto const& chan : *c) { if (!chan->rbuf->can_seek (distance)) { return false; } } /* 2. MIDI can always seek any distance */ return true; } void DiskReader::internal_playback_seek (sampleoffset_t distance) { if (distance == 0) { return; } sampleoffset_t off = distance; std::shared_ptr c = channels.reader (); for (auto const& chan : *c) { if (distance < 0) { off = 0 - (sampleoffset_t)chan->rbuf->decrement_read_ptr (::llabs (distance)); } else { off = chan->rbuf->increment_read_ptr (distance); } } playback_sample += off; } static void swap_by_ptr (Sample* first, Sample* last) { while (first < last) { Sample tmp = *first; *first++ = *last; *last-- = tmp; } } /** Read some data for 1 channel from our playlist into a buffer. * * @param sum_buf sample-containing buffer to write to. Must be contiguous. * @param mixdown_buffer sample-containing buffer that will be used to mix layers * @param gain_buffer ptr to a buffer used to hold any necessary gain (automation) data * @param start Session sample to start reading from; updated to where we end up * after the read. Global timeline position. * @param cnt Count of samples to read. * @param rci ptr to ReaderChannelInfo for the channel we're reading * @param channel the number of the channel we're reading (0..N) * @param reversed true if we are running backwards, otherwise false. */ samplecnt_t DiskReader::audio_read (Sample* sum_buffer, Sample* mixdown_buffer, float* gain_buffer, samplepos_t& start, samplecnt_t cnt, ReaderChannelInfo* rci, int channel, bool reversed) { samplecnt_t this_read = 0; bool reloop = false; samplepos_t loop_end = 0; samplepos_t loop_start = 0; Location* loc = 0; const samplecnt_t rcnt = cnt; /* 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 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_sample (); loop_end = loc->end_sample (); const Temporal::Range loop_range (loc->start (), loc->end ()); start = loop_range.squish (timepos_t (start)).samples (); } } else { start -= cnt; start = max (samplepos_t (0), start); } /* 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); /* note that the mixdown and gain buffers are purely for the * internal use of the playlist, and cannot be considered * useful after the return from AudioPlayback::read() */ if (audio_playlist ()->read (sum_buffer, mixdown_buffer, gain_buffer, timepos_t (start), timecnt_t::from_samples (this_read), channel) != this_read) { error << string_compose (_("DiskReader %1: cannot read %2 from playlist at sample %3"), id (), this_read, start) << endmsg; return 0; } if (loc) { /* Looping: do something (maybe) about the loop boundaries */ switch (Config->get_loop_fade_choice ()) { case NoLoopFade: break; case BothLoopFade: loop_declick_in.run (sum_buffer, start, start + this_read); loop_declick_out.run (sum_buffer, start, start + this_read); break; case EndLoopFade: loop_declick_out.run (sum_buffer, start, start + this_read); break; case XFadeLoop: if (last_refill_loop_start != loop_start || rci->pre_loop_buffer == 0) { setup_preloop_buffer (); last_refill_loop_start = loop_start; } maybe_xfade_loop (sum_buffer, start, start + this_read, rci); break; } } if (reversed) { swap_by_ptr (sum_buffer, sum_buffer + 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; sum_buffer += this_read; } _last_read_reversed = reversed; _last_read_loop = (bool)loc; return rcnt; } int DiskReader::do_refill () { const bool reversed = !_session.transport_will_roll_forwards (); return refill (_sum_buffer, _mixdown_buffer, _gain_buffer, 0, reversed); } int DiskReader::do_refill_with_alloc (bool partial_fill, bool reversed) { /* 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). */ boost::scoped_array sum_buf (new Sample[2 * 1048576]); boost::scoped_array mix_buf (new Sample[2 * 1048576]); boost::scoped_array gain_buf (new float[2 * 1048576]); return refill_audio (sum_buf.get (), mix_buf.get (), gain_buf.get (), (partial_fill ? _chunk_samples : 0), reversed); } int DiskReader::refill (Sample* sum_buffer, Sample* mixdown_buffer, float* gain_buffer, samplecnt_t fill_level, bool reversed) { /* NOTE: Audio refill MUST come first so that in contexts where ONLY it * is called, _last_read_reversed is set correctly. */ if (refill_audio (sum_buffer, mixdown_buffer, gain_buffer, fill_level, reversed)) { return -1; } if (rt_midibuffer () && (reversed != rt_midibuffer ()->reversed ())) { rt_midibuffer ()->reverse (); } return 0; } /** Get some more data from disk and put it in our channels' 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 DiskReader::refill_audio (Sample* sum_buffer, Sample* mixdown_buffer, float* gain_buffer, samplecnt_t fill_level, bool reversed) { /* do not read from disk while session is marked as Loading, to avoid useless redundant I/O. */ if (_session.loading ()) { return 0; } int32_t ret = 0; samplecnt_t zero_fill; uint32_t chan_n; ChannelList::const_iterator i; std::shared_ptr c = channels.reader (); _last_read_reversed = reversed; if (c->empty ()) { return 0; } assert (mixdown_buffer); assert (gain_buffer); samplecnt_t total_space = c->front ()->rbuf->write_space (); if (total_space == 0) { DEBUG_TRACE (DEBUG::DiskIO, string_compose ("'%1': no space to refill\n", name ())); /* 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_samples 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_samples is smaller * than the playback buffer size, so this check should never trip when * the playback buffer is empty. */ DEBUG_TRACE (DEBUG::DiskIO, string_compose ("'%1': space to refill %2 vs. chunk %3 (speed = %4)\n", name (), total_space, _chunk_samples, _session.transport_speed ())); if ((total_space < _chunk_samples) && fabs (_session.transport_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 < (samplecnt_t) (c->front ()->rbuf->bufsize () / 2)) { DEBUG_TRACE (DEBUG::DiskIO, string_compose ("'%1': not enough to refill while slaved\n", this)); return 0; } samplepos_t fsa = file_sample[DataType::AUDIO]; if (reversed) { if (fsa == 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->rbuf->write_zero (chan->rbuf->write_space ()); } return 0; } if (fsa < total_space) { /* too close to the start: read what we can, and then zero fill the rest */ zero_fill = total_space - fsa; total_space = fsa; } else { zero_fill = 0; } } else { if (fsa == max_samplepos) { /* 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->rbuf->write_zero (chan->rbuf->write_space ()); } return 0; } if (fsa > max_samplepos - total_space) { /* to close to the end: read what we can, and zero fill the rest */ zero_fill = total_space - (max_samplepos - fsa); total_space = max_samplepos - fsa; } else { zero_fill = 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 */ samplecnt_t samples_to_read = byte_size_for_read / (bits_per_sample / 8); DEBUG_TRACE (DEBUG::DiskIO, string_compose ("'%1': will refill %2 channels with %3 samples\n", name (), c->size (), total_space)); samplepos_t file_sample_tmp = fsa; #if 0 int64_t before = g_get_monotonic_time (); int64_t elapsed; #endif for (chan_n = 0, i = c->begin (); i != c->end (); ++i, ++chan_n) { ChannelInfo* chan (*i); /* we want all channels to read from the same position, but * audio_read() will increment its position argument. So * reinitialize this for every channel. */ file_sample_tmp = fsa; samplecnt_t ts = total_space; const guint wr_space = chan->rbuf->write_space (); samplecnt_t to_read = min (ts, (samplecnt_t)wr_space); to_read = min (to_read, samples_to_read); assert (to_read >= 0); if (to_read) { ReaderChannelInfo* rci = dynamic_cast (chan); if (!_playlists[DataType::AUDIO]) { chan->rbuf->write_zero (to_read); } else { samplecnt_t nread, nwritten; if ((nread = audio_read (sum_buffer, mixdown_buffer, gain_buffer, file_sample_tmp, to_read, rci, chan_n, reversed)) != to_read) { error << string_compose (_("DiskReader %1: when refilling, cannot read %2 from playlist at sample %3 (rv: %4)"), name (), to_read, fsa, nread) << endmsg; ret = -1; goto out; } if ((nwritten = chan->rbuf->write (sum_buffer, nread)) != nread) { error << string_compose (_("DiskReader %1: when refilling, cannot write %2 into buffer (wrote %3, space %4)"), name (), nread, nwritten, wr_space) << endmsg; ret = -1; } } if (!rci->initialized) { DEBUG_TRACE (DEBUG::DiskIO, string_compose ("'%1' Init ReaderChannel read: %2 samples, at: %4, avail: %5\n", name (), to_read, file_sample_tmp, rci->rbuf->read_space ())); rci->initialized = true; } } if (zero_fill) { /* not sure if action is needed, * we'll later hit the "to close to the end" case */ //chan->rbuf->write_zero (zero_fill); } } #if 0 elapsed = g_get_monotonic_time () - before; cerr << '\t' << name() << ": bandwidth = " << (byte_size_for_read / 1048576.0) / (elapsed/1000000.0) << "MB/sec\n"; #endif file_sample[DataType::AUDIO] = file_sample_tmp; assert (file_sample[DataType::AUDIO] >= 0); ret = ((total_space - samples_to_read) > _chunk_samples); out: return ret; } void DiskReader::playlist_ranges_moved (list const& movements, bool from_undo_or_shift) { /* 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. * * Likewise when shifting regions (insert/remove time) * automation is taken care of separately (busses with * automation have no disk-reader). */ if (from_undo_or_shift) { return; } if (Config->get_automation_follows_regions () == false) { return; } /* move panner automation (route owned control) */ std::shared_ptr pannable = _track.pannable (); Evoral::ControlSet::Controls& c (pannable->controls ()); for (Evoral::ControlSet::Controls::iterator ci = c.begin (); ci != c.end (); ++ci) { std::shared_ptr ac = std::dynamic_pointer_cast (ci->second); if (!ac) { continue; } std::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 mute automation (route owned control) */ std::shared_ptr alist = _track.mute_control ()->alist (); if (alist->size ()) { 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 (&DiskReader::move_processor_automation, this, _1, movements)); } void DiskReader::move_processor_automation (std::weak_ptr p, list const& movements) { std::shared_ptr processor (p.lock ()); if (!processor) { return; } set const a = processor->what_can_be_automated (); for (set::const_iterator i = a.begin (); i != a.end (); ++i) { std::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 DiskReader::reset_tracker () { _tracker.reset (); } void DiskReader::resolve_tracker (Evoral::EventSink& buffer, samplepos_t time) { _tracker.resolve_notes (buffer, time); } /** Writes playback events from playback_sample for nframes to dst, translating time stamps * so that an event at playback_sample has time = 0 */ void DiskReader::get_midi_playback (MidiBuffer& dst, samplepos_t start_sample, samplepos_t end_sample, MonitorState ms, BufferSet& scratch_bufs, double speed, samplecnt_t disk_samples_to_consume) { RTMidiBuffer* rtmb = rt_midibuffer (); if (!rtmb || (rtmb->size () == 0)) { /* no data to read, so do nothing */ return; } MidiBuffer* target; if (ms & MonitoringInput) { /* data from disk needs to be *merged* not written into the * dst, because it may contain input data that we want to * monitor. Since RTMidiBuffer currently (Oct 2019) has no * suitable method, put the disk data into a scratch buffer and * then merge later. */ target = &scratch_bufs.get_midi (0); } else { /* No need to preserve the contents of the input buffer. But * Route::process_output_buffers() clears the buffer as-needed * so know we do not need to clear it. */ target = &dst; } if (_no_disk_output.load ()) { return; } const samplecnt_t nframes = abs (end_sample - start_sample); if (ms & MonitoringDisk) { /* disk data needed */ Location* loc = _loop_location; if (loc) { /* squish() operates in the location's time-domain. When the location was created * using music-time, and later converted to audio-time, it can happen that the * corresponding super-clock is "between samples". e.g loop-end is at sample 1000.12. * if start_sample = 1000; squish() does nothing because 1000 < 1000.12. * This is solved by creating the range using (rounded) sample-times. */ const Temporal::Range loop_range (loc->start ().samples (), loc->end ().samples ()); samplepos_t effective_start = start_sample; samplecnt_t cnt = nframes; sampleoffset_t offset = 0; const samplepos_t loop_end = loc->end_sample (); DEBUG_TRACE (DEBUG::MidiDiskIO, string_compose ("LOOP read, loop is %1..%2 range is %3..%4 nf %5\n", loc->start (), loc->end (), start_sample, end_sample, nframes)); do { samplepos_t effective_end; effective_start = loop_range.squish (timepos_t (effective_start)).samples (); effective_end = min (effective_start + cnt, loop_end); assert (effective_end > effective_start); if (_midi_catchup && _need_midi_catchup) { MidiStateTracker mst; rtmb->track (mst, effective_start, effective_end); mst.flush (dst, 0, false); _need_midi_catchup = false; } const samplecnt_t this_read = effective_end - effective_start; DEBUG_TRACE (DEBUG::MidiDiskIO, string_compose ("playback buffer LOOP read, from %1 to %2 (%3)\n", effective_start, effective_end, this_read)); #ifndef NDEBUG size_t events_read = #endif rtmb->read (*target, effective_start, effective_end, _tracker, offset); cnt -= this_read; effective_start += this_read; offset += this_read; DEBUG_TRACE (DEBUG::MidiDiskIO, string_compose ("%1 MDS events LOOP read %2 cnt now %3\n", _name, events_read, cnt)); if (cnt) { /* We re going to have to read across the loop end. Resolve any notes the extend across the loop end. * Time is relative to start_sample. */ DEBUG_TRACE (DEBUG::MidiDiskIO, string_compose ("read crosses loop end, resolve @ %1\n", effective_end - start_sample)); _tracker.resolve_notes (*target, effective_end - start_sample); } } while (cnt); } else { DEBUG_TRACE (DEBUG::MidiDiskIO, string_compose ("playback buffer read, from %1 to %2 (%3)\n", start_sample, end_sample, nframes)); if (_midi_catchup && _need_midi_catchup) { MidiStateTracker mst; rtmb->track (mst, start_sample, end_sample); mst.flush (dst, 0, false); _need_midi_catchup = false; } DEBUG_RESULT (size_t, events_read, rtmb->read (*target, start_sample, end_sample, _tracker)); DEBUG_TRACE (DEBUG::MidiDiskIO, string_compose ("%1 MDS events read %2 range %3 .. %4\n", _name, events_read, playback_sample, playback_sample + nframes)); } } if (ms & MonitoringInput) { /* merges data from disk (in "target", which is a scratch * buffer in this case) into the actual destination buffer * (which holds existing input data). */ dst.merge_from (*target, nframes); } #if 0 if (!target->empty ()) { cerr << "======== MIDI OUT ========\n"; for (MidiBuffer::iterator i = target->begin(); i != target->end(); ++i) { const Evoral::Event ev (*i, false); cerr << "MIDI EVENT (from disk) @ " << ev.time(); for (size_t xx = 0; xx < ev.size(); ++xx) { cerr << ' ' << hex << (int) ev.buffer()[xx]; } cerr << dec << endl; } cerr << "----------------\n"; } #endif } void DiskReader::inc_no_disk_output () { _no_disk_output.fetch_add (1); } void DiskReader::dec_no_disk_output () { /* this is called unconditionally when things happen that ought to end * a period of "no disk output". It's OK for that to happen when there * was no corresponding call to ::inc_no_disk_output(), but we must * stop the value from becoming negative. */ do { gint v = _no_disk_output.load (); if (v > 0) { if (_no_disk_output.compare_exchange_strong (v, v - 1)) { break; } } else { break; } } while (true); } /* min gain difference for de-click and loop-fadess * (-60dB difference to target) */ #define GAIN_COEFF_DELTA (1e-5) DiskReader::DeclickAmp::DeclickAmp (samplecnt_t sample_rate) { _a = 800.f / (gain_t)sample_rate; // ~ 1/50Hz to fade by 40dB _l = -log1p (_a); _g = 0; } void DiskReader::DeclickAmp::apply_gain (AudioBuffer& buf, samplecnt_t n_samples, const float target, sampleoffset_t buffer_offset) { if (n_samples == 0) { return; } float g = _g; if (g == target) { assert (buffer_offset == 0); Amp::apply_simple_gain (buf, n_samples, target, 0); return; } const float a = _a; Sample* const buffer = buf.data (); const int max_nproc = 4; uint32_t remain = n_samples; uint32_t offset = buffer_offset; while (remain > 0) { uint32_t n_proc = remain > max_nproc ? max_nproc : remain; for (uint32_t i = 0; i < n_proc; ++i) { buffer[offset + i] *= g; } #if 1 g += a * (target - g); #else /* accurate exponential fade */ if (n_proc == max_nproc) { g += a * (target - g); } else { g = target - (target - g) * expf (_l * n_proc / max_nproc); } #endif remain -= n_proc; offset += n_proc; } if (fabsf (g - target) < GAIN_COEFF_DELTA) { _g = target; } else { _g = g; } } DiskReader::Declicker::Declicker () : fade_start (0) , fade_end (0) , fade_length (0) , vec (0) { } DiskReader::Declicker::~Declicker () { delete[] vec; } void DiskReader::Declicker::alloc (samplecnt_t sr, bool fadein, bool linear) { delete[] vec; vec = new Sample[loop_fade_length]; if (linear) { if (fadein) { for (samplecnt_t n = 0; n < loop_fade_length; ++n) { vec[n] = n / (float)loop_fade_length; } } else { for (samplecnt_t n = 0; n < loop_fade_length; ++n) { vec[n] = 1.f - n / (float)loop_fade_length; } } fade_length = loop_fade_length - 1; return; } /* Exponential fade */ const float a = 390.f / sr; // ~ 1/100Hz for 40dB /* build a psuedo-exponential (linear-volume) shape for the fade */ samplecnt_t n; if (fadein) { gain_t g = 0.0; for (n = 0; (n < loop_fade_length) && ((1.f - g) > GAIN_COEFF_DELTA); ++n) { vec[n] = g; g += a * (1.0 - g); } } else { gain_t g = 1.0; for (n = 0; (n < loop_fade_length) && (g > GAIN_COEFF_DELTA); ++n) { vec[n] = g; g += a * -g; } } assert (n > 0 && n <= loop_fade_length); fade_length = n - 1; /* Fill remaining fader-buffer with the target value. * * This is needed for loop x-fade. Due to float precision near 1.0, fade-in length * is can be one or two samples shorter than fade-out length (depending on sample-rate). * Summing the fade-in and fade-out curve over the complete fade-range (fade-out, * as done by DiskReader::maybe_xfade_loop) must yield 1.0 +/- GAIN_COEFF_DELTA. */ for (; n < loop_fade_length; ++n) { vec[n] = fadein ? 1.f : 0.f; } } void DiskReader::Declicker::reset (samplepos_t loop_start, samplepos_t loop_end, bool fadein, samplecnt_t sr) { if (loop_start == loop_end) { fade_start = 0; fade_end = 0; return; } /* adjust the position of the fade (this is absolute (global) timeline units) */ if (fadein) { fade_start = loop_start; fade_end = loop_start + fade_length; } else { fade_start = loop_end - fade_length; fade_end = loop_end; } } void DiskReader::Declicker::run (Sample* buf, samplepos_t read_start, samplepos_t read_end) { samplecnt_t n = 0; /* how many samples to process */ sampleoffset_t bo = 0; /* offset into buffer */ sampleoffset_t vo = 0; /* offset into gain vector */ if (fade_start == fade_end) { return; } /* Determine how the read range overlaps with the fade range, so we can determine * which part of the fade gain vector to apply to which part of the buffer. * * see also DiskReader::maybe_xfade_loop() */ switch (Temporal::coverage_exclusive_ends (fade_start, fade_end, read_start, read_end)) { case Temporal::OverlapInternal: /* note: start and end points cannot coincide (see evoral/Range.h) * * read range is entirely within fade range */ bo = 0; vo = read_start - fade_start; n = read_end - read_start; break; case Temporal::OverlapExternal: /* read range extends on either side of fade range * * External allows coincidental start & end points, so check for that */ if (fade_start == read_start && fade_end == read_end) { /* fade entire read ... this is SO unlikely ! */ bo = 0; vo = 0; n = fade_end - fade_start; } else { bo = fade_start - read_start; vo = 0; n = fade_end - fade_start; } break; case Temporal::OverlapStart: /* read range starts before and ends within fade or at same end as fade */ n = fade_end - read_start; vo = 0; bo = fade_start - read_start; break; case Temporal::OverlapEnd: /* read range starts within fade range, but possibly at it's end, so check */ if (read_start == fade_end) { /* nothing to do */ return; } bo = 0; vo = read_start - fade_start; n = fade_end - read_start; break; case Temporal::OverlapNone: /* no overlap ... nothing to do */ return; } Sample* b = &buf[bo]; gain_t* g = &vec[vo]; for (sampleoffset_t i = 0; i < n; ++i) { b[i] *= g[i]; } } void DiskReader::maybe_xfade_loop (Sample* buf, samplepos_t read_start, samplepos_t read_end, ReaderChannelInfo* chan) { samplecnt_t n = 0; /* how many samples to process */ sampleoffset_t bo = 0; /* offset into buffer */ sampleoffset_t vo = 0; /* offset into gain vector */ const samplepos_t fade_start = loop_declick_out.fade_start; const samplepos_t fade_end = loop_declick_out.fade_end; if (fade_start == fade_end) { return; } /* Determine how the read range overlaps with the fade range, so we can determine * which part of the fade gain vector to apply to which part of the buffer. * * see also DiskReader::Declicker::run() */ switch (Temporal::coverage_exclusive_ends (fade_start, fade_end, read_start, read_end)) { case Temporal::OverlapInternal: /* note: start and end points cannot coincide (see evoral/Range.h) * * read range is entirely within fade range */ bo = 0; vo = read_start - fade_start; n = read_end - read_start; break; case Temporal::OverlapExternal: /* read range extends on either side of fade range * * External allows coincidental start & end points, so check for that */ if (fade_start == read_start && fade_end == read_end) { /* fade entire read ... this is SO unlikely ! */ bo = 0; vo = 0; n = fade_end - fade_start; } else { bo = fade_start - read_start; vo = 0; n = fade_end - fade_start; } break; case Temporal::OverlapStart: /* read range starts before and ends within fade or at same end as fade */ n = read_end - fade_start; vo = 0; bo = fade_start - read_start; break; case Temporal::OverlapEnd: /* read range starts within fade range, but possibly at it's end, so check */ if (read_start == fade_end) { /* nothing to do */ return; } bo = 0; vo = read_start - fade_start; n = fade_end - read_start; break; case Temporal::OverlapNone: /* no overlap ... nothing to do */ return; } Sample* b = &buf[bo]; /* data to be faded out */ Sample* sbuf = &chan->pre_loop_buffer[vo]; /* pre-loop (maybe silence) to be faded in */ gain_t* og = &loop_declick_out.vec[vo]; /* fade out gain vector */ gain_t* ig = &loop_declick_in.vec[vo]; /* fade in gain vector */ for (sampleoffset_t i = 0; i < n; ++i) { b[i] = (b[i] * og[i]) + (sbuf[i] * ig[i]); } } RTMidiBuffer* DiskReader::rt_midibuffer () { std::shared_ptr pl = _playlists[DataType::MIDI]; if (!pl) { return 0; } std::shared_ptr mpl = std::dynamic_pointer_cast (pl); if (!mpl) { /* error, but whatever ... */ return 0; } return mpl->rendered (); } void DiskReader::alloc_loop_declick (samplecnt_t sr) { loop_fade_length = lrintf (ceil (-log (GAIN_COEFF_DELTA / 2.) / (390. / sr))); loop_declick_in.alloc (sr, true, Config->get_loop_fade_choice () == XFadeLoop); loop_declick_out.alloc (sr, false, Config->get_loop_fade_choice () == XFadeLoop); } #undef GAIN_COEFF_DELTA void DiskReader::reset_loop_declick (Location* loc, samplecnt_t sr) { if (loc) { loop_declick_in.reset (loc->start_sample (), loc->end_sample (), true, sr); loop_declick_out.reset (loc->start_sample (), loc->end_sample (), false, sr); } else { loop_declick_in.reset (0, 0, true, sr); loop_declick_out.reset (0, 0, false, sr); } } void DiskReader::set_loop (Location* loc) { Processor::set_loop (loc); if (!loc) { return; } } void DiskReader::setup_preloop_buffer () { if (!_loop_location) { return; } std::shared_ptr c = channels.reader (); if (c->empty () || !_playlists[DataType::AUDIO]) { return; } Location* loc = _loop_location; boost::scoped_array mix_buf (new Sample[loop_fade_length]); boost::scoped_array gain_buf (new Sample[loop_fade_length]); const timepos_t read_start = timepos_t (loc->start_sample () - loop_declick_out.fade_length); const timecnt_t read_cnt = timecnt_t (loop_declick_out.fade_length); uint32_t channel = 0; for (auto const& chan : *c) { ReaderChannelInfo* rci = dynamic_cast (chan); rci->resize_preloop (loop_fade_length); if (loc->start () > loop_fade_length) { audio_playlist ()->read (rci->pre_loop_buffer, mix_buf.get (), gain_buf.get (), read_start, read_cnt, channel); } else { memset (rci->pre_loop_buffer, 0, sizeof (Sample) * loop_fade_length); } ++channel; } } void DiskReader::set_need_midi_catchup (bool yn) { _need_midi_catchup = yn; }