Skip to content
Draft
Show file tree
Hide file tree
Changes from all 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
51 changes: 31 additions & 20 deletions libs/ardour/import.cc
Original file line number Diff line number Diff line change
Expand Up @@ -405,34 +405,45 @@ write_midi_type0_data_to_one_file (Evoral::SMF* source, ImportStatus& status, si
continue;
}

if (ret == 0) {
/* set note id, but we ignored it */
continue;
}

if (size > bufsize) {
bufsize = size;
}

/* if requested by user, each sourcefile gets only a single channel's data */
if (ret > 0) {
/* regular MIDI event */

/* if requested by user, each sourcefile gets only a single channel's data */

if (ret > 0 && split_midi_channels) {
uint8_t type = buf[0] & 0xf0;
uint8_t chan = buf[0] & 0x0f;
if (type >= 0x80 && type <= 0xE0) {
if (chan != channel) {
continue;
if (split_midi_channels) {
uint8_t type = buf[0] & 0xf0;
uint8_t chan = buf[0] & 0x0f;
if (type >= 0x80 && type <= 0xE0) {
if (chan != channel) {
continue;
}
}
}
}

smfs->append_event_beats (
target_lock,
Evoral::Event<Temporal::Beats>(
Evoral::MIDI_EVENT,
Temporal::Beats::ticks_at_rate(t, source->ppqn()),
size,
buf));
smfs->append_event_beats (
target_lock,
Evoral::Event<Temporal::Beats>(
Evoral::MIDI_EVENT,
Temporal::Beats::ticks_at_rate(t, source->ppqn()),
size,
buf));

} else if (ret == 0) {
/* meta event — write text types (0x01-0x09)
through to preserve them in the SMF file */

smfs->append_event_beats (
target_lock,
Evoral::Event<Temporal::Beats>(
Evoral::MIDI_EVENT,
Temporal::Beats::ticks_at_rate(t, source->ppqn()),
size,
buf), true); /* allow meta-events */
}

written++;

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