Pan automation/serialization fixes.
authorDavid Robillard <d@drobilla.net>
Tue, 3 Jul 2007 02:37:24 +0000 (02:37 +0000)
committerDavid Robillard <d@drobilla.net>
Tue, 3 Jul 2007 02:37:24 +0000 (02:37 +0000)
"Live" Midi CC sending from Midi CC automation track controllers.

git-svn-id: svn://localhost/ardour2/trunk@2097 d708f5d6-7413-0410-9779-e7cbd77b26cf

12 files changed:
gtk2_ardour/audio_time_axis.cc
gtk2_ardour/automation_controller.cc
gtk2_ardour/midi_time_axis.cc
gtk2_ardour/route_time_axis.cc
libs/ardour/ardour/midi_ring_buffer.h
libs/ardour/ardour/midi_track.h
libs/ardour/ardour/panner.h
libs/ardour/ardour/param_id.h
libs/ardour/automatable.cc
libs/ardour/automation_event.cc
libs/ardour/midi_track.cc
libs/ardour/panner.cc

index d032aaca9f3699f6cd9f9a80749a4934a91f776f..6e80c0b636a662d24da230d81b774f94d5d1d7c1 100644 (file)
@@ -336,13 +336,20 @@ AudioTimeAxisView::update_pans ()
        /* Man I hate that damn stereo->stereo panner */
        uint32_t i = 0;
        for (p = _route->panner().begin(); p != _route->panner().end(); ++p) {
+               boost::shared_ptr<AutomationControl> pan_control = (*p)->pan_control();
+               
+               if (pan_control->list()->param_id().type() == NullAutomation) {
+                       error << "Pan control has NULL automation type!" << endmsg;
+                       continue;
+               }
+
                boost::shared_ptr<AutomationTimeAxisView> pan_track(new AutomationTimeAxisView (_session,
-                                       _route, _route/*FIXME*/, (*p)->pan_control()
+                                       _route, _route/*FIXME*/, pan_control
                                        editor,
                                        *this,
                                        parent_canvas,
-                                       _route->describe_parameter((*p)->pan_control()->list()->param_id()),
-                                       ParamID(PanAutomation, i).to_string()/* FIXME: correct state name? */));
+                                       _route->describe_parameter(pan_control->list()->param_id()),
+                                       pan_control->list()->param_id().to_string()/* FIXME: correct state name? */));
                add_automation_child(ParamID(PanAutomation, i), pan_track);
                ++i;
        }
index a95d469d7f5c5e889d8c6f1f58a5610afb9b8594..3cb4cc5a1e8fa86db2bc0b0f548b79208ae1d3a2 100644 (file)
@@ -75,7 +75,11 @@ void
 AutomationController::update_label(char* label, int label_len)
 {
        if (label && label_len)
-               snprintf(label, label_len, "%.3f", _controllable->get_value());
+               // Hack to display CC rounded to int
+               if (_controllable->list()->param_id().type() == MidiCCAutomation)
+                       snprintf(label, label_len, "%d", (int)_controllable->get_value());
+               else
+                       snprintf(label, label_len, "%.3f", _controllable->get_value());
 }
 
 void
index 541a49d35088ac70d65c839db4ed07ca92d406ec..09bda6a2d743c6aec376e2ad7ea650f668daf5a0 100644 (file)
@@ -166,13 +166,20 @@ MidiTimeAxisView::build_automation_action_menu ()
 void
 MidiTimeAxisView::add_controller_track()
 {
-       AddMidiCCTrackDialog dialog;
-       dialog.set_transient_for(editor);
-       int response = dialog.run();
-       if (response == Gtk::RESPONSE_ACCEPT) {
-               ParamID param = dialog.parameter();
-               create_automation_child(param);
+       int response;
+       ParamID param;
+
+       {
+               AddMidiCCTrackDialog dialog;
+               dialog.set_transient_for(editor);
+               response = dialog.run();
+               
+               if (response == Gtk::RESPONSE_ACCEPT)
+                       param = dialog.parameter();
        }
+
+       if (response == Gtk::RESPONSE_ACCEPT)
+               create_automation_child(param);
 }
 
 void
@@ -182,11 +189,11 @@ MidiTimeAxisView::create_automation_child (ParamID param)
        
                /* FIXME: this all probably leaks */
 
-               boost::shared_ptr<AutomationControl> c =_route->control(param);
+               boost::shared_ptr<AutomationControl> c = _route->control(param);
 
                if (!c) {
                        boost::shared_ptr<AutomationList> al(new ARDOUR::AutomationList(param, 0, 127, 64));
-                       c = boost::shared_ptr<AutomationControl>(new AutomationControl(_session, al));
+                       c = boost::shared_ptr<AutomationControl>(new MidiTrack::MidiControl(midi_track(), al));
                        _route->add_control(c);
                }
 
@@ -197,6 +204,7 @@ MidiTimeAxisView::create_automation_child (ParamID param)
                                parent_canvas,
                                _route->describe_parameter(param),
                                c->list()->param_id().to_string() /* FIXME: correct state name? */));
+               
                add_automation_child(param, track);
 
        } else {
index 3f37b73f12bab8f41a8d0763580642a536e836ff..e4c285f7acc5002a23b11736f82635c053cf6417 100644 (file)
@@ -434,6 +434,9 @@ RouteTimeAxisView::build_automation_action_menu ()
        automation_items.push_back (MenuElem (_("Hide all automation"),
                                              mem_fun(*this, &RouteTimeAxisView::hide_all_automation)));
 
+       if (subplugin_menu.get_parent())
+               subplugin_menu.detach();
+
        automation_items.push_back (MenuElem (_("Plugins"), subplugin_menu));
        
        map<ARDOUR::ParamID, RouteAutomationNode*>::iterator i;
@@ -441,18 +444,16 @@ RouteTimeAxisView::build_automation_action_menu ()
 
                automation_items.push_back (SeparatorElem());
 
-               if ( ! i->second->menu_item) {
-                       automation_items.push_back(CheckMenuElem (_route->describe_parameter(i->second->param), 
-                                       bind (mem_fun(*this, &RouteTimeAxisView::toggle_automation_track), i->second->param)));
+               if (i->second->menu_item)
+                       delete i->second->menu_item;
 
-                       i->second->menu_item = static_cast<Gtk::CheckMenuItem*>(&automation_items.back());
+               automation_items.push_back(CheckMenuElem (_route->describe_parameter(i->second->param), 
+                               bind (mem_fun(*this, &RouteTimeAxisView::toggle_automation_track), i->second->param)));
 
-               } else {
-                       automation_items.push_back (*i->second->menu_item);
-               }
-               
-               //i->second->menu_item->set_active(show_automation(i->second->param));
-               i->second->menu_item->set_active(false);
+               i->second->menu_item = static_cast<Gtk::CheckMenuItem*>(&automation_items.back());
+
+               i->second->menu_item->set_active(show_automation(i->second->param));
+               //i->second->menu_item->set_active(false);
        }
 }
 
@@ -1796,6 +1797,8 @@ RouteTimeAxisView::add_automation_child(ParamID param, boost::shared_ptr<Automat
                _show_automation.insert(param);
                _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
        }
+
+       build_display_menu();
 }
 
 
index 0ceedd7d741bbe8c7602756215df700a0982911d..3effb96664dddfec05b00f37dc1068048a9d5876 100644 (file)
@@ -232,7 +232,7 @@ public:
        size_t write(double time, size_t size, const Byte* buf);
        bool   read(double* time, size_t* size, Byte* buf);
 
-       size_t read(MidiBuffer& dst, nframes_t start, nframes_t end);
+       size_t read(MidiBuffer& dst, nframes_t start, nframes_t end, nframes_t offset=0);
 };
 
 
@@ -268,10 +268,10 @@ MidiRingBuffer::write(double time, size_t size, const Byte* buf)
 /** Read a block of MIDI events from buffer.
  *
  * Timestamps of events returned are relative to start (ie event with stamp 0
- * occurred at start).
+ * occurred at start), with offset added.
  */
 inline size_t
-MidiRingBuffer::read(MidiBuffer& dst, nframes_t start, nframes_t end)
+MidiRingBuffer::read(MidiBuffer& dst, nframes_t start, nframes_t end, nframes_t offset)
 {
        if (read_space() == 0)
                return 0;
index fb350b88381ffe5b77bbc14bb3a87961e4febc1b..c437ca73dee06407204af60fbbe21024437748b6 100644 (file)
@@ -21,6 +21,7 @@
 #define __ardour_midi_track_h__
 
 #include <ardour/track.h>
+#include <ardour/midi_ring_buffer.h>
 
 namespace ARDOUR
 {
@@ -71,6 +72,19 @@ public:
 
        int set_state(const XMLNode& node);
 
+       bool write_immediate_event(size_t size, const Byte* buf);
+       
+       struct MidiControl : public AutomationControl {
+           MidiControl(boost::shared_ptr<MidiTrack> route, boost::shared_ptr<AutomationList> al)
+                       : AutomationControl (route->session(), al, al->param_id().to_string())
+                       , _route (route)
+               {}
+        
+           void set_value (float val);
+   
+               boost::weak_ptr<MidiTrack> _route;
+       };
+
 protected:
        XMLNode& state (bool full);
        
@@ -80,6 +94,8 @@ private:
        int set_diskstream (boost::shared_ptr<MidiDiskstream> ds);
        void set_state_part_two ();
        void set_state_part_three ();
+
+       MidiRingBuffer _immediate_events;
 };
 
 } /* namespace ARDOUR*/
index a6cfc9a6c1230c1e0595256ca790ebcad0854afc..1852bdd1891ca70cc305f845cdeec5750ea18a3b 100644 (file)
@@ -21,6 +21,7 @@
 #define __ardour_panner_h__
 
 #include <cmath>
+#include <cassert>
 #include <vector>
 #include <string>
 #include <iostream>
@@ -106,7 +107,7 @@ class StreamPanner : public sigc::trackable, public PBD::Stateful
            PanControllable (Session& s, std::string name, StreamPanner& p, ParamID param)
                        : AutomationControl (s, boost::shared_ptr<AutomationList>(new AutomationList(
                                        param, 0.0, 1.0, 0.5)), name)
-                       , panner (p) {}
+                       , panner (p) { assert(param.type() != NullAutomation); }
            
            StreamPanner& panner;
            
@@ -124,7 +125,7 @@ class StreamPanner : public sigc::trackable, public PBD::Stateful
 class BaseStereoPanner : public StreamPanner
 {
   public:
-       BaseStereoPanner (Panner&);
+       BaseStereoPanner (Panner&, ParamID param);
        ~BaseStereoPanner ();
 
        /* this class just leaves the pan law itself to be defined
@@ -151,7 +152,7 @@ class BaseStereoPanner : public StreamPanner
 class EqualPowerStereoPanner : public BaseStereoPanner
 {
   public:
-       EqualPowerStereoPanner (Panner&);
+       EqualPowerStereoPanner (Panner&, ParamID param);
        ~EqualPowerStereoPanner ();
 
        void distribute_automated (AudioBuffer& src, BufferSet& obufs, 
index d9763d5594b857542e7ffb557fc5db445859947c..954b62894cad9982290da0480c2a41af2b98bad9 100644 (file)
@@ -55,8 +55,6 @@ public:
        ParamID(const std::string& str) : _type(NullAutomation), _id(0) {
                if (str == "gain") {
                        _type = GainAutomation;
-               } else if (str == "pan") {
-                       _type = PanAutomation;
                } else if (str == "solo") {
                        _type = SoloAutomation;
                } else if (str == "mute") {
@@ -67,14 +65,17 @@ public:
                        _type = FadeOutAutomation;
                } else if (str == "envelope") {
                        _type = EnvelopeAutomation;
+               } else if (str == "pan") {
+                       _type = PanAutomation;
+               } else if (str.length() > 4 && str.substr(0, 4) == "pan-") {
+                       _type = PanAutomation;
+                       _id = atoi(str.c_str()+4);
                } else if (str.length() > 10 && str.substr(0, 10) == "parameter-") {
                        _type = PluginAutomation;
                        _id = atoi(str.c_str()+10);
-                       //PBD::info << "Parameter: " << str << " -> " << _id << endl;
                } else if (str.length() > 7 && str.substr(0, 7) == "midicc-") {
                        _type = MidiCCAutomation;
                        _id = atoi(str.c_str()+7);
-                       //PBD::info << "MIDI CC: " << str << " -> " << _id << endl;
                } else {
                        PBD::warning << "Unknown ParamID '" << str << "'" << endmsg;
                }
@@ -119,6 +120,7 @@ public:
                } else if (_type == MidiCCAutomation) {
                        return string_compose("midicc-%1", _id);
                } else {
+                       assert(false);
                        PBD::warning << "Uninitialized ParamID to_string() called." << endmsg;
                        return "";
                }
index 5b31904615e096fc7ef7cad295b29971e3f8ee5f..ab45edee0312aec8edc7b8664b2c17e29b219cf3 100644 (file)
@@ -167,11 +167,11 @@ Automatable::control (ParamID parameter, bool create_if_missing)
                return i->second;
 
        } else if (create_if_missing) {
-               assert(parameter.type() != GainAutomation);
                boost::shared_ptr<AutomationList> al (new AutomationList (
                                        parameter, FLT_MIN, FLT_MAX, default_parameter_value (parameter)));
                boost::shared_ptr<AutomationControl> ac (new AutomationControl(_session, al));
                add_control(ac);
+               cerr << "WARNING: Default AutomationControl created for " << parameter.to_string() << endl;
                return ac;
 
        } else {
index 9c1a6126f6f9031169c95fe57bd87aaf02f9be34..8545f47e42f371334be2012e7097c537221649ff 100644 (file)
@@ -57,7 +57,7 @@ static void dumpit (const AutomationList& al, string prefix = "")
 AutomationList::AutomationList (ParamID id, double min_val, double max_val, double default_val)
        : _param_id(id)
        , _curve(new Curve(*this))
-{
+{      
        _param_id = id;
        _frozen = 0;
        _changed_when_thawed = false;
@@ -73,7 +73,7 @@ AutomationList::AutomationList (ParamID id, double min_val, double max_val, doub
        _lookup_cache.range.first = _events.end();
        _sort_pending = false;
 
-
+       assert(_param_id.type() != NullAutomation);
        AutomationListCreated(this);
 }
 
@@ -100,6 +100,7 @@ AutomationList::AutomationList (const AutomationList& other)
        }
 
        mark_dirty ();
+       assert(_param_id.type() != NullAutomation);
        AutomationListCreated(this);
 }
 
@@ -135,6 +136,7 @@ AutomationList::AutomationList (const AutomationList& other, double start, doubl
 
        mark_dirty ();
 
+       assert(_param_id.type() != NullAutomation);
        AutomationListCreated(this);
 }
 
@@ -162,6 +164,7 @@ AutomationList::AutomationList (const XMLNode& node, ParamID id)
        if (id)
                _param_id = id;
 
+       assert(_param_id.type() != NullAutomation);
        AutomationListCreated(this);
 }
 
index a10161bc7256c72874ce4b2c1ce2cb5b0a018576..075497c1959445e6a9dc88f4565541ba181eff7a 100644 (file)
@@ -36,6 +36,7 @@
 #include <ardour/utils.h>
 #include <ardour/buffer_set.h>
 #include <ardour/meter.h>
+#include <ardour/midi_events.h>
 
 #include "i18n.h"
 
@@ -45,6 +46,7 @@ using namespace PBD;
 
 MidiTrack::MidiTrack (Session& sess, string name, Route::Flag flag, TrackMode mode)
        : Track (sess, name, flag, mode, DataType::MIDI)
+       , _immediate_events(1024) // FIXME: size?
 {
        MidiDiskstream::Flag dflags = MidiDiskstream::Flag (0);
 
@@ -74,6 +76,7 @@ MidiTrack::MidiTrack (Session& sess, string name, Route::Flag flag, TrackMode mo
 
 MidiTrack::MidiTrack (Session& sess, const XMLNode& node)
        : Track (sess, node)
+       , _immediate_events(1024) // FIXME: size?
 {
        _set_state(node, false);
        
@@ -556,6 +559,8 @@ MidiTrack::process_output_buffers (BufferSet& bufs,
        if (muted()) {
                IO::silence(nframes, offset);
        } else {
+               MidiBuffer& out_buf = bufs.get_midi(0);
+               _immediate_events.read(out_buf, 0, 0, offset + nframes-1); // all stamps = 0
                deliver_output(bufs, start_frame, end_frame, nframes, offset);
        }
 }
@@ -620,3 +625,28 @@ MidiTrack::set_mode (TrackMode m)
 
        return 0;
 }
+       
+/** \return true on success, false on failure (no buffer space left)
+ */
+bool
+MidiTrack::write_immediate_event(size_t size, const Byte* buf)
+{
+       return (_immediate_events.write(0, size, buf) == size);
+}
+
+void
+MidiTrack::MidiControl::set_value(float val)
+{
+       assert(val >= 0);
+       assert(val <= 127.0);
+
+       boost::shared_ptr<MidiTrack> midi_track = _route.lock();
+
+       if (midi_track && !_list->automation_playback()) {
+               Byte ev[3] = { MIDI_CMD_CONTROL, _list->param_id().id(), (int)val };
+               midi_track->write_immediate_event(3,  ev);
+       }
+
+       AutomationControl::set_value(val);
+} 
+
index a20342330fe4a9b4aef5d3c31dd17c4c61e114fe..3912faf9e77cec115c8433cba42d05f4a55bad20 100644 (file)
@@ -73,6 +73,8 @@ StreamPanner::StreamPanner (Panner& p, ParamID param)
        : parent (p)
        , _control (new PanControllable(p.session(), X_("panner"), *this, param))
 {
+       assert(param.type() != NullAutomation);
+
        _muted = false;
 
        parent.session().add_controllable (_control);
@@ -189,8 +191,8 @@ StreamPanner::add_state (XMLNode& node)
 
 /*---------------------------------------------------------------------- */
 
-BaseStereoPanner::BaseStereoPanner (Panner& p)
-       : StreamPanner (p, ParamID(PanAutomation, 0))
+BaseStereoPanner::BaseStereoPanner (Panner& p, ParamID param)
+       : StreamPanner (p, param)
 {
 }
 
@@ -346,8 +348,8 @@ BaseStereoPanner::distribute (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain
 
 /*---------------------------------------------------------------------- */
 
-EqualPowerStereoPanner::EqualPowerStereoPanner (Panner& p)
-       : BaseStereoPanner (p)
+EqualPowerStereoPanner::EqualPowerStereoPanner (Panner& p, ParamID param)
+       : BaseStereoPanner (p, param)
 {
        update ();
 
@@ -461,9 +463,9 @@ EqualPowerStereoPanner::distribute_automated (AudioBuffer& srcbuf, BufferSet& ob
 }
 
 StreamPanner*
-EqualPowerStereoPanner::factory (Panner& parent, ParamID who_cares)
+EqualPowerStereoPanner::factory (Panner& parent, ParamID param)
 {
-       return new EqualPowerStereoPanner (parent);
+       return new EqualPowerStereoPanner (parent, param);
 }
 
 XMLNode&
@@ -798,7 +800,7 @@ Panner::reset (uint32_t nouts, uint32_t npans)
                outputs.push_back (Output (1.0, 0));
 
                for (n = 0; n < npans; ++n) {
-                       push_back (new EqualPowerStereoPanner (*this));
+                       push_back (new EqualPowerStereoPanner (*this, ParamID(PanAutomation, n)));
                }
                break;