add the concept of a length that is data-independent to MIDI files (libs)
This commit is contained in:
@@ -60,7 +60,7 @@ public:
|
||||
virtual int update_header (samplepos_t when, struct tm&, time_t) = 0;
|
||||
virtual int flush_header () = 0;
|
||||
|
||||
void mark_streaming_write_completed (const WriterLock& lock);
|
||||
void mark_streaming_write_completed (const WriterLock& lock, Temporal::timecnt_t const & duration);
|
||||
|
||||
int setup_peakfile ();
|
||||
void set_gain (float g, bool temporarily = false);
|
||||
|
||||
@@ -57,7 +57,7 @@ class LIBARDOUR_API AudioSource : virtual public Source, public ARDOUR::AudioRea
|
||||
|
||||
virtual float sample_rate () const = 0;
|
||||
|
||||
virtual void mark_streaming_write_completed (const WriterLock& lock);
|
||||
virtual void mark_streaming_write_completed (const WriterLock& lock, Temporal::timecnt_t const & duration);
|
||||
|
||||
virtual bool can_truncate_peaks() const { return true; }
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ public:
|
||||
|
||||
std::string steal_write_source_name ();
|
||||
int use_new_write_source (DataType, uint32_t n = 0);
|
||||
void reset_write_sources (bool, bool force = false);
|
||||
void reset_write_sources ();
|
||||
|
||||
AlignStyle alignment_style () const { return _alignment_style; }
|
||||
void set_align_style (AlignStyle, bool force = false);
|
||||
|
||||
@@ -138,7 +138,7 @@ class LIBARDOUR_API MidiSource : virtual public Source
|
||||
|
||||
virtual void mark_streaming_midi_write_started (const WriterLock& lock, NoteMode mode);
|
||||
virtual void mark_streaming_write_started (const WriterLock& lock);
|
||||
virtual void mark_streaming_write_completed (const WriterLock& lock);
|
||||
virtual void mark_streaming_write_completed (const WriterLock& lock, Temporal::timecnt_t const & duration);
|
||||
|
||||
/** Mark write starting with the given time parameters.
|
||||
*
|
||||
@@ -160,7 +160,7 @@ class LIBARDOUR_API MidiSource : virtual public Source
|
||||
virtual void mark_midi_streaming_write_completed (
|
||||
const WriterLock& lock,
|
||||
Evoral::Sequence<Temporal::Beats>::StuckNoteOption stuck_option,
|
||||
Temporal::Beats when = Temporal::Beats());
|
||||
Temporal::timecnt_t const & when);
|
||||
|
||||
virtual void session_saved();
|
||||
|
||||
|
||||
@@ -1978,7 +1978,7 @@ public:
|
||||
ARDOUR::CueMarkers pending_source_markers; // source markers created while recording
|
||||
|
||||
private:
|
||||
void reset_write_sources (bool mark_write_complete, bool force = false);
|
||||
void reset_write_sources ();
|
||||
SourceMap sources;
|
||||
|
||||
int load_sources (const XMLNode& node);
|
||||
|
||||
@@ -58,10 +58,11 @@ public:
|
||||
void update_length (timepos_t const & dur);
|
||||
|
||||
void mark_streaming_midi_write_started (const WriterLock& lock, NoteMode mode);
|
||||
void mark_streaming_write_completed (const WriterLock& lock);
|
||||
|
||||
void mark_streaming_write_completed (const WriterLock& lock, Temporal::timecnt_t const & duration);
|
||||
void mark_midi_streaming_write_completed (const WriterLock& lock,
|
||||
Evoral::Sequence<Temporal::Beats>::StuckNoteOption,
|
||||
Temporal::Beats when = Temporal::Beats());
|
||||
Temporal::timecnt_t const & duration);
|
||||
|
||||
XMLNode& get_state () const;
|
||||
int set_state (const XMLNode&, int version);
|
||||
|
||||
@@ -83,7 +83,12 @@ public:
|
||||
void mark_for_remove();
|
||||
|
||||
virtual void mark_streaming_write_started (const WriterLock& lock) {}
|
||||
virtual void mark_streaming_write_completed (const WriterLock& lock) = 0;
|
||||
/* The duration argument is ignored for audio data, where length is
|
||||
implicitly given by the sample data. It matters for MIDI data, where
|
||||
the file may be intended to be N bars long, but has no events that
|
||||
occur at that duration.
|
||||
*/
|
||||
virtual void mark_streaming_write_completed (const WriterLock& lock, Temporal::timecnt_t const & duration) = 0;
|
||||
|
||||
virtual void session_saved() {}
|
||||
|
||||
|
||||
@@ -145,7 +145,7 @@ public:
|
||||
void ensure_input_monitoring (bool);
|
||||
std::list<std::shared_ptr<Source> > & last_capture_sources ();
|
||||
std::string steal_write_source_name ();
|
||||
void reset_write_sources (bool, bool force = false);
|
||||
void reset_write_sources ();
|
||||
float playback_buffer_load () const;
|
||||
float capture_buffer_load () const;
|
||||
int do_refill ();
|
||||
|
||||
@@ -715,7 +715,7 @@ class LIBARDOUR_API TriggerBoxThread
|
||||
|
||||
void set_region (TriggerBox&, uint32_t slot, std::shared_ptr<Region>);
|
||||
void request_delete_trigger (Trigger* t);
|
||||
void request_build_source (Trigger* t);
|
||||
void request_build_source (Trigger* t, Temporal::timecnt_t const & duration);
|
||||
|
||||
void summon();
|
||||
void stop();
|
||||
@@ -743,6 +743,7 @@ class LIBARDOUR_API TriggerBoxThread
|
||||
std::shared_ptr<Region> region;
|
||||
/* for DeleteTrigger and BuildSourceAndRegion */
|
||||
Trigger* trigger;
|
||||
Temporal::timecnt_t duration;
|
||||
|
||||
void* operator new (size_t);
|
||||
void operator delete (void* ptr, size_t);
|
||||
@@ -757,9 +758,9 @@ class LIBARDOUR_API TriggerBoxThread
|
||||
CrossThreadChannel _xthread;
|
||||
void queue_request (Request*);
|
||||
void delete_trigger (Trigger*);
|
||||
void build_source (Trigger*);
|
||||
void build_midi_source (MIDITrigger*);
|
||||
void build_audio_source (AudioTrigger*);
|
||||
void build_source (Trigger*, Temporal::timecnt_t const & duration);
|
||||
void build_midi_source (MIDITrigger*, Temporal::timecnt_t const &);
|
||||
void build_audio_source (AudioTrigger*, Temporal::timecnt_t const &);
|
||||
};
|
||||
|
||||
struct CueRecord {
|
||||
@@ -783,7 +784,7 @@ struct SlotArmInfo {
|
||||
samplepos_t start_samples;
|
||||
Temporal::Beats end_beats;
|
||||
samplepos_t end_samples;
|
||||
Temporal::timecnt_t captured;
|
||||
samplecnt_t captured;
|
||||
RTMidiBufferBeats* midi_buf;
|
||||
AudioTrigger::AudioData audio_buf;
|
||||
RubberBand::RubberBandStretcher* stretcher;
|
||||
@@ -942,7 +943,7 @@ class LIBARDOUR_API TriggerBox : public Processor, public std::enable_shared_fro
|
||||
|
||||
void dump (std::ostream &) const;
|
||||
|
||||
PBD::Signal<void(timecnt_t)> Captured;
|
||||
PBD::Signal<void(samplecnt_t)> Captured;
|
||||
|
||||
private:
|
||||
struct Requests {
|
||||
|
||||
@@ -249,13 +249,13 @@ AudioFileSource::set_state (const XMLNode& node, int version)
|
||||
}
|
||||
|
||||
void
|
||||
AudioFileSource::mark_streaming_write_completed (const WriterLock& lock)
|
||||
AudioFileSource::mark_streaming_write_completed (const WriterLock& lock, Temporal::timecnt_t const & duration)
|
||||
{
|
||||
if (!writable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
AudioSource::mark_streaming_write_completed (lock);
|
||||
AudioSource::mark_streaming_write_completed (lock, duration);
|
||||
}
|
||||
|
||||
int
|
||||
|
||||
@@ -1150,7 +1150,7 @@ AudioSource::available_peaks (double zoom_factor) const
|
||||
}
|
||||
|
||||
void
|
||||
AudioSource::mark_streaming_write_completed (const WriterLock& lock)
|
||||
AudioSource::mark_streaming_write_completed (const WriterLock& lock, Temporal::timecnt_t const &)
|
||||
{
|
||||
Glib::Threads::Mutex::Lock lm (_peaks_ready_lock);
|
||||
|
||||
|
||||
@@ -123,7 +123,7 @@ DiskWriter::set_write_source_name (string const & str)
|
||||
{
|
||||
_write_source_name = str;
|
||||
|
||||
reset_write_sources (false);
|
||||
reset_write_sources ();
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -390,7 +390,7 @@ DiskWriter::set_state (const XMLNode& node, int version)
|
||||
node.get_property (X_("record-safe"), rec_safe);
|
||||
_record_safe.store (rec_safe);
|
||||
|
||||
reset_write_sources (false, true);
|
||||
reset_write_sources ();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1072,7 +1072,7 @@ DiskWriter::do_flush (RunContext ctxt, bool force_flush)
|
||||
}
|
||||
|
||||
void
|
||||
DiskWriter::reset_write_sources (bool mark_write_complete, bool /*force*/)
|
||||
DiskWriter::reset_write_sources ()
|
||||
{
|
||||
std::shared_ptr<ChannelList const> c = channels.reader();
|
||||
uint32_t n = 0;
|
||||
@@ -1087,12 +1087,6 @@ DiskWriter::reset_write_sources (bool mark_write_complete, bool /*force*/)
|
||||
|
||||
if (chan->write_source) {
|
||||
|
||||
if (mark_write_complete) {
|
||||
Source::WriterLock lock(chan->write_source->mutex());
|
||||
chan->write_source->mark_streaming_write_completed (lock);
|
||||
chan->write_source->done_with_peakfile_writes ();
|
||||
}
|
||||
|
||||
if (chan->write_source->removable()) {
|
||||
chan->write_source->mark_for_remove ();
|
||||
chan->write_source->drop_references ();
|
||||
@@ -1108,13 +1102,6 @@ DiskWriter::reset_write_sources (bool mark_write_complete, bool /*force*/)
|
||||
}
|
||||
}
|
||||
|
||||
if (_midi_write_source) {
|
||||
if (mark_write_complete) {
|
||||
Source::WriterLock lm(_midi_write_source->mutex());
|
||||
_midi_write_source->mark_streaming_write_completed (lm);
|
||||
}
|
||||
}
|
||||
|
||||
if (_playlists[DataType::MIDI]) {
|
||||
use_new_write_source (DataType::MIDI);
|
||||
}
|
||||
@@ -1186,7 +1173,6 @@ DiskWriter::transport_stopped_wallclock (struct tm& when, time_t twhen, bool abo
|
||||
ChannelList::const_iterator chan;
|
||||
std::shared_ptr<ChannelList const> c = channels.reader();
|
||||
uint32_t n = 0;
|
||||
bool mark_write_completed = false;
|
||||
|
||||
finish_capture (c);
|
||||
|
||||
@@ -1315,7 +1301,11 @@ DiskWriter::transport_stopped_wallclock (struct tm& when, time_t twhen, bool abo
|
||||
total_capture += timecnt_t ((*ci)->samples);
|
||||
}
|
||||
|
||||
_midi_write_source->mark_midi_streaming_write_completed (source_lock, Evoral::Sequence<Temporal::Beats>::ResolveStuckNotes, total_capture.beats());
|
||||
/* XXX we need to consider snapping the duration up to the next
|
||||
* beat/bar here
|
||||
*/
|
||||
|
||||
_midi_write_source->mark_midi_streaming_write_completed (source_lock, Evoral::Sequence<Temporal::Beats>::ResolveStuckNotes, timecnt_t (total_capture));
|
||||
}
|
||||
|
||||
_last_capture_sources.insert (_last_capture_sources.end(), audio_srcs.begin(), audio_srcs.end());
|
||||
@@ -1325,10 +1315,8 @@ DiskWriter::transport_stopped_wallclock (struct tm& when, time_t twhen, bool abo
|
||||
_track.use_captured_sources (audio_srcs, capture_info);
|
||||
_track.use_captured_sources (midi_srcs, capture_info);
|
||||
|
||||
mark_write_completed = true;
|
||||
|
||||
out:
|
||||
reset_write_sources (mark_write_completed);
|
||||
reset_write_sources ();
|
||||
|
||||
for (vector<CaptureInfo*>::iterator ci = capture_info.begin(); ci != capture_info.end(); ++ci) {
|
||||
delete *ci;
|
||||
@@ -1457,7 +1445,7 @@ DiskWriter::configure_io (ChanCount in, ChanCount out)
|
||||
}
|
||||
|
||||
if (record_enabled() || changed) {
|
||||
reset_write_sources (false, true);
|
||||
reset_write_sources ();
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -1472,7 +1460,7 @@ DiskWriter::use_playlist (DataType dt, std::shared_ptr<Playlist> playlist)
|
||||
return -1;
|
||||
}
|
||||
if (reset_ws) {
|
||||
reset_write_sources (false, true);
|
||||
reset_write_sources ();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ open_importable_source (const string& path, samplecnt_t samplerate, ARDOUR::SrcQ
|
||||
}
|
||||
|
||||
/* rewrap as a resampled source */
|
||||
return std::shared_ptr<ImportableSource>(new ResampledImportableSource(source, samplerate, quality));
|
||||
return std::shared_ptr<ImportableSource>(new ResampledImportableSource(source, samplerate, quality));
|
||||
} catch (...) { }
|
||||
|
||||
throw failed_constructor ();
|
||||
@@ -464,18 +464,7 @@ write_midi_data_to_new_files (Evoral::SMF* source, ImportStatus& status,
|
||||
|
||||
/* we wrote something */
|
||||
|
||||
/* try to guess at the meter, for 5/4 midi loop oddballs */
|
||||
int pulses_per_bar = 4;
|
||||
Evoral::SMF::Tempo *tempo = source->nth_tempo (0);
|
||||
if (tempo && (tempo->numerator>0) ) {
|
||||
pulses_per_bar = tempo->numerator;
|
||||
}
|
||||
|
||||
/* extend the length of the region to the end of a bar */
|
||||
const Temporal::Beats length_beats = Temporal::Beats::ticks_at_rate(t, source->ppqn());
|
||||
smfs->update_length (timepos_t (length_beats.round_up_to_multiple(Temporal::Beats(pulses_per_bar,0))));
|
||||
|
||||
smfs->mark_streaming_write_completed (source_lock);
|
||||
smfs->mark_streaming_write_completed (source_lock, timecnt_t (source->duration()));
|
||||
|
||||
/* the streaming write that we've just finished
|
||||
* only wrote data to the SMF object, which is
|
||||
@@ -486,12 +475,10 @@ write_midi_data_to_new_files (Evoral::SMF* source, ImportStatus& status,
|
||||
|
||||
smfs->load_model (source_lock, true);
|
||||
|
||||
/* Now that there is a model, we can set interpolation of parameters. */
|
||||
smfs->mark_streaming_write_completed (source_lock);
|
||||
|
||||
if (status.cancel) {
|
||||
break;
|
||||
}
|
||||
|
||||
} else {
|
||||
info << string_compose (_("Track %1 of %2 contained no usable MIDI data"), i, total_files) << endmsg;
|
||||
}
|
||||
|
||||
@@ -1165,7 +1165,6 @@ LuaProc::parse_scale_points (luabridge::LuaRef* lr)
|
||||
return std::shared_ptr<ScalePoints> ();
|
||||
}
|
||||
|
||||
int cnt = 0;
|
||||
std::shared_ptr<ScalePoints> rv = std::shared_ptr<ScalePoints>(new ScalePoints());
|
||||
luabridge::LuaRef scalepoints ((*lr)["scalepoints"]);
|
||||
|
||||
@@ -1174,7 +1173,6 @@ LuaProc::parse_scale_points (luabridge::LuaRef* lr)
|
||||
if (!i.value ().isNumber ()) { continue; }
|
||||
rv->insert(make_pair(i.key ().cast<std::string> (),
|
||||
i.value ().cast<float> ()));
|
||||
++cnt;
|
||||
}
|
||||
|
||||
if (rv->size() > 0) {
|
||||
|
||||
@@ -1244,15 +1244,15 @@ MidiModel::write_to (std::shared_ptr<MidiSource> source,
|
||||
{
|
||||
ReadLock lock (read_lock()); /* Sequence read-lock */
|
||||
|
||||
source->drop_model(source_lock);
|
||||
source->drop_model (source_lock);
|
||||
/* as of March 2022 or long before , the note mode argument does nothing */
|
||||
source->mark_streaming_midi_write_started (source_lock, Sustained);
|
||||
|
||||
for (Evoral::Sequence<TimeType>::const_iterator i = begin(TimeType(), true); i != end(); ++i) {
|
||||
source->append_event_beats(source_lock, *i);
|
||||
source->append_event_beats (source_lock, *i);
|
||||
}
|
||||
|
||||
source->mark_streaming_write_completed(source_lock);
|
||||
source->mark_streaming_write_completed (source_lock, timecnt_t (duration()));
|
||||
|
||||
/* no call to set_edited() because writing to "newsrc" doesn't remove
|
||||
* the need to write to "our own" source in ::sync_to_source()
|
||||
@@ -1284,7 +1284,7 @@ MidiModel::sync_to_source (const Source::WriterLock& source_lock)
|
||||
_midi_source.append_event_beats(source_lock, *i);
|
||||
}
|
||||
|
||||
_midi_source.mark_streaming_write_completed (source_lock);
|
||||
_midi_source.mark_streaming_write_completed (source_lock, timecnt_t (duration()));
|
||||
|
||||
set_edited (false);
|
||||
|
||||
@@ -1300,10 +1300,10 @@ MidiModel::sync_to_source (const Source::WriterLock& source_lock)
|
||||
*/
|
||||
bool
|
||||
MidiModel::write_section_to (std::shared_ptr<MidiSource> source,
|
||||
const Source::WriterLock& source_lock,
|
||||
TimeType begin_time,
|
||||
TimeType end_time,
|
||||
bool offset_events)
|
||||
const Source::WriterLock& source_lock,
|
||||
TimeType begin_time,
|
||||
TimeType end_time,
|
||||
bool offset_events)
|
||||
{
|
||||
ReadLock lock(read_lock());
|
||||
MidiNoteTracker mst;
|
||||
@@ -1348,7 +1348,11 @@ MidiModel::write_section_to (std::shared_ptr<MidiSource> source,
|
||||
}
|
||||
mst.resolve_notes (*source, source_lock, end_time);
|
||||
|
||||
source->mark_streaming_write_completed(source_lock);
|
||||
/* the new source will have precisely the length given by begin_time
|
||||
* and end_time. That might not be quite right in some cases.
|
||||
*/
|
||||
|
||||
source->mark_streaming_write_completed (source_lock, timecnt_t (end_time - begin_time));
|
||||
|
||||
set_edited(false);
|
||||
|
||||
|
||||
@@ -376,10 +376,10 @@ MidiSource::mark_streaming_write_started (const WriterLock& lock)
|
||||
void
|
||||
MidiSource::mark_midi_streaming_write_completed (const WriterLock& lock,
|
||||
Evoral::Sequence<Temporal::Beats>::StuckNoteOption option,
|
||||
Temporal::Beats end)
|
||||
Temporal::timecnt_t const & duration)
|
||||
{
|
||||
if (_model) {
|
||||
_model->end_write (option, end);
|
||||
_model->end_write (option, duration.beats());
|
||||
|
||||
/* Make captured controls discrete to play back user input exactly. */
|
||||
for (MidiModel::Controls::iterator i = _model->controls().begin(); i != _model->controls().end(); ++i) {
|
||||
@@ -395,9 +395,9 @@ MidiSource::mark_midi_streaming_write_completed (const WriterLock&
|
||||
}
|
||||
|
||||
void
|
||||
MidiSource::mark_streaming_write_completed (const WriterLock& lock)
|
||||
MidiSource::mark_streaming_write_completed (const WriterLock& lock, Temporal::timecnt_t const & duration)
|
||||
{
|
||||
mark_midi_streaming_write_completed (lock, Evoral::Sequence<Temporal::Beats>::DeleteStuckNotes);
|
||||
mark_midi_streaming_write_completed (lock, Evoral::Sequence<Temporal::Beats>::DeleteStuckNotes, duration);
|
||||
}
|
||||
|
||||
int
|
||||
|
||||
@@ -2411,7 +2411,7 @@ Session::set_sample_rate (samplecnt_t frames_per_second)
|
||||
sync_time_vars();
|
||||
|
||||
clear_clicks ();
|
||||
reset_write_sources (false);
|
||||
reset_write_sources ();
|
||||
|
||||
DiskReader::alloc_loop_declick (nominal_sample_rate());
|
||||
Location* loc = _locations->auto_loop_location ();
|
||||
@@ -6123,7 +6123,7 @@ Session::reset_native_file_format ()
|
||||
if (tr) {
|
||||
/* don't save state as we do this, there's no point */
|
||||
_state_of_the_state = StateOfTheState (_state_of_the_state | InCleanup);
|
||||
tr->reset_write_sources (false);
|
||||
tr->reset_write_sources ();
|
||||
_state_of_the_state = StateOfTheState (_state_of_the_state & ~InCleanup);
|
||||
}
|
||||
}
|
||||
@@ -6468,6 +6468,10 @@ Session::write_one_track (Track& track, samplepos_t start, samplepos_t end,
|
||||
time (&now);
|
||||
xnow = localtime (&now);
|
||||
|
||||
/* XXX we may want to round this up to the next beat or bar */
|
||||
|
||||
const timecnt_t duration (end - start);
|
||||
|
||||
for (vector<std::shared_ptr<Source> >::iterator src=srcs.begin(); src != srcs.end(); ++src) {
|
||||
std::shared_ptr<AudioFileSource> afs = std::dynamic_pointer_cast<AudioFileSource>(*src);
|
||||
std::shared_ptr<MidiSource> ms;
|
||||
@@ -6479,9 +6483,9 @@ Session::write_one_track (Track& track, samplepos_t start, samplepos_t end,
|
||||
plist.add (Properties::start, timepos_t (0));
|
||||
} else if ((ms = std::dynamic_pointer_cast<MidiSource>(*src))) {
|
||||
Source::WriterLock lock (ms->mutex());
|
||||
ms->mark_streaming_write_completed(lock);
|
||||
ms->mark_streaming_write_completed (lock, duration);
|
||||
plist.add (Properties::start, timepos_t (Beats()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* construct a whole-file region to represent the bounced material */
|
||||
|
||||
@@ -2687,14 +2687,14 @@ Session::get_sources_as_xml ()
|
||||
}
|
||||
|
||||
void
|
||||
Session::reset_write_sources (bool mark_write_complete, bool force)
|
||||
Session::reset_write_sources ()
|
||||
{
|
||||
std::shared_ptr<RouteList const> rl = routes.reader();
|
||||
for (auto const& i : *rl) {
|
||||
std::shared_ptr<Track> tr = std::dynamic_pointer_cast<Track> (i);
|
||||
if (tr) {
|
||||
_state_of_the_state = StateOfTheState (_state_of_the_state | InCleanup);
|
||||
tr->reset_write_sources(mark_write_complete, force);
|
||||
tr->reset_write_sources ();
|
||||
_state_of_the_state = StateOfTheState (_state_of_the_state & ~InCleanup);
|
||||
}
|
||||
}
|
||||
@@ -5442,7 +5442,7 @@ Session::save_as (SaveAs& saveas)
|
||||
|
||||
/* ensure that all existing tracks reset their current capture source paths
|
||||
*/
|
||||
reset_write_sources (true, true);
|
||||
reset_write_sources ();
|
||||
|
||||
/* creating new write sources marks the session as
|
||||
dirty. If the new session is empty, then
|
||||
|
||||
@@ -462,6 +462,7 @@ void
|
||||
SMFSource::update_length (timepos_t const & dur)
|
||||
{
|
||||
assert (!_length || (_length.time_domain() == dur.time_domain()));
|
||||
Evoral::SMF::set_duration (dur.beats());
|
||||
_length = dur;
|
||||
}
|
||||
|
||||
@@ -470,7 +471,7 @@ void
|
||||
SMFSource::append_event_beats (const WriterLock& lock,
|
||||
const Evoral::Event<Temporal::Beats>& ev)
|
||||
{
|
||||
if (!_writing || ev.size() == 0) {
|
||||
if (!_writing || ev.size() == 0 || ev.is_realtime()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -516,7 +517,7 @@ SMFSource::append_event_beats (const WriterLock& lock,
|
||||
const Temporal::Beats delta_time_beats = time - _last_ev_time_beats;
|
||||
const uint32_t delta_time_ticks = delta_time_beats.to_ticks(ppqn());
|
||||
|
||||
Evoral::SMF::append_event_delta(delta_time_ticks, ev.size(), ev.buffer(), event_id);
|
||||
Evoral::SMF::append_event_delta (delta_time_ticks, ev.size(), ev.buffer(), event_id);
|
||||
_last_ev_time_beats = time;
|
||||
_flags = Source::Flag (_flags & ~Empty);
|
||||
_flags = Source::Flag (_flags & ~Missing);
|
||||
@@ -528,7 +529,7 @@ SMFSource::append_event_samples (const WriterLock& lock,
|
||||
const Evoral::Event<samplepos_t>& ev,
|
||||
samplepos_t position)
|
||||
{
|
||||
if (!_writing || ev.size() == 0) {
|
||||
if (!_writing || ev.size() == 0 || ev.is_realtime()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -622,15 +623,15 @@ SMFSource::mark_streaming_midi_write_started (const WriterLock& lock, NoteMode m
|
||||
}
|
||||
|
||||
void
|
||||
SMFSource::mark_streaming_write_completed (const WriterLock& lock)
|
||||
SMFSource::mark_streaming_write_completed (const WriterLock& lm, Temporal::timecnt_t const & duration)
|
||||
{
|
||||
mark_midi_streaming_write_completed (lock, Evoral::Sequence<Temporal::Beats>::DeleteStuckNotes);
|
||||
mark_midi_streaming_write_completed (lm, Evoral::Sequence<Temporal::Beats>::DeleteStuckNotes, duration);
|
||||
}
|
||||
|
||||
void
|
||||
SMFSource::mark_midi_streaming_write_completed (const WriterLock& lm, Evoral::Sequence<Temporal::Beats>::StuckNoteOption stuck_notes_option, Temporal::Beats when)
|
||||
SMFSource::mark_midi_streaming_write_completed (const WriterLock& lm, Evoral::Sequence<Temporal::Beats>::StuckNoteOption stuck_notes_option, Temporal::timecnt_t const & duration)
|
||||
{
|
||||
MidiSource::mark_midi_streaming_write_completed (lm, stuck_notes_option, when);
|
||||
MidiSource::mark_midi_streaming_write_completed (lm, stuck_notes_option, duration);
|
||||
|
||||
if (!writable()) {
|
||||
warning << string_compose ("attempt to write to unwritable SMF file %1", _path) << endmsg;
|
||||
@@ -638,10 +639,11 @@ SMFSource::mark_midi_streaming_write_completed (const WriterLock& lm, Evoral::Se
|
||||
}
|
||||
|
||||
if (_model) {
|
||||
_model->set_edited(false);
|
||||
_model->set_edited (false);
|
||||
}
|
||||
|
||||
try {
|
||||
Evoral::SMF::set_duration (duration.beats());
|
||||
Evoral::SMF::end_write (_path);
|
||||
} catch (std::exception & e) {
|
||||
error << string_compose (_("Exception while writing %1, file may be corrupt/unusable"), _path) << endmsg;
|
||||
@@ -812,6 +814,7 @@ SMFSource::load_model_unlocked (bool force_reload)
|
||||
}
|
||||
|
||||
_num_channels = _used_channels.size();
|
||||
_length = duration();
|
||||
|
||||
eventlist.sort(compare_eventlist);
|
||||
|
||||
|
||||
@@ -592,9 +592,9 @@ Track::steal_write_source_name()
|
||||
}
|
||||
|
||||
void
|
||||
Track::reset_write_sources (bool r, bool force)
|
||||
Track::reset_write_sources ()
|
||||
{
|
||||
_disk_writer->reset_write_sources (r, force);
|
||||
_disk_writer->reset_write_sources ();
|
||||
}
|
||||
|
||||
float
|
||||
|
||||
@@ -1966,6 +1966,8 @@ AudioTrigger::captured (SlotArmInfo& ai, BufferSet&)
|
||||
|
||||
_box.queue_explict (index());
|
||||
|
||||
TriggerBox::worker->request_build_source (this, timecnt_t (data.length));
|
||||
|
||||
_armed = false;
|
||||
ArmChanged(); /* EMIT SIGNAL */
|
||||
TriggerArmChanged (this);
|
||||
@@ -2452,8 +2454,11 @@ MIDITrigger::captured (SlotArmInfo& ai, BufferSet& bufs)
|
||||
|
||||
old_rt_midibuffer = rt_midibuffer.exchange (ai.midi_buf);
|
||||
|
||||
Temporal::TempoMap::SharedPtr tmap (Temporal::TempoMap::use());
|
||||
timecnt_t dur = tmap->convert_duration (timecnt_t (ai.captured), timepos_t (ai.start_samples), Temporal::BeatTime);
|
||||
|
||||
loop_start = Temporal::Beats();
|
||||
loop_end = (*ai.midi_buf)[ai.midi_buf->size()-1].timestamp;
|
||||
loop_end = dur.beats();
|
||||
data_length = loop_end;
|
||||
_follow_length = Temporal::BBT_Offset (0, data_length.get_beats(), 0);
|
||||
set_length (timecnt_t (data_length));
|
||||
@@ -2471,6 +2476,9 @@ MIDITrigger::captured (SlotArmInfo& ai, BufferSet& bufs)
|
||||
|
||||
/* Meanwhile, build a new source and region from the data now in rt_midibuffer */
|
||||
|
||||
// std::cerr << "capture done, ask for a source of length " << loop_end.str() << std::endl;
|
||||
TriggerBox::worker->request_build_source (this, timecnt_t (loop_end));
|
||||
|
||||
_armed = false;
|
||||
ArmChanged(); /* EMIT SIGNAL */
|
||||
TriggerArmChanged (this);
|
||||
@@ -3437,6 +3445,7 @@ SlotArmInfo::SlotArmInfo (Trigger& s)
|
||||
: slot (s)
|
||||
, start_samples (0)
|
||||
, end_samples (0)
|
||||
, captured (0)
|
||||
, midi_buf (nullptr)
|
||||
, stretcher (nullptr)
|
||||
{
|
||||
@@ -3571,11 +3580,9 @@ TriggerBox::arm_from_another_thread (Trigger& slot, samplepos_t now, uint32_t ch
|
||||
ai->start_samples = t_samples;
|
||||
ai->start_beats = t_beats;
|
||||
|
||||
if (_data_type == DataType::AUDIO) {
|
||||
ai->captured = timecnt_t (timepos_t (0), timepos_t (0));
|
||||
} else {
|
||||
ai->captured = timecnt_t::from_ticks (0, timepos_t (Beats()));
|
||||
}
|
||||
// std::cerr << "Will start at " << t_beats.str() << std::endl;
|
||||
|
||||
ai->captured = 0;
|
||||
|
||||
_arm_info = ai;
|
||||
}
|
||||
@@ -3638,7 +3645,8 @@ TriggerBox::maybe_capture (BufferSet& bufs, samplepos_t start_sample, samplepos_
|
||||
t_bbt, t_beats, t_samples, tmap, ai->slot.quantization());
|
||||
ai->end_samples = t_samples;
|
||||
ai->end_beats = t_beats;
|
||||
return;
|
||||
|
||||
//std::cerr << "will end at " << t_beats.str() << " samples " << ai->start_samples << " .. " << ai->end_samples << " = " << (ai->end_samples - ai->start_samples) << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3665,12 +3673,14 @@ TriggerBox::maybe_capture (BufferSet& bufs, samplepos_t start_sample, samplepos_
|
||||
offset = ai->start_samples - start_sample;
|
||||
nframes -= offset;
|
||||
_record_state = Recording;
|
||||
// std::cerr << "Hit start @ " << ai->start_samples << " within " << start_sample << " ... " << end_sample << " offset will be " << offset << " nf " << nframes << std::endl;
|
||||
}
|
||||
|
||||
if ((ai->end_samples != 0) && (start_sample <= ai->end_samples && ai->end_samples < end_sample)) {
|
||||
/* we're going to stop */
|
||||
nframes -= (end_sample - ai->end_samples);
|
||||
reached_end = true;
|
||||
// std::cerr << "Hit end @ " << ai->end_samples << " within " << start_sample << " ... " << end_sample << " nf " << nframes << std::endl;
|
||||
}
|
||||
|
||||
/* Audio */
|
||||
@@ -3686,8 +3696,6 @@ TriggerBox::maybe_capture (BufferSet& bufs, samplepos_t start_sample, samplepos_
|
||||
AudioBuffer& buf (bufs.get_audio (n));
|
||||
ai->audio_buf.append (buf.data() + offset, nframes, n);
|
||||
}
|
||||
|
||||
ai->captured += timecnt_t (start_sample, timepos_t (ai->start_samples));
|
||||
}
|
||||
|
||||
n_buffers = bufs.count().n_midi();
|
||||
@@ -3734,11 +3742,11 @@ TriggerBox::maybe_capture (BufferSet& bufs, samplepos_t start_sample, samplepos_
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
timecnt_t dur = tmap->convert_duration (timecnt_t (nframes), timepos_t (start_sample), Temporal::BeatTime);
|
||||
ai->captured += dur;
|
||||
}
|
||||
|
||||
ai->captured += nframes;
|
||||
//std::cerr << "Captured " << nframes << " total " << ai->captured << std::endl;
|
||||
|
||||
Captured (ai->captured); /* EMIT SIGNAL */
|
||||
|
||||
if (reached_end) {
|
||||
@@ -5562,7 +5570,7 @@ TriggerBoxThread::thread_work ()
|
||||
delete_trigger (req->trigger);
|
||||
break;
|
||||
case BuildSourceAndRegion:
|
||||
build_source (req->trigger);
|
||||
build_source (req->trigger, req->duration);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -5632,10 +5640,11 @@ TriggerBoxThread::request_delete_trigger (Trigger* t)
|
||||
}
|
||||
|
||||
void
|
||||
TriggerBoxThread::request_build_source (Trigger* t)
|
||||
TriggerBoxThread::request_build_source (Trigger* t, Temporal::timecnt_t const & len)
|
||||
{
|
||||
TriggerBoxThread::Request* req = new TriggerBoxThread::Request (BuildSourceAndRegion);
|
||||
req->trigger = t;
|
||||
req->duration = len;
|
||||
queue_request (req);
|
||||
}
|
||||
|
||||
@@ -5646,20 +5655,20 @@ TriggerBoxThread::delete_trigger (Trigger* t)
|
||||
}
|
||||
|
||||
void
|
||||
TriggerBoxThread::build_source (Trigger* t)
|
||||
TriggerBoxThread::build_source (Trigger* t, Temporal::timecnt_t const & duration)
|
||||
{
|
||||
MIDITrigger* mt = dynamic_cast<MIDITrigger*> (t);
|
||||
AudioTrigger* at;
|
||||
|
||||
if (mt) {
|
||||
build_midi_source (mt);
|
||||
build_midi_source (mt, duration);
|
||||
} else if ((at = dynamic_cast<AudioTrigger*> (t))) {
|
||||
build_audio_source (at);
|
||||
build_audio_source (at, duration);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TriggerBoxThread::build_audio_source (AudioTrigger* t)
|
||||
TriggerBoxThread::build_audio_source (AudioTrigger* t, Temporal::timecnt_t const & duration)
|
||||
{
|
||||
Track* trk = static_cast<Track*> (t->box().owner());
|
||||
SourceList sources;
|
||||
@@ -5710,7 +5719,7 @@ TriggerBoxThread::build_audio_source (AudioTrigger* t)
|
||||
}
|
||||
|
||||
void
|
||||
TriggerBoxThread::build_midi_source (MIDITrigger* t)
|
||||
TriggerBoxThread::build_midi_source (MIDITrigger* t, Temporal::timecnt_t const & duration)
|
||||
{
|
||||
Track* trk = static_cast<Track*> (t->box().owner());
|
||||
std::shared_ptr<MidiSource> ms = t->box().session().create_midi_source_for_session (trk->name());
|
||||
@@ -5729,7 +5738,7 @@ TriggerBoxThread::build_midi_source (MIDITrigger* t)
|
||||
|
||||
ms->append_event_beats (lock, ev);
|
||||
}
|
||||
ms->mark_streaming_write_completed (lock);
|
||||
ms->mark_streaming_write_completed (lock, duration);
|
||||
ms->load_model (lock);
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ using namespace std;
|
||||
namespace Evoral {
|
||||
|
||||
SMF::SMF()
|
||||
: _smf (0)
|
||||
: _smf (nullptr)
|
||||
, _smf_track (0)
|
||||
, _empty (true)
|
||||
, _n_note_on_events (0)
|
||||
@@ -238,7 +238,7 @@ SMF::create(const std::string& path, int track, uint16_t ppqn)
|
||||
|
||||
_smf = smf_new();
|
||||
|
||||
if (_smf == NULL) {
|
||||
if (_smf == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -508,7 +508,7 @@ SMF::begin_write()
|
||||
}
|
||||
|
||||
void
|
||||
SMF::end_write(string const & path)
|
||||
SMF::end_write (string const & path)
|
||||
{
|
||||
Glib::Threads::Mutex::Lock lm (_smf_lock);
|
||||
|
||||
@@ -516,6 +516,9 @@ SMF::end_write(string const & path)
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
FILE* f = g_fopen (path.c_str(), "w+b");
|
||||
if (f == 0) {
|
||||
throw FileError (path);
|
||||
@@ -530,20 +533,30 @@ SMF::end_write(string const & path)
|
||||
}
|
||||
|
||||
void
|
||||
SMF::set_length (Temporal::Beats const & b)
|
||||
SMF::set_duration (Temporal::Beats const & b)
|
||||
{
|
||||
if (!_smf) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t their_pulses = b.to_ticks (ppqn());
|
||||
|
||||
for (uint16_t n = 0; n < _smf->number_of_tracks; ++n) {
|
||||
smf_track_t* trk = smf_get_track_by_number (_smf, n+1);
|
||||
if (trk) {
|
||||
smf_track_add_eot_pulses (trk, (int) floor (b.get_ticks() * ((double) ppqn() / Temporal::ticks_per_beat)));
|
||||
}
|
||||
(void) smf_track_add_eot_pulses (trk, their_pulses);
|
||||
}
|
||||
}
|
||||
|
||||
Temporal::Beats
|
||||
SMF::duration () const
|
||||
{
|
||||
if (!_smf) {
|
||||
return Temporal::Beats();
|
||||
}
|
||||
|
||||
return Temporal::Beats::ticks_at_rate (smf_get_length_pulses (_smf), ppqn());
|
||||
}
|
||||
|
||||
double
|
||||
SMF::round_to_file_precision (double val) const
|
||||
{
|
||||
|
||||
@@ -482,6 +482,7 @@ Sequence<Time>::Sequence(const TypeMap& type_map)
|
||||
, _end_iter(*this, std::numeric_limits<Time>::max(), false, std::set<Evoral::Parameter> ())
|
||||
, _lowest_note(127)
|
||||
, _highest_note(0)
|
||||
, _explicit_duration (false)
|
||||
{
|
||||
DEBUG_TRACE (DEBUG::Sequence, string_compose ("Sequence constructed: %1\n", this));
|
||||
assert(_end_iter._is_end);
|
||||
@@ -503,6 +504,8 @@ Sequence<Time>::Sequence(const Sequence<Time>& other)
|
||||
, _end_iter(*this, std::numeric_limits<Time>::max(), false, std::set<Evoral::Parameter> ())
|
||||
, _lowest_note(other._lowest_note)
|
||||
, _highest_note(other._highest_note)
|
||||
, _duration (other._duration)
|
||||
, _explicit_duration (other._explicit_duration)
|
||||
{
|
||||
for (typename Notes::const_iterator i = other._notes.begin(); i != other._notes.end(); ++i) {
|
||||
NotePtr n (new Note<Time> (**i));
|
||||
@@ -726,6 +729,8 @@ Sequence<Time>::add_note_unlocked(const NotePtr note, void* arg)
|
||||
_notes.insert (note);
|
||||
_pitches[note->channel()].insert (note);
|
||||
|
||||
update_duration_unlocked (note->time());
|
||||
|
||||
_edited = true;
|
||||
|
||||
return true;
|
||||
@@ -912,7 +917,7 @@ Sequence<Time>::remove_sysex_unlocked (const SysExPtr sysex)
|
||||
*/
|
||||
template<typename Time>
|
||||
void
|
||||
Sequence<Time>::append(const Event<Time>& ev, event_id_t evid)
|
||||
Sequence<Time>::append (const Event<Time>& ev, event_id_t evid)
|
||||
{
|
||||
WriteLock lock(write_lock());
|
||||
|
||||
@@ -924,6 +929,11 @@ Sequence<Time>::append(const Event<Time>& ev, event_id_t evid)
|
||||
return;
|
||||
}
|
||||
|
||||
if (ev.is_realtime()) {
|
||||
/* relax - we do not store these in a Sequence */
|
||||
return;
|
||||
}
|
||||
|
||||
if (ev.is_note_on() && ev.velocity() > 0) {
|
||||
append_note_on_unlocked (ev, evid);
|
||||
} else if (ev.is_note_off() || (ev.is_note_on() && ev.velocity() == 0)) {
|
||||
@@ -1090,6 +1100,7 @@ Sequence<Time>::append_control_unlocked(const Parameter& param, Time time, doubl
|
||||
std::shared_ptr<Control> c = control(param, true);
|
||||
c->list()->add (Temporal::timepos_t (time), value, true, false);
|
||||
/* XXX control events should use IDs */
|
||||
update_duration_unlocked (time);
|
||||
}
|
||||
|
||||
template<typename Time>
|
||||
@@ -1106,6 +1117,7 @@ Sequence<Time>::append_sysex_unlocked(const Event<Time>& ev, event_id_t /* evid
|
||||
std::shared_ptr< Event<Time> > event(new Event<Time>(ev, true));
|
||||
/* XXX sysex events should use IDs */
|
||||
_sysexes.insert(event);
|
||||
update_duration_unlocked (ev.time());
|
||||
}
|
||||
|
||||
template<typename Time>
|
||||
@@ -1119,6 +1131,7 @@ Sequence<Time>::append_patch_change_unlocked (const PatchChange<Time>& ev, event
|
||||
}
|
||||
|
||||
_patch_changes.insert (p);
|
||||
update_duration_unlocked (ev.time());
|
||||
}
|
||||
|
||||
template<typename Time>
|
||||
@@ -1130,6 +1143,7 @@ Sequence<Time>::add_patch_change_unlocked (PatchChangePtr p)
|
||||
}
|
||||
|
||||
_patch_changes.insert (p);
|
||||
update_duration_unlocked (p->time());
|
||||
}
|
||||
|
||||
template<typename Time>
|
||||
@@ -1141,6 +1155,33 @@ Sequence<Time>::add_sysex_unlocked (SysExPtr s)
|
||||
}
|
||||
|
||||
_sysexes.insert (s);
|
||||
update_duration_unlocked (s->time());
|
||||
}
|
||||
|
||||
template<typename Time>
|
||||
void
|
||||
Sequence<Time>::set_duration (Time const & t)
|
||||
{
|
||||
WriteLock lock(write_lock());
|
||||
_duration = t;
|
||||
_explicit_duration = true;
|
||||
}
|
||||
|
||||
template<typename Time>
|
||||
void
|
||||
Sequence<Time>::update_duration_unlocked (Time const & t, bool can_shorten)
|
||||
{
|
||||
if (_explicit_duration) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (t < _duration) {
|
||||
if (can_shorten) {
|
||||
_duration = t;
|
||||
}
|
||||
} else if (t > _duration) {
|
||||
_duration = t;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Time>
|
||||
|
||||
@@ -96,6 +96,9 @@ public:
|
||||
|
||||
int smf_format () const;
|
||||
|
||||
void set_duration (Temporal::Beats const &);
|
||||
Temporal::Beats duration() const;
|
||||
|
||||
int num_channels () const { return _num_channels; }
|
||||
typedef std::bitset<16> UsedChannels;
|
||||
UsedChannels const& used_channels () const { return _used_channels; }
|
||||
|
||||
@@ -316,6 +316,8 @@ public:
|
||||
uint8_t lowest_note() const { return _lowest_note; }
|
||||
uint8_t highest_note() const { return _highest_note; }
|
||||
|
||||
Time duration() const { return _duration; }
|
||||
void set_duration (Time const &);
|
||||
|
||||
protected:
|
||||
bool _edited;
|
||||
@@ -370,6 +372,11 @@ private:
|
||||
|
||||
uint8_t _lowest_note;
|
||||
uint8_t _highest_note;
|
||||
|
||||
Time _duration;
|
||||
bool _explicit_duration;
|
||||
|
||||
void update_duration_unlocked (Time const &, bool can_shorten = false);
|
||||
};
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user