13
0

* UI fixes for track channel selection

* implemented 'forcing midi events into a single channel'
* see http://www.flickr.com/photos/24012642@N02/2430165889/

git-svn-id: svn://localhost/ardour2/branches/3.0@3273 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Hans Baier 2008-04-21 09:39:05 +00:00
parent e8c2b6f371
commit f31abc5eaf
9 changed files with 230 additions and 37 deletions

View File

@ -8,7 +8,7 @@ using namespace Gtk;
using namespace sigc;
MidiChannelSelector::MidiChannelSelector(int no_rows, int no_columns, int start_row, int start_column) :
Table(no_rows, no_columns, true)
Table(no_rows, no_columns, true), _recursion_counter(0)
{
assert(no_rows >= 4);
assert(no_rows >= start_row + 4);
@ -46,28 +46,37 @@ MidiChannelSelector::~MidiChannelSelector()
SingleMidiChannelSelector::SingleMidiChannelSelector(uint8_t active_channel)
: MidiChannelSelector()
{
_active_button = 0;
_last_active_button = 0;
ToggleButton *button = &_buttons[active_channel / 4][active_channel % 4];
button->set_active(true);
_active_button = button;
_active_channel = active_channel;
button->set_active(true);
_last_active_button = button;
}
void
SingleMidiChannelSelector::button_toggled(ToggleButton *button, uint8_t channel)
{
if(button->get_active()) {
if(_active_button) {
_active_button->set_active(false);
{
++_recursion_counter;
if(_recursion_counter == 1) {
// if the current button is active it must
// be different from the first one
if(button->get_active()) {
if(_last_active_button) {
_last_active_button->set_active(false);
_active_channel = channel;
_last_active_button = button;
}
} else {
// if not, the user pressed the already active button
button->set_active(true);
_active_channel = channel;
}
_active_button = button;
_active_channel = channel;
channel_selected.emit(channel);
}
}
--_recursion_counter;
}
MidiMultipleChannelSelector::MidiMultipleChannelSelector(uint16_t initial_selection)
: MidiChannelSelector(4, 6, 0, 0)
: MidiChannelSelector(4, 6, 0, 0), _mode(FILTERING_MULTIPLE_CHANNELS)
{
_select_all.add(*manage(new Label(_("All"))));
_select_all.signal_clicked().connect(
@ -82,6 +91,8 @@ MidiMultipleChannelSelector::MidiMultipleChannelSelector(uint16_t initial_select
mem_fun(this, &MidiMultipleChannelSelector::invert_selection));
_force_channel.add(*manage(new Label(_("Force"))));
_force_channel.signal_toggled().connect(
mem_fun(this, &MidiMultipleChannelSelector::force_channels_button_toggled));
set_homogeneous(false);
attach(*manage(new VSeparator()), 4, 5, 0, 4, SHRINK, FILL, 0, 0);
@ -91,10 +102,53 @@ MidiMultipleChannelSelector::MidiMultipleChannelSelector(uint16_t initial_select
attach(_invert_selection, 5, 6, 2, 3);
attach(_force_channel, 5, 6, 3, 4);
_selected_channels = 0;
set_selected_channels(initial_selection);
}
MidiMultipleChannelSelector::~MidiMultipleChannelSelector()
{
selection_changed.clear();
force_channel_changed.clear();
}
const int8_t
MidiMultipleChannelSelector::get_force_channel() const
{
if(_mode == FORCING_SINGLE_CHANNEL) {
for(int8_t i = 0; i < 16; i++) {
const ToggleButton *button = &_buttons[i / 4][i % 4];
if(button->get_active()) {
return i;
}
}
// this point should not be reached.
assert(false);
}
return -1;
}
const uint16_t
MidiMultipleChannelSelector::get_selected_channels() const
{
uint16_t selected_channels = 0;
for(uint16_t i = 0; i < 16; i++) {
const ToggleButton *button = &_buttons[i / 4][i % 4];
if(button->get_active()) {
selected_channels |= (1L << i);
}
}
return selected_channels;
}
void
MidiMultipleChannelSelector::set_selected_channels(uint16_t selected_channels)
{
for(uint16_t i = 0; i < 16; i++) {
ToggleButton *button = &_buttons[i / 4][i % 4];
if(initial_selection & (1L << i)) {
if(selected_channels & (1L << i)) {
button->set_active(true);
} else {
button->set_active(false);
@ -105,23 +159,73 @@ MidiMultipleChannelSelector::MidiMultipleChannelSelector(uint16_t initial_select
void
MidiMultipleChannelSelector::button_toggled(ToggleButton *button, uint8_t channel)
{
_selected_channels = _selected_channels ^ (1L << channel);
selection_changed.emit(_selected_channels);
++_recursion_counter;
if(_recursion_counter == 1) {
if(_mode == FORCING_SINGLE_CHANNEL) {
set_selected_channels(1 << channel);
}
force_channel_changed.emit(get_force_channel());
selection_changed.emit(get_selected_channels());
}
--_recursion_counter;
}
void
MidiMultipleChannelSelector::force_channels_button_toggled()
{
if(_force_channel.get_active()) {
_mode = FORCING_SINGLE_CHANNEL;
bool found_first_active = false;
// leave only the first button enabled
for(int i = 0; i <= 15; i++) {
ToggleButton *button = &_buttons[i / 4][i % 4];
if(button->get_active()) {
if(found_first_active) {
++_recursion_counter;
button->set_active(false);
--_recursion_counter;
} else {
found_first_active = true;
}
}
}
if(!found_first_active) {
_buttons[0][0].set_active(true);
}
_select_all.set_sensitive(false);
_select_none.set_sensitive(false);
_invert_selection.set_sensitive(false);
force_channel_changed.emit(get_force_channel());
selection_changed.emit(get_selected_channels());
} else {
_mode = FILTERING_MULTIPLE_CHANNELS;
_select_all.set_sensitive(true);
_select_none.set_sensitive(true);
_invert_selection.set_sensitive(true);
force_channel_changed.emit(get_force_channel());
selection_changed.emit(get_selected_channels());
}
}
void
MidiMultipleChannelSelector::select_all(bool on)
{
++_recursion_counter;
for(uint16_t i = 0; i < 16; i++) {
ToggleButton *button = &_buttons[i / 4][i % 4];
button->set_active(on);
}
selection_changed.emit(_selected_channels);
--_recursion_counter;
selection_changed.emit(get_selected_channels());
}
void
MidiMultipleChannelSelector::invert_selection(void)
{
++_recursion_counter;
for(uint16_t i = 0; i < 16; i++) {
ToggleButton *button = &_buttons[i / 4][i % 4];
if(button->get_active()) {
@ -130,6 +234,7 @@ MidiMultipleChannelSelector::invert_selection(void)
button->set_active(true);
}
}
selection_changed.emit(_selected_channels);
--_recursion_counter;
selection_changed.emit(get_selected_channels());
}

View File

@ -19,6 +19,7 @@ protected:
virtual void button_toggled(Gtk::ToggleButton *button, uint8_t button_nr) = 0;
Gtk::Label _button_labels[4][4];
Gtk::ToggleButton _buttons[4][4];
int _recursion_counter;
};
class SingleMidiChannelSelector : public MidiChannelSelector
@ -33,7 +34,7 @@ public:
protected:
virtual void button_toggled(Gtk::ToggleButton *button, uint8_t button_nr);
Gtk::ToggleButton *_active_button;
Gtk::ToggleButton *_last_active_button;
uint8_t _active_channel;
};
@ -41,13 +42,30 @@ class MidiMultipleChannelSelector : public MidiChannelSelector
{
public:
MidiMultipleChannelSelector(uint16_t initial_selection = 1);
virtual ~MidiMultipleChannelSelector();
const uint16_t get_selected_channels() const { return _selected_channels; }
/**
* @return each bit in the returned word represents a midi channel, eg.
* bit 0 represents channel 0 and bit 15 represents channel 15
*
*/
const uint16_t get_selected_channels() const;
void set_selected_channels(uint16_t selected_channels);
sigc::signal<void, uint16_t> selection_changed;
sigc::signal<void, int8_t> force_channel_changed;
const int8_t get_force_channel() const;
protected:
enum Mode {
FILTERING_MULTIPLE_CHANNELS,
FORCING_SINGLE_CHANNEL
};
Mode _mode;
virtual void button_toggled(Gtk::ToggleButton *button, uint8_t button_nr);
void force_channels_button_toggled();
void select_all(bool on);
void invert_selection(void);
@ -56,7 +74,6 @@ protected:
Gtk::Button _select_none;
Gtk::Button _invert_selection;
Gtk::ToggleButton _force_channel;
uint16_t _selected_channels;
};
#endif /*__ardour_ui_midi_channel_selector_h__*/

View File

@ -60,6 +60,7 @@ using namespace ArdourCanvas;
MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv, boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color& basic_color)
: RegionView (parent, tv, r, spu, basic_color)
, force_channel(-1)
, last_channel_selection(0xFFFF)
, _default_note_length(0.0)
, _active_notes(0)
@ -76,6 +77,7 @@ MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &
MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv, boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color& basic_color, TimeAxisViewItem::Visibility visibility)
: RegionView (parent, tv, r, spu, basic_color, visibility)
, force_channel(-1)
, last_channel_selection(0xFFFF)
, _default_note_length(0.0)
, _active_notes(0)
@ -131,6 +133,8 @@ MidiRegionView::init (Gdk::Color& basic_color, bool wfd)
group->signal_event().connect (mem_fun (this, &MidiRegionView::canvas_event), false);
midi_view()->signal_force_channel_changed().connect(
mem_fun(this, &MidiRegionView::midi_force_channel_changed));
midi_view()->signal_channel_selection_changed().connect(
mem_fun(this, &MidiRegionView::midi_channel_selection_changed));
}
@ -1233,9 +1237,19 @@ MidiRegionView::set_frame_color()
}
}
void
MidiRegionView::midi_force_channel_changed(int8_t channel)
{
force_channel = channel;
}
void
MidiRegionView::midi_channel_selection_changed(uint16_t selection)
{
if(force_channel >= 0) {
selection = 0xFFFF;
}
for(std::vector<ArdourCanvas::CanvasMidiEvent*>::iterator i = _events.begin();
i != _events.end();
++i) {

View File

@ -238,6 +238,8 @@ class MidiRegionView : public RegionView
bool canvas_event(GdkEvent* ev);
bool note_canvas_event(GdkEvent* ev);
int8_t force_channel;
void midi_force_channel_changed(int8_t channel);
uint16_t last_channel_selection;
void midi_channel_selection_changed(uint16_t selection);

View File

@ -146,7 +146,9 @@ MidiTimeAxisView::MidiTimeAxisView (PublicEditor& ed, Session& sess, boost::shar
controls_vbox.pack_end(_midi_expander, SHRINK, 0);
_channel_selector.selection_changed.connect(
mem_fun(*midi_track()->midi_diskstream(), &MidiDiskstream::set_channel_mask));
_channel_selector.force_channel_changed.connect(
mem_fun(*midi_track()->midi_diskstream(), &MidiDiskstream::set_force_channel));
}
MidiTimeAxisView::~MidiTimeAxisView ()

View File

@ -74,8 +74,12 @@ class MidiTimeAxisView : public RouteTimeAxisView
void update_range();
sigc::signal<void, uint16_t>& signal_channel_selection_changed() { return _channel_selector.selection_changed; }
sigc::signal<void, uint16_t>& signal_channel_selection_changed()
{ return _channel_selector.selection_changed; }
sigc::signal<void, int8_t>& signal_force_channel_changed()
{ return _channel_selector.force_channel_changed; }
private:
void append_extra_display_menu_items ();

View File

@ -102,6 +102,18 @@ class MidiDiskstream : public Diskstream
return playback_mask;
}
void set_force_channel(int8_t force_channel) {
_playback_buf->set_force_channel(force_channel);
_capture_buf->set_force_channel(force_channel);
}
int8_t get_force_channel() {
int8_t playback_force_channel = _playback_buf->get_force_channel();
int8_t capture_force_channel = _capture_buf->get_force_channel();
assert(playback_force_channel == capture_force_channel);
return playback_force_channel;
}
protected:
friend class Session;

View File

@ -227,7 +227,7 @@ public:
/** @param size Size in bytes.
*/
MidiRingBuffer(size_t size)
: MidiRingBufferBase<Byte>(size), _channel_mask(0xFFFF)
: MidiRingBufferBase<Byte>(size), _channel_mask(0xFFFF), _force_channel(-1)
{}
size_t write(double time, size_t size, const Byte* buf);
@ -238,8 +238,21 @@ public:
size_t read(MidiBuffer& dst, nframes_t start, nframes_t end, nframes_t offset=0);
void set_channel_mask(uint16_t channel_mask) { _channel_mask = channel_mask; }
uint16_t get_channel_mask() { return _channel_mask; }
/**
* @param channel_mask each bit in channel_mask represents a midi channel: bit 0 = channel 0,
* bit 1 = channel 1 etc. the read and write methods will only allow
* events to pass, whose channel bit is 1.
*/
void set_channel_mask(uint16_t channel_mask) { g_atomic_int_set(&channel_mask, channel_mask); }
uint16_t get_channel_mask() { return g_atomic_int_get(&_channel_mask); }
/**
* @param channel if negative, forcing channels is deactivated and filtering channels
* is activated, if positive, the LSB of channel is the channel number
* of the channel all events are forced into and filtering is deactivated
*/
void set_force_channel(int8_t channel) { g_atomic_int_set(&_force_channel, channel); }
int8_t get_force_channel() { return g_atomic_int_get(&_force_channel); }
protected:
inline bool is_channel_event(Byte event_type_byte) {
@ -250,7 +263,8 @@ protected:
}
private:
uint16_t _channel_mask;
volatile uint16_t _channel_mask;
volatile int8_t _force_channel;
};
@ -296,13 +310,13 @@ MidiRingBuffer::write(double time, size_t size, const Byte* buf)
{
printf("MRB - write %#X %d %d with time %lf\n",
buf[0], buf[1], buf[2], time);
assert(size > 0);
// filter events for channels
if(is_channel_event(buf[0])) {
if(is_channel_event(buf[0]) && (g_atomic_int_get(&_force_channel) < 0)) {
// filter events for channels
Byte channel_nr = buf[0] & 0x0F;
if( !(_channel_mask & (1L << channel_nr)) ) {
if( !(g_atomic_int_get(&_channel_mask) & (1L << channel_nr)) ) {
return 0;
}
}
@ -312,7 +326,17 @@ MidiRingBuffer::write(double time, size_t size, const Byte* buf)
} else {
MidiRingBufferBase<Byte>::write(sizeof(double), (Byte*)&time);
MidiRingBufferBase<Byte>::write(sizeof(size_t), (Byte*)&size);
MidiRingBufferBase<Byte>::write(size, buf);
if(is_channel_event(buf[0]) && (g_atomic_int_get(&_force_channel) >= 0)) {
assert(size == 3);
Byte tmp_buf[3];
//force event into channel
tmp_buf[0] = (buf[0] & 0xF0) | (g_atomic_int_get(&_force_channel) & 0x0F);
tmp_buf[1] = buf[1];
tmp_buf[2] = buf[2];
MidiRingBufferBase<Byte>::write(size, tmp_buf);
} else {
MidiRingBufferBase<Byte>::write(size, buf);
}
return size;
}
@ -363,9 +387,10 @@ MidiRingBuffer::read(MidiBuffer& dst, nframes_t start, nframes_t end, nframes_t
}
// filter events for channels
if(is_channel_event(first_event_byte)) {
// filtering is only active, if forcing channels is not active
if(is_channel_event(first_event_byte) && (g_atomic_int_get(&_force_channel) < 0)) {
Byte channel_nr = first_event_byte & 0x0F;
if( !(_channel_mask & (1L << channel_nr)) ) {
if( !(g_atomic_int_get(&_channel_mask) & (1L << channel_nr)) ) {
return 0;
}
}
@ -376,6 +401,9 @@ MidiRingBuffer::read(MidiBuffer& dst, nframes_t start, nframes_t end, nframes_t
success = MidiRingBufferBase<Byte>::full_read(ev.size(), write_loc);
if (success) {
if(is_channel_event(first_event_byte) && (g_atomic_int_get(&_force_channel) >= 0)) {
write_loc[0] = (write_loc[0] & 0xF0) | (g_atomic_int_get(&_force_channel) & 0x0F);
}
++count;
printf("MRB - read event at time %lf\n", ev.time());
} else {

View File

@ -1226,6 +1226,9 @@ MidiDiskstream::get_state ()
snprintf (buf, sizeof(buf), "0x%x", _flags);
node->add_property ("flags", buf);
snprintf (buf, sizeof(buf), "0x%x", get_channel_mask());
node->add_property("channel_mask", buf);
node->add_property ("playlist", _playlist->name());
snprintf (buf, sizeof(buf), "%f", _visible_speed);
@ -1303,6 +1306,12 @@ MidiDiskstream::set_state (const XMLNode& node)
if ((prop = node.property ("flags")) != 0) {
_flags = Flag (string_2_enum (prop->value(), _flags));
}
if ((prop = node.property ("channel_mask")) != 0) {
unsigned int channel_mask;
sscanf (prop->value().c_str(), "0x%x", &channel_mask);
set_channel_mask(channel_mask);
}
if ((prop = node.property ("channels")) != 0) {
nchans = atoi (prop->value().c_str());