triggerbox: maintain UsedChannels and Patch Change data
preserve this data across drag&drop, session save, etc
This commit is contained in:
parent
dce3f2eb65
commit
0cee4f45ce
|
@ -41,6 +41,7 @@
|
|||
#include "temporal/tempo.h"
|
||||
|
||||
#include "evoral/PatchChange.h"
|
||||
#include "evoral/SMF.h"
|
||||
|
||||
#include "ardour/midi_model.h"
|
||||
#include "ardour/midi_state_tracker.h"
|
||||
|
@ -178,6 +179,9 @@ class LIBARDOUR_API Trigger : public PBD::Stateful {
|
|||
bool cue_isolated = false;
|
||||
StretchMode stretch_mode = Trigger::Crisp;
|
||||
|
||||
Evoral::SMF::UsedChannels used_channels = Evoral::SMF::UsedChannels();
|
||||
Evoral::PatchChange<MidiBuffer::TimeType> patch_change[16];
|
||||
|
||||
std::string name = "";
|
||||
color_t color = 0xBEBEBEFF;
|
||||
double tempo = 0; //unset
|
||||
|
@ -204,6 +208,11 @@ class LIBARDOUR_API Trigger : public PBD::Stateful {
|
|||
stretchable = other.stretchable;
|
||||
cue_isolated = other.cue_isolated;
|
||||
stretch_mode = other.stretch_mode;
|
||||
used_channels = other.used_channels;
|
||||
|
||||
for (int i = 0; i<16; i++) {
|
||||
patch_change[i] = other.patch_change[i];
|
||||
}
|
||||
|
||||
name = other.name;
|
||||
color = other.color;
|
||||
|
@ -229,6 +238,7 @@ class LIBARDOUR_API Trigger : public PBD::Stateful {
|
|||
TRIGGERBOX_PROPERTY_DECL (stretchable, bool);
|
||||
TRIGGERBOX_PROPERTY_DECL (cue_isolated, bool);
|
||||
TRIGGERBOX_PROPERTY_DECL (stretch_mode, StretchMode);
|
||||
TRIGGERBOX_PROPERTY_DECL (used_channels, Evoral::SMF::UsedChannels);
|
||||
TRIGGERBOX_PROPERTY_DECL (color, color_t);
|
||||
TRIGGERBOX_PROPERTY_DECL_CONST_REF (name, std::string);
|
||||
|
||||
|
@ -345,9 +355,23 @@ class LIBARDOUR_API Trigger : public PBD::Stateful {
|
|||
TriggerBox& box() const { return _box; }
|
||||
|
||||
double estimated_tempo() const { return _estimated_tempo; }
|
||||
|
||||
/* the following functions deal with audio- or midi-specific SegmentDescriptor properties, provided as virtuals so we don't have to do lots of dynamic_casting */
|
||||
/* segment_tempo is currently a no-op for MIDI, but may be implemented later */
|
||||
virtual double segment_tempo() const = 0;
|
||||
virtual void set_segment_tempo (double t) = 0;
|
||||
|
||||
/* used_segment_channels is a no-op for audio */
|
||||
virtual Evoral::SMF::UsedChannels segment_used_channels() const { return Evoral::SMF::UsedChannels(); }
|
||||
virtual void set_segment_used_channels (Evoral::SMF::UsedChannels) {}
|
||||
|
||||
/* patch changes are a no-op for audio */
|
||||
virtual void set_patch_change (Evoral::PatchChange<MidiBuffer::TimeType> const &) {}
|
||||
virtual Evoral::PatchChange<MidiBuffer::TimeType> const patch_change (uint8_t) const { return Evoral::PatchChange<MidiBuffer::TimeType>(); }
|
||||
virtual void unset_patch_change (uint8_t channel) {}
|
||||
virtual void unset_all_patch_changes () {}
|
||||
virtual bool patch_change_set (uint8_t channel) const { return false; }
|
||||
|
||||
virtual void setup_stretcher () = 0;
|
||||
|
||||
Temporal::Meter meter() const { return _meter; }
|
||||
|
@ -554,11 +578,15 @@ class LIBARDOUR_API MIDITrigger : public Trigger {
|
|||
void start_and_roll_to (samplepos_t start, samplepos_t position);
|
||||
|
||||
void set_patch_change (Evoral::PatchChange<MidiBuffer::TimeType> const &);
|
||||
Evoral::PatchChange<MidiBuffer::TimeType> const & patch_change (uint8_t) const;
|
||||
Evoral::PatchChange<MidiBuffer::TimeType> const patch_change (uint8_t) const;
|
||||
void unset_patch_change (uint8_t channel);
|
||||
void unset_all_patch_changes ();
|
||||
bool patch_change_set (uint8_t channel) const;
|
||||
|
||||
/* It's possible that a portion of a midi file would use a subset of the total channels used, so store that info in the segment descriptor */
|
||||
Evoral::SMF::UsedChannels segment_used_channels() const { return _segment_used_channels; }
|
||||
void set_segment_used_channels (Evoral::SMF::UsedChannels);
|
||||
|
||||
/* theoretically, MIDI files can have a dedicated tempo outside the session tempo map (*un-stretched*) but this is currently unimplemented */
|
||||
/* boilerplate tempo functions are provided here so we don't have to do constant dynamic_cast checks to use the tempo+stretch APIs */
|
||||
virtual double segment_tempo() const {return 120.0;}
|
||||
|
@ -591,6 +619,8 @@ class LIBARDOUR_API MIDITrigger : public Trigger {
|
|||
Evoral::PatchChange<MidiBuffer::TimeType> _patch_change[16];
|
||||
std::vector<int> _channel_map;
|
||||
|
||||
Evoral::SMF::UsedChannels _segment_used_channels;
|
||||
|
||||
int load_data (boost::shared_ptr<MidiRegion>);
|
||||
void compute_and_set_length ();
|
||||
void _startup (BufferSet&, pframes_t dest_offset, Temporal::BBT_Offset const &);
|
||||
|
@ -894,6 +924,7 @@ namespace Properties {
|
|||
LIBARDOUR_API extern PBD::PropertyDescriptor<bool> cue_isolated;
|
||||
LIBARDOUR_API extern PBD::PropertyDescriptor<bool> patch_change; /* type not important */
|
||||
LIBARDOUR_API extern PBD::PropertyDescriptor<bool> channel_map; /* type not important */
|
||||
LIBARDOUR_API extern PBD::PropertyDescriptor<bool> used_channels; /* type not important */
|
||||
|
||||
LIBARDOUR_API extern PBD::PropertyDescriptor<bool> tempo_meter; /* only used to transmit changes, not storage */
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include "temporal/tempo.h"
|
||||
|
||||
#include "ardour/auditioner.h"
|
||||
#include "ardour/audioengine.h"
|
||||
#include "ardour/audioregion.h"
|
||||
#include "ardour/audio_buffer.h"
|
||||
|
@ -68,6 +69,7 @@ namespace ARDOUR {
|
|||
PBD::PropertyDescriptor<bool> tempo_meter; /* only to transmit updates, not storage */
|
||||
PBD::PropertyDescriptor<bool> patch_change; /* only to transmit updates, not storage */
|
||||
PBD::PropertyDescriptor<bool> channel_map; /* only to transmit updates, not storage */
|
||||
PBD::PropertyDescriptor<bool> used_channels; /* only to transmit updates, not storage */
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -240,6 +242,11 @@ Trigger::get_ui_state (Trigger::UIState &state) const
|
|||
state.name = _name;
|
||||
state.color = _color;
|
||||
|
||||
state.used_channels = segment_used_channels();
|
||||
for (int i = 0; i<16; i++) {
|
||||
state.patch_change[i] = patch_change(i);
|
||||
}
|
||||
|
||||
/* tempo is currently not a property */
|
||||
state.tempo = segment_tempo();
|
||||
}
|
||||
|
@ -257,6 +264,13 @@ Trigger::set_ui_state (Trigger::UIState &state)
|
|||
if (state.tempo > 0) {
|
||||
set_segment_tempo(state.tempo);
|
||||
}
|
||||
|
||||
set_segment_used_channels(state.used_channels);
|
||||
for (int chan = 0; chan<16; chan++) {
|
||||
if (state.patch_change[chan].is_set()) {
|
||||
set_patch_change(state.patch_change[chan]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -303,6 +317,14 @@ Trigger::update_properties ()
|
|||
_name = ui_state.name;
|
||||
}
|
||||
|
||||
set_segment_used_channels(ui_state.used_channels);
|
||||
|
||||
for (int chan = 0; chan<16; chan++) {
|
||||
if (ui_state.patch_change[chan].is_set()) {
|
||||
set_patch_change(ui_state.patch_change[chan]);
|
||||
}
|
||||
}
|
||||
|
||||
last_property_generation = g;
|
||||
}
|
||||
|
||||
|
@ -333,6 +355,11 @@ Trigger::copy_to_ui_state ()
|
|||
ui_state.stretch_mode = _stretch_mode;
|
||||
ui_state.name = _name;
|
||||
ui_state.color = _color;
|
||||
|
||||
ui_state.used_channels = segment_used_channels();
|
||||
for (int i = 0; i<16; i++) {
|
||||
ui_state.patch_change[i] = patch_change(i); //TODO: maybe these should be initialized here instead of later
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1999,12 +2026,8 @@ MIDITrigger::MIDITrigger (uint32_t n, TriggerBox& b)
|
|||
, last_event_beats (Temporal::Beats())
|
||||
, _start_offset (0, 0, 0)
|
||||
, _legato_offset (0, 0, 0)
|
||||
, _segment_used_channels (Evoral::SMF::UsedChannels())
|
||||
{
|
||||
#if 0 /* for prototype + testing only */
|
||||
Evoral::PatchChange<MidiBuffer::TimeType> pc (0, 0, 12, 0);
|
||||
set_patch_change (pc);
|
||||
#endif
|
||||
|
||||
_channel_map.assign (16, -1);
|
||||
}
|
||||
|
||||
|
@ -2012,6 +2035,28 @@ MIDITrigger::~MIDITrigger ()
|
|||
{
|
||||
}
|
||||
|
||||
void
|
||||
MIDITrigger::set_segment_used_channels (Evoral::SMF::UsedChannels used)
|
||||
{
|
||||
if (_segment_used_channels != used) {
|
||||
|
||||
_segment_used_channels = used;
|
||||
|
||||
send_property_change (ARDOUR::Properties::used_channels);
|
||||
_box.session().set_dirty();
|
||||
}
|
||||
|
||||
/* TODO: once we have a Region Trimmer, this could get more complicated:
|
||||
* this segment might overlap another SD (Coverage==Internal|Start|End)
|
||||
* in which case we might be setting both SDs, or not. TBD*/
|
||||
if (_region) {
|
||||
SegmentDescriptor segment = get_segment_descriptor();
|
||||
for (auto & src : _region->sources()) {
|
||||
src->set_segment_descriptor (segment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MIDITrigger::set_channel_map (int channel, int target)
|
||||
{
|
||||
|
@ -2091,11 +2136,15 @@ MIDITrigger::patch_change_set (uint8_t channel) const
|
|||
return _patch_change[channel].is_set();
|
||||
}
|
||||
|
||||
Evoral::PatchChange<MidiBuffer::TimeType> const &
|
||||
Evoral::PatchChange<MidiBuffer::TimeType> const
|
||||
MIDITrigger::patch_change (uint8_t channel) const
|
||||
{
|
||||
Evoral::PatchChange<MidiBuffer::TimeType> ret;
|
||||
|
||||
assert (channel < 16);
|
||||
return _patch_change[channel];
|
||||
ret = _patch_change[channel];
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
@ -2227,6 +2276,9 @@ MIDITrigger::get_state (void)
|
|||
|
||||
node.set_property (X_("start"), start_offset());
|
||||
|
||||
std::string uchan = string_compose ("%1", _segment_used_channels.to_ulong());
|
||||
node.set_property (X_("used-channels"), uchan);
|
||||
|
||||
XMLNode* patches_node = 0;
|
||||
|
||||
for (int chn = 0; chn < 16; ++chn) {
|
||||
|
@ -2274,6 +2326,18 @@ MIDITrigger::set_state (const XMLNode& node, int version)
|
|||
return -1;
|
||||
}
|
||||
|
||||
std::string uchan;
|
||||
if (node.get_property (X_("used-channels"), uchan)) {
|
||||
} else {
|
||||
unsigned long ul;
|
||||
std::stringstream ss (uchan);
|
||||
ss >> ul;
|
||||
if (!ss) {
|
||||
return -1;
|
||||
}
|
||||
set_segment_used_channels( Evoral::SMF::UsedChannels(ul) );
|
||||
}
|
||||
|
||||
node.get_property (X_("start"), t);
|
||||
Temporal::Beats b (t.beats());
|
||||
/* XXX need to deal with bar offsets */
|
||||
|
@ -2312,6 +2376,9 @@ MIDITrigger::set_state (const XMLNode& node, int version)
|
|||
}
|
||||
}
|
||||
|
||||
/* we've changed our internal values; we need to update our queued UIState or they will be lost when UIState is applied */
|
||||
copy_to_ui_state ();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue