13
0

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
This commit is contained in:
David Robillard 2007-08-11 06:17:42 +00:00
parent 861181d742
commit 5156998e6e
16 changed files with 233 additions and 82 deletions

View File

@ -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());

View File

@ -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());
}

View File

@ -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);

View File

@ -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():

View File

@ -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);

View File

@ -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()

View File

@ -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 */

View File

@ -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;

View File

@ -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);

View File

@ -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>

View File

@ -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 ();
}
}

View File

@ -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;

View File

@ -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;
cerr << "Writing model to " << source->name() << endl;
#if 0
SourceFactory::createWritable (region->data_type(), session, path, false, session.frame_rate());
/* 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.
*/
catch (failed_constructor& err) {
error << string_compose (_("filter: error creating new file %1 (%2)"), path, strerror (errno)) << endmsg;
return -1;
/* 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;
}

View File

@ -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());
}

View File

@ -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);
}
}

View File

@ -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<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();
}