From 39661732c3035b355ae41e1cc712fd5ab933c7c0 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Wed, 30 Oct 2024 12:31:33 -0600 Subject: [PATCH] modify how Evoral::SMF (maybe) writes an EOT event into an SMF every write pass deletes existing tracks, which means it also deletes any existing EOT event. Rather than try to replicate the _length value() that is kept in a Source object in the SMF object, add a virtual method to SMF that returns the _length value (or std::numeric_limits::max() if not set). If the _length value is not the max, we will add EOT events to each track (usually just one) right before writing to disk. --- libs/ardour/ardour/export_smf_writer.h | 1 + libs/ardour/ardour/smf_source.h | 2 ++ libs/ardour/export_smf_writer.cc | 6 +++++ libs/ardour/smf_source.cc | 9 +++++-- libs/evoral/SMF.cc | 34 +++++++++++--------------- libs/evoral/evoral/SMF.h | 8 +++--- 6 files changed, 35 insertions(+), 25 deletions(-) diff --git a/libs/ardour/ardour/export_smf_writer.h b/libs/ardour/ardour/export_smf_writer.h index ea58b4b099..e3d5fdd3bd 100644 --- a/libs/ardour/ardour/export_smf_writer.h +++ b/libs/ardour/ardour/export_smf_writer.h @@ -38,6 +38,7 @@ public: ~ExportSMFWriter (); int init (std::string const& path, samplepos_t); + Temporal::Beats duration() const; void process (MidiBuffer const&, sampleoffset_t, samplecnt_t, bool); diff --git a/libs/ardour/ardour/smf_source.h b/libs/ardour/ardour/smf_source.h index 19cfcc1bef..b40aab9dd8 100644 --- a/libs/ardour/ardour/smf_source.h +++ b/libs/ardour/ardour/smf_source.h @@ -81,6 +81,8 @@ public: void render (const ReaderLock& lock, Evoral::EventSink& dst); + Temporal::Beats duration() const; + protected: void close (); void flush_midi (const WriterLock& lock); diff --git a/libs/ardour/export_smf_writer.cc b/libs/ardour/export_smf_writer.cc index 16aeff975b..cfdbe2d1d4 100644 --- a/libs/ardour/export_smf_writer.cc +++ b/libs/ardour/export_smf_writer.cc @@ -95,3 +95,9 @@ ExportSMFWriter::process (MidiBuffer const& buf, sampleoffset_t off, samplecnt_t _pos += n_samples; } } + +Temporal::Beats +ExportSMFWriter::duration() const +{ + return std::numeric_limits::max (); +} diff --git a/libs/ardour/smf_source.cc b/libs/ardour/smf_source.cc index a08e7ac4e0..488352e193 100644 --- a/libs/ardour/smf_source.cc +++ b/libs/ardour/smf_source.cc @@ -462,10 +462,15 @@ void SMFSource::update_length (timepos_t const & dur) { assert (!_length || (_length.time_domain() == dur.time_domain())); - Evoral::SMF::set_duration (dur.beats()); _length = dur; } +Temporal::Beats +SMFSource::duration() const +{ + return _length.beats (); +} + /** Append an event with a timestamp in beats */ void SMFSource::append_event_beats (const WriterLock& lock, @@ -643,7 +648,7 @@ SMFSource::mark_midi_streaming_write_completed (const WriterLock& lm, Evoral::Se } try { - Evoral::SMF::set_duration (duration.beats()); + update_length (duration.distance()); Evoral::SMF::end_write (_path); } catch (std::exception & e) { error << string_compose (_("Exception while writing %1, file may be corrupt/unusable"), _path) << endmsg; diff --git a/libs/evoral/SMF.cc b/libs/evoral/SMF.cc index caadc2d918..42718777cf 100644 --- a/libs/evoral/SMF.cc +++ b/libs/evoral/SMF.cc @@ -516,15 +516,24 @@ SMF::end_write (string const & path) return; } - - - FILE* f = g_fopen (path.c_str(), "w+b"); if (f == 0) { throw FileError (path); } - if (smf_save(_smf, f) != 0) { + Temporal::Beats b = duration(); + + if (b != std::numeric_limits::max()) { + + int64_t their_pulses = b.to_ticks (_smf->ppqn); + + for (uint16_t n = 0; n < _smf->number_of_tracks; ++n) { + smf_track_t* trk = smf_get_track_by_number (_smf, n+1); + (void) smf_track_add_eot_pulses (trk, their_pulses); + } + } + + if (smf_save (_smf, f) != 0) { fclose(f); throw FileError (path); } @@ -532,23 +541,8 @@ SMF::end_write (string const & path) fclose(f); } -void -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); - (void) smf_track_add_eot_pulses (trk, their_pulses); - } -} - Temporal::Beats -SMF::duration () const +SMF::file_duration () const { if (!_smf) { return Temporal::Beats(); diff --git a/libs/evoral/evoral/SMF.h b/libs/evoral/evoral/SMF.h index 57e58db09a..fd664df72a 100644 --- a/libs/evoral/evoral/SMF.h +++ b/libs/evoral/evoral/SMF.h @@ -27,6 +27,8 @@ #include #include +#include "temporal/beats.h" + #include "evoral/visibility.h" #include "evoral/types.h" @@ -39,7 +41,6 @@ typedef smf_tempo_struct smf_tempo_t; namespace Temporal { class TempoMap; - class Beats; } namespace Evoral { @@ -70,6 +71,8 @@ public: SMF(); virtual ~SMF(); + virtual Temporal::Beats duration() const { return std::numeric_limits::max(); } + static bool test(const std::string& path); int open (const std::string& path, int track = 1, bool scan = true); // XXX 19200 = 10 * Temporal::ticks_per_beat @@ -96,8 +99,7 @@ public: int smf_format () const; - void set_duration (Temporal::Beats const &); - Temporal::Beats duration() const; + Temporal::Beats file_duration() const; int num_channels () const { return _num_channels; } typedef std::bitset<16> UsedChannels;