From 3845af6ce92ef15637ffb09410f442e7b4a104c3 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Thu, 27 Aug 2009 03:09:30 +0000 Subject: lots of MIDI editing stuff. to be explained on the website when its done git-svn-id: svn://localhost/ardour2/branches/3.0@5596 d708f5d6-7413-0410-9779-e7cbd77b26cf --- libs/ardour/ardour/midi_model.h | 3 +- libs/ardour/ardour/midi_operator.h | 42 +++++++++++++++ libs/ardour/ardour/midi_source.h | 4 +- libs/ardour/ardour/midi_track.h | 3 +- libs/ardour/ardour/quantize.h | 28 +++++++--- libs/ardour/ardour/route.h | 3 ++ libs/ardour/ardour/smf_source.h | 2 +- libs/ardour/ardour/tempo.h | 2 +- libs/ardour/ardour/types.h | 6 +++ libs/ardour/delivery.cc | 2 + libs/ardour/midi_model.cc | 29 +++++++--- libs/ardour/midi_ring_buffer.cc | 10 ++-- libs/ardour/midi_track.cc | 21 ++++---- libs/ardour/quantize.cc | 108 ++++++++++++++++++++++++++----------- libs/ardour/route.cc | 5 +- libs/ardour/tempo.cc | 98 +++++++++++++++++++++++++++------ libs/ardour/track.cc | 3 ++ 17 files changed, 289 insertions(+), 80 deletions(-) create mode 100644 libs/ardour/ardour/midi_operator.h (limited to 'libs/ardour') diff --git a/libs/ardour/ardour/midi_model.h b/libs/ardour/ardour/midi_model.h index 3feda1a22c..967372fa9a 100644 --- a/libs/ardour/ardour/midi_model.h +++ b/libs/ardour/ardour/midi_model.h @@ -47,7 +47,7 @@ class MidiSource; * Because of this MIDI controllers and automatable controllers/widgets/etc * are easily interchangeable. */ -class MidiModel : public AutomatableSequence { +class MidiModel : public AutomatableSequence { public: typedef double TimeType; @@ -91,6 +91,7 @@ public: MidiModel::DeltaCommand* new_delta_command(const std::string name="midi edit"); void apply_command(Session& session, Command* cmd); + void apply_command_as_subcommand(Session& session, Command* cmd); bool write_to(boost::shared_ptr source); diff --git a/libs/ardour/ardour/midi_operator.h b/libs/ardour/ardour/midi_operator.h new file mode 100644 index 0000000000..e3ed6aabfd --- /dev/null +++ b/libs/ardour/ardour/midi_operator.h @@ -0,0 +1,42 @@ +/* + Copyright (C) 2009 Paul Davis + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __libardour_midi_operator_h__ +#define __libardour_midi_operator_h__ + +#include +#include + +#include "evoral/types.hpp" +#include "evoral/Sequence.hpp" + +namespace ARDOUR { + +class MidiOperator { + public: + MidiOperator() {} + virtual ~MidiOperator() {} + + virtual int operator() (std::vector::Notes>&) = 0; + virtual std::string name() const = 0; +}; + +} /* namespace */ + +#endif /* __libardour_midi_operator_h__ */ diff --git a/libs/ardour/ardour/midi_source.h b/libs/ardour/ardour/midi_source.h index cf49e59458..a479b4a8a8 100644 --- a/libs/ardour/ardour/midi_source.h +++ b/libs/ardour/ardour/midi_source.h @@ -65,7 +65,7 @@ class MidiSource : virtual public Source sframes_t source_start, nframes_t cnt); - virtual void append_event_unlocked_beats(const Evoral::Event& ev) = 0; + virtual void append_event_unlocked_beats(const Evoral::Event& ev) = 0; virtual void append_event_unlocked_frames(const Evoral::Event& ev, sframes_t source_start) = 0; @@ -128,7 +128,7 @@ class MidiSource : virtual public Source boost::shared_ptr _model; bool _writing; - mutable Evoral::Sequence::const_iterator _model_iter; + mutable Evoral::Sequence::const_iterator _model_iter; mutable double _length_beats; mutable sframes_t _last_read_end; diff --git a/libs/ardour/ardour/midi_track.h b/libs/ardour/ardour/midi_track.h index e7ffd40d67..5c5ef5c26c 100644 --- a/libs/ardour/ardour/midi_track.h +++ b/libs/ardour/ardour/midi_track.h @@ -84,7 +84,8 @@ protected: int _set_state (const XMLNode&, bool call_base); private: - void write_controller_messages(MidiBuffer& buf, sframes_t start_frame, sframes_t end_frame, nframes_t nframes); + void write_out_of_band_data (BufferSet& bufs, sframes_t start_frame, sframes_t end_frame, + nframes_t nframes); int set_diskstream (boost::shared_ptr ds); void use_new_diskstream (); diff --git a/libs/ardour/ardour/quantize.h b/libs/ardour/ardour/quantize.h index f7307c194c..57e5467294 100644 --- a/libs/ardour/ardour/quantize.h +++ b/libs/ardour/ardour/quantize.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2007 Paul Davis + Copyright (C) 2007-2009 Paul Davis Author: Dave Robillard This program is free software; you can redistribute it and/or modify @@ -21,19 +21,33 @@ #ifndef __ardour_quantize_h__ #define __ardour_quantize_h__ -#include "ardour/filter.h" +#include "ardour/types.h" +#include "ardour/midi_operator.h" namespace ARDOUR { -class Quantize : public Filter { +class Session; + +class Quantize : public MidiOperator { public: - Quantize (ARDOUR::Session&, double q); - ~Quantize (); + Quantize (ARDOUR::Session&, QuantizeType type, + bool snap_start, bool snap_end, + double start_grid, double end_grid, + float strength, float swing, float threshold); + ~Quantize (); - int run (boost::shared_ptr); + int operator() (std::vector::Notes>&); + std::string name() const { return std::string ("quantize"); } private: - double _q; + ARDOUR::Session& session; + bool _snap_start; + bool _snap_end; + double _start_grid; + double _end_grid; + float _strength; + float _swing; + float _threshold; }; } /* namespace */ diff --git a/libs/ardour/ardour/route.h b/libs/ardour/ardour/route.h index 9195a50060..34bf9e8d9f 100644 --- a/libs/ardour/ardour/route.h +++ b/libs/ardour/ardour/route.h @@ -326,6 +326,9 @@ class Route : public SessionObject, public AutomatableControls void passthru (sframes_t start_frame, sframes_t end_frame, nframes_t nframes, int declick); + virtual void write_out_of_band_data (BufferSet& /* bufs */, sframes_t /* start_frame */, sframes_t /* end_frame */, + nframes_t /* nframes */) {} + virtual void process_output_buffers (BufferSet& bufs, sframes_t start_frame, sframes_t end_frame, nframes_t nframes, bool with_processors, int declick); diff --git a/libs/ardour/ardour/smf_source.h b/libs/ardour/ardour/smf_source.h index dc9cbeee55..73bef5480a 100644 --- a/libs/ardour/ardour/smf_source.h +++ b/libs/ardour/ardour/smf_source.h @@ -51,7 +51,7 @@ public: bool set_name (const std::string& newname) { return (set_source_name(newname, false) == 0); } - void append_event_unlocked_beats (const Evoral::Event& ev); + void append_event_unlocked_beats (const Evoral::Event& ev); void append_event_unlocked_frames (const Evoral::Event& ev, sframes_t source_start); void mark_streaming_midi_write_started (NoteMode mode, sframes_t start_time); diff --git a/libs/ardour/ardour/tempo.h b/libs/ardour/ardour/tempo.h index fcda463fe1..843437dab7 100644 --- a/libs/ardour/ardour/tempo.h +++ b/libs/ardour/ardour/tempo.h @@ -204,7 +204,7 @@ class TempoMap : public PBD::StatefulDestructible nframes_t round_to_bar (nframes_t frame, int dir); nframes_t round_to_beat (nframes_t frame, int dir); - nframes_t round_to_beat_subdivision (nframes_t fr, int sub_num); + nframes_t round_to_beat_subdivision (nframes_t fr, int sub_num, int dir); nframes_t round_to_tick (nframes_t frame, int dir); void set_length (nframes_t frames); diff --git a/libs/ardour/ardour/types.h b/libs/ardour/ardour/types.h index 809eb5b2b2..1a30cfd769 100644 --- a/libs/ardour/ardour/types.h +++ b/libs/ardour/ardour/types.h @@ -446,6 +446,12 @@ namespace ARDOUR { Rectified }; + enum QuantizeType { + Plain, + Legato, + Groove + }; + } // namespace ARDOUR std::istream& operator>>(std::istream& o, ARDOUR::SampleFormat& sf); diff --git a/libs/ardour/delivery.cc b/libs/ardour/delivery.cc index 01ff226b5e..1b5f96d36c 100644 --- a/libs/ardour/delivery.cc +++ b/libs/ardour/delivery.cc @@ -22,6 +22,8 @@ #include "pbd/enumwriter.h" #include "pbd/convert.h" +#include "ardour/midi_buffer.h" + #include "ardour/delivery.h" #include "ardour/audio_buffer.h" #include "ardour/amp.h" diff --git a/libs/ardour/midi_model.cc b/libs/ardour/midi_model.cc index 0c0c0ead5b..da524307f6 100644 --- a/libs/ardour/midi_model.cc +++ b/libs/ardour/midi_model.cc @@ -70,6 +70,19 @@ MidiModel::apply_command(Session& session, Command* cmd) set_edited(true); } +/** Apply a command as part of a larger reversible transaction + * + * Ownership of cmd is taken, it must not be deleted by the caller. + * The command will constitute one item on the undo stack. + */ +void +MidiModel::apply_command_as_subcommand(Session& session, Command* cmd) +{ + (*cmd)(); + session.add_command(cmd); + set_edited(true); +} + // DeltaCommand @@ -107,17 +120,19 @@ MidiModel::DeltaCommand::operator()() { // This could be made much faster by using a priority_queue for added and // removed notes (or sort here), and doing a single iteration over _model - + Glib::Mutex::Lock lm (_model->_midi_source->mutex()); _model->_midi_source->invalidate(); // release model read lock _model->write_lock(); - for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) + for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) { _model->add_note_unlocked(*i); + } - for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) + for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) { _model->remove_note_unlocked(*i); - + } + _model->write_unlock(); _model->ContentsChanged(); /* EMIT SIGNAL */ } @@ -132,11 +147,13 @@ MidiModel::DeltaCommand::undo() _model->_midi_source->invalidate(); // release model read lock _model->write_lock(); - for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) + for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) { _model->remove_note_unlocked(*i); + } - for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) + for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) { _model->add_note_unlocked(*i); + } _model->write_unlock(); _model->ContentsChanged(); /* EMIT SIGNAL */ diff --git a/libs/ardour/midi_ring_buffer.cc b/libs/ardour/midi_ring_buffer.cc index 746dc3b3eb..007d5271c8 100644 --- a/libs/ardour/midi_ring_buffer.cc +++ b/libs/ardour/midi_ring_buffer.cc @@ -24,7 +24,7 @@ using namespace std; namespace ARDOUR { -/** Read a block of MIDI events from buffer. +/** Read a block of MIDI events from buffer into a MidiBuffer. * * Timestamps of events returned are relative to start (i.e. event with stamp 0 * occurred at start), with offset added. @@ -106,11 +106,13 @@ MidiRingBuffer::read(MidiBuffer& dst, nframes_t start, nframes_t end, nframes // write MIDI buffer contents success = Evoral::EventRingBuffer::full_read(ev_size, write_loc); - /*cerr << "wrote MidiEvent to Buffer: "; +#if 0 + cerr << "wrote MidiEvent to Buffer: " << hex; for (size_t i=0; i < ev_size; ++i) { - printf("%X ", write_loc[i]); + cerr << (int) write_loc[i] << ' '; } - printf("\n");*/ + cerr << dec << endl; +#endif if (success) { if (is_channel_event(status) && get_channel_mode() == ForceChannel) { diff --git a/libs/ardour/midi_track.cc b/libs/ardour/midi_track.cc index 5a53cecc3c..4c575e8cc4 100644 --- a/libs/ardour/midi_track.cc +++ b/libs/ardour/midi_track.cc @@ -401,7 +401,6 @@ MidiTrack::roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame, _silent = false; if ((dret = diskstream->process (transport_frame, nframes, can_record, rec_monitors_input)) != 0) { - silence (nframes); return dret; } @@ -416,10 +415,10 @@ MidiTrack::roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame, /* not actually recording, but we want to hear the input material anyway, at least potentially (depending on monitoring options) - */ + */ passthru (start_frame, end_frame, nframes, 0); - + } else { /* XXX is it true that the earlier test on n_outputs() @@ -434,12 +433,15 @@ MidiTrack::roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame, //const size_t limit = n_process_buffers().n_audio(); BufferSet& bufs = _session.get_scratch_buffers (n_process_buffers()); + + diskstream->get_playback (bufs.get_midi(0), start_frame, end_frame); + + /* append immediate messages to the first MIDI buffer (thus sending it to the first output port) */ - diskstream->get_playback(bufs.get_midi(0), start_frame, end_frame); + write_out_of_band_data (bufs, start_frame, end_frame, nframes); process_output_buffers (bufs, start_frame, end_frame, nframes, (!_session.get_record_enabled() || !Config->get_do_not_record_plugins()), declick); - } _main_outs->flush (nframes); @@ -448,13 +450,12 @@ MidiTrack::roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame, } void -MidiTrack::write_controller_messages(MidiBuffer& output_buf, sframes_t /*start*/, sframes_t /*end*/, nframes_t nframes) +MidiTrack::write_out_of_band_data (BufferSet& bufs, sframes_t /*start*/, sframes_t /*end*/, nframes_t nframes) { - // Append immediate events (UI controls) - - // XXX TAKE _port_offset in Port into account??? + // Append immediate events - _immediate_events.read (output_buf, 0, 0, nframes - 1); // all stamps = 0 + MidiBuffer& buf (bufs.get_midi (0)); + _immediate_events.read (buf, 0, 0, nframes - 1); // all stamps = 0 } int diff --git a/libs/ardour/quantize.cc b/libs/ardour/quantize.cc index e5f4abeccf..becb3bd491 100644 --- a/libs/ardour/quantize.cc +++ b/libs/ardour/quantize.cc @@ -16,6 +16,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include #include "pbd/basename.h" @@ -31,15 +32,24 @@ using namespace std; using namespace ARDOUR; - -/** Quantize notes, valid for MIDI regions only. +/** Quantize notes * - * Q is the quantize value in beats, ie 1.0 = quantize to beats, + * grid parameters are the quantize value in beats, ie 1.0 = quantize to beats, * 0.25 = quantize to beats/4, etc. */ -Quantize::Quantize (Session& s, double q) - : Filter (s) - , _q(q) + +Quantize::Quantize (Session& s, QuantizeType /* type */, + bool snap_start, bool snap_end, + double start_grid, double end_grid, + float strength, float swing, float threshold) + : session (s) + , _snap_start (snap_start) + , _snap_end (snap_end) + , _start_grid(start_grid) + , _end_grid(end_grid) + , _strength (strength/100.0) + , _swing (swing/100.0) + , _threshold (threshold) { } @@ -48,32 +58,70 @@ Quantize::~Quantize () } int -Quantize::run (boost::shared_ptr r) +Quantize::operator () (std::vector::Notes>& seqs) { - boost::shared_ptr region = boost::dynamic_pointer_cast(r); - if (!region) - return -1; - - // FIXME: how to make a whole file region if it isn't? - //assert(region->whole_file()); - - boost::shared_ptr src = region->midi_source(0); - src->load_model(); - - boost::shared_ptr model = src->model(); - - for (Evoral::Sequence::Notes::iterator i = model->notes().begin(); - i != model->notes().end(); ++i) { - const double new_time = lrint((*i)->time() / _q) * _q; - double new_dur = lrint((*i)->length() / _q) * _q; - if (new_dur == 0.0) - new_dur = _q; - - (*i)->set_time(new_time); - (*i)->set_length(new_dur); + bool even; + + for (std::vector::Notes>::iterator s = seqs.begin(); + s != seqs.end(); ++s) { + + even = false; + + for (Evoral::Sequence::Notes::iterator i = (*s).begin(); + i != (*s).end(); ++i) { + + double new_start = round ((*i)->time() / _start_grid) * _start_grid; + double new_end = round ((*i)->end_time() / _end_grid) * _end_grid; + double delta; + + if (_swing > 0.0 && !even) { + + double next_grid = new_start + _start_grid; + + /* find a spot 2/3 (* swing factor) of the way between the grid point + we would put this note at, and the nominal position of the next note. + */ + + new_start = new_start + (2.0/3.0 * _swing * (next_grid - new_start)); + + } else if (_swing < 0.0 && !even) { + + double prev_grid = new_start - _start_grid; + + /* find a spot 2/3 (* swing factor) of the way between the grid point + we would put this note at, and the nominal position of the previous note. + */ + + new_start = new_start - (2.0/3.0 * _swing * (new_start - prev_grid)); + + } + + delta = new_start - (*i)->time(); + + if (fabs (delta) >= _threshold) { + if (_snap_start) { + delta *= _strength; + (*i)->set_time ((*i)->time() + delta); + } + } + + if (_snap_end) { + delta = new_end - (*i)->end_time(); + + if (fabs (delta) >= _threshold) { + double new_dur = new_end - new_start; + + if (new_dur == 0.0) { + new_dur = _end_grid; + } + + (*i)->set_length (new_dur); + } + } + + even = !even; + } } - model->set_edited(true); - return 0; } diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index 989cecdd6b..e922a2f33f 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -479,13 +479,16 @@ Route::passthru (sframes_t start_frame, sframes_t end_frame, nframes_t nframes, } } + write_out_of_band_data (bufs, start_frame, end_frame, nframes); process_output_buffers (bufs, start_frame, end_frame, nframes, true, declick); } void Route::passthru_silence (sframes_t start_frame, sframes_t end_frame, nframes_t nframes, int declick) { - process_output_buffers (_session.get_silent_buffers (n_process_buffers()), start_frame, end_frame, nframes, true, declick); + BufferSet& bufs (_session.get_silent_buffers (n_process_buffers())); + write_out_of_band_data (bufs, start_frame, end_frame, nframes); + process_output_buffers (bufs, start_frame, end_frame, nframes, true, declick); } void diff --git a/libs/ardour/tempo.cc b/libs/ardour/tempo.cc index 3056016752..a2307901bc 100644 --- a/libs/ardour/tempo.cc +++ b/libs/ardour/tempo.cc @@ -1099,30 +1099,91 @@ TempoMap::round_to_beat (nframes_t fr, int dir) } nframes_t - -TempoMap::round_to_beat_subdivision (nframes_t fr, int sub_num) +TempoMap::round_to_beat_subdivision (nframes_t fr, int sub_num, int dir) { BBT_Time the_beat; uint32_t ticks_one_half_subdivisions_worth; uint32_t ticks_one_subdivisions_worth; + uint32_t difference; bbt_time(fr, the_beat); ticks_one_subdivisions_worth = (uint32_t)Meter::ticks_per_beat / sub_num; ticks_one_half_subdivisions_worth = ticks_one_subdivisions_worth / 2; + + if (dir > 0) { + + /* round to next */ - if (the_beat.ticks % ticks_one_subdivisions_worth > ticks_one_half_subdivisions_worth) { - uint32_t difference = ticks_one_subdivisions_worth - (the_beat.ticks % ticks_one_subdivisions_worth); - if (the_beat.ticks + difference >= (uint32_t)Meter::ticks_per_beat) { - the_beat.beats++; - the_beat.ticks += difference; - the_beat.ticks -= (uint32_t)Meter::ticks_per_beat; - } else { - the_beat.ticks += difference; - } - } else { - the_beat.ticks -= the_beat.ticks % ticks_one_subdivisions_worth; + uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth; + + if (mod == 0) { + /* right on the subdivision, so the difference is just the subdivision ticks */ + difference = ticks_one_subdivisions_worth; + + } else { + /* not on subdivision, compute distance to next subdivision */ + + difference = ticks_one_subdivisions_worth - mod; + } + + if (the_beat.ticks + difference >= (uint32_t)Meter::ticks_per_beat) { + the_beat.beats++; + the_beat.ticks += difference; + the_beat.ticks -= (uint32_t)Meter::ticks_per_beat; + } else { + the_beat.ticks += difference; + } + + } else if (dir < 0) { + + /* round to previous */ + + uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth; + + if (mod == 0) { + /* right on the subdivision, so the difference is just the subdivision ticks */ + difference = ticks_one_subdivisions_worth; + cerr << "On the sub, move by 1 sub = " << difference << endl; + } else { + /* not on subdivision, compute distance to previous subdivision, which + is just the modulus. + */ + + difference = mod; + cerr << "off the sub, move by 1 sub = " << difference << endl; + } + + + cerr << "ticks = " << the_beat.ticks << endl; + + if (the_beat.ticks < difference) { + cerr << "backup beats, set ticks to " + << (uint32_t)Meter::ticks_per_beat - difference << endl; + the_beat.beats--; + the_beat.ticks = (uint32_t)Meter::ticks_per_beat - difference; + } else { + cerr << " reduce ticks\n"; + the_beat.ticks -= difference; + } + + } else { + /* round to nearest */ + + if (the_beat.ticks % ticks_one_subdivisions_worth > ticks_one_half_subdivisions_worth) { + difference = ticks_one_subdivisions_worth - (the_beat.ticks % ticks_one_subdivisions_worth); + if (the_beat.ticks + difference >= (uint32_t)Meter::ticks_per_beat) { + the_beat.beats++; + the_beat.ticks += difference; + the_beat.ticks -= (uint32_t)Meter::ticks_per_beat; + } else { + the_beat.ticks += difference; + } + } else { + // difference = ticks_one_subdivisions_worth - (the_beat.ticks % ticks_one_subdivisions_worth); + the_beat.ticks -= the_beat.ticks % ticks_one_subdivisions_worth; + } } return frame_time (the_beat); @@ -1139,7 +1200,9 @@ TempoMap::round_to_type (nframes_t frame, int dir, BBTPointType type) switch (type) { case Bar: if (dir < 0) { - /* relax */ + if (bbt.bars > 1) { + bbt.bars--; + } } else if (dir > 0) { if (bbt.beats > 0) { bbt.bars++; @@ -1157,7 +1220,9 @@ TempoMap::round_to_type (nframes_t frame, int dir, BBTPointType type) case Beat: if (dir < 0) { - /* relax */ + if (bbt.beats > 1) { + bbt.beats--; + } } else if (dir > 0) { if (bbt.ticks > 0) { bbt.beats++; @@ -1178,11 +1243,12 @@ TempoMap::round_to_type (nframes_t frame, int dir, BBTPointType type) } - /* + /* cerr << "for " << frame << " round to " << bbt << " using " << metric.start() << endl; */ + return metric.frame() + count_frames_between (metric.start(), bbt); } diff --git a/libs/ardour/track.cc b/libs/ardour/track.cc index 64ec77b683..23da42f86e 100644 --- a/libs/ardour/track.cc +++ b/libs/ardour/track.cc @@ -24,6 +24,7 @@ #include "ardour/audioplaylist.h" #include "ardour/audioregion.h" #include "ardour/audiosource.h" +#include "ardour/delivery.h" #include "ardour/diskstream.h" #include "ardour/io_processor.h" #include "ardour/meter.h" @@ -316,6 +317,8 @@ Track::no_roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame, passthru (start_frame, end_frame, nframes, false); } + _main_outs->flush (nframes); + return 0; } -- cgit v1.2.3