diff --git a/libs/ardour/import.cc b/libs/ardour/import.cc index 833df4440d..79646d514f 100644 --- a/libs/ardour/import.cc +++ b/libs/ardour/import.cc @@ -363,61 +363,132 @@ write_audio_data_to_new_files (ImportableSource* source, ImportStatus& status, } static void -write_midi_data_to_new_files (Evoral::SMF* source, ImportStatus& status, - vector >& newfiles, - bool split_midi_channels) +write_midi_type0_data_to_one_file (Evoral::SMF* source, ImportStatus& status, size_t n, size_t nfiles, std::shared_ptr smfs, bool split_midi_channels, int channel) { - uint32_t buf_size = 4; - uint8_t* buf = (uint8_t*) malloc (buf_size); + uint32_t bufsize = 4; + uint8_t* buf = (uint8_t*) malloc (bufsize); + bool had_meta = false; + Evoral::event_id_t ignored_note_id; /* imported files either don't have noted IDs or we ignore them */ - status.progress = 0.0f; - - bool type0 = source->smf_format()==0; - - int total_files = newfiles.size(); + Source::WriterLock target_lock (smfs->mutex()); + smfs->mark_streaming_write_started (target_lock); + smfs->drop_model (target_lock); try { - vector >::iterator s = newfiles.begin(); - int cur_chan = 0; + source->seek_to_start(); - for (int i = 0; i < total_files; ++i) { + uint64_t t = 0; + uint32_t delta_t = 0; + uint32_t size = 0; + uint32_t written = 0; - int cur_track = i+1; //first Track of a type-1 file is metadata only. Start importing sourcefiles at Track index 1 + while (!status.cancel) { - if (split_midi_channels) { //if splitting channels we will need to fill 16x sources. empties will be disposed-of later - cur_track = 1 + (int) floor((float)i/16.f); //calculate the Track needed for this sourcefile (offset by 1) + size = bufsize; + + int ret = source->read_event (&delta_t, &size, &buf, &ignored_note_id); + + if (size > bufsize) { + bufsize = size; } - std::shared_ptr smfs = std::dynamic_pointer_cast (*s); - if (!smfs) { - continue; //should never happen. The calling code should provide exactly the number of tracks&channels we need + if (ret < 0) { // EOT + break; } - Source::WriterLock source_lock(smfs->mutex()); + t += delta_t; - smfs->drop_model (source_lock); - if (type0) { - source->seek_to_start (); - } else { - source->seek_to_track (cur_track); + /* if requested by user, each sourcefile gets only a single channel's data */ + + 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( + Evoral::MIDI_EVENT, + Temporal::Beats::ticks_at_rate(t, source->ppqn()), + size, + buf)); + + written++; + + if (status.progress < 0.99) { + status.progress += 0.01; + } + } + + if (had_meta || written) { + + /* we wrote something */ + + smfs->mark_streaming_write_completed (target_lock, timecnt_t (source->duration())); + + /* the streaming write that we've just finished + * only wrote data to the SMF object, which is + * ultimately an on-disk data structure. So now + * we pull the data back from disk to build our + * in-memory MidiModel version. + */ + + smfs->load_model (target_lock, true); + + } else { + info << string_compose (_("No usable MIDI data found for file %1 of %2"), n, nfiles) << endmsg; + } + + } catch (exception& e) { + error << string_compose (_("MIDI file could not be written (best guess: %1)"), e.what()) << endmsg; + } + + free (buf); + +} + +static void +write_midi_type1_data_to_one_file (Evoral::SMF* source, ImportStatus& status, std::shared_ptr smfs, + int track, bool split_midi_channels, int channel) +{ + uint32_t bufsize = 4; + uint8_t* buf = (uint8_t*) malloc (bufsize); + bool had_meta = false; + Evoral::event_id_t ignored_note_id; /* imported files either don't have noted IDs or we ignore them */ + + Source::WriterLock target_lock (smfs->mutex()); + smfs->mark_streaming_write_started (target_lock); + smfs->drop_model (target_lock); + + try { + + /* Check track number is legal. Remember, track 0 is metadata, so the number of + * real tracks is one less than the number of tracks reported via libsmf. + */ + + if (track >= source->num_tracks() - 1) { + return; + } + + /* Get metadata first */ + + if (source->seek_to_track (1) == 0) { /* type 1 has metadata in track 1 */ + uint64_t t = 0; uint32_t delta_t = 0; uint32_t size = 0; - bool first = true; while (!status.cancel) { - gint note_id_ignored; // imported files either don't have NoteID's or we ignore them. - size = buf_size; + size = bufsize; - int ret = source->read_event (&delta_t, &size, &buf, ¬e_id_ignored); - - if (size > buf_size) { - buf_size = size; - } + int ret = source->read_event (&delta_t, &size, &buf, &ignored_note_id); if (ret < 0) { // EOT break; @@ -425,76 +496,187 @@ write_midi_data_to_new_files (Evoral::SMF* source, ImportStatus& status, t += delta_t; - if (ret == 0) { // Meta + if (size == 0) { + /* meta event that is not for us */ continue; } - /* if requested by user, each sourcefile gets only a single channel's data */ - if (split_midi_channels) { - uint8_t type = buf[0] & 0xf0; - uint8_t chan = buf[0] & 0x0f; - if (type >= 0x80 && type <= 0xE0) { - if (chan != cur_chan) { - continue; - } - } + if (size > bufsize) { + bufsize = size; } - if (first) { - smfs->mark_streaming_write_started (source_lock); - first = false; - } + if (ret == 0) { // meta event - smfs->append_event_beats( - source_lock, - Evoral::Event( - Evoral::MIDI_EVENT, - Temporal::Beats::ticks_at_rate(t, source->ppqn()), - size, - buf)); + had_meta = true; + + smfs->append_event_beats ( + target_lock, + Evoral::Event( + Evoral::MIDI_EVENT, + Temporal::Beats::ticks_at_rate(t, source->ppqn()), + size, + buf), true); /* allow meta-events */ + } if (status.progress < 0.99) { status.progress += 0.01; } } - if (!first) { + if (had_meta) { + smfs->end_track (target_lock); + } + } - /* we wrote something */ + uint32_t written = 0; - smfs->mark_streaming_write_completed (source_lock, timecnt_t (source->duration())); + if (source->seek_to_track (track+2) == 0) { /* type 1 has metadata in track 1, so the nth real track is track n+2 */ - /* the streaming write that we've just finished - * only wrote data to the SMF object, which is - * ultimately an on-disk data structure. So now - * we pull the data back from disk to build our - * in-memory MidiModel version. - */ + uint64_t t = 0; + uint32_t delta_t = 0; + uint32_t size = 0; - smfs->load_model (source_lock, true); + while (!status.cancel) { + gint note_id_ignored; // imported files either don't have NoteID's or we ignore them. - if (status.cancel) { + size = bufsize; + + int ret = source->read_event (&delta_t, &size, &buf, ¬e_id_ignored); + + if (ret < 0) { // EOT break; } - } else { - info << string_compose (_("Track %1 of %2 contained no usable MIDI data"), i, total_files) << endmsg; - } + t += delta_t; - ++s; // next source + if (size == 0) { + /* meta event, not for us */ + continue; + } - ++cur_chan; - if (cur_chan > 15) { - cur_chan=0; + if (size > bufsize) { + bufsize = size; + } + + if (ret > 0) { // non-meta event + + /* if requested by user, each sourcefile gets only a single channel's data */ + + 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( + Evoral::MIDI_EVENT, + Temporal::Beats::ticks_at_rate(t, source->ppqn()), + size, + buf)); + + written++; + } + + if (status.progress < 0.99) { + status.progress += 0.01; + } } + } else { + std::cerr << "could not seek to " << track + 2 << std::endl; + } + + if (had_meta || written) { + + /* we wrote something */ + + smfs->mark_streaming_write_completed (target_lock, timecnt_t (source->duration())); + + /* the streaming write that we've just finished + * only wrote data to the SMF object, which is + * ultimately an on-disk data structure. So now + * we pull the data back from disk to build our + * in-memory MidiModel version. + */ + + smfs->load_model (target_lock, true); + + } else { + info << string_compose (_("Track %1 contained no usable MIDI data"), track) << endmsg; } } catch (exception& e) { error << string_compose (_("MIDI file could not be written (best guess: %1)"), e.what()) << endmsg; } - if (buf) { - free (buf); + free (buf); +} + +static void +write_midi_data_to_new_files (Evoral::SMF* source, ImportStatus& status, + vector >& newsrcs, + bool split_midi_channels) +{ + int track; + int channel; + + status.progress = 0.0f; + size_t nfiles = newsrcs.size(); + size_t n = 0; + + switch (source->smf_format()) { + case 0: + channel = 0; + + for (auto & newsrc : newsrcs) { + std::shared_ptr smfs = std::dynamic_pointer_cast (newsrc); + assert (smfs); + + write_midi_type0_data_to_one_file (source, status, n, nfiles, smfs, split_midi_channels, channel); + + if (split_midi_channels) { + channel = (channel + 1) % 16; + } + + if (status.cancel) { + break; + } + + ++n; + } + break; + + case 1: + track = 0; + channel = 0; + + for (auto & newsrc : newsrcs) { + std::shared_ptr smfs = std::dynamic_pointer_cast (newsrc); + assert (smfs); + + write_midi_type1_data_to_one_file (source, status, smfs, track, split_midi_channels, channel); + + track = (track + 1) % 16; + + if (split_midi_channels) { + channel = (channel + 1) % 16; + } + + if (status.cancel) { + break; + } + + ++n; + } + break; + + default: + error << string_compose (_("MIDI file has unsupported SMF format type %1"), source->smf_format()) << endmsg; + return; } } @@ -535,8 +717,8 @@ Session::deinterlace_midi_region (std::shared_ptr mr) /* create new file paths for 16 potential channels of midi data */ vector smf_names; - for (int i = 0; i<16; i++) { - smf_names.push_back(string_compose("-ch%1", i+1)); + for (int i = 0; i < 16; i++) { + smf_names.push_back (string_compose ("-ch%1", i+1)); } vector new_paths = get_paths_for_new_sources (false, source_path, 16, smf_names, true); @@ -654,8 +836,8 @@ Session::import_files (ImportStatus& status) /* Type0: we should prepare filenames for up to 16 channels in the file; we will throw out the empty ones later */ if (status.split_midi_channels) { num_channels = 16; - for (uint32_t i = 0; i