LV2 extension for host's time-scale vari-speed
authorRobin Gareus <robin@gareus.org>
Thu, 14 Nov 2019 20:15:30 +0000 (21:15 +0100)
committerRobin Gareus <robin@gareus.org>
Thu, 14 Nov 2019 20:15:30 +0000 (21:15 +0100)
Ardour 6 internally always runs at speed 1.0 (or -1.0, or stopped 0.0).
There is no vari-speed that scale "BPM" or "n_sample" time progression
per cycle.

Instead Ardour 6 vari-speed mechanism transparently re-samples I/O.
So process-time is scaled only relative to wall-clock time.

From a plugin's POV this is similar to "freewheeling": The plugin
processes data as if the host plays at speed 1.0. While the host
plays this data at a different rate.

Some plugins may like to be informed about the host's actual
playback rate.

Currently this is mainly for the benefit of github.com/x42/repitch.lv2.git

libs/ardour/ardour/lv2_plugin.h
libs/ardour/ardour/uri_map.h
libs/ardour/lv2_plugin.cc
libs/ardour/uri_map.cc

index 7b8233ee5a1adab992c06ff652289aea71f54aee..247b7b603a14d2884662d2f478fab77a94fc0c90 100644 (file)
@@ -207,6 +207,7 @@ class LIBARDOUR_API LV2Plugin : public ARDOUR::Plugin, public ARDOUR::Workee
        double        _next_cycle_speed; ///< Expected start sample of next run cycle
        double        _next_cycle_beat;  ///< Expected bar_beat of next run cycle
        double        _current_bpm;
+       double        _prev_time_scale;  ///< previous Port::speed_ratio
        PBD::ID       _insert_id;
        std::string   _plugin_state_dir;
        uint32_t      _bpm_control_port_index;
index 6c0b3c8991ee861150edeabf66301b897857e24f..9134c6be44fe691edda3b8abafe588a7414d29f6 100644 (file)
@@ -81,6 +81,7 @@ public:
                uint32_t time_beatsPerMinute;
                uint32_t time_frame;
                uint32_t time_speed;
+               uint32_t time_scale;
                uint32_t patch_Get;
                uint32_t patch_Set;
                uint32_t patch_property;
index 865c14d1d19ebd39ef5d68db1c9acce51ca0d327..514496e788150e59377efffcd2ac8bcb549c044b 100644 (file)
@@ -449,6 +449,7 @@ LV2Plugin::init(const void* c_plugin, samplecnt_t rate)
        _next_cycle_speed       = 1.0;
        _next_cycle_beat        = 0.0;
        _current_bpm            = 0.0;
+       _prev_time_scale        = 0.0;
        _seq_size               = _engine.raw_buffer_size(DataType::MIDI);
        _state_version          = 0;
        _was_activated          = false;
@@ -2574,6 +2575,7 @@ write_position(LV2_Atom_Forge*     forge,
                const TempoMetric&  t,
                Timecode::BBT_Time& bbt,
                double              speed,
+               double              time_scale,
                double              bpm,
                samplepos_t         position,
                samplecnt_t         offset)
@@ -2600,6 +2602,8 @@ write_position(LV2_Atom_Forge*     forge,
        lv2_atom_forge_float(forge, t.meter().divisions_per_bar());
        lv2_atom_forge_key(forge, urids.time_beatsPerMinute);
        lv2_atom_forge_float(forge, bpm);
+       lv2_atom_forge_key(forge, urids.time_scale);
+       lv2_atom_forge_float(forge, time_scale);
 #else
        lv2_atom_forge_blank(forge, &frame, 1, urids.time_Position);
        lv2_atom_forge_property_head(forge, urids.time_frame, 0);
@@ -2617,6 +2621,8 @@ write_position(LV2_Atom_Forge*     forge,
        lv2_atom_forge_float(forge, t.meter().divisions_per_bar());
        lv2_atom_forge_property_head(forge, urids.time_beatsPerMinute, 0);
        lv2_atom_forge_float(forge, bpm);
+       lv2_atom_forge_key(forge, urids.time_scale);
+       lv2_atom_forge_float(forge, time_scale);
 #endif
 
        LV2_Evbuf_Iterator    end  = lv2_evbuf_end(buf);
@@ -2725,6 +2731,7 @@ LV2Plugin::connect_and_run(BufferSet& bufs,
                        if (valid && (flags & PORT_INPUT)) {
                                if ((flags & PORT_POSITION)) {
                                        Timecode::BBT_Time bbt (tmap.bbt_at_sample (start));
+                                       double time_scale = Port::speed_ratio ();
                                        double bpm = tmap.tempo_at_sample (start).note_types_per_minute();
                                        double beatpos = (bbt.bars - 1) * tmetric.meter().divisions_per_bar()
                                                       + (bbt.beats - 1)
@@ -2732,11 +2739,12 @@ LV2Plugin::connect_and_run(BufferSet& bufs,
                                        beatpos *= tmetric.meter().note_divisor() / 4.0;
                                        if (start != _next_cycle_start ||
                                                        speed != _next_cycle_speed ||
+                                                       time_scale != _prev_time_scale ||
                                                        rint (1000 * beatpos) != rint(1000 * _next_cycle_beat) ||
                                                        bpm != _current_bpm) {
                                                // Transport or Tempo has changed, write position at cycle start
                                                write_position(&_impl->forge, _ev_buffers[port_index],
-                                                               tmetric, bbt, speed, bpm, start, 0);
+                                                               tmetric, bbt, speed, time_scale,  bpm, start, 0);
                                        }
                                }
 
@@ -2770,8 +2778,8 @@ LV2Plugin::connect_and_run(BufferSet& bufs,
                                                bbt = tmap.bbt_at_sample (metric->sample());
                                                double bpm = tmap.tempo_at_sample (start/*XXX*/).note_types_per_minute();
                                                write_position(&_impl->forge, _ev_buffers[port_index],
-                                                              tmetric, bbt, speed, bpm,
-                                                              metric->sample(),
+                                                              tmetric, bbt, speed, Port::speed_ratio (),
+                                                              bpm, metric->sample(),
                                                               metric->sample() - start);
                                                ++metric_i;
                                        }
@@ -3035,6 +3043,7 @@ LV2Plugin::connect_and_run(BufferSet& bufs,
        // Update expected transport information for next cycle so we can detect changes
        _next_cycle_speed = speed;
        _next_cycle_start = end;
+       _prev_time_scale = Port::speed_ratio ();
 
        {
                /* keep track of lv2:timePosition like plugins can do.
index 18c7a1ac9530121af604c92f1f9b0b76a066f931..51c94e42a38c59550cd82a09c686e9c27d4f9b63 100644 (file)
@@ -59,6 +59,7 @@ URIMap::URIDs::init(URIMap& uri_map)
        time_beatsPerMinute = uri_map.uri_to_id("http://lv2plug.in/ns/ext/time#beatsPerMinute");
        time_frame          = uri_map.uri_to_id("http://lv2plug.in/ns/ext/time#frame");
        time_speed          = uri_map.uri_to_id("http://lv2plug.in/ns/ext/time#speed");
+       time_scale          = uri_map.uri_to_id("http://ardour.org/lv2/time#scale"); // XXX
        patch_Get           = uri_map.uri_to_id("http://lv2plug.in/ns/ext/patch#Get");
        patch_Set           = uri_map.uri_to_id("http://lv2plug.in/ns/ext/patch#Set");
        patch_property      = uri_map.uri_to_id("http://lv2plug.in/ns/ext/patch#property");