replace fixed-point linear interpolation with double-based version, thereby removing...
[ardour.git] / libs / ardour / smf_source.cc
index be9b9dd3f125033fbb73b069a69d428b6080ed87..40848dc9edfe5c21a13de406551eddec08fa88f4 100644 (file)
 #include <unistd.h>
 #include <errno.h>
 
-#include <pbd/mountpoint.h>
-#include <pbd/pathscanner.h>
-#include <pbd/stl_delete.h>
-#include <pbd/strsplit.h>
+#include "pbd/mountpoint.h"
+#include "pbd/pathscanner.h"
+#include "pbd/stl_delete.h"
+#include "pbd/strsplit.h"
 
 #include <glibmm/miscutils.h>
 
-#include <evoral/SMFReader.hpp>
-#include <evoral/Control.hpp>
+#include "evoral/SMFReader.hpp"
+#include "evoral/Control.hpp"
 
-#include <ardour/audioengine.h>
-#include <ardour/event_type_map.h>
-#include <ardour/midi_model.h>
-#include <ardour/midi_ring_buffer.h>
-#include <ardour/session.h>
-#include <ardour/smf_source.h>
+#include "ardour/audioengine.h"
+#include "ardour/event_type_map.h"
+#include "ardour/midi_model.h"
+#include "ardour/midi_ring_buffer.h"
+#include "ardour/session.h"
+#include "ardour/smf_source.h"
 
 #include "i18n.h"
 
@@ -95,8 +95,9 @@ SMFSource::~SMFSource ()
 
 /** All stamps in audio frames */
 nframes_t
-SMFSource::read_unlocked (MidiRingBuffer<nframes_t>& dst, nframes_t start, nframes_t dur,
-               nframes_t stamp_offset, nframes_t negative_stamp_offset) const
+SMFSource::read_unlocked (MidiRingBuffer<nframes_t>& destination, sframes_t source_start,
+               sframes_t start, nframes_t duration,
+               sframes_t stamp_offset, sframes_t negative_stamp_offset) const
 {
        int      ret  = 0;
        uint64_t time = 0; // in SMF ticks, 1 tick per _ppqn
@@ -110,23 +111,25 @@ SMFSource::read_unlocked (MidiRingBuffer<nframes_t>& dst, nframes_t start, nfram
        uint8_t* ev_buffer  = 0;
 
        size_t scratch_size = 0; // keep track of scratch to minimize reallocs
+       
+       BeatsFramesConverter converter(_session, source_start);
 
-       const uint64_t start_ticks = (uint64_t)(_converter.from(start) * ppqn());
+       const uint64_t start_ticks = (uint64_t)(converter.from(start) * ppqn());
 
        if (_last_read_end == 0 || start != _last_read_end) {
-               cerr << "SMFSource::read_unlocked seeking to " << start << endl;
+               //cerr << "SMFSource::read_unlocked seeking to " << start << endl;
                Evoral::SMF::seek_to_start();
                while (time < start_ticks) {
                        ret = read_event(&ev_delta_t, &ev_size, &ev_buffer);
                        if (ret == -1) { // EOF
-                               _last_read_end = start + dur;
-                               return dur;
+                               _last_read_end = start + duration;
+                               return duration;
                        }
                        time += ev_delta_t; // accumulate delta time
                }
        }
        
-       _last_read_end = start + dur;
+       _last_read_end = start + duration;
 
        while (true) {
                ret = read_event(&ev_delta_t, &ev_size, &ev_buffer);
@@ -143,10 +146,10 @@ SMFSource::read_unlocked (MidiRingBuffer<nframes_t>& dst, nframes_t start, nfram
                ev_type = EventTypeMap::instance().midi_event_type(ev_buffer[0]);
 
                assert(time >= start_ticks);
-               const nframes_t ev_frame_time = _converter.to(time / (double)ppqn()) + stamp_offset;
+               const sframes_t ev_frame_time = converter.to(time / (double)ppqn()) + stamp_offset;
 
-               if (ev_frame_time < start + dur) {
-                       dst.write(ev_frame_time - negative_stamp_offset, ev_type, ev_size, ev_buffer);
+               if (ev_frame_time < start + duration) {
+                       destination.write(ev_frame_time - negative_stamp_offset, ev_type, ev_size, ev_buffer);
                } else {
                        break;
                }
@@ -159,12 +162,12 @@ SMFSource::read_unlocked (MidiRingBuffer<nframes_t>& dst, nframes_t start, nfram
                ev_size = scratch_size; // ensure read_event only allocates if necessary
        }
        
-       return dur;
+       return duration;
 }
 
 /** All stamps in audio frames */
 nframes_t
-SMFSource::write_unlocked (MidiRingBuffer<nframes_t>& src, nframes_t dur)
+SMFSource::write_unlocked (MidiRingBuffer<nframes_t>& source, sframes_t position, nframes_t duration)
 {
        _write_data_count = 0;
                
@@ -179,16 +182,17 @@ SMFSource::write_unlocked (MidiRingBuffer<nframes_t>& src, nframes_t dur)
                _model->start_write();
        }
 
-       Evoral::MIDIEvent<nframes_t> ev(0, 0.0, 4, NULL, true);
+       Evoral::MIDIEvent<nframes_t> ev;
 
        while (true) {
-               bool ret = src.peek_time(&time);
-               if (!ret || time - _timeline_position > _length + dur) {
+               bool ret = source.peek_time(&time);
+               if (!ret || time > _last_write_end + duration) {
                        break;
                }
 
-               ret = src.read_prefix(&time, &type, &size);
+               ret = source.read_prefix(&time, &type, &size);
                if (!ret) {
+                       cerr << "ERROR: Unable to read event prefix, corrupt MIDI ring buffer" << endl;
                        break;
                }
 
@@ -197,14 +201,14 @@ SMFSource::write_unlocked (MidiRingBuffer<nframes_t>& src, nframes_t dur)
                        buf = (uint8_t*)realloc(buf, size);
                }
 
-               ret = src.read_contents(size, buf);
+               ret = source.read_contents(size, buf);
                if (!ret) {
                        cerr << "ERROR: Read time/size but not buffer, corrupt MIDI ring buffer" << endl;
                        break;
                }
                
-               assert(time >= _timeline_position);
-               time -= _timeline_position;
+               assert(time >= position);
+               time -= position;
                
                ev.set(buf, size, time);
                ev.set_event_type(EventTypeMap::instance().midi_event_type(ev.buffer()[0]));
@@ -214,7 +218,7 @@ SMFSource::write_unlocked (MidiRingBuffer<nframes_t>& src, nframes_t dur)
                        continue;
                }
                
-               append_event_unlocked_frames(ev);
+               append_event_unlocked_frames(ev, position);
        }
 
        if (_model) {
@@ -224,12 +228,9 @@ SMFSource::write_unlocked (MidiRingBuffer<nframes_t>& src, nframes_t dur)
        Evoral::SMF::flush();
        free(buf);
 
-       const nframes_t oldlen = _length;
-       update_length(oldlen, dur);
-
-       ViewDataRangeReady(_timeline_position + oldlen, dur); /* EMIT SIGNAL */
+       ViewDataRangeReady(position + _last_write_end, duration); /* EMIT SIGNAL */
 
-       return dur;
+       return duration;
 }
                
 
@@ -251,6 +252,8 @@ SMFSource::append_event_unlocked_beats (const Evoral::Event<double>& ev)
                return;
        }
        
+       _length_beats = max(_length_beats, ev.time());
+       
        const double delta_time_beats   = ev.time() - _last_ev_time_beats;
        const uint32_t delta_time_ticks = (uint32_t)lrint(delta_time_beats * (double)ppqn());
 
@@ -266,7 +269,7 @@ SMFSource::append_event_unlocked_beats (const Evoral::Event<double>& ev)
 
 /** Append an event with a timestamp in frames (nframes_t) */
 void
-SMFSource::append_event_unlocked_frames (const Evoral::Event<nframes_t>& ev)
+SMFSource::append_event_unlocked_frames (const Evoral::Event<nframes_t>& ev, sframes_t position)
 {
        if (ev.size() == 0)  {
                return;
@@ -276,14 +279,17 @@ SMFSource::append_event_unlocked_frames (const Evoral::Event<nframes_t>& ev)
                        name().c_str(), ev.time(), ev.size()); 
        for (size_t i=0; i < ev.size(); ++i) printf("%X ", ev.buffer()[i]); printf("\n");*/
        
-       assert(ev.time() >= 0);
        if (ev.time() < _last_ev_time_frames) {
                cerr << "SMFSource: Warning: Skipping event with non-monotonic time" << endl;
                return;
        }
        
-       const nframes_t delta_time_frames = ev.time() - _last_ev_time_frames;
-       const double    delta_time_beats  = _converter.from(delta_time_frames);
+       BeatsFramesConverter converter(_session, position);
+       
+       _length_beats = max(_length_beats, converter.from(ev.time()));
+       
+       const sframes_t delta_time_frames = ev.time() - _last_ev_time_frames;
+       const double    delta_time_beats  = converter.from(delta_time_frames);
        const uint32_t  delta_time_ticks  = (uint32_t)(lrint(delta_time_beats * (double)ppqn()));
 
        Evoral::SMF::append_event_delta(delta_time_ticks, ev.size(), ev.buffer());
@@ -292,7 +298,7 @@ SMFSource::append_event_unlocked_frames (const Evoral::Event<nframes_t>& ev)
        _write_data_count += ev.size();
 
        if (_model) {
-               const double ev_time_beats = _converter.from(ev.time());
+               const double ev_time_beats = converter.from(ev.time());
                const Evoral::Event<double> beat_ev(
                                ev.event_type(), ev_time_beats, ev.size(), (uint8_t*)ev.buffer());
                _model->append(beat_ev);
@@ -324,7 +330,7 @@ SMFSource::set_state (const XMLNode& node)
 }
 
 void
-SMFSource::mark_streaming_midi_write_started (NoteMode mode, nframes_t start_frame)
+SMFSource::mark_streaming_midi_write_started (NoteMode mode, sframes_t start_frame)
 {
        MidiSource::mark_streaming_midi_write_started (mode, start_frame);
        Evoral::SMF::begin_write ();
@@ -368,10 +374,10 @@ SMFSource::load_model (bool lock, bool force_reload)
 
        if (! _model) {
                _model = boost::shared_ptr<MidiModel>(new MidiModel(this));
-               cerr << _name << " loaded new model " << _model.get() << endl;
+               //cerr << _name << " loaded new model " << _model.get() << endl;
        } else {
-               cerr << _name << " reloading model " << _model.get()
-                       << " (" << _model->n_notes() << " notes)" <<endl;
+               /*cerr << _name << " reloading model " << _model.get()
+                       << " (" << _model->n_notes() << " notes)" << endl;*/
                _model->clear();
        }
 
@@ -400,6 +406,8 @@ SMFSource::load_model (bool lock, bool force_reload)
                        scratch_size = ev.size();
                }
                ev.size() = scratch_size; // ensure read_event only allocates if necessary
+               
+               _length_beats = max(_length_beats, ev.time());
        }
 
        set_default_controls_interpolation();