Skip to content
Draft
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions libs/ardour/ardour/lua_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,13 @@ namespace ARDOUR { namespace LuaAPI {
std::list<std::shared_ptr< Evoral::PatchChange<Temporal::Beats> > >
patch_change_list (std::shared_ptr<ARDOUR::MidiModel>);

std::list<std::shared_ptr< Evoral::Event<Temporal::Beats> > >
meta_event_list (std::shared_ptr<ARDOUR::MidiModel>);

/** Return the raw buffer of an EventPtr as a Lua binary string.
* This makes the meta-event (or sysex) payload accessible from Lua,
* working around the commented-out EventPtr.buffer() binding. */
std::string event_buffer (std::shared_ptr<Evoral::Event<Temporal::Beats> >);

} } /* namespace */

Expand Down
6 changes: 6 additions & 0 deletions libs/ardour/ardour/smf_source.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ class LIBARDOUR_API SMFSource : public MidiSource, public FileSource, public Evo

Temporal::Beats duration() const;

std::string smf_track_name () const { return _smf_track_name; }
std::string smf_instrument_name () const { return _smf_instrument_name; }

protected:
void close ();
void flush_midi (const WriterLock& lock);
Expand Down Expand Up @@ -118,6 +121,9 @@ class LIBARDOUR_API SMFSource : public MidiSource, public FileSource, public Evo

void load_model_unlocked (bool force_reload=false);

std::string _smf_track_name;
std::string _smf_instrument_name;

};

}; /* namespace ARDOUR */
Expand Down
22 changes: 22 additions & 0 deletions libs/ardour/lua_api.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1184,6 +1184,28 @@ LuaAPI::patch_change_list (std::shared_ptr<MidiModel> mm)
return patch_change_ptr_list;
}

std::list<std::shared_ptr<Evoral::Event<Temporal::Beats> > >
LuaAPI::meta_event_list (std::shared_ptr<MidiModel> mm)
{
typedef std::shared_ptr<Evoral::Event<Temporal::Beats> > EventPtr;

std::list<EventPtr> event_ptr_list;

for (auto const& i : mm->meta_events ()) {
event_ptr_list.push_back (i);
}
return event_ptr_list;
}

std::string
LuaAPI::event_buffer (std::shared_ptr<Evoral::Event<Temporal::Beats> > ev)
{
if (!ev || ev->size () == 0) {
return std::string ();
}
return std::string (reinterpret_cast<const char*> (ev->buffer ()), ev->size ());
}

/* ****************************************************************************/

const samplecnt_t LuaAPI::Rubberband::_bufsize = 256;
Expand Down
2 changes: 2 additions & 0 deletions libs/ardour/luabindings.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3316,6 +3316,8 @@ LuaBindings::common (lua_State* L)
.addFunction ("note_list", ARDOUR::LuaAPI::note_list)
.addFunction ("sysex_list", ARDOUR::LuaAPI::sysex_list)
.addFunction ("patch_change_list", ARDOUR::LuaAPI::patch_change_list)
.addFunction ("meta_event_list", ARDOUR::LuaAPI::meta_event_list)
.addFunction ("event_buffer", ARDOUR::LuaAPI::event_buffer)
.addCFunction ("sample_to_timecode", ARDOUR::LuaAPI::sample_to_timecode)
.addCFunction ("timecode_to_sample", ARDOUR::LuaAPI::timecode_to_sample)
.addFunction ("wait_for_process_callback", ARDOUR::LuaAPI::wait_for_process_callback)
Expand Down
6 changes: 3 additions & 3 deletions libs/ardour/midi_model.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1299,7 +1299,7 @@ MidiModel::write_to (std::shared_ptr<MidiSource> source,
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, i->is_smf_meta_event());
}

source->mark_streaming_write_completed (source_lock, timecnt_t (duration()));
Expand Down Expand Up @@ -1329,7 +1329,7 @@ MidiModel::sync_to_source (const Source::WriterLock& source_lock)
_midi_source.mark_streaming_midi_write_started (source_lock, Sustained);

for (Evoral::Sequence<TimeType>::const_iterator i = begin(TimeType(), true); i != end(); ++i) {
_midi_source.append_event_beats(source_lock, *i);
_midi_source.append_event_beats(source_lock, *i, i->is_smf_meta_event());
}

_midi_source.mark_streaming_write_completed (source_lock, timecnt_t (duration()));
Expand Down Expand Up @@ -1386,7 +1386,7 @@ MidiModel::write_section_to (std::shared_ptr<MidiSource> source,
mst.add (mev.note(), mev.channel(), mev.velocity());
source->append_event_beats(source_lock, mev);
} else {
source->append_event_beats(source_lock, mev);
source->append_event_beats(source_lock, mev, mev.is_smf_meta_event());
}
}
}
Expand Down
6 changes: 6 additions & 0 deletions libs/ardour/midi_source.cc
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,12 @@ MidiSource::midi_read (const ReaderLock& lm,

/* in range */

/* Meta events (0xFF) are model-only metadata; never send them
to MIDI output or the RT buffer. */
if (i->is_smf_meta_event()) {
continue;
}

timepos_t seb = timepos_t (session_event_beats);
samplepos_t time_samples = seb.samples();

Expand Down
59 changes: 53 additions & 6 deletions libs/ardour/smf_source.cc
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,12 @@ SMFSource::get_state () const
{
XMLNode& node = MidiSource::get_state();
node.set_property (X_("origin"), _origin);
if (!_smf_track_name.empty()) {
node.set_property (X_("smf-track-name"), _smf_track_name);
}
if (!_smf_instrument_name.empty()) {
node.set_property (X_("smf-instrument-name"), _smf_instrument_name);
}
return node;
}

Expand All @@ -629,6 +635,16 @@ SMFSource::set_state (const XMLNode& node, int version)
return -1;
}

{
std::string str;
if (node.get_property (X_("smf-track-name"), str)) {
_smf_track_name = str;
}
if (node.get_property (X_("smf-instrument-name"), str)) {
_smf_instrument_name = str;
}
}

return 0;
}

Expand Down Expand Up @@ -764,6 +780,15 @@ SMFSource::load_model_unlocked (bool force_reload)
// TODO simplify event allocation
std::list< std::pair< Evoral::Event<Temporal::Beats>*, gint > > eventlist;

/* Capture track-level metadata populated by libsmf from 0x03/0x04 meta events */
{
std::vector<std::string> tnames, inames;
track_names (tnames);
instrument_names (inames);
_smf_track_name = tnames.empty() ? "" : tnames.front();
_smf_instrument_name = inames.empty() ? "" : inames.front();
}

for (unsigned i = 1; i <= num_tracks(); ++i) {
if (seek_to_track(i)) {
continue;
Expand All @@ -777,10 +802,31 @@ SMFSource::load_model_unlocked (bool force_reload)
time += delta_t;

if (ret == 0) {
/* meta-event : did we get an event ID ? */
/* meta-event : did we get an Ardour note ID ? */
if (event_id >= 0) {
have_event_id = true;
}
/* Route text meta events (types 0x01-0x09: Text, Copyright,
Track Name, Instrument Name, Lyric, Marker, Cue Point,
Program Name, Device Name) into the model so they survive
the load -> save -> reload cycle.
All other meta types are skipped here:
0x51/0x58/0x59 are handled by the TempoMap,
0x2F (End of Track) is structural,
0x7F (Sequencer-Specific) carries Ardour note IDs.
*/
if (size >= 2 && buf[0] == 0xFF && buf[1] >= 0x01 && buf[1] <= 0x09) {
const Temporal::Beats event_time = Temporal::Beats::ticks_at_rate (time, ppqn());
eventlist.push_back (make_pair (
new Evoral::Event<Temporal::Beats> (
Evoral::MIDI_EVENT, event_time,
size, buf, true),
Evoral::next_event_id()));
}
/* Restore size to scratch buffer capacity to
minimize reallocs in subsequent read_event() calls */
scratch_size = std::max (size, scratch_size);
size = scratch_size;
continue;
}

Expand All @@ -802,7 +848,8 @@ SMFSource::load_model_unlocked (bool force_reload)
}

if (ret > 0) {
/* not a meta-event */
/* not a meta-event : ret is the actual event size */
uint32_t event_size = (uint32_t) ret;

if (!have_event_id) {
event_id = Evoral::next_event_id();
Expand All @@ -811,24 +858,24 @@ SMFSource::load_model_unlocked (bool force_reload)
#ifndef NDEBUG
std::string ss;

for (uint32_t xx = 0; xx < size; ++xx) {
for (uint32_t xx = 0; xx < event_size; ++xx) {
char b[8];
snprintf (b, sizeof (b), "0x%x ", buf[xx]);
ss += b;
}

DEBUG_TRACE (DEBUG::MidiSourceIO, string_compose ("SMF %7 load model delta %1, time %2, size %3 buf %4, id %6\n",
delta_t, time, size, ss, event_id, name()));
delta_t, time, event_size, ss, event_id, name()));
#endif

eventlist.push_back(make_pair (
new Evoral::Event<Temporal::Beats> (
Evoral::MIDI_EVENT, event_time,
size, buf, true)
event_size, buf, true)
, event_id));

// Set size to max capacity to minimize allocs in read_event
scratch_size = std::max(size, scratch_size);
scratch_size = std::max(event_size, scratch_size);
size = scratch_size;

assert (!_length || (_length.time_domain() == Temporal::BeatTime));
Expand Down
39 changes: 20 additions & 19 deletions libs/evoral/SMF.cc
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems incorrect to me that the return 0 statements here are deleted. This value is used by the caller to indicate the specific condition of an Ardour-specific sequencer-specific event.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't it the idea, if we want the meta-event to be returned so it is in the track, by copying it on line 377?

memcpy(*buf, event->midi_buffer, size_t(event_size));

There is still later on line 386:

		if (is_meta) {
			return 0;
		}

left for the caller interpretation.
Actually, looking at the change you made in 1ee76f4 shows that it is actually the size == 0 which is used to discard the meta-event, not the the return value.

Your comment made be look at Type 0 track and there the meta-event was not kept. I have updated it a new commit.

Original file line number Diff line number Diff line change
Expand Up @@ -358,16 +358,9 @@ SMF::read_event(uint32_t* delta_t, uint32_t* bufsize, uint8_t** buf, event_id_t*

if (smf_extract_vlq (&event->midi_buffer[4+lenlen], event->midi_buffer_length-(4+lenlen), &id, &idlen) == 0) {
*note_id = id;
return 0;
}
}
}

/* We do not return sequencer-specific events
* that are not known to us.
*/

return 0;
}

is_meta = true;
Expand Down Expand Up @@ -433,6 +426,8 @@ SMF::is_meta (uint8_t const* buf, uint32_t size)
case 0x05: /* lyric */
case 0x06: /* marker */
case 0x07: /* cue point */
case 0x08: /* program name (RP-019) */
case 0x09: /* device name (RP-019) */
case 0x20: /* channel prefix */
case 0x2f: /* end of track */
case 0x51: /* set tempo */
Expand Down Expand Up @@ -795,18 +790,24 @@ SMF::load_markers ()
name = "Cue Point:";
allow_empty = true;
break;
case 0x01: // "Text:"
/* fallthtough */
case 0x02: // "Copyright:"
/* fallthtough */
case 0x03: // "Sequence/Track Name:"
/* fallthtough */
case 0x04: // "Instrument:"
/* fallthtough */
case 0x08: // "Program Name:"
/* fallthtough */
case 0x09: // "Device (Port) Name:"
/* fallthtough */
case 0x01:
name = "Text:";
break;
case 0x02:
name = "Copyright:";
break;
case 0x03:
name = "Track Name:";
break;
case 0x04:
name = "Instrument:";
break;
case 0x08:
name = "Program Name:";
break;
case 0x09:
name = "Device Name:";
break;
default:
continue;
}
Expand Down
Loading