Add a scratch buffer for automation.
[ardour.git] / libs / ardour / midi_track.cc
index b79a1cf52cd6b56f85e7d20650ae45b596dff29a..0dd34d7bf67926542ed733f6403057cfc1ecee65 100644 (file)
@@ -30,7 +30,7 @@
 #endif
 
 #include "pbd/enumwriter.h"
-#include "pbd/convert.h"
+#include "pbd/types_convert.h"
 #include "evoral/midi_util.h"
 
 #include "ardour/beats_frames_converter.h"
@@ -52,6 +52,7 @@
 #include "ardour/route_group_specialized.h"
 #include "ardour/session.h"
 #include "ardour/session_playlists.h"
+#include "ardour/types_convert.h"
 #include "ardour/utils.h"
 
 #include "pbd/i18n.h"
@@ -69,12 +70,13 @@ using namespace PBD;
 
 MidiTrack::MidiTrack (Session& sess, string name, TrackMode mode)
        : Track (sess, name, PresentationInfo::MidiTrack, mode, DataType::MIDI)
-       , _immediate_events(1024) // FIXME: size?
+       , _immediate_events(6096) // FIXME: size?
        , _step_edit_ring_buffer(64) // FIXME: size?
        , _note_mode(Sustained)
        , _step_editing (false)
        , _input_active (true)
 {
+       _session.SessionLoaded.connect_same_thread (*this, boost::bind (&MidiTrack::restore_controls, this));
 }
 
 MidiTrack::~MidiTrack ()
@@ -138,11 +140,13 @@ MidiTrack::set_diskstream (boost::shared_ptr<Diskstream> ds)
        mds->reset_tracker ();
 
        _diskstream->set_track (this);
+#ifdef XXX_OLD_DESTRUCTIVE_API_XXX
        if (Profile->get_trx()) {
                _diskstream->set_destructive (false);
        } else {
                _diskstream->set_destructive (_mode == Destructive);
        }
+#endif
        _diskstream->set_record_enabled (false);
 
        _diskstream_data_recorded_connection.disconnect ();
@@ -162,15 +166,11 @@ MidiTrack::midi_diskstream() const
 int
 MidiTrack::set_state (const XMLNode& node, int version)
 {
-       XMLProperty const * prop;
-
        /* This must happen before Track::set_state(), as there will be a buffer
           fill during that call, and we must fill buffers using the correct
           _note_mode.
        */
-       if ((prop = node.property (X_("note-mode"))) != 0) {
-               _note_mode = NoteMode (string_2_enum (prop->value(), _note_mode));
-       } else {
+       if (!node.get_property (X_("note-mode"), _note_mode)) {
                _note_mode = Sustained;
        }
 
@@ -181,25 +181,24 @@ MidiTrack::set_state (const XMLNode& node, int version)
        // No destructive MIDI tracks (yet?)
        _mode = Normal;
 
-       if ((prop = node.property ("input-active")) != 0) {
-               set_input_active (string_is_affirmative (prop->value()));
+       bool yn;
+       if (node.get_property ("input-active", yn)) {
+               set_input_active (yn);
        }
 
        ChannelMode playback_channel_mode = AllChannels;
        ChannelMode capture_channel_mode = AllChannels;
 
-       if ((prop = node.property ("playback-channel-mode")) != 0) {
-               playback_channel_mode = ChannelMode (string_2_enum(prop->value(), playback_channel_mode));
-       }
-       if ((prop = node.property ("capture-channel-mode")) != 0) {
-               capture_channel_mode = ChannelMode (string_2_enum(prop->value(), capture_channel_mode));
-       }
-       if ((prop = node.property ("channel-mode")) != 0) {
+       node.get_property ("playback-channel-mode", playback_channel_mode);
+       node.get_property ("capture-channel-mode", capture_channel_mode);
+
+       if (node.get_property ("channel-mode", playback_channel_mode)) {
                /* 3.0 behaviour where capture and playback modes were not separated */
-               playback_channel_mode = ChannelMode (string_2_enum(prop->value(), playback_channel_mode));
                capture_channel_mode = playback_channel_mode;
        }
 
+       XMLProperty const * prop;
+
        unsigned int playback_channel_mask = 0xffff;
        unsigned int capture_channel_mask = 0xffff;
 
@@ -240,13 +239,12 @@ MidiTrack::state(bool full_state)
                XMLNode* inode;
 
                freeze_node = new XMLNode (X_("freeze-info"));
-               freeze_node->add_property ("playlist", _freeze_record.playlist->name());
-               freeze_node->add_property ("state", enum_2_string (_freeze_record.state));
+               freeze_node->set_property ("playlist", _freeze_record.playlist->name());
+               freeze_node->set_property ("state", _freeze_record.state);
 
                for (vector<FreezeRecordProcessorInfo*>::iterator i = _freeze_record.processor_info.begin(); i != _freeze_record.processor_info.end(); ++i) {
                        inode = new XMLNode (X_("processor"));
-                       (*i)->id.print (buf, sizeof(buf));
-                       inode->add_property (X_("id"), buf);
+                       inode->set_property (X_("id"), id());
                        inode->add_child_copy ((*i)->state);
 
                        freeze_node->add_child_nocopy (*inode);
@@ -255,16 +253,24 @@ MidiTrack::state(bool full_state)
                root.add_child_nocopy (*freeze_node);
        }
 
-       root.add_property("playback_channel-mode", enum_2_string(get_playback_channel_mode()));
-       root.add_property("capture_channel-mode", enum_2_string(get_capture_channel_mode()));
+       root.set_property("playback-channel-mode", get_playback_channel_mode());
+       root.set_property("capture-channel-mode", get_capture_channel_mode());
        snprintf (buf, sizeof(buf), "0x%x", get_playback_channel_mask());
-       root.add_property("playback-channel-mask", buf);
+       root.set_property("playback-channel-mask", std::string(buf));
        snprintf (buf, sizeof(buf), "0x%x", get_capture_channel_mask());
-       root.add_property("capture-channel-mask", buf);
+       root.set_property("capture-channel-mask", std::string(buf));
 
-       root.add_property ("note-mode", enum_2_string (_note_mode));
-       root.add_property ("step-editing", (_step_editing ? "yes" : "no"));
-       root.add_property ("input-active", (_input_active ? "yes" : "no"));
+       root.set_property ("note-mode", _note_mode);
+       root.set_property ("step-editing", _step_editing);
+       root.set_property ("input-active", _input_active);
+
+       for (Controls::const_iterator c = _controls.begin(); c != _controls.end(); ++c) {
+               if (boost::dynamic_pointer_cast<MidiTrack::MidiControl>(c->second)) {
+                       boost::shared_ptr<AutomationControl> ac = boost::dynamic_pointer_cast<AutomationControl> (c->second);
+                       assert (ac);
+                       root.add_child_nocopy (ac->get_state ());
+               }
+       }
 
        return root;
 }
@@ -273,7 +279,6 @@ void
 MidiTrack::set_state_part_two ()
 {
        XMLNode* fnode;
-       XMLProperty const * prop;
        LocaleGuard lg;
 
        /* This is called after all session state has been restored but before
@@ -293,20 +298,19 @@ MidiTrack::set_state_part_two ()
                }
                _freeze_record.processor_info.clear ();
 
-               if ((prop = fnode->property (X_("playlist"))) != 0) {
-                       boost::shared_ptr<Playlist> pl = _session.playlists->by_name (prop->value());
+               std::string str;
+               if (fnode->get_property (X_("playlist"), str)) {
+                       boost::shared_ptr<Playlist> pl = _session.playlists->by_name (str);
                        if (pl) {
                                _freeze_record.playlist = boost::dynamic_pointer_cast<MidiPlaylist> (pl);
                        } else {
                                _freeze_record.playlist.reset();
                                _freeze_record.state = NoFreeze;
-                       return;
+                               return;
                        }
                }
 
-               if ((prop = fnode->property (X_("state"))) != 0) {
-                       _freeze_record.state = FreezeState (string_2_enum (prop->value(), _freeze_record.state));
-               }
+               fnode->get_property (X_("state"), _freeze_record.state);
 
                XMLNodeConstIterator citer;
                XMLNodeList clist = fnode->children();
@@ -316,13 +320,13 @@ MidiTrack::set_state_part_two ()
                                continue;
                        }
 
-                       if ((prop = (*citer)->property (X_("id"))) == 0) {
+                       if (!(*citer)->get_property (X_("id"), str)) {
                                continue;
                        }
 
                        FreezeRecordProcessorInfo* frii = new FreezeRecordProcessorInfo (*((*citer)->children().front()),
                                                                                   boost::shared_ptr<Processor>());
-                       frii->id = prop->value ();
+                       frii->id = str;
                        _freeze_record.processor_info.push_back (frii);
                }
        }
@@ -334,12 +338,24 @@ MidiTrack::set_state_part_two ()
        return;
 }
 
+void
+MidiTrack::restore_controls ()
+{
+       // TODO order events (CC before PGM to set banks)
+       for (Controls::const_iterator c = _controls.begin(); c != _controls.end(); ++c) {
+               boost::shared_ptr<MidiTrack::MidiControl> mctrl = boost::dynamic_pointer_cast<MidiTrack::MidiControl>(c->second);
+               if (mctrl) {
+                       mctrl->restore_value();
+               }
+       }
+}
+
 void
 MidiTrack::update_controls(const BufferSet& bufs)
 {
        const MidiBuffer& buf = bufs.get_midi(0);
        for (MidiBuffer::const_iterator e = buf.begin(); e != buf.end(); ++e) {
-               const Evoral::MIDIEvent<framepos_t>&     ev      = *e;
+               const Evoral::Event<framepos_t>&         ev      = *e;
                const Evoral::Parameter                  param   = midi_parameter(ev.buffer(), ev.size());
                const boost::shared_ptr<Evoral::Control> control = this->control(param);
                if (control) {
@@ -549,7 +565,7 @@ MidiTrack::push_midi_input_to_step_edit_ringbuffer (framecnt_t nframes)
 
                for (MidiBuffer::const_iterator e = mb->begin(); e != mb->end(); ++e) {
 
-                       const Evoral::MIDIEvent<framepos_t> ev(*e, false);
+                       const Evoral::Event<framepos_t> ev(*e, false);
 
                        /* note on, since for step edit, note length is determined
                           elsewhere
@@ -557,7 +573,7 @@ MidiTrack::push_midi_input_to_step_edit_ringbuffer (framecnt_t nframes)
 
                        if (ev.is_note_on()) {
                                /* we don't care about the time for this purpose */
-                               _step_edit_ring_buffer.write (0, ev.type(), ev.size(), ev.buffer());
+                               _step_edit_ring_buffer.write (0, ev.event_type(), ev.size(), ev.buffer());
                        }
                }
        }
@@ -689,8 +705,7 @@ MidiTrack::write_immediate_event(size_t size, const uint8_t* buf)
                cerr << "WARNING: Ignoring illegal immediate MIDI event" << endl;
                return false;
        }
-       const uint32_t type = midi_parameter_type(buf[0]);
-       return (_immediate_events.write (0, type, size, buf) == size);
+       return (_immediate_events.write (0, Evoral::MIDI_EVENT, size, buf) == size);
 }
 
 void
@@ -701,6 +716,7 @@ MidiTrack::set_parameter_automation_state (Evoral::Parameter param, AutoState st
        case MidiPgmChangeAutomation:
        case MidiPitchBenderAutomation:
        case MidiChannelPressureAutomation:
+       case MidiNotePressureAutomation:
        case MidiSystemExclusiveAutomation:
                /* The track control for MIDI parameters is for immediate events to act
                   as a control surface, write/touch for them is not currently
@@ -711,6 +727,12 @@ MidiTrack::set_parameter_automation_state (Evoral::Parameter param, AutoState st
        }
 }
 
+void
+MidiTrack::MidiControl::restore_value ()
+{
+       actually_set_value (get_value(), Controllable::NoGroup);
+}
+
 void
 MidiTrack::MidiControl::actually_set_value (double val, PBD::Controllable::GroupControlDisposition group_override)
 {
@@ -757,6 +779,12 @@ MidiTrack::MidiControl::actually_set_value (double val, PBD::Controllable::Group
                        ev[1] = int(val);
                        break;
 
+               case MidiNotePressureAutomation:
+                       ev[0] += MIDI_CMD_NOTE_PRESSURE;
+                       ev[1] = parameter.id();
+                       ev[2] = int(val);
+                       break;
+
                case MidiPitchBenderAutomation:
                        ev[0] += MIDI_CMD_BENDER;
                        ev[1] = 0x7F & int(val);