13
0

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<Beats>::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.
This commit is contained in:
Paul Davis 2024-10-30 12:31:33 -06:00
parent a5dac1578e
commit 39661732c3
6 changed files with 35 additions and 25 deletions

View File

@ -38,6 +38,7 @@ public:
~ExportSMFWriter (); ~ExportSMFWriter ();
int init (std::string const& path, samplepos_t); int init (std::string const& path, samplepos_t);
Temporal::Beats duration() const;
void process (MidiBuffer const&, sampleoffset_t, samplecnt_t, bool); void process (MidiBuffer const&, sampleoffset_t, samplecnt_t, bool);

View File

@ -81,6 +81,8 @@ public:
void render (const ReaderLock& lock, Evoral::EventSink<Temporal::Beats>& dst); void render (const ReaderLock& lock, Evoral::EventSink<Temporal::Beats>& dst);
Temporal::Beats duration() const;
protected: protected:
void close (); void close ();
void flush_midi (const WriterLock& lock); void flush_midi (const WriterLock& lock);

View File

@ -95,3 +95,9 @@ ExportSMFWriter::process (MidiBuffer const& buf, sampleoffset_t off, samplecnt_t
_pos += n_samples; _pos += n_samples;
} }
} }
Temporal::Beats
ExportSMFWriter::duration() const
{
return std::numeric_limits<Temporal::Beats>::max ();
}

View File

@ -462,10 +462,15 @@ void
SMFSource::update_length (timepos_t const & dur) SMFSource::update_length (timepos_t const & dur)
{ {
assert (!_length || (_length.time_domain() == dur.time_domain())); assert (!_length || (_length.time_domain() == dur.time_domain()));
Evoral::SMF::set_duration (dur.beats());
_length = dur; _length = dur;
} }
Temporal::Beats
SMFSource::duration() const
{
return _length.beats ();
}
/** Append an event with a timestamp in beats */ /** Append an event with a timestamp in beats */
void void
SMFSource::append_event_beats (const WriterLock& lock, SMFSource::append_event_beats (const WriterLock& lock,
@ -643,7 +648,7 @@ SMFSource::mark_midi_streaming_write_completed (const WriterLock& lm, Evoral::Se
} }
try { try {
Evoral::SMF::set_duration (duration.beats()); update_length (duration.distance());
Evoral::SMF::end_write (_path); Evoral::SMF::end_write (_path);
} catch (std::exception & e) { } catch (std::exception & e) {
error << string_compose (_("Exception while writing %1, file may be corrupt/unusable"), _path) << endmsg; error << string_compose (_("Exception while writing %1, file may be corrupt/unusable"), _path) << endmsg;

View File

@ -516,15 +516,24 @@ SMF::end_write (string const & path)
return; return;
} }
FILE* f = g_fopen (path.c_str(), "w+b"); FILE* f = g_fopen (path.c_str(), "w+b");
if (f == 0) { if (f == 0) {
throw FileError (path); throw FileError (path);
} }
if (smf_save(_smf, f) != 0) { Temporal::Beats b = duration();
if (b != std::numeric_limits<Temporal::Beats>::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); fclose(f);
throw FileError (path); throw FileError (path);
} }
@ -532,23 +541,8 @@ SMF::end_write (string const & path)
fclose(f); 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 Temporal::Beats
SMF::duration () const SMF::file_duration () const
{ {
if (!_smf) { if (!_smf) {
return Temporal::Beats(); return Temporal::Beats();

View File

@ -27,6 +27,8 @@
#include <memory> #include <memory>
#include <set> #include <set>
#include "temporal/beats.h"
#include "evoral/visibility.h" #include "evoral/visibility.h"
#include "evoral/types.h" #include "evoral/types.h"
@ -39,7 +41,6 @@ typedef smf_tempo_struct smf_tempo_t;
namespace Temporal { namespace Temporal {
class TempoMap; class TempoMap;
class Beats;
} }
namespace Evoral { namespace Evoral {
@ -70,6 +71,8 @@ public:
SMF(); SMF();
virtual ~SMF(); virtual ~SMF();
virtual Temporal::Beats duration() const { return std::numeric_limits<Temporal::Beats>::max(); }
static bool test(const std::string& path); static bool test(const std::string& path);
int open (const std::string& path, int track = 1, bool scan = true); int open (const std::string& path, int track = 1, bool scan = true);
// XXX 19200 = 10 * Temporal::ticks_per_beat // XXX 19200 = 10 * Temporal::ticks_per_beat
@ -96,8 +99,7 @@ public:
int smf_format () const; int smf_format () const;
void set_duration (Temporal::Beats const &); Temporal::Beats file_duration() const;
Temporal::Beats duration() const;
int num_channels () const { return _num_channels; } int num_channels () const { return _num_channels; }
typedef std::bitset<16> UsedChannels; typedef std::bitset<16> UsedChannels;