From 5156998e6e2536c9c713974d3ae719a5d2ef5c7f Mon Sep 17 00:00:00 2001 From: David Robillard Date: Sat, 11 Aug 2007 06:17:42 +0000 Subject: [PATCH] Saving of edited MIDI data to disk (on session save). Seems to be a pretty random problem with note duration restoring though... git-svn-id: svn://localhost/ardour2/trunk@2290 d708f5d6-7413-0410-9779-e7cbd77b26cf --- gtk2_ardour/editor_mouse.cc | 5 -- gtk2_ardour/midi_region_view.cc | 13 ++++ gtk2_ardour/midi_region_view.h | 1 + libs/ardour/ardour/midi_model.h | 12 +++- libs/ardour/ardour/midi_region.h | 2 + libs/ardour/ardour/midi_source.h | 7 +++ libs/ardour/ardour/region.h | 11 ++-- libs/ardour/ardour/smf_source.h | 12 ++-- libs/ardour/ardour/source.h | 4 +- libs/ardour/audio_diskstream.cc | 1 - libs/ardour/filter.cc | 2 +- libs/ardour/midi_diskstream.cc | 2 +- libs/ardour/midi_model.cc | 101 ++++++++++++++++++++++++------- libs/ardour/midi_region.cc | 22 +++++++ libs/ardour/midi_source.cc | 42 ++++++++++++- libs/ardour/smf_source.cc | 82 ++++++++++++++----------- 16 files changed, 235 insertions(+), 84 deletions(-) diff --git a/gtk2_ardour/editor_mouse.cc b/gtk2_ardour/editor_mouse.cc index c5a1768496..e8bc98a73c 100644 --- a/gtk2_ardour/editor_mouse.cc +++ b/gtk2_ardour/editor_mouse.cc @@ -375,11 +375,6 @@ Editor::set_midi_edit_mode (MidiEditMode m, bool force) break; } - if (mouse_mode == MouseNote) - midi_toolbar_frame.show(); - else - midi_toolbar_frame.hide(); - ignore_midi_edit_mode_toggle = false; set_midi_edit_cursor (current_midi_edit_mode()); diff --git a/gtk2_ardour/midi_region_view.cc b/gtk2_ardour/midi_region_view.cc index 241eb03091..e96a65d893 100644 --- a/gtk2_ardour/midi_region_view.cc +++ b/gtk2_ardour/midi_region_view.cc @@ -122,6 +122,8 @@ MidiRegionView::init (Gdk::Color& basic_color, bool wfd) } _model->ContentsChanged.connect(sigc::mem_fun(this, &MidiRegionView::redisplay_model)); } + + midi_region()->midi_source(0)->Switched.connect(sigc::mem_fun(this, &MidiRegionView::switch_source)); group->signal_event().connect (mem_fun (this, &MidiRegionView::canvas_event), false); @@ -393,6 +395,8 @@ void MidiRegionView::redisplay_model() { clear_events(); + + //cerr << "Redisplaying model " << _model << endl; if (_model) { begin_write(); @@ -758,4 +762,13 @@ MidiRegionView::note_entered(ArdourCanvas::CanvasMidiEvent* ev) note_selected(ev, true); } } + + +void +MidiRegionView::switch_source(boost::shared_ptr src) +{ + boost::shared_ptr msrc = boost::dynamic_pointer_cast(src); + if (msrc) + display_model(msrc->model()); +} diff --git a/gtk2_ardour/midi_region_view.h b/gtk2_ardour/midi_region_view.h index a15ce31e92..22196846c5 100644 --- a/gtk2_ardour/midi_region_view.h +++ b/gtk2_ardour/midi_region_view.h @@ -161,6 +161,7 @@ class MidiRegionView : public RegionView private: void clear_events(); + void switch_source(boost::shared_ptr src); bool canvas_event(GdkEvent* ev); bool note_canvas_event(GdkEvent* ev); diff --git a/libs/ardour/ardour/midi_model.h b/libs/ardour/ardour/midi_model.h index f1fcabfb1f..63b87c9683 100644 --- a/libs/ardour/ardour/midi_model.h +++ b/libs/ardour/ardour/midi_model.h @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -31,6 +32,7 @@ namespace ARDOUR { class Session; +class MidiSource; /** This is a slightly higher level (than MidiBuffer) model of MIDI note data. @@ -143,14 +145,15 @@ public: MidiModel::DeltaCommand* new_delta_command(const std::string name="midi edit"); void apply_command(Command* cmd); - bool write_new_source(const std::string& path); + bool edited() const { return _edited; } + bool write_to(boost::shared_ptr source); sigc::signal ContentsChanged; private: friend class DeltaCommand; - void add_note(const Note& note); - void remove_note(const Note& note); + void add_note_unlocked(const Note& note); + void remove_note_unlocked(const Note& note); bool is_sorted() const; @@ -159,12 +162,15 @@ private: Session& _session; + Glib::RWLock _lock; + Notes _notes; NoteMode _note_mode; typedef std::vector WriteNotes; WriteNotes _write_notes; bool _writing; + bool _edited; // note state for read(): diff --git a/libs/ardour/ardour/midi_region.h b/libs/ardour/ardour/midi_region.h index ade839ab3e..294c2d3291 100644 --- a/libs/ardour/ardour/midi_region.h +++ b/libs/ardour/ardour/midi_region.h @@ -90,6 +90,8 @@ class MidiRegion : public Region void recompute_at_start (); void recompute_at_end (); + void switch_source(boost::shared_ptr source); + protected: int set_live_state (const XMLNode&, Change&, bool send); diff --git a/libs/ardour/ardour/midi_source.h b/libs/ardour/ardour/midi_source.h index 2de31f879e..088175ab75 100644 --- a/libs/ardour/ardour/midi_source.h +++ b/libs/ardour/ardour/midi_source.h @@ -51,12 +51,17 @@ class MidiSource : public Source virtual nframes_t read (MidiRingBuffer& dst, nframes_t start, nframes_t cnt, nframes_t stamp_offset) const; virtual nframes_t write (MidiRingBuffer& src, nframes_t cnt); + virtual void append_event_unlocked(const MidiEvent& ev) = 0; + + virtual void flush() {} virtual void mark_for_remove() = 0; virtual void mark_streaming_midi_write_started (NoteMode mode); virtual void mark_streaming_write_started (); virtual void mark_streaming_write_completed (); + void set_timeline_position (nframes_t when) { _timeline_position = when; } + virtual void session_saved(); string captured_for() const { return _captured_for; } @@ -80,6 +85,7 @@ class MidiSource : public Source virtual bool model_loaded() const { return _model_loaded; } boost::shared_ptr model() { return _model; } + void set_model(boost::shared_ptr m) { _model = m; _model_loaded = true; } protected: virtual nframes_t read_unlocked (MidiRingBuffer& dst, nframes_t start, nframes_t cnt, nframes_t stamp_offset) const = 0; @@ -87,6 +93,7 @@ class MidiSource : public Source mutable Glib::Mutex _lock; string _captured_for; + uint64_t _timeline_position; mutable uint32_t _read_data_count; ///< modified in read() mutable uint32_t _write_data_count; ///< modified in write() diff --git a/libs/ardour/ardour/region.h b/libs/ardour/ardour/region.h index e6b799fdcf..496dc7874e 100644 --- a/libs/ardour/ardour/region.h +++ b/libs/ardour/ardour/region.h @@ -237,10 +237,10 @@ class Region : public PBD::StatefulDestructible, public boost::enable_shared_fro string _name; DataType _type; Flag _flags; - nframes_t _start; - nframes_t _length; - nframes_t _position; - nframes_t _sync_position; + nframes_t _start; + nframes_t _length; + nframes_t _position; + nframes_t _sync_position; layer_t _layer; mutable RegionEditState _first_edit; int _frozen; @@ -248,10 +248,11 @@ class Region : public PBD::StatefulDestructible, public boost::enable_shared_fro Change _pending_changed; uint64_t _last_layer_op; ///< timestamp Glib::Mutex _lock; - boost::weak_ptr _playlist; SourceList _sources; /** Used when timefx are applied, so we can always use the original source */ SourceList _master_sources; + + boost::weak_ptr _playlist; }; } /* namespace ARDOUR */ diff --git a/libs/ardour/ardour/smf_source.h b/libs/ardour/ardour/smf_source.h index fde91ed946..c852266e9c 100644 --- a/libs/ardour/ardour/smf_source.h +++ b/libs/ardour/ardour/smf_source.h @@ -57,9 +57,9 @@ class SMFSource : public MidiSource { // FIXME and thus are useless for MIDI.. but make MidiDiskstream compile easier! :) virtual nframes_t last_capture_start_frame() const { return 0; } - virtual void mark_capture_start (nframes_t) {} - virtual void mark_capture_end () {} - virtual void clear_capture_marks() {} + virtual void mark_capture_start (nframes_t) {} + virtual void mark_capture_end () {} + virtual void clear_capture_marks() {} bool set_name (const std::string& newname) { return (set_source_name(newname, false) == 0); } int set_source_name (string newname, bool destructive); @@ -69,9 +69,12 @@ class SMFSource : public MidiSource { void set_allow_remove_if_empty (bool yn); void mark_for_remove(); - int update_header (nframes_t when, struct tm&, time_t); + void append_event_unlocked(const MidiEvent& ev); + int flush_header (); int flush_footer (); + + void flush() { flush_header(); flush_footer(); } int move_to_trash (const string trash_dir_name); @@ -120,7 +123,6 @@ class SMFSource : public MidiSource { Flag _flags; string _take_id; bool _allow_remove_if_empty; - uint64_t _timeline_position; FILE* _fd; double _last_ev_time; // last frame time written, relative to source start uint32_t _track_size; diff --git a/libs/ardour/ardour/source.h b/libs/ardour/ardour/source.h index bbb7d798b2..6f323dd878 100644 --- a/libs/ardour/ardour/source.h +++ b/libs/ardour/ardour/source.h @@ -71,8 +71,8 @@ class Source : public SessionObject uint32_t used() const; - - static sigc::signal SourceCreated; + static sigc::signal SourceCreated; + sigc::signal > Switched; protected: void update_length (nframes_t pos, nframes_t cnt); diff --git a/libs/ardour/audio_diskstream.cc b/libs/ardour/audio_diskstream.cc index 135aa4747f..7d95b78706 100644 --- a/libs/ardour/audio_diskstream.cc +++ b/libs/ardour/audio_diskstream.cc @@ -31,7 +31,6 @@ #include #include -#include #include #include #include diff --git a/libs/ardour/filter.cc b/libs/ardour/filter.cc index f655f71668..208377cc4a 100644 --- a/libs/ardour/filter.cc +++ b/libs/ardour/filter.cc @@ -87,7 +87,7 @@ Filter::finish (boost::shared_ptr region, SourceList& nsrcs) boost::shared_ptr smfs = boost::dynamic_pointer_cast(*si); if (smfs) { - smfs->update_header (region->position(), *now, xnow); + smfs->set_timeline_position (region->position()); smfs->flush_footer (); } } diff --git a/libs/ardour/midi_diskstream.cc b/libs/ardour/midi_diskstream.cc index 493a29f4ce..54f4c10698 100644 --- a/libs/ardour/midi_diskstream.cc +++ b/libs/ardour/midi_diskstream.cc @@ -1009,7 +1009,7 @@ MidiDiskstream::transport_stopped (struct tm& when, time_t twhen, bool abort_cap /* figure out the name for this take */ srcs.push_back (_write_source); - _write_source->update_header (capture_info.front()->start, when, twhen); + _write_source->set_timeline_position (capture_info.front()->start); _write_source->set_captured_for (_name); string whole_file_region_name; diff --git a/libs/ardour/midi_model.cc b/libs/ardour/midi_model.cc index c68dcff6b6..34c345254b 100644 --- a/libs/ardour/midi_model.cc +++ b/libs/ardour/midi_model.cc @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -102,6 +103,7 @@ MidiModel::MidiModel(Session& s, size_t size) , _notes(size) , _note_mode(Sustained) , _writing(false) + , _edited(false) , _active_notes(LaterNoteEndComparator()) { } @@ -116,7 +118,7 @@ MidiModel::read (MidiRingBuffer& dst, nframes_t start, nframes_t nframes, nframe { size_t read_events = 0; - //cerr << "MM READ @ " << start << " + " << nframes << endl; + cerr << "MM READ @ " << start << " + " << nframes << endl; /* FIXME: cache last lookup value to avoid the search */ @@ -125,13 +127,13 @@ MidiModel::read (MidiRingBuffer& dst, nframes_t start, nframes_t nframes, nframe /* FIXME: cache last lookup value to avoid the search */ for (Notes::const_iterator n = _notes.begin(); n != _notes.end(); ++n) { - //cerr << "MM ON " << n->time() << endl; + cerr << "MM NOTE " << n->time() << endl; while ( ! _active_notes.empty() ) { const Note* const earliest_off = _active_notes.top(); - const MidiEvent& ev = earliest_off->off_event(); - if (ev.time() < start + nframes && ev.time() <= n->time()) { - dst.write(ev.time() + stamp_offset, ev.size(), ev.buffer()); + const MidiEvent& off_ev = earliest_off->off_event(); + if (off_ev.time() < start + nframes && off_ev.time() <= n->time()) { + dst.write(off_ev.time() + stamp_offset, off_ev.size(), off_ev.buffer()); _active_notes.pop(); ++read_events; } else { @@ -144,8 +146,8 @@ MidiModel::read (MidiRingBuffer& dst, nframes_t start, nframes_t nframes, nframe // Note on if (n->time() >= start) { - const MidiEvent& ev = n->on_event(); - dst.write(ev.time() + stamp_offset, ev.size(), ev.buffer()); + const MidiEvent& on_ev = n->on_event(); + dst.write(on_ev.time() + stamp_offset, on_ev.size(), on_ev.buffer()); _active_notes.push(&(*n)); ++read_events; } @@ -234,6 +236,8 @@ MidiModel::append(const MidiBuffer& buf) { assert(_writing); + _lock.writer_lock(); + for (MidiBuffer::const_iterator i = buf.begin(); i != buf.end(); ++i) { const MidiEvent& ev = *i; @@ -244,6 +248,8 @@ MidiModel::append(const MidiBuffer& buf) else if (ev.type() == MIDI_CMD_NOTE_OFF) append_note_off(ev.time(), ev.note()); } + + _lock.writer_unlock(); } @@ -311,16 +317,18 @@ MidiModel::append_note_off(double time, uint8_t note_num) void -MidiModel::add_note(const Note& note) +MidiModel::add_note_unlocked(const Note& note) { + cerr << "MidiModel " << this << " add note " << (int)note.note() << " @ " << note.time() << endl; Notes::iterator i = upper_bound(_notes.begin(), _notes.end(), note, note_time_comparator); _notes.insert(i, note); } void -MidiModel::remove_note(const Note& note) +MidiModel::remove_note_unlocked(const Note& note) { + cerr << "MidiModel " << this << " remove note " << (int)note.note() << " @ " << note.time() << endl; Notes::iterator n = find(_notes.begin(), _notes.end(), note); if (n != _notes.end()) _notes.erase(n); @@ -367,6 +375,7 @@ MidiModel::apply_command(Command* cmd) (*cmd)(); assert(is_sorted()); _session.commit_reversible_command(cmd); + _edited = true; } @@ -399,11 +408,15 @@ MidiModel::DeltaCommand::operator()() // This could be made much faster by using a priority_queue for added and // removed notes (or sort here), and doing a single iteration over _model + _model._lock.writer_lock(); + for (std::list::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) - _model.add_note(*i); + _model.add_note_unlocked(*i); for (std::list::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) - _model.remove_note(*i); + _model.remove_note_unlocked(*i); + + _model._lock.writer_unlock(); _model.ContentsChanged(); /* EMIT SIGNAL */ } @@ -414,31 +427,73 @@ MidiModel::DeltaCommand::undo() { // This could be made much faster by using a priority_queue for added and // removed notes (or sort here), and doing a single iteration over _model + + _model._lock.writer_lock(); for (std::list::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) - _model.remove_note(*i); + _model.remove_note_unlocked(*i); for (std::list::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) - _model.add_note(*i); + _model.add_note_unlocked(*i); + + _model._lock.writer_unlock(); _model.ContentsChanged(); /* EMIT SIGNAL */ } bool -MidiModel::write_new_source(const std::string& path) +MidiModel::write_to(boost::shared_ptr source) { - cerr << "Writing model to " << path << endl; - -#if 0 - SourceFactory::createWritable (region->data_type(), session, path, false, session.frame_rate()); - - catch (failed_constructor& err) { - error << string_compose (_("filter: error creating new file %1 (%2)"), path, strerror (errno)) << endmsg; - return -1; + cerr << "Writing model to " << source->name() << endl; + + /* This could be done using a temporary MidiRingBuffer and using + * MidiModel::read and MidiSource::write, but this is more efficient + * and doesn't require any buffer size assumptions (ie it's worth + * the code duplication). + * + * This is also different from read in that note off events are written + * regardless of the track mode. This is so the user can switch a + * recorded track (with note durations from some instrument) to percussive, + * save, reload, then switch it back to sustained preserving the original + * note durations. + */ + + /* Percussive + for (Notes::const_iterator n = _notes.begin(); n != _notes.end(); ++n) { + const MidiEvent& ev = n->on_event(); + source->append_event_unlocked(ev); + }*/ + + _lock.reader_lock(); + + LaterNoteEndComparator cmp; + ActiveNotes active_notes(cmp); + + // Foreach note + for (Notes::const_iterator n = _notes.begin(); n != _notes.end(); ++n) { + + // Write any pending note offs earlier than this note on + while ( ! active_notes.empty() ) { + const Note* const earliest_off = active_notes.top(); + const MidiEvent& off_ev = earliest_off->off_event(); + if (off_ev.time() <= n->time()) { + source->append_event_unlocked(off_ev); + active_notes.pop(); + } else { + break; + } } + + // Write this note on + source->append_event_unlocked(n->on_event()); + if (n->duration() > 0) + active_notes.push(&(*n)); } -#endif + + _edited = false; + + _lock.reader_unlock(); return true; } diff --git a/libs/ardour/midi_region.cc b/libs/ardour/midi_region.cc index 1ef52c743d..dc115cd55a 100644 --- a/libs/ardour/midi_region.cc +++ b/libs/ardour/midi_region.cc @@ -53,6 +53,7 @@ MidiRegion::MidiRegion (boost::shared_ptr src, nframes_t start, nfra : Region (src, start, length, PBD::basename_nosuffix(src->name()), DataType::MIDI, 0, Region::Flag(Region::DefaultFlags|Region::External)) { assert(_name.find("/") == string::npos); + midi_source(0)->Switched.connect(sigc::mem_fun(this, &MidiRegion::switch_source)); } /* Basic MidiRegion constructor (one channel) */ @@ -60,6 +61,7 @@ MidiRegion::MidiRegion (boost::shared_ptr src, nframes_t start, nfra : Region (src, start, length, name, DataType::MIDI, layer, flags) { assert(_name.find("/") == string::npos); + midi_source(0)->Switched.connect(sigc::mem_fun(this, &MidiRegion::switch_source)); } /* Basic MidiRegion constructor (many channels) */ @@ -67,6 +69,7 @@ MidiRegion::MidiRegion (SourceList& srcs, nframes_t start, nframes_t length, con : Region (srcs, start, length, name, DataType::MIDI, layer, flags) { assert(_name.find("/") == string::npos); + midi_source(0)->Switched.connect(sigc::mem_fun(this, &MidiRegion::switch_source)); } @@ -75,12 +78,14 @@ MidiRegion::MidiRegion (boost::shared_ptr other, nframes_t off : Region (other, offset, length, name, layer, flags) { assert(_name.find("/") == string::npos); + midi_source(0)->Switched.connect(sigc::mem_fun(this, &MidiRegion::switch_source)); } MidiRegion::MidiRegion (boost::shared_ptr other) : Region (other) { assert(_name.find("/") == string::npos); + midi_source(0)->Switched.connect(sigc::mem_fun(this, &MidiRegion::switch_source)); } MidiRegion::MidiRegion (boost::shared_ptr src, const XMLNode& node) @@ -90,6 +95,7 @@ MidiRegion::MidiRegion (boost::shared_ptr src, const XMLNode& node) throw failed_constructor(); } + midi_source(0)->Switched.connect(sigc::mem_fun(this, &MidiRegion::switch_source)); assert(_name.find("/") == string::npos); assert(_type == DataType::MIDI); } @@ -101,6 +107,7 @@ MidiRegion::MidiRegion (SourceList& srcs, const XMLNode& node) throw failed_constructor(); } + midi_source(0)->Switched.connect(sigc::mem_fun(this, &MidiRegion::switch_source)); assert(_name.find("/") == string::npos); assert(_type == DataType::MIDI); } @@ -302,3 +309,18 @@ MidiRegion::midi_source (uint32_t n) const return boost::dynamic_pointer_cast(source(n)); } + +void +MidiRegion::switch_source(boost::shared_ptr src) +{ + boost::shared_ptr msrc = boost::dynamic_pointer_cast(src); + if (!msrc) + return; + + // MIDI regions have only one source + _sources.clear(); + _sources.push_back(msrc); + + set_name(msrc->name()); +} + diff --git a/libs/ardour/midi_source.cc b/libs/ardour/midi_source.cc index e52e39e121..c6e3bd0a08 100644 --- a/libs/ardour/midi_source.cc +++ b/libs/ardour/midi_source.cc @@ -30,9 +30,13 @@ #include #include +#include #include #include +#include +#include +#include #include "i18n.h" @@ -44,6 +48,7 @@ sigc::signal MidiSource::MidiSourceCreated; MidiSource::MidiSource (Session& s, string name) : Source (s, name, DataType::MIDI) + , _timeline_position(0) , _model(new MidiModel(s)) , _model_loaded (false) , _writing (false) @@ -54,6 +59,7 @@ MidiSource::MidiSource (Session& s, string name) MidiSource::MidiSource (Session& s, const XMLNode& node) : Source (s, node) + , _timeline_position(0) , _model(new MidiModel(s)) , _model_loaded (false) , _writing (false) @@ -158,6 +164,40 @@ MidiSource::mark_streaming_write_completed () void MidiSource::session_saved() { - cerr << "MidiSource saving, name = " << _name << endl; + flush(); + + if (_model && _model_loaded && _model->edited()) { + string newname; + const string basename = PBD::basename_nosuffix(_name); + string::size_type last_dash = basename.find_last_of("-"); + if (last_dash == string::npos || last_dash == basename.find_first_of("-")) { + newname = basename + "-1"; + } else { + stringstream ss(basename.substr(last_dash+1)); + unsigned write_count = 0; + ss >> write_count; + cerr << "WRITE COUNT: " << write_count << endl; + ++write_count; // start at 1 + ss.clear(); + ss << basename.substr(0, last_dash) << "-" << write_count; + newname = ss.str(); + } + + string newpath = _session.session_directory().midi_path().to_string() +"/"+ newname + ".mid"; + + boost::shared_ptr newsrc = boost::dynamic_pointer_cast( + SourceFactory::createWritable(DataType::MIDI, _session, newpath, 1, 0, true)); + + newsrc->set_timeline_position(_timeline_position); + _model->write_to(newsrc); + + newsrc->set_model(_model); + _model.reset(); + _model_loaded = false; + + newsrc->flush(); + + Switched.emit(newsrc); + } } diff --git a/libs/ardour/smf_source.cc b/libs/ardour/smf_source.cc index 92dfed3cec..1f7221c316 100644 --- a/libs/ardour/smf_source.cc +++ b/libs/ardour/smf_source.cc @@ -55,7 +55,6 @@ SMFSource::SMFSource (Session& s, std::string path, Flag flags) , _channel(0) , _flags (Flag(flags | Writable)) // FIXME: this needs to be writable for now , _allow_remove_if_empty(true) - , _timeline_position (0) , _fd (0) , _last_ev_time(0) , _track_size(4) // 4 bytes for the ever-present EOT event @@ -70,6 +69,8 @@ SMFSource::SMFSource (Session& s, std::string path, Flag flags) if (open()) { throw failed_constructor (); } + + cerr << "SMF Source path: " << path << endl; assert(_name.find("/") == string::npos); } @@ -79,7 +80,6 @@ SMFSource::SMFSource (Session& s, const XMLNode& node) , _channel(0) , _flags (Flag (Writable|CanRename)) , _allow_remove_if_empty(true) - , _timeline_position (0) , _fd (0) , _last_ev_time(0) , _track_size(4) // 4 bytes for the ever-present EOT event @@ -99,6 +99,8 @@ SMFSource::SMFSource (Session& s, const XMLNode& node) throw failed_constructor (); } + cerr << "SMF Source name: " << _name << endl; + assert(_name.find("/") == string::npos); } @@ -156,21 +158,16 @@ SMFSource::open() _fd = fopen(path().c_str(), "w+"); _track_size = 0; - // write a tentative header just to pad things out so writing happens in the right spot + // Write a tentative header just to pad things out so writing happens in the right spot + set_timeline_position(0); flush_header(); - // FIXME: write the footer here too so it's a valid SMF (screw up writing ATM though) + + // Note file isn't a valid SMF at this point (no footer). Does it matter? } return (_fd == 0) ? -1 : 0; } -int -SMFSource::update_header (nframes_t when, struct tm&, time_t) -{ - _timeline_position = when; - return flush_header(); -} - int SMFSource::flush_header () { @@ -205,12 +202,12 @@ SMFSource::flush_header () int SMFSource::flush_footer() { - //cerr << "SMF - Writing EOT\n"; + cerr << "SMF " << name() << " writing EOT\n"; fseek(_fd, 0, SEEK_END); - write_var_len(1); // whatever... - char eot[4] = { 0xFF, 0x2F, 0x00 }; // end-of-track meta-event - fwrite(eot, 1, 4, _fd); + write_var_len(0); + char eot[3] = { 0xFF, 0x2F, 0x00 }; // end-of-track meta-event + fwrite(eot, 1, 3, _fd); fflush(_fd); return 0; } @@ -290,8 +287,7 @@ SMFSource::read_event(jack_midi_event_t& ev) const ev.buffer[0] = (unsigned char)status; fread(ev.buffer+1, 1, ev.size - 1, _fd); - /*printf("SMF - read event, delta = %u, size = %zu, data = ", - delta_time, ev.size); + /*printf("%s read event: delta = %u, size = %zu, data = ", _name.c_str(), delta_time, ev.size); for (size_t i=0; i < ev.size; ++i) { printf("%X ", ev.buffer[i]); } @@ -375,32 +371,14 @@ SMFSource::write_unlocked (MidiRingBuffer& src, nframes_t cnt) src.read(buf, /*_length*/0, _length + cnt); // FIXME? fseek(_fd, 0, SEEK_END); - - // FIXME: assumes tempo never changes after start - const double frames_per_beat = _session.tempo_map().tempo_at(_timeline_position).frames_per_beat( - _session.engine().frame_rate()); for (MidiBuffer::iterator i = buf.begin(); i != buf.end(); ++i) { MidiEvent& ev = *i; assert(ev.time() >= _timeline_position); ev.time() -= _timeline_position; assert(ev.time() >= _last_ev_time); - const uint32_t delta_time = (uint32_t)((ev.time() - _last_ev_time) / frames_per_beat * _ppqn); - - /*printf("SMF - writing event, delta = %u, size = %zu, data = ", - delta_time, ev.size); - for (size_t i=0; i < ev.size; ++i) { - printf("%X ", ev.buffer[i]); - } - printf("\n"); - */ - size_t stamp_size = write_var_len(delta_time); - fwrite(ev.buffer(), 1, ev.size(), _fd); - _track_size += stamp_size + ev.size(); - _write_data_count += ev.size(); - - _last_ev_time = ev.time(); + append_event_unlocked(ev); } fflush(_fd); @@ -419,6 +397,34 @@ SMFSource::write_unlocked (MidiRingBuffer& src, nframes_t cnt) return cnt; } + + +void +SMFSource::append_event_unlocked(const MidiEvent& ev) +{ + printf("SMF - writing event, time = %lf, size = %u, data = ", ev.time(), ev.size()); + for (size_t i=0; i < ev.size(); ++i) { + printf("%X ", ev.buffer()[i]); + } + printf("\n"); + + assert(ev.time() >= _last_ev_time); + + // FIXME: assumes tempo never changes after start + const double frames_per_beat = _session.tempo_map().tempo_at + (_timeline_position).frames_per_beat(_session.engine().frame_rate()); + + const uint32_t delta_time = (uint32_t)((ev.time() - _last_ev_time) / frames_per_beat * _ppqn); + + const size_t stamp_size = write_var_len(delta_time); + fwrite(ev.buffer(), 1, ev.size(), _fd); + + _track_size += stamp_size + ev.size(); + _write_data_count += ev.size(); + + _last_ev_time = ev.time(); +} + XMLNode& SMFSource::get_state () @@ -810,9 +816,10 @@ SMFSource::load_model(bool lock, bool force_reload) } if (! _model) { + cerr << "SMFSource: Loading new model " << _model.get() << endl; _model = boost::shared_ptr(new MidiModel(_session)); } else { - cerr << "SMFSource: Reloading model." << endl; + cerr << "SMFSource: Reloading model " << _model.get() << endl; _model->clear(); } @@ -860,6 +867,7 @@ SMFSource::load_model(bool lock, bool force_reload) void SMFSource::destroy_model() { + cerr << "SMFSource: Destroying model " << _model.get() << endl; _model.reset(); } -- 2.30.2