Fix MIDI session path problems (thanks timbyr).
[ardour.git] / libs / ardour / smf_source.cc
index aca8cf5a2576d5d0bfbe8b0e5ced87eba9a8c7cd..730ba053bd6df8a4922bcc849f6d67ee121e691e 100644 (file)
@@ -36,6 +36,8 @@
 #include <ardour/session.h>
 #include <ardour/midi_ring_buffer.h>
 #include <ardour/midi_util.h>
+#include <ardour/tempo.h>
+#include <ardour/audioengine.h>
 
 #include "i18n.h"
 
@@ -147,7 +149,7 @@ SMFSource::open()
                uint32_t track_size_be = 0;
                fread(&track_size_be, 4, 1, _fd);
                _track_size = GUINT32_FROM_BE(track_size_be);
-               cerr << "SMF - read track size " << _track_size;
+               cerr << "SMF - read track size " << _track_size << endl;
 
        // We're making a new file
        } else {
@@ -163,7 +165,7 @@ SMFSource::open()
 }
 
 int
-SMFSource::update_header (jack_nframes_t when, struct tm&, time_t)
+SMFSource::update_header (nframes_t when, struct tm&, time_t)
 {
        _timeline_position = when;
        return flush_header();
@@ -178,9 +180,9 @@ SMFSource::flush_header ()
 
        assert(_fd);
 
-       const uint16_t type     = GUINT16_TO_BE(0);    // SMF Type 0 (single track)
-       const uint16_t ntracks  = GUINT16_TO_BE(1);    // Number of tracks (always 1 for Type 0)
-       const uint16_t division = GUINT16_TO_BE(1920); // FIXME FIXME FIXME PPQN
+       const uint16_t type     = GUINT16_TO_BE(0);     // SMF Type 0 (single track)
+       const uint16_t ntracks  = GUINT16_TO_BE(1);     // Number of tracks (always 1 for Type 0)
+       const uint16_t division = GUINT16_TO_BE(_ppqn); // Pulses per beat
 
        char data[6];
        memcpy(data, &type, 2);
@@ -220,7 +222,7 @@ SMFSource::flush_footer()
  */
 /*
 long
-SMFSource::find_first_event_after(jack_nframes_t start)
+SMFSource::find_first_event_after(nframes_t start)
 {
        // FIXME: obviously this is slooow
        
@@ -241,8 +243,8 @@ SMFSource::find_first_event_after(jack_nframes_t start)
  *
  * File position MUST be at the beginning of a delta time, or this will die very messily.
  * ev.buffer must be of size ev.size, and large enough for the event.  The returned event
- * will have it's time field set to it's delta time (so it's the caller's responsibility
- * to calculate a real time for the event).
+ * will have it's time field set to it's delta time, in SMF tempo-based ticks, using the
+ * rate given by ppqn() (it is the caller's responsibility to calculate a real time).
  *
  * Returns event length (including status byte) on success, 0 if event was
  * skipped (eg a meta event), or -1 on EOF (or end of track).
@@ -287,12 +289,16 @@ SMFSource::read_event(MidiEvent& ev) const
        return ev.size;
 }
 
-jack_nframes_t
-SMFSource::read_unlocked (MidiRingBuffer& dst, jack_nframes_t start, jack_nframes_t cnt, jack_nframes_t stamp_offset) const
+/** All stamps in audio frames */
+nframes_t
+SMFSource::read_unlocked (MidiRingBuffer& dst, nframes_t start, nframes_t cnt, nframes_t stamp_offset) const
 {
        //cerr << "SMF - read " << start << ", count=" << cnt << ", offset=" << stamp_offset << endl;
 
-       jack_nframes_t time = 0;
+       // 64 bits ought to be enough for anybody
+       uint64_t time = 0; // in SMF ticks, 1 tick per _ppqn
+
+       _read_data_count = 0;
 
        // FIXME: ugh
        unsigned char ev_buf[MidiBuffer::max_event_size()];
@@ -305,6 +311,12 @@ SMFSource::read_unlocked (MidiRingBuffer& dst, jack_nframes_t start, jack_nframe
        
        fseek(_fd, _header_size, 0);
        
+       // 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());
+       
+       uint64_t start_ticks = (uint64_t)((start / frames_per_beat) * _ppqn);
+
        while (!feof(_fd)) {
                int ret = read_event(ev);
                if (ret == -1) { // EOF
@@ -321,35 +333,44 @@ SMFSource::read_unlocked (MidiRingBuffer& dst, jack_nframes_t start, jack_nframe
                time += ev.time; // accumulate delta time
                ev.time = time; // set ev.time to actual time (relative to source start)
 
-               if (ev.time >= start) {
-                       if (ev.time > start + cnt) {
+               if (ev.time >= start_ticks) {
+                       if (ev.time < start_ticks + (cnt / frames_per_beat)) {
                                break;
                        } else {
-                               ev.time += stamp_offset;
-                               dst.write(ev);
+                               ev.time = (nframes_t)(((ev.time / (double)_ppqn) * frames_per_beat)) + stamp_offset;
+                               // write event time in absolute frames
+                               dst.write(ev.time, ev.size, ev.buffer);
                        }
                }
+
+               _read_data_count += ev.size;
        }
        
        return cnt;
 }
 
-jack_nframes_t
-SMFSource::write_unlocked (MidiRingBuffer& src, jack_nframes_t cnt)
+/** All stamps in audio frames */
+nframes_t
+SMFSource::write_unlocked (MidiRingBuffer& src, nframes_t cnt)
 {
-       //cerr << "SMF WRITE -- " << _length << "--" << cnt << endl;
-       
-       MidiBuffer buf(1024); // FIXME: allocation, size?
+       _write_data_count = 0;
+
+       boost::shared_ptr<MidiBuffer> buf_ptr(new MidiBuffer(1024)); // FIXME: size?
+       MidiBuffer& buf = *buf_ptr.get();
        src.read(buf, /*_length*/0, _length + cnt); // FIXME?
 
        fseek(_fd, 0, SEEK_END);
 
-       // FIXME: start of source 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());
        
        for (size_t i=0; i < buf.size(); ++i) {
-               const MidiEvent& ev = buf[i];
+               MidiEvent& ev = buf[i];
                assert(ev.time >= _timeline_position);
-               uint32_t delta_time = (ev.time - _timeline_position) - _last_ev_time;
+               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);
@@ -360,17 +381,22 @@ SMFSource::write_unlocked (MidiRingBuffer& src, jack_nframes_t cnt)
                */
                size_t stamp_size = write_var_len(delta_time);
                fwrite(ev.buffer, 1, ev.size, _fd);
-               _last_ev_time += delta_time;
+
                _track_size += stamp_size + ev.size;
+               _write_data_count += ev.size;
+               
+               _last_ev_time = ev.time;
        }
 
        fflush(_fd);
 
-       if (buf.size() > 0) {
-               ViewDataRangeReady (_length, cnt); /* EMIT SIGNAL */
-       }
+       const nframes_t oldlen = _length;
+       update_length(oldlen, cnt);
 
-       update_length(_length, cnt);
+       _model.append(buf);
+
+       ViewDataRangeReady (buf_ptr, oldlen, cnt); /* EMIT SIGNAL */
+       
        return cnt;
 }
 
@@ -744,3 +770,33 @@ SMFSource::read_var_len() const
 
        return value;
 }
+
+void
+SMFSource::load_model(bool lock)
+{
+       if (lock)
+               Glib::Mutex::Lock lm (_lock);
+
+       _model.clear();
+       
+       fseek(_fd, _header_size, 0);
+
+       nframes_t time = 0;
+       MidiEvent ev;
+       
+       int ret;
+       while ((ret = read_event(ev)) >= 0) {
+               time += ev.time;
+               ev.time = time;
+               if (ret > 0) { // didn't skip (meta) event
+                       _model.append(ev);
+               }
+       }
+}
+
+void
+SMFSource::destroy_model()
+{
+       _model.clear();
+}
+