add the concept of a length that is data-independent to MIDI files (libs)

This commit is contained in:
Paul Davis
2024-10-24 11:43:30 -06:00
parent f8ddc827c7
commit ebcc6b8250
25 changed files with 182 additions and 118 deletions

View File

@@ -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);

View File

@@ -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; }

View File

@@ -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);

View File

@@ -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();

View File

@@ -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);

View File

@@ -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);

View File

@@ -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() {}

View File

@@ -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 ();

View File

@@ -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 {

View File

@@ -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

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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) {

View File

@@ -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);

View File

@@ -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

View File

@@ -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 */

View File

@@ -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

View File

@@ -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);

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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
{

View File

@@ -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>

View File

@@ -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; }

View File

@@ -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);
};