summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2007-08-11 06:17:42 +0000
committerDavid Robillard <d@drobilla.net>2007-08-11 06:17:42 +0000
commit5156998e6e2536c9c713974d3ae719a5d2ef5c7f (patch)
tree6223846321fe3f611b952b2c2e77c349d0b7072f
parent861181d7421012486e36327c464b957ec08e396b (diff)
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
-rw-r--r--gtk2_ardour/editor_mouse.cc5
-rw-r--r--gtk2_ardour/midi_region_view.cc13
-rw-r--r--gtk2_ardour/midi_region_view.h1
-rw-r--r--libs/ardour/ardour/midi_model.h12
-rw-r--r--libs/ardour/ardour/midi_region.h2
-rw-r--r--libs/ardour/ardour/midi_source.h7
-rw-r--r--libs/ardour/ardour/region.h11
-rw-r--r--libs/ardour/ardour/smf_source.h12
-rw-r--r--libs/ardour/ardour/source.h4
-rw-r--r--libs/ardour/audio_diskstream.cc1
-rw-r--r--libs/ardour/filter.cc2
-rw-r--r--libs/ardour/midi_diskstream.cc2
-rw-r--r--libs/ardour/midi_model.cc101
-rw-r--r--libs/ardour/midi_region.cc22
-rw-r--r--libs/ardour/midi_source.cc42
-rw-r--r--libs/ardour/smf_source.cc82
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<Source> src)
+{
+ boost::shared_ptr<MidiSource> msrc = boost::dynamic_pointer_cast<MidiSource>(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<ARDOUR::Source> 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 <queue>
#include <boost/utility.hpp>
+#include <glibmm/thread.h>
#include <pbd/command.h>
#include <ardour/types.h>
#include <ardour/midi_buffer.h>
@@ -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<MidiSource> source);
sigc::signal<void> 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<size_t> 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> 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<MidiModel> model() { return _model; }
+ void set_model(boost::shared_ptr<MidiModel> 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<ARDOUR::Playlist> _playlist;
SourceList _sources;
/** Used when timefx are applied, so we can always use the original source */
SourceList _master_sources;
+
+ boost::weak_ptr<ARDOUR::Playlist> _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<void,Source*> SourceCreated;
+ static sigc::signal<void,Source*> SourceCreated;
+ sigc::signal<void,boost::shared_ptr<Source> > 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 <sys/mman.h>
#include <pbd/error.h>
-#include <pbd/basename.h>
#include <glibmm/thread.h>
#include <pbd/xml++.h>
#include <pbd/memento_command.h>
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> region, SourceList& nsrcs)
boost::shared_ptr<SMFSource> smfs = boost::dynamic_pointer_cast<SMFSource>(*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 <pbd/enumwriter.h>
#include <ardour/midi_model.h>
#include <ardour/midi_events.h>
+#include <ardour/midi_source.h>
#include <ardour/types.h>
#include <ardour/session.h>
@@ -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<Note>::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i)
- _model.add_note(*i);
+ _model.add_note_unlocked(*i);
for (std::list<Note>::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<Note>::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i)
- _model.remove_note(*i);
+ _model.remove_note_unlocked(*i);
for (std::list<Note>::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<MidiSource> 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<MidiSource> 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<MidiSource> 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<const MidiRegion> 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<const MidiRegion> 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<MidiSource> src, const XMLNode& node)
@@ -90,6 +95,7 @@ MidiRegion::MidiRegion (boost::shared_ptr<MidiSource> 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<MidiSource>(source(n));
}
+
+void
+MidiRegion::switch_source(boost::shared_ptr<Source> src)
+{
+ boost::shared_ptr<MidiSource> msrc = boost::dynamic_pointer_cast<MidiSource>(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 <pbd/xml++.h>
#include <pbd/pthread_utils.h>
+#include <pbd/basename.h>
#include <ardour/midi_source.h>
#include <ardour/midi_ring_buffer.h>
+#include <ardour/session.h>
+#include <ardour/session_directory.h>
+#include <ardour/source_factory.h>
#include "i18n.h"
@@ -44,6 +48,7 @@ sigc::signal<void,MidiSource *> 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<MidiSource> newsrc = boost::dynamic_pointer_cast<MidiSource>(
+ 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,22 +158,17 @@ 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 ()
{
// FIXME: write timeline position somehow?
@@ -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<MidiModel>(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();
}