mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-10 16:46:35 +01:00
'Channel safe' MIDI:
Resolve note on/off pairs in MidiModel. Add channel field to Parameter (for associating a channel with a CC list). Add channel selector to 'add controller automation' dialog. Write out note and CC MIDI events with proper channel. git-svn-id: svn://localhost/ardour2/branches/3.0@3085 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
parent
466500fdaf
commit
b79d5bfad3
7 changed files with 52 additions and 91 deletions
|
|
@ -36,40 +36,50 @@ using namespace PBD;
|
||||||
using namespace ARDOUR;
|
using namespace ARDOUR;
|
||||||
|
|
||||||
AddMidiCCTrackDialog::AddMidiCCTrackDialog ()
|
AddMidiCCTrackDialog::AddMidiCCTrackDialog ()
|
||||||
: Dialog (_("ardour: add midi controller track")),
|
: Dialog (_("ardour: add midi controller track"))
|
||||||
_cc_num_adjustment (1, 0, 127, 1, 10),
|
, _chan_adjustment (1, 1, 16, 8)
|
||||||
_cc_num_spinner (_cc_num_adjustment)
|
, _chan_spinner (_chan_adjustment)
|
||||||
|
, _cc_num_adjustment (1, 0, 127, 1, 10)
|
||||||
|
, _cc_num_spinner (_cc_num_adjustment)
|
||||||
{
|
{
|
||||||
set_name ("AddMidiCCTrackDialog");
|
set_name ("AddMidiCCTrackDialog");
|
||||||
set_wmclass (X_("ardour_add_track_bus"), "Ardour");
|
set_wmclass (X_("ardour_add_track_bus"), "Ardour");
|
||||||
set_position (Gtk::WIN_POS_MOUSE);
|
set_position (Gtk::WIN_POS_MOUSE);
|
||||||
set_resizable (false);
|
set_resizable (false);
|
||||||
|
|
||||||
|
_chan_spinner.set_name ("AddMidiCCTrackDialogSpinner");
|
||||||
_cc_num_spinner.set_name ("AddMidiCCTrackDialogSpinner");
|
_cc_num_spinner.set_name ("AddMidiCCTrackDialogSpinner");
|
||||||
|
|
||||||
HBox *hbox = manage (new HBox());
|
HBox *chan_box = manage (new HBox());
|
||||||
Label *label = manage(new Label("Controller Number: "));
|
Label *chan_label = manage(new Label("Channel: "));
|
||||||
|
chan_box->pack_start(*chan_label, true, true, 4);
|
||||||
|
chan_box->pack_start(_chan_spinner, false, false, 4);
|
||||||
|
get_vbox()->pack_start(*chan_box, true, true, 4);
|
||||||
|
|
||||||
hbox->pack_start(*label, true, true, 4);
|
HBox* num_box = manage (new HBox());
|
||||||
hbox->pack_start(_cc_num_spinner, false, false, 4);
|
Label *num_label = manage(new Label("Controller: "));
|
||||||
|
num_box->pack_start(*num_label, true, true, 4);
|
||||||
get_vbox()->pack_start(*hbox, true, true, 4);
|
num_box->pack_start(_cc_num_spinner, false, false, 4);
|
||||||
|
get_vbox()->pack_start(*num_box, true, true, 4);
|
||||||
|
|
||||||
add_button (Stock::CANCEL, RESPONSE_CANCEL);
|
add_button (Stock::CANCEL, RESPONSE_CANCEL);
|
||||||
add_button (Stock::ADD, RESPONSE_ACCEPT);
|
add_button (Stock::ADD, RESPONSE_ACCEPT);
|
||||||
|
|
||||||
|
_chan_spinner.show();
|
||||||
|
chan_box->show();
|
||||||
|
chan_label->show();
|
||||||
_cc_num_spinner.show();
|
_cc_num_spinner.show();
|
||||||
hbox->show();
|
num_box->show();
|
||||||
label->show();
|
num_label->show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ARDOUR::Parameter
|
ARDOUR::Parameter
|
||||||
AddMidiCCTrackDialog::parameter ()
|
AddMidiCCTrackDialog::parameter ()
|
||||||
{
|
{
|
||||||
|
int chan = _chan_spinner.get_value_as_int() - 1;
|
||||||
int cc_num = _cc_num_spinner.get_value_as_int();
|
int cc_num = _cc_num_spinner.get_value_as_int();
|
||||||
|
|
||||||
return Parameter(MidiCCAutomation, cc_num);
|
return Parameter(MidiCCAutomation, cc_num, chan);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,8 @@ class AddMidiCCTrackDialog : public Gtk::Dialog
|
||||||
ARDOUR::Parameter parameter ();
|
ARDOUR::Parameter parameter ();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
Gtk::Adjustment _chan_adjustment;
|
||||||
|
Gtk::SpinButton _chan_spinner;
|
||||||
Gtk::Adjustment _cc_num_adjustment;
|
Gtk::Adjustment _cc_num_adjustment;
|
||||||
Gtk::SpinButton _cc_num_spinner;
|
Gtk::SpinButton _cc_num_spinner;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -95,6 +95,7 @@ mtc_slave.cc
|
||||||
named_selection.cc
|
named_selection.cc
|
||||||
note.cc
|
note.cc
|
||||||
panner.cc
|
panner.cc
|
||||||
|
parameter.cc
|
||||||
pcm_utils.cc
|
pcm_utils.cc
|
||||||
playlist.cc
|
playlist.cc
|
||||||
playlist_factory.cc
|
playlist_factory.cc
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ namespace ARDOUR {
|
||||||
*/
|
*/
|
||||||
class Note {
|
class Note {
|
||||||
public:
|
public:
|
||||||
Note(double time=0, double dur=0, uint8_t note=0, uint8_t vel=0x40);
|
Note(uint8_t chan=0, double time=0, double dur=0, uint8_t note=0, uint8_t vel=0x40);
|
||||||
Note(const Note& copy);
|
Note(const Note& copy);
|
||||||
|
|
||||||
const Note& operator=(const Note& copy);
|
const Note& operator=(const Note& copy);
|
||||||
|
|
|
||||||
|
|
@ -48,85 +48,32 @@ namespace ARDOUR {
|
||||||
class Parameter
|
class Parameter
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
inline Parameter(AutomationType type = NullAutomation, uint32_t id=0) : _type(type), _id(id) {}
|
Parameter(AutomationType type = NullAutomation, uint32_t id=0, uint8_t channel=0)
|
||||||
|
: _type(type), _id(id), _channel(channel)
|
||||||
|
{}
|
||||||
|
|
||||||
/** Construct an Parameter from a string returned from Parameter::to_string
|
Parameter(const std::string& str);
|
||||||
* (AutomationList automation-id property)
|
|
||||||
*/
|
inline AutomationType type() const { return _type; }
|
||||||
Parameter(const std::string& str) : _type(NullAutomation), _id(0) {
|
inline uint32_t id() const { return _id; }
|
||||||
if (str == "gain") {
|
inline uint8_t channel() const { return _channel; }
|
||||||
_type = GainAutomation;
|
|
||||||
} else if (str == "solo") {
|
inline bool operator==(const Parameter& id) const {
|
||||||
_type = SoloAutomation;
|
return (_type == id._type && _id == id._id);
|
||||||
} else if (str == "mute") {
|
|
||||||
_type = MuteAutomation;
|
|
||||||
} else if (str == "fadein") {
|
|
||||||
_type = FadeInAutomation;
|
|
||||||
} else if (str == "fadeout") {
|
|
||||||
_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);
|
|
||||||
} else if (str.length() > 7 && str.substr(0, 7) == "midicc-") {
|
|
||||||
_type = MidiCCAutomation;
|
|
||||||
_id = atoi(str.c_str()+7);
|
|
||||||
} else {
|
|
||||||
PBD::warning << "Unknown Parameter '" << str << "'" << endmsg;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline AutomationType type() const { return _type; }
|
/** Arbitrary but fixed ordering (for use in e.g. std::map) */
|
||||||
inline uint32_t id() const { return _id; }
|
|
||||||
|
|
||||||
inline bool operator==(const Parameter& id) const
|
|
||||||
{ return (_type == id._type && _id == id._id); }
|
|
||||||
|
|
||||||
/** Arbitrary but fixed ordering, so we're comparable (usable in std::map) */
|
|
||||||
inline bool operator<(const Parameter& id) const {
|
inline bool operator<(const Parameter& id) const {
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
if (_type == NullAutomation)
|
if (_type == NullAutomation)
|
||||||
PBD::warning << "Uninitialized Parameter compared." << endmsg;
|
PBD::warning << "Uninitialized Parameter compared." << endmsg;
|
||||||
#endif
|
#endif
|
||||||
return (_type < id._type || _id < id._id);
|
return (_channel < id._channel || _type < id._type || _id < id._id);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline operator bool() const { return (_type != 0); }
|
inline operator bool() const { return (_type != 0); }
|
||||||
|
|
||||||
/** Unique string representation, suitable as an XML property value.
|
std::string to_string() const;
|
||||||
* e.g. <AutomationList automation-id="whatthisreturns">
|
|
||||||
*/
|
|
||||||
inline std::string to_string() const {
|
|
||||||
if (_type == GainAutomation) {
|
|
||||||
return "gain";
|
|
||||||
} else if (_type == PanAutomation) {
|
|
||||||
return string_compose("pan-%1", _id);
|
|
||||||
} else if (_type == SoloAutomation) {
|
|
||||||
return "solo";
|
|
||||||
} else if (_type == MuteAutomation) {
|
|
||||||
return "mute";
|
|
||||||
} else if (_type == FadeInAutomation) {
|
|
||||||
return "fadein";
|
|
||||||
} else if (_type == FadeOutAutomation) {
|
|
||||||
return "fadeout";
|
|
||||||
} else if (_type == EnvelopeAutomation) {
|
|
||||||
return "envelope";
|
|
||||||
} else if (_type == PluginAutomation) {
|
|
||||||
return string_compose("parameter-%1", _id);
|
|
||||||
} else if (_type == MidiCCAutomation) {
|
|
||||||
return string_compose("midicc-%1", _id);
|
|
||||||
} else {
|
|
||||||
assert(false);
|
|
||||||
PBD::warning << "Uninitialized Parameter to_string() called." << endmsg;
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The below properties are only used for CC right now, but unchanging properties
|
/* The below properties are only used for CC right now, but unchanging properties
|
||||||
* of parameters (rather than changing parameters of automation lists themselves)
|
* of parameters (rather than changing parameters of automation lists themselves)
|
||||||
|
|
@ -154,6 +101,7 @@ private:
|
||||||
// default copy constructor is ok
|
// default copy constructor is ok
|
||||||
AutomationType _type;
|
AutomationType _type;
|
||||||
uint32_t _id;
|
uint32_t _id;
|
||||||
|
uint8_t _channel;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -304,9 +304,10 @@ MidiModel::control_to_midi_event(MidiEvent& ev, const MidiControlIterator& iter)
|
||||||
ev.set_buffer((Byte*)malloc(3), true);
|
ev.set_buffer((Byte*)malloc(3), true);
|
||||||
|
|
||||||
assert(iter.first);
|
assert(iter.first);
|
||||||
|
assert(iter.first->parameter().channel() < 16);
|
||||||
assert(iter.first->parameter().id() <= INT8_MAX);
|
assert(iter.first->parameter().id() <= INT8_MAX);
|
||||||
assert(iter.second.second <= INT8_MAX);
|
assert(iter.second.second <= INT8_MAX);
|
||||||
ev.buffer()[0] = MIDI_CMD_CONTROL;
|
ev.buffer()[0] = MIDI_CMD_CONTROL + iter.first->parameter().channel();
|
||||||
ev.buffer()[1] = (Byte)iter.first->parameter().id();
|
ev.buffer()[1] = (Byte)iter.first->parameter().id();
|
||||||
ev.buffer()[2] = (Byte)iter.second.second;
|
ev.buffer()[2] = (Byte)iter.second.second;
|
||||||
ev.time() = iter.second.first; // x
|
ev.time() = iter.second.first; // x
|
||||||
|
|
@ -412,7 +413,7 @@ MidiModel::append_note_on_unlocked(uint8_t chan, double time, uint8_t note_num,
|
||||||
assert(chan < 16);
|
assert(chan < 16);
|
||||||
assert(_writing);
|
assert(_writing);
|
||||||
|
|
||||||
_notes.push_back(boost::shared_ptr<Note>(new Note(time, 0, note_num, velocity)));
|
_notes.push_back(boost::shared_ptr<Note>(new Note(chan, time, 0, note_num, velocity)));
|
||||||
if (_note_mode == Sustained) {
|
if (_note_mode == Sustained) {
|
||||||
//cerr << "MM Sustained: Appending active note on " << (unsigned)(uint8_t)note_num << endl;
|
//cerr << "MM Sustained: Appending active note on " << (unsigned)(uint8_t)note_num << endl;
|
||||||
_write_notes[chan].push_back(_notes.size() - 1);
|
_write_notes[chan].push_back(_notes.size() - 1);
|
||||||
|
|
@ -471,10 +472,7 @@ MidiModel::append_cc_unlocked(uint8_t chan, double time, uint8_t number, uint8_t
|
||||||
assert(chan < 16);
|
assert(chan < 16);
|
||||||
assert(_writing);
|
assert(_writing);
|
||||||
|
|
||||||
if (chan != 0) // FIXME
|
Parameter param(MidiCCAutomation, number, chan);
|
||||||
cerr << "WARNING: CC on non-0 channel, channel information lost!" << endl;
|
|
||||||
|
|
||||||
Parameter param(MidiCCAutomation, number);
|
|
||||||
|
|
||||||
boost::shared_ptr<AutomationControl> control = Automatable::control(param, true);
|
boost::shared_ptr<AutomationControl> control = Automatable::control(param, true);
|
||||||
control->list()->fast_simple_add(time, (double)value);
|
control->list()->fast_simple_add(time, (double)value);
|
||||||
|
|
|
||||||
|
|
@ -22,15 +22,17 @@
|
||||||
|
|
||||||
namespace ARDOUR {
|
namespace ARDOUR {
|
||||||
|
|
||||||
Note::Note(double t, double d, uint8_t n, uint8_t v)
|
Note::Note(uint8_t chan, double t, double d, uint8_t n, uint8_t v)
|
||||||
: _on_event(t, 3, NULL, true)
|
: _on_event(t, 3, NULL, true)
|
||||||
, _off_event(t + d, 3, NULL, true)
|
, _off_event(t + d, 3, NULL, true)
|
||||||
{
|
{
|
||||||
_on_event.buffer()[0] = MIDI_CMD_NOTE_ON;
|
assert(chan < 16);
|
||||||
|
|
||||||
|
_on_event.buffer()[0] = MIDI_CMD_NOTE_ON + chan;
|
||||||
_on_event.buffer()[1] = n;
|
_on_event.buffer()[1] = n;
|
||||||
_on_event.buffer()[2] = v;
|
_on_event.buffer()[2] = v;
|
||||||
|
|
||||||
_off_event.buffer()[0] = MIDI_CMD_NOTE_OFF;
|
_off_event.buffer()[0] = MIDI_CMD_NOTE_OFF + chan;
|
||||||
_off_event.buffer()[1] = n;
|
_off_event.buffer()[1] = n;
|
||||||
_off_event.buffer()[2] = 0x40;
|
_off_event.buffer()[2] = 0x40;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue