mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-08 07:45:00 +01:00
* first working version of editing MIDI channels of individual notes, see: http://www.flickr.com/photos/24012642@N02/2412142661/
git-svn-id: svn://localhost/ardour2/branches/3.0@3252 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
parent
2656e0a43b
commit
8b3d298f6b
9 changed files with 239 additions and 13 deletions
|
|
@ -192,6 +192,7 @@ midi_region_view.cc
|
||||||
midi_scroomer.cc
|
midi_scroomer.cc
|
||||||
midi_streamview.cc
|
midi_streamview.cc
|
||||||
midi_time_axis.cc
|
midi_time_axis.cc
|
||||||
|
midi_channel_selector.cc
|
||||||
mixer_strip.cc
|
mixer_strip.cc
|
||||||
mixer_ui.cc
|
mixer_ui.cc
|
||||||
new_session_dialog.cc
|
new_session_dialog.cc
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ CanvasMidiEvent::CanvasMidiEvent(MidiRegionView& region, Item* item,
|
||||||
: _region(region)
|
: _region(region)
|
||||||
, _item(item)
|
, _item(item)
|
||||||
, _text(0)
|
, _text(0)
|
||||||
|
, _channel_selector_widget()
|
||||||
, _state(None)
|
, _state(None)
|
||||||
, _note(note)
|
, _note(note)
|
||||||
, _selected(false)
|
, _selected(false)
|
||||||
|
|
@ -43,6 +44,12 @@ CanvasMidiEvent::CanvasMidiEvent(MidiRegionView& region, Item* item,
|
||||||
_text = new Text(*(item->property_parent()));
|
_text = new Text(*(item->property_parent()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CanvasMidiEvent::~CanvasMidiEvent()
|
||||||
|
{
|
||||||
|
if(_text) delete _text;
|
||||||
|
if(_channel_selector_widget) delete _channel_selector_widget;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CanvasMidiEvent::move_event(double dx, double dy)
|
CanvasMidiEvent::move_event(double dx, double dy)
|
||||||
{
|
{
|
||||||
|
|
@ -71,6 +78,50 @@ CanvasMidiEvent::hide_velocity(void)
|
||||||
_text->hide();
|
_text->hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CanvasMidiEvent::on_channel_change(uint8_t channel)
|
||||||
|
{
|
||||||
|
_region.note_selected(this, true);
|
||||||
|
hide_channel_selector();
|
||||||
|
_region.change_channel(channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CanvasMidiEvent::show_channel_selector(void)
|
||||||
|
{
|
||||||
|
if(_channel_selector_widget == 0) {
|
||||||
|
cerr << "Note has channel: " << int(_note->channel()) << endl;
|
||||||
|
SingleMidiChannelSelector* _channel_selector = new SingleMidiChannelSelector(_note->channel());
|
||||||
|
_channel_selector->show_all();
|
||||||
|
_channel_selector->channel_selected.connect(
|
||||||
|
sigc::mem_fun(this, &CanvasMidiEvent::on_channel_change));
|
||||||
|
|
||||||
|
_channel_selector_widget =
|
||||||
|
new Widget(*(_item->property_parent()),
|
||||||
|
x1(),
|
||||||
|
y2() + 2,
|
||||||
|
(Gtk::Widget &) *_channel_selector);
|
||||||
|
|
||||||
|
_channel_selector_widget->hide();
|
||||||
|
_channel_selector_widget->property_height() = 100;
|
||||||
|
_channel_selector_widget->property_width() = 100;
|
||||||
|
_channel_selector_widget->raise_to_top();
|
||||||
|
_channel_selector_widget->show();
|
||||||
|
} else {
|
||||||
|
hide_channel_selector();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CanvasMidiEvent::hide_channel_selector(void)
|
||||||
|
{
|
||||||
|
if(_channel_selector_widget) {
|
||||||
|
_channel_selector_widget->hide();
|
||||||
|
delete _channel_selector_widget;
|
||||||
|
_channel_selector_widget = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CanvasMidiEvent::selected(bool yn)
|
CanvasMidiEvent::selected(bool yn)
|
||||||
{
|
{
|
||||||
|
|
@ -120,7 +171,6 @@ CanvasMidiEvent::on_event(GdkEvent* ev)
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} else if(ev->scroll.direction == GDK_SCROLL_DOWN) {
|
} else if(ev->scroll.direction == GDK_SCROLL_DOWN) {
|
||||||
|
|
||||||
_region.note_selected(this, true);
|
_region.note_selected(this, true);
|
||||||
if (_region.mouse_state() == MidiRegionView::SelectTouchDragging) {
|
if (_region.mouse_state() == MidiRegionView::SelectTouchDragging) {
|
||||||
// TODO: absolute velocity
|
// TODO: absolute velocity
|
||||||
|
|
@ -164,6 +214,8 @@ CanvasMidiEvent::on_event(GdkEvent* ev)
|
||||||
case GDK_BUTTON_PRESS:
|
case GDK_BUTTON_PRESS:
|
||||||
if (ev->button.button == 1) {
|
if (ev->button.button == 1) {
|
||||||
_state = Pressed;
|
_state = Pressed;
|
||||||
|
} else if (ev->button.button == 3) {
|
||||||
|
show_channel_selector();
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
|
@ -237,6 +289,10 @@ CanvasMidiEvent::on_event(GdkEvent* ev)
|
||||||
event_y = ev->button.y;
|
event_y = ev->button.y;
|
||||||
_item->property_parent().get_value()->w2i(event_x, event_y);
|
_item->property_parent().get_value()->w2i(event_x, event_y);
|
||||||
|
|
||||||
|
if(ev->button.button == 3) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
switch (_state) {
|
switch (_state) {
|
||||||
case Pressed: // Clicked
|
case Pressed: // Clicked
|
||||||
if (_region.midi_view()->editor.current_midi_edit_mode() == Editing::MidiEditSelect) {
|
if (_region.midi_view()->editor.current_midi_edit_mode() == Editing::MidiEditSelect) {
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,9 @@
|
||||||
#define __gtk_ardour_canvas_midi_event_h__
|
#define __gtk_ardour_canvas_midi_event_h__
|
||||||
|
|
||||||
#include "simplerect.h"
|
#include "simplerect.h"
|
||||||
|
#include "midi_channel_selector.h"
|
||||||
#include <libgnomecanvasmm/text.h>
|
#include <libgnomecanvasmm/text.h>
|
||||||
|
#include <libgnomecanvasmm/widget.h>
|
||||||
#include <ardour/midi_model.h>
|
#include <ardour/midi_model.h>
|
||||||
|
|
||||||
class Editor;
|
class Editor;
|
||||||
|
|
@ -49,7 +51,7 @@ public:
|
||||||
Item* item,
|
Item* item,
|
||||||
const boost::shared_ptr<ARDOUR::Note> note = boost::shared_ptr<ARDOUR::Note>());
|
const boost::shared_ptr<ARDOUR::Note> note = boost::shared_ptr<ARDOUR::Note>());
|
||||||
|
|
||||||
virtual ~CanvasMidiEvent() { if(_text) delete _text; }
|
virtual ~CanvasMidiEvent();
|
||||||
|
|
||||||
bool on_event(GdkEvent* ev);
|
bool on_event(GdkEvent* ev);
|
||||||
|
|
||||||
|
|
@ -61,6 +63,15 @@ public:
|
||||||
void show_velocity();
|
void show_velocity();
|
||||||
void hide_velocity();
|
void hide_velocity();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This slot is called, when a new channel is selected for the event
|
||||||
|
* */
|
||||||
|
void on_channel_change(uint8_t channel);
|
||||||
|
|
||||||
|
void show_channel_selector();
|
||||||
|
|
||||||
|
void hide_channel_selector();
|
||||||
|
|
||||||
virtual void set_outline_color(uint32_t c) = 0;
|
virtual void set_outline_color(uint32_t c) = 0;
|
||||||
virtual void set_fill_color(uint32_t c) = 0;
|
virtual void set_fill_color(uint32_t c) = 0;
|
||||||
|
|
||||||
|
|
@ -77,6 +88,7 @@ protected:
|
||||||
MidiRegionView& _region;
|
MidiRegionView& _region;
|
||||||
Item* const _item;
|
Item* const _item;
|
||||||
Text* _text;
|
Text* _text;
|
||||||
|
Widget* _channel_selector_widget;
|
||||||
State _state;
|
State _state;
|
||||||
const boost::shared_ptr<ARDOUR::Note> _note;
|
const boost::shared_ptr<ARDOUR::Note> _note;
|
||||||
bool _own_note;
|
bool _own_note;
|
||||||
|
|
|
||||||
|
|
@ -433,12 +433,12 @@ CrossfadeEditor::canvas_event (GdkEvent* event)
|
||||||
case GDK_BUTTON_PRESS:
|
case GDK_BUTTON_PRESS:
|
||||||
add_control_point ((event->button.x - canvas_border)/effective_width(),
|
add_control_point ((event->button.x - canvas_border)/effective_width(),
|
||||||
1.0 - ((event->button.y - canvas_border)/effective_height()));
|
1.0 - ((event->button.y - canvas_border)/effective_height()));
|
||||||
return TRUE;
|
return true;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return FALSE;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
CrossfadeEditor::Point::~Point()
|
CrossfadeEditor::Point::~Point()
|
||||||
|
|
|
||||||
65
gtk2_ardour/midi_channel_selector.cc
Normal file
65
gtk2_ardour/midi_channel_selector.cc
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
#include "midi_channel_selector.h"
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
MidiChannelSelector::MidiChannelSelector() :
|
||||||
|
Gtk::Table(4,4,true)
|
||||||
|
{
|
||||||
|
property_column_spacing() = 0;
|
||||||
|
property_row_spacing() = 0;
|
||||||
|
|
||||||
|
uint8_t channel_nr = 0;
|
||||||
|
for(int row = 0; row < 4; ++row) {
|
||||||
|
for(int column = 0; column < 4; ++column) {
|
||||||
|
ostringstream channel;
|
||||||
|
channel << int(++channel_nr);
|
||||||
|
_button_labels[row][column].set_text(channel.str());
|
||||||
|
_button_labels[row][column].set_justify(Gtk::JUSTIFY_RIGHT);
|
||||||
|
_buttons[row][column].add(_button_labels[row][column]);
|
||||||
|
_buttons[row][column].signal_toggled().connect(
|
||||||
|
sigc::bind(
|
||||||
|
sigc::mem_fun(this, &MidiChannelSelector::button_toggled),
|
||||||
|
&_buttons[row][column],
|
||||||
|
channel_nr - 1));
|
||||||
|
attach(_buttons[row][column], column, column + 1, row, row + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MidiChannelSelector::~MidiChannelSelector()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SingleMidiChannelSelector::SingleMidiChannelSelector(uint8_t active_channel)
|
||||||
|
: MidiChannelSelector()
|
||||||
|
{
|
||||||
|
_active_button = 0;
|
||||||
|
Gtk::ToggleButton *button = &_buttons[active_channel / 4][active_channel % 4];
|
||||||
|
button->set_active(true);
|
||||||
|
_active_button = button;
|
||||||
|
_active_channel = active_channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SingleMidiChannelSelector::button_toggled(Gtk::ToggleButton *button, uint8_t channel)
|
||||||
|
{
|
||||||
|
if(button->get_active()) {
|
||||||
|
if(_active_button) {
|
||||||
|
_active_button->set_active(false);
|
||||||
|
}
|
||||||
|
_active_button = button;
|
||||||
|
_active_channel = channel;
|
||||||
|
channel_selected.emit(channel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MidiMultipleChannelSelector::button_toggled(Gtk::ToggleButton *button, uint8_t channel)
|
||||||
|
{
|
||||||
|
if(button->get_active()) {
|
||||||
|
_selected_channels.insert(channel);
|
||||||
|
} else {
|
||||||
|
_selected_channels.erase(channel);
|
||||||
|
}
|
||||||
|
}
|
||||||
49
gtk2_ardour/midi_channel_selector.h
Normal file
49
gtk2_ardour/midi_channel_selector.h
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
#ifndef __ardour_ui_midi_channel_selector_h__
|
||||||
|
#define __ardour_ui_midi_channel_selector_h__
|
||||||
|
|
||||||
|
#include "gtkmm/table.h"
|
||||||
|
#include "sigc++/trackable.h"
|
||||||
|
#include "gtkmm/togglebutton.h"
|
||||||
|
#include "gtkmm/label.h"
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
class MidiChannelSelector : public Gtk::Table
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MidiChannelSelector();
|
||||||
|
virtual ~MidiChannelSelector() = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void button_toggled(Gtk::ToggleButton *button, uint8_t button_nr) = 0;
|
||||||
|
Gtk::Label _button_labels[4][4];
|
||||||
|
Gtk::ToggleButton _buttons[4][4];
|
||||||
|
};
|
||||||
|
|
||||||
|
class SingleMidiChannelSelector : public MidiChannelSelector
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SingleMidiChannelSelector(uint8_t active_channel = 0);
|
||||||
|
|
||||||
|
const uint8_t get_active_channel() const { return _active_channel; }
|
||||||
|
|
||||||
|
sigc::signal<void, uint8_t> channel_selected;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void button_toggled(Gtk::ToggleButton *button, uint8_t button_nr);
|
||||||
|
|
||||||
|
Gtk::ToggleButton *_active_button;
|
||||||
|
uint8_t _active_channel;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MidiMultipleChannelSelector : public MidiChannelSelector
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
const std::set<uint8_t>& get_selected_channels() const { return _selected_channels; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void button_toggled(Gtk::ToggleButton *button, uint8_t button_nr);
|
||||||
|
|
||||||
|
std::set<uint8_t> _selected_channels;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /*__ardour_ui_midi_channel_selector_h__*/
|
||||||
|
|
@ -184,10 +184,15 @@ MidiRegionView::canvas_event(GdkEvent* ev)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
case GDK_BUTTON_PRESS:
|
case GDK_BUTTON_PRESS:
|
||||||
if (_mouse_state != SelectTouchDragging && _mouse_state != EraseTouchDragging)
|
if (_mouse_state != SelectTouchDragging &&
|
||||||
_mouse_state = Pressed;
|
_mouse_state != EraseTouchDragging &&
|
||||||
|
ev->button.button == 1 ) {
|
||||||
_pressed_button = ev->button.button;
|
_pressed_button = ev->button.button;
|
||||||
|
_mouse_state = Pressed;
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
_pressed_button = ev->button.button;
|
||||||
|
return false;
|
||||||
|
|
||||||
case GDK_ENTER_NOTIFY:
|
case GDK_ENTER_NOTIFY:
|
||||||
/* FIXME: do this on switch to note tool, too, if the pointer is already in */
|
/* FIXME: do this on switch to note tool, too, if the pointer is already in */
|
||||||
|
|
@ -306,6 +311,10 @@ MidiRegionView::canvas_event(GdkEvent* ev)
|
||||||
group->ungrab(ev->button.time);
|
group->ungrab(ev->button.time);
|
||||||
event_frame = trackview.editor.pixel_to_frame(event_x);
|
event_frame = trackview.editor.pixel_to_frame(event_x);
|
||||||
|
|
||||||
|
if(_pressed_button != 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
switch (_mouse_state) {
|
switch (_mouse_state) {
|
||||||
case Pressed: // Clicked
|
case Pressed: // Clicked
|
||||||
switch (trackview.editor.current_midi_edit_mode()) {
|
switch (trackview.editor.current_midi_edit_mode()) {
|
||||||
|
|
@ -328,8 +337,7 @@ MidiRegionView::canvas_event(GdkEvent* ev)
|
||||||
case AddDragging: // Add drag done
|
case AddDragging: // Add drag done
|
||||||
_mouse_state = None;
|
_mouse_state = None;
|
||||||
if (drag_rect->property_x2() > drag_rect->property_x1() + 2) {
|
if (drag_rect->property_x2() > drag_rect->property_x1() + 2) {
|
||||||
const double x = drag_rect->property_x1()
|
const double x = drag_rect->property_x1();
|
||||||
+ trackview.editor.frame_to_pixel(_region->start());
|
|
||||||
const double length = trackview.editor.pixel_to_frame(
|
const double length = trackview.editor.pixel_to_frame(
|
||||||
drag_rect->property_x2() - drag_rect->property_x1());
|
drag_rect->property_x2() - drag_rect->property_x1());
|
||||||
|
|
||||||
|
|
@ -365,6 +373,7 @@ MidiRegionView::create_note_at(double x, double y, double duration)
|
||||||
|
|
||||||
nframes_t new_note_time = trackview.editor.pixel_to_frame (x);
|
nframes_t new_note_time = trackview.editor.pixel_to_frame (x);
|
||||||
assert(new_note_time >= 0);
|
assert(new_note_time >= 0);
|
||||||
|
new_note_time += _region->start();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
const Meter& m = trackview.session().tempo_map().meter_at(new_note_time);
|
const Meter& m = trackview.session().tempo_map().meter_at(new_note_time);
|
||||||
|
|
@ -1161,6 +1170,34 @@ MidiRegionView::change_velocity(uint8_t velocity, bool relative)
|
||||||
apply_command();
|
apply_command();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MidiRegionView::change_channel(uint8_t channel)
|
||||||
|
{
|
||||||
|
start_delta_command(_("change channel"));
|
||||||
|
for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
|
||||||
|
Selection::iterator next = i;
|
||||||
|
++next;
|
||||||
|
|
||||||
|
CanvasMidiEvent *event = *i;
|
||||||
|
const boost::shared_ptr<Note> copy(new Note(*(event->note().get())));
|
||||||
|
|
||||||
|
copy->set_channel(channel);
|
||||||
|
|
||||||
|
command_remove_note(event);
|
||||||
|
command_add_note(copy);
|
||||||
|
|
||||||
|
_marked_for_selection.insert(copy);
|
||||||
|
i = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
// dont keep notes selected if tweaking a single note
|
||||||
|
if(_marked_for_selection.size() == 1) {
|
||||||
|
_marked_for_selection.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
apply_command();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
MidiRegionView::note_entered(ArdourCanvas::CanvasMidiEvent* ev)
|
MidiRegionView::note_entered(ArdourCanvas::CanvasMidiEvent* ev)
|
||||||
|
|
|
||||||
|
|
@ -165,12 +165,18 @@ class MidiRegionView : public RegionView
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function is called while the user adjusts the velocity on a selection of notes
|
* This function is called while the user adjusts the velocity on a selection of notes
|
||||||
* @param velocity the relative or absolute velocity, dependin on the value of relative
|
* @param velocity the relative or absolute velocity, depending on the value of relative
|
||||||
* @param relative true if the given velocity represents a delta to be applied to all notes, false
|
* @param relative true if the given velocity represents a delta to be applied to all notes, false
|
||||||
* if the absolute value of the note shoud be set
|
* if the absolute value of the note shoud be set
|
||||||
*/
|
*/
|
||||||
void change_velocity(uint8_t velocity, bool relative=false);
|
void change_velocity(uint8_t velocity, bool relative=false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is called when the user adjusts the midi channel of a selection of notes
|
||||||
|
* @param channel - the channel number of the new channel, zero-based
|
||||||
|
*/
|
||||||
|
void change_channel(uint8_t channel);
|
||||||
|
|
||||||
enum MouseState { None, Pressed, SelectTouchDragging, SelectRectDragging, AddDragging, EraseTouchDragging };
|
enum MouseState { None, Pressed, SelectTouchDragging, SelectRectDragging, AddDragging, EraseTouchDragging };
|
||||||
MouseState mouse_state() const { return _mouse_state; }
|
MouseState mouse_state() const { return _mouse_state; }
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue