mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-06 14:54:56 +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_streamview.cc
|
||||
midi_time_axis.cc
|
||||
midi_channel_selector.cc
|
||||
mixer_strip.cc
|
||||
mixer_ui.cc
|
||||
new_session_dialog.cc
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ CanvasMidiEvent::CanvasMidiEvent(MidiRegionView& region, Item* item,
|
|||
: _region(region)
|
||||
, _item(item)
|
||||
, _text(0)
|
||||
, _channel_selector_widget()
|
||||
, _state(None)
|
||||
, _note(note)
|
||||
, _selected(false)
|
||||
|
|
@ -43,6 +44,12 @@ CanvasMidiEvent::CanvasMidiEvent(MidiRegionView& region, Item* item,
|
|||
_text = new Text(*(item->property_parent()));
|
||||
}
|
||||
|
||||
CanvasMidiEvent::~CanvasMidiEvent()
|
||||
{
|
||||
if(_text) delete _text;
|
||||
if(_channel_selector_widget) delete _channel_selector_widget;
|
||||
}
|
||||
|
||||
void
|
||||
CanvasMidiEvent::move_event(double dx, double dy)
|
||||
{
|
||||
|
|
@ -71,6 +78,50 @@ CanvasMidiEvent::hide_velocity(void)
|
|||
_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
|
||||
CanvasMidiEvent::selected(bool yn)
|
||||
{
|
||||
|
|
@ -120,7 +171,6 @@ CanvasMidiEvent::on_event(GdkEvent* ev)
|
|||
}
|
||||
return true;
|
||||
} else if(ev->scroll.direction == GDK_SCROLL_DOWN) {
|
||||
|
||||
_region.note_selected(this, true);
|
||||
if (_region.mouse_state() == MidiRegionView::SelectTouchDragging) {
|
||||
// TODO: absolute velocity
|
||||
|
|
@ -164,6 +214,8 @@ CanvasMidiEvent::on_event(GdkEvent* ev)
|
|||
case GDK_BUTTON_PRESS:
|
||||
if (ev->button.button == 1) {
|
||||
_state = Pressed;
|
||||
} else if (ev->button.button == 3) {
|
||||
show_channel_selector();
|
||||
}
|
||||
return true;
|
||||
|
||||
|
|
@ -236,7 +288,11 @@ CanvasMidiEvent::on_event(GdkEvent* ev)
|
|||
event_x = ev->button.x;
|
||||
event_y = ev->button.y;
|
||||
_item->property_parent().get_value()->w2i(event_x, event_y);
|
||||
|
||||
|
||||
if(ev->button.button == 3) {
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (_state) {
|
||||
case Pressed: // Clicked
|
||||
if (_region.midi_view()->editor.current_midi_edit_mode() == Editing::MidiEditSelect) {
|
||||
|
|
|
|||
|
|
@ -21,7 +21,9 @@
|
|||
#define __gtk_ardour_canvas_midi_event_h__
|
||||
|
||||
#include "simplerect.h"
|
||||
#include "midi_channel_selector.h"
|
||||
#include <libgnomecanvasmm/text.h>
|
||||
#include <libgnomecanvasmm/widget.h>
|
||||
#include <ardour/midi_model.h>
|
||||
|
||||
class Editor;
|
||||
|
|
@ -49,7 +51,7 @@ public:
|
|||
Item* item,
|
||||
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);
|
||||
|
||||
|
|
@ -60,6 +62,15 @@ public:
|
|||
|
||||
void show_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_fill_color(uint32_t c) = 0;
|
||||
|
|
@ -76,7 +87,8 @@ protected:
|
|||
|
||||
MidiRegionView& _region;
|
||||
Item* const _item;
|
||||
Text* _text;
|
||||
Text* _text;
|
||||
Widget* _channel_selector_widget;
|
||||
State _state;
|
||||
const boost::shared_ptr<ARDOUR::Note> _note;
|
||||
bool _own_note;
|
||||
|
|
|
|||
|
|
@ -47,8 +47,8 @@ CanvasNote::on_event(GdkEvent* ev)
|
|||
last_x = event_x;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
case GDK_MOTION_NOTIFY:
|
||||
event_x = ev->motion.x;
|
||||
|
||||
|
|
|
|||
|
|
@ -433,12 +433,12 @@ CrossfadeEditor::canvas_event (GdkEvent* event)
|
|||
case GDK_BUTTON_PRESS:
|
||||
add_control_point ((event->button.x - canvas_border)/effective_width(),
|
||||
1.0 - ((event->button.y - canvas_border)/effective_height()));
|
||||
return TRUE;
|
||||
return true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
case GDK_BUTTON_PRESS:
|
||||
if (_mouse_state != SelectTouchDragging && _mouse_state != EraseTouchDragging)
|
||||
if (_mouse_state != SelectTouchDragging &&
|
||||
_mouse_state != EraseTouchDragging &&
|
||||
ev->button.button == 1 ) {
|
||||
_pressed_button = ev->button.button;
|
||||
_mouse_state = Pressed;
|
||||
return true;
|
||||
}
|
||||
_pressed_button = ev->button.button;
|
||||
return true;
|
||||
return false;
|
||||
|
||||
case GDK_ENTER_NOTIFY:
|
||||
/* 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);
|
||||
event_frame = trackview.editor.pixel_to_frame(event_x);
|
||||
|
||||
if(_pressed_button != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (_mouse_state) {
|
||||
case Pressed: // Clicked
|
||||
switch (trackview.editor.current_midi_edit_mode()) {
|
||||
|
|
@ -328,8 +337,7 @@ MidiRegionView::canvas_event(GdkEvent* ev)
|
|||
case AddDragging: // Add drag done
|
||||
_mouse_state = None;
|
||||
if (drag_rect->property_x2() > drag_rect->property_x1() + 2) {
|
||||
const double x = drag_rect->property_x1()
|
||||
+ trackview.editor.frame_to_pixel(_region->start());
|
||||
const double x = drag_rect->property_x1();
|
||||
const double length = trackview.editor.pixel_to_frame(
|
||||
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);
|
||||
assert(new_note_time >= 0);
|
||||
new_note_time += _region->start();
|
||||
|
||||
/*
|
||||
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();
|
||||
}
|
||||
|
||||
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
|
||||
MidiRegionView::note_entered(ArdourCanvas::CanvasMidiEvent* ev)
|
||||
|
|
|
|||
|
|
@ -165,11 +165,17 @@ class MidiRegionView : public RegionView
|
|||
|
||||
/**
|
||||
* 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
|
||||
* if the absolute value of the note shoud be set
|
||||
*/
|
||||
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 };
|
||||
MouseState mouse_state() const { return _mouse_state; }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue