diff options
31 files changed, 1113 insertions, 450 deletions
diff --git a/gtk2_ardour/canvas-note-event.cc b/gtk2_ardour/canvas-note-event.cc index 94b675b224..867e3cdf1c 100644 --- a/gtk2_ardour/canvas-note-event.cc +++ b/gtk2_ardour/canvas-note-event.cc @@ -22,6 +22,7 @@ #include "gtkmm2ext/keyboard.h" #include "canvas-note-event.h" +#include "midi_channel_selector.h" #include "midi_region_view.h" #include "public_editor.h" #include "editing_syms.h" diff --git a/gtk2_ardour/midi_channel_selector.cc b/gtk2_ardour/midi_channel_selector.cc index 6770f0e60d..aea9d31f03 100644 --- a/gtk2_ardour/midi_channel_selector.cc +++ b/gtk2_ardour/midi_channel_selector.cc @@ -19,12 +19,25 @@ #include <algorithm> #include <sstream> +#include <gtkmm/separator.h> +#include <gtkmm/box.h> +#include <gtkmm/label.h> +#include <gtkmm/togglebutton.h> +#include <gtkmm/radiobutton.h> +#include <gtkmm/table.h> + +#include "pbd/compose.h" + +#include "gtkmm2ext/gtk_ui.h" +#include "gtkmm2ext/gui_thread.h" + +#include "ardour/midi_track.h" #include "midi_channel_selector.h" -#include "gtkmm/separator.h" -#include "i18n.h" #include "rgb_macros.h" +#include "i18n.h" + using namespace std; using namespace Gtk; using namespace ARDOUR; @@ -310,3 +323,364 @@ MidiMultipleChannelSelector::invert_selection(void) mode_changed.emit(_channel_mode, get_selected_channels()); } +/*-----------------------------------------*/ + +MidiChannelSelectorWindow::MidiChannelSelectorWindow (boost::shared_ptr<MidiTrack> mt) + : ArdourWindow (string_compose (_("MIDI Channel Control for %1"), mt->name())) + , track (mt) +{ + build (); + + playback_mask_changed (); + playback_mode_changed (); + capture_mask_changed (); + capture_mode_changed (); + + track->PlaybackChannelMaskChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&MidiChannelSelectorWindow::playback_mask_changed, this), gui_context()); + track->PlaybackChannelModeChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&MidiChannelSelectorWindow::playback_mode_changed, this), gui_context()); + track->CaptureChannelMaskChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&MidiChannelSelectorWindow::capture_mask_changed, this), gui_context()); + track->CaptureChannelModeChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&MidiChannelSelectorWindow::capture_mode_changed, this), gui_context()); +} + +MidiChannelSelectorWindow::~MidiChannelSelectorWindow() +{ +} + +void +MidiChannelSelectorWindow::build () +{ + VBox* vpacker; + HBox* capture_mask; + HBox* capture_mask_controls; + HBox* playback_mask; + HBox* playback_mask_controls; + Button* b; + ToggleButton* tb; + Label* l; + + vpacker = manage (new VBox); + vpacker->set_spacing (6); + vpacker->set_border_width (12); + + l = manage (new Label (string_compose ("<span size=\"large\" weight=\"bold\">%1</span>", _("Capture")))); + l->set_use_markup (true); + vpacker->pack_start (*l); + + { + RadioButtonGroup group; + + capture_all_button = manage (new RadioButton (group, "Record all channels")); + vpacker->pack_start (*capture_all_button); + capture_all_button->signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &MidiChannelSelectorWindow::capture_mode_toggled), AllChannels)); + + capture_filter_button = manage (new RadioButton (group, "Record only selected channels")); + vpacker->pack_start (*capture_filter_button); + capture_filter_button->signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &MidiChannelSelectorWindow::capture_mode_toggled), FilterChannels)); + + capture_force_button = manage (new RadioButton (group, "Force all channels to a single fixed channel")); + vpacker->pack_start (*capture_force_button); + capture_force_button->signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &MidiChannelSelectorWindow::capture_mode_toggled), ForceChannel)); + } + + capture_mask = manage (new HBox); + + for (uint32_t n = 0; n < 16; ++n) { + char buf[3]; + snprintf (buf, sizeof (buf), "%d", n+1); + tb = manage (new ToggleButton (buf)); + Gtkmm2ext::UI::instance()->set_tip (*tb, string_compose (_("Click to toggle recording of channel %1"), n+1)); + capture_buttons.push_back (tb); + tb->set_name (X_("MidiChannelSelectorButton")); + capture_mask->pack_start (*tb); + tb->signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &MidiChannelSelectorWindow::capture_channel_clicked), n)); + } + + vpacker->pack_start (*capture_mask); + + capture_mask_controls = manage (new HBox); + capture_mask_controls->set_spacing (6); + + b = manage (new Button (_("All"))); + Gtkmm2ext::UI::instance()->set_tip (*b, _("Click to enable recording all channels")); + capture_mask_controls->pack_start (*b); + b->signal_clicked().connect (sigc::mem_fun (*this, &MidiChannelSelectorWindow::fill_capture_mask)); + b = manage (new Button (_("None"))); + Gtkmm2ext::UI::instance()->set_tip (*b, _("Click to disable recording all channels")); + capture_mask_controls->pack_start (*b); + b->signal_clicked().connect (sigc::mem_fun (*this, &MidiChannelSelectorWindow::zero_capture_mask)); + b = manage (new Button (_("Invert"))); + Gtkmm2ext::UI::instance()->set_tip (*b, _("Click to invert currently selected recording channels")); + capture_mask_controls->pack_start (*b); + b->signal_clicked().connect (sigc::mem_fun (*this, &MidiChannelSelectorWindow::invert_capture_mask)); + + vpacker->pack_start (*capture_mask_controls); + + playback_mask = manage (new HBox); + + l = manage (new Label (string_compose ("<span size=\"large\" weight=\"bold\">%1</span>", _("Playback")))); + l->set_use_markup (true); + vpacker->pack_start (*l); + + { + RadioButtonGroup group; + + playback_all_button = manage (new RadioButton (group, "Playback all channels")); + vpacker->pack_start (*playback_all_button); + playback_all_button->signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &MidiChannelSelectorWindow::playback_mode_toggled), AllChannels)); + + playback_filter_button = manage (new RadioButton (group, "Play only selected channels")); + vpacker->pack_start (*playback_filter_button); + playback_filter_button->signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &MidiChannelSelectorWindow::playback_mode_toggled), FilterChannels)); + + playback_force_button = manage (new RadioButton (group, "Use a single fixed channel for all playback")); + vpacker->pack_start (*playback_force_button); + playback_force_button->signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &MidiChannelSelectorWindow::playback_mode_toggled), ForceChannel)); + } + + for (uint32_t n = 0; n < 16; ++n) { + char buf[3]; + snprintf (buf, sizeof (buf), "%d", n+1); + tb = manage (new ToggleButton (buf)); + tb->set_name (X_("MidiChannelSelectorButton")); + playback_buttons.push_back (tb); + playback_mask->pack_start (*tb); + tb->signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &MidiChannelSelectorWindow::playback_channel_clicked), n)); + } + + vpacker->pack_start (*playback_mask); + + playback_mask_controls = manage (new HBox); + playback_mask_controls->set_spacing (6); + + b = manage (new Button (_("All"))); + Gtkmm2ext::UI::instance()->set_tip (*b, _("Click to enable playback of all channels")); + playback_mask_controls->pack_start (*b); + b->signal_clicked().connect (sigc::mem_fun (*this, &MidiChannelSelectorWindow::fill_playback_mask)); + b = manage (new Button (_("None"))); + Gtkmm2ext::UI::instance()->set_tip (*b, _("Click to disable playback of all channels")); + playback_mask_controls->pack_start (*b); + b->signal_clicked().connect (sigc::mem_fun (*this, &MidiChannelSelectorWindow::zero_playback_mask)); + b = manage (new Button (_("Invert"))); + Gtkmm2ext::UI::instance()->set_tip (*b, _("Click to invert current selected playback channels")); + playback_mask_controls->pack_start (*b); + b->signal_clicked().connect (sigc::mem_fun (*this, &MidiChannelSelectorWindow::invert_playback_mask)); + + vpacker->pack_start (*playback_mask_controls); + + add (*vpacker); +} + +void +MidiChannelSelectorWindow::fill_playback_mask () +{ + track->set_playback_channel_mask (0xffff); +} + +void +MidiChannelSelectorWindow::zero_playback_mask () +{ + track->set_playback_channel_mask (0); +} + +void +MidiChannelSelectorWindow::invert_playback_mask () +{ + track->set_playback_channel_mask (~track->get_playback_channel_mask()); +} + +void +MidiChannelSelectorWindow::fill_capture_mask () +{ + track->set_capture_channel_mask (0xffff); +} + +void +MidiChannelSelectorWindow::zero_capture_mask () +{ + track->set_capture_channel_mask (0); +} + +void +MidiChannelSelectorWindow::invert_capture_mask () +{ + track->set_capture_channel_mask (~track->get_capture_channel_mask()); +} + +void +MidiChannelSelectorWindow::set_playback_selected_channels (uint16_t mask) +{ + for (uint16_t i = 0; i < 16; i++) { + playback_buttons[i]->set_active ((1<<i) & mask); + } +} + +void +MidiChannelSelectorWindow::set_capture_selected_channels (uint16_t mask) +{ + for (uint16_t i = 0; i < 16; i++) { + capture_buttons[i]->set_active ((1<<i) & mask); + } +} + +void +MidiChannelSelectorWindow::playback_mask_changed () +{ + set_playback_selected_channels (track->get_playback_channel_mask()); +} + +void +MidiChannelSelectorWindow::capture_mask_changed () +{ + set_capture_selected_channels (track->get_capture_channel_mask()); +} + +void +MidiChannelSelectorWindow::playback_mode_changed () +{ + switch (track->get_playback_channel_mode()) { + case AllChannels: + playback_all_button->set_active (); + break; + case FilterChannels: + playback_filter_button->set_active (); + break; + case ForceChannel: + playback_force_button->set_active (); + break; + } +} + +void +MidiChannelSelectorWindow::capture_mode_changed () +{ + switch (track->get_capture_channel_mode()) { + case AllChannels: + capture_all_button->set_active (); + break; + case FilterChannels: + capture_filter_button->set_active (); + break; + case ForceChannel: + capture_force_button->set_active (); + break; + } +} + +void +MidiChannelSelectorWindow::playback_channel_clicked (uint16_t n) +{ + if (playback_buttons[n]->get_active()) { + track->set_playback_channel_mask (track->get_playback_channel_mask() | (1<<n)); + } else { + track->set_playback_channel_mask (track->get_playback_channel_mask() & ~(1<<n)); + } +} + +void +MidiChannelSelectorWindow::capture_channel_clicked (uint16_t n) +{ + if (capture_buttons[n]->get_active()) { + track->set_capture_channel_mask (track->get_capture_channel_mask() | (1<<n)); + } else { + track->set_capture_channel_mask (track->get_capture_channel_mask() & ~(1<<n)); + } +} + +void +MidiChannelSelectorWindow::capture_mode_toggled (ChannelMode mode) +{ + /* this is called twice for every radio button change. the first time + is for the button/mode that has been turned off, and the second is for the + button/mode that has been turned on. + + so we have to check the button state to know what to do. + */ + + switch (mode) { + case AllChannels: + if (!capture_all_button->get_active()) { + return; + } + track->set_capture_channel_mode (AllChannels, track->get_capture_channel_mask()); + break; + case FilterChannels: + if (!capture_filter_button->get_active()) { + return; + } + track->set_capture_channel_mode (FilterChannels, track->get_capture_channel_mask()); + break; + case ForceChannel: + if (!capture_force_button->get_active()) { + return; + } + track->set_capture_channel_mode (ForceChannel, track->get_capture_channel_mask()); + break; + } +} + +void +MidiChannelSelectorWindow::playback_mode_toggled (ChannelMode mode) +{ + /* this is called twice for every radio button change. the first time + is for the button/mode that has been turned off, and the second is for the + button/mode that has been turned on. + + so we have to check the button state to know what to do. + */ + + switch (mode) { + case AllChannels: + if (!playback_all_button->get_active()) { + return; + } + track->set_playback_channel_mode (AllChannels, track->get_playback_channel_mask()); + break; + case FilterChannels: + if (!playback_filter_button->get_active()) { + return; + } + track->set_playback_channel_mode (FilterChannels, track->get_playback_channel_mask()); + break; + case ForceChannel: + if (!playback_force_button->get_active()) { + return; + } + track->set_playback_channel_mode (ForceChannel, track->get_playback_channel_mask()); + break; + } +} + +void +MidiChannelSelectorWindow::set_channel_colors (const uint32_t new_channel_colors[16]) +{ + for (uint32_t n = 0; n < 16; ++n) { + + char color_normal[8]; + char color_active[8]; + + snprintf(color_normal, 8, "#%x", UINT_INTERPOLATE(new_channel_colors[n], 0x000000ff, 0.6)); + snprintf(color_active, 8, "#%x", new_channel_colors[n]); + + playback_buttons[n]->modify_bg(STATE_NORMAL, Gdk::Color(color_normal)); + playback_buttons[n]->modify_bg(STATE_ACTIVE, Gdk::Color(color_active)); + + capture_buttons[n]->modify_bg(STATE_NORMAL, Gdk::Color(color_normal)); + capture_buttons[n]->modify_bg(STATE_ACTIVE, Gdk::Color(color_active)); + } +} + +void +MidiChannelSelectorWindow::set_default_channel_color() +{ + for (uint32_t n = 0; n < 16; ++n) { + playback_buttons[n]->unset_fg (STATE_NORMAL); + playback_buttons[n]->unset_bg (STATE_NORMAL); + playback_buttons[n]->unset_fg (STATE_ACTIVE); + playback_buttons[n]->unset_bg (STATE_ACTIVE); + + capture_buttons[n]->unset_fg (STATE_NORMAL); + capture_buttons[n]->unset_bg (STATE_NORMAL); + capture_buttons[n]->unset_fg (STATE_ACTIVE); + capture_buttons[n]->unset_bg (STATE_ACTIVE); + } +} diff --git a/gtk2_ardour/midi_channel_selector.h b/gtk2_ardour/midi_channel_selector.h index 5764a8d813..f6d3206c31 100644 --- a/gtk2_ardour/midi_channel_selector.h +++ b/gtk2_ardour/midi_channel_selector.h @@ -26,11 +26,18 @@ #include "gtkmm/table.h" #include "gtkmm/button.h" +#include "gtkmm/radiobutton.h" #include "gtkmm/label.h" #include "gtkmm2ext/stateful_button.h" #include "ardour/types.h" +#include "ardour_window.h" + +namespace ARDOUR { + class MidiTrack; +} + class MidiChannelSelector : public Gtk::Table { public: @@ -108,4 +115,57 @@ protected: Gtk::ToggleButton _force_channel; }; +class MidiChannelSelectorWindow : public ArdourWindow, public PBD::ScopedConnectionList +{ + public: + MidiChannelSelectorWindow (boost::shared_ptr<ARDOUR::MidiTrack>); + ~MidiChannelSelectorWindow (); + + void set_channel_colors (const uint32_t new_channel_colors[16]); + void set_default_channel_color(); + + private: + boost::shared_ptr<ARDOUR::MidiTrack> track; + std::vector<Gtk::ToggleButton*> playback_buttons; + std::vector<Gtk::ToggleButton*> capture_buttons; + + Gtk::ToggleButton* playback_all_button; + Gtk::ToggleButton* playback_filter_button; + Gtk::ToggleButton* playback_force_button; + Gtk::ToggleButton* capture_all_button; + Gtk::ToggleButton* capture_filter_button; + Gtk::ToggleButton* capture_force_button; + + void build(); + void set_capture_selected_channels (uint16_t); + void set_playback_selected_channels (uint16_t); + + void fill_playback_mask (); + void zero_playback_mask (); + void invert_playback_mask (); + + void fill_capture_mask (); + void zero_capture_mask (); + void invert_capture_mask (); + + void playback_mask_changed (); + void capture_mask_changed (); + void playback_mode_changed (); + void capture_mode_changed (); + + void playback_channel_clicked (uint16_t); + void capture_channel_clicked (uint16_t); + + void playback_all_clicked(); + void playback_none_clicked(); + void playback_invert_clicked(); + + void capture_all_clicked(); + void capture_none_clicked(); + void capture_invert_clicked(); + + void capture_mode_toggled (ARDOUR::ChannelMode); + void playback_mode_toggled (ARDOUR::ChannelMode); +}; + #endif /*__ardour_ui_midi_channel_selector_h__*/ diff --git a/gtk2_ardour/midi_region_view.cc b/gtk2_ardour/midi_region_view.cc index 81e5cf23e9..d6b6935e11 100644 --- a/gtk2_ardour/midi_region_view.cc +++ b/gtk2_ardour/midi_region_view.cc @@ -66,6 +66,7 @@ #include "mouse_cursors.h" #include "note_player.h" #include "public_editor.h" +#include "route_time_axis.h" #include "rgb_macros.h" #include "selection.h" #include "simpleline.h" @@ -89,7 +90,6 @@ PBD::Signal1<void, MidiRegionView *> MidiRegionView::SelectionCleared; MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv, boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color const & basic_color) : RegionView (parent, tv, r, spu, basic_color) - , _last_channel_selection(0xFFFF) , _current_range_min(0) , _current_range_max(0) , _active_notes(0) @@ -116,13 +116,6 @@ MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView & _note_group->raise_to_top(); PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys)); - - MidiTimeAxisView *time_axis = dynamic_cast<MidiTimeAxisView *>(&tv); - if (time_axis) { - _last_channel_mode = time_axis->channel_selector().get_channel_mode(); - _last_channel_selection = time_axis->channel_selector().get_selected_channels(); - } - Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context()); connect_to_diskstream (); @@ -133,7 +126,6 @@ MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView & boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color& basic_color, TimeAxisViewItem::Visibility visibility) : RegionView (parent, tv, r, spu, basic_color, false, visibility) - , _last_channel_selection(0xFFFF) , _current_range_min(0) , _current_range_max(0) , _active_notes(0) @@ -160,12 +152,6 @@ MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView & _note_group->raise_to_top(); PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys)); - MidiTimeAxisView *time_axis = dynamic_cast<MidiTimeAxisView *>(&tv); - if (time_axis) { - _last_channel_mode = time_axis->channel_selector().get_channel_mode(); - _last_channel_selection = time_axis->channel_selector().get_selected_channels(); - } - connect_to_diskstream (); SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ()); @@ -184,7 +170,6 @@ MidiRegionView::parameter_changed (std::string const & p) MidiRegionView::MidiRegionView (const MidiRegionView& other) : sigc::trackable(other) , RegionView (other) - , _last_channel_selection(0xFFFF) , _current_range_min(0) , _current_range_max(0) , _active_notes(0) @@ -219,7 +204,6 @@ MidiRegionView::MidiRegionView (const MidiRegionView& other) MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region) : RegionView (other, boost::shared_ptr<Region> (region)) - , _last_channel_selection(0xFFFF) , _current_range_min(0) , _current_range_max(0) , _active_notes(0) @@ -293,8 +277,10 @@ MidiRegionView::init (Gdk::Color const & basic_color, bool wfd) group->raise_to_top(); group->signal_event().connect (sigc::mem_fun (this, &MidiRegionView::canvas_event), false); - midi_view()->signal_channel_mode_changed().connect( - sigc::mem_fun(this, &MidiRegionView::midi_channel_mode_changed)); + + midi_view()->midi_track()->PlaybackChannelModeChanged.connect (_channel_mode_changed_connection, invalidator (*this), + boost::bind (&MidiRegionView::midi_channel_mode_changed, this), + gui_context ()); instrument_info().Changed.connect (_instrument_changed_connection, invalidator (*this), boost::bind (&MidiRegionView::instrument_settings_changed, this), gui_context()); @@ -1214,7 +1200,7 @@ void MidiRegionView::display_patch_changes () { MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview); - uint16_t chn_mask = mtv->channel_selector().get_selected_channels(); + uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask(); for (uint8_t i = 0; i < 16; ++i) { display_patch_changes_on_channel (i, chn_mask & (1 << i)); @@ -1673,7 +1659,7 @@ MidiRegionView::update_note (CanvasNote* ev, bool update_ghost_regions) /* outline all edges */ ev->property_outline_what() = (guint32) 0xF; } - + if (update_ghost_regions) { for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) { MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i); @@ -1751,7 +1737,7 @@ MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible) event->show_velocity(); } - event->on_channel_selection_change(_last_channel_selection); + event->on_channel_selection_change (get_selected_channels()); _events.push_back(event); if (visible) { @@ -3220,20 +3206,21 @@ MidiRegionView::set_frame_color() } void -MidiRegionView::midi_channel_mode_changed(ChannelMode mode, uint16_t mask) +MidiRegionView::midi_channel_mode_changed () { + MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview); + uint16_t mask = mtv->midi_track()->get_playback_channel_mask(); + ChannelMode mode = mtv->midi_track()->get_playback_channel_mode (); + if (mode == ForceChannel) { mask = 0xFFFF; // Show all notes as active (below) } // Update notes for selection for (Events::iterator i = _events.begin(); i != _events.end(); ++i) { - (*i)->on_channel_selection_change(mask); + (*i)->on_channel_selection_change (mask); } - _last_channel_selection = mask; - _last_channel_mode = mode; - _patch_changes.clear (); display_patch_changes (); } @@ -3399,7 +3386,7 @@ MidiRegionView::goto_next_note (bool add_to_selection) time_sort_events (); MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview); - uint16_t const channel_mask = mtv->channel_selector().get_selected_channels (); + uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask(); for (Events::iterator i = _events.begin(); i != _events.end(); ++i) { if ((*i)->selected()) { @@ -3436,7 +3423,7 @@ MidiRegionView::goto_previous_note (bool add_to_selection) time_sort_events (); MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview); - uint16_t const channel_mask = mtv->channel_selector().get_selected_channels (); + uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask (); for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) { if ((*i)->selected()) { @@ -3562,7 +3549,7 @@ MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, doub Events e; MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview); - uint16_t chn_mask = mtv->channel_selector().get_selected_channels(); + uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask(); if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) { get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask); @@ -3687,8 +3674,8 @@ MidiRegionView::data_recorded (boost::weak_ptr<MidiSource> w) Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false); if (ev.is_channel_event()) { - if (_last_channel_mode == FilterChannels) { - if (((uint16_t(1) << ev.channel()) & _last_channel_selection) == 0) { + if (get_channel_mode() == FilterChannels) { + if (((uint16_t(1) << ev.channel()) & get_selected_channels()) == 0) { continue; } } @@ -3883,3 +3870,18 @@ MidiRegionView::note_button_release () delete _note_player; _note_player = 0; } + +ChannelMode +MidiRegionView::get_channel_mode () const +{ + RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview); + return rtav->midi_track()->get_playback_channel_mode(); +} + +uint16_t +MidiRegionView::get_selected_channels () const +{ + RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview); + return rtav->midi_track()->get_playback_channel_mask(); +} + diff --git a/gtk2_ardour/midi_region_view.h b/gtk2_ardour/midi_region_view.h index 689e0ab2d0..5f374da55c 100644 --- a/gtk2_ardour/midi_region_view.h +++ b/gtk2_ardour/midi_region_view.h @@ -355,7 +355,8 @@ private: bool canvas_event(GdkEvent* ev); bool note_canvas_event(GdkEvent* ev); - void midi_channel_mode_changed(ARDOUR::ChannelMode mode, uint16_t mask); + void midi_channel_mode_changed (); + PBD::ScopedConnection _channel_mode_changed_connection; void instrument_settings_changed (); PBD::ScopedConnection _instrument_changed_connection; @@ -377,9 +378,6 @@ private: void show_verbose_cursor (std::string const &, double, double) const; void show_verbose_cursor (boost::shared_ptr<NoteType>) const; - ARDOUR::ChannelMode _last_channel_mode; - uint16_t _last_channel_selection; - uint8_t _current_range_min; uint8_t _current_range_max; @@ -480,6 +478,9 @@ private: Gdk::Cursor* pre_press_cursor; NotePlayer* _note_player; + + ARDOUR::ChannelMode get_channel_mode() const; + uint16_t get_selected_channels () const; }; diff --git a/gtk2_ardour/midi_time_axis.cc b/gtk2_ardour/midi_time_axis.cc index 9fe3a77450..6c284fa0a0 100644 --- a/gtk2_ardour/midi_time_axis.cc +++ b/gtk2_ardour/midi_time_axis.cc @@ -66,6 +66,7 @@ #include "ghostregion.h" #include "gui_thread.h" #include "keyboard.h" +#include "midi_channel_selector.h" #include "midi_scroomer.h" #include "midi_streamview.h" #include "midi_region_view.h" @@ -94,8 +95,8 @@ using namespace Gtkmm2ext; using namespace Editing; // Minimum height at which a control is displayed -static const uint32_t MIDI_CONTROLS_BOX_MIN_HEIGHT = 162; -static const uint32_t KEYBOARD_MIN_HEIGHT = 140; +static const uint32_t MIDI_CONTROLS_BOX_MIN_HEIGHT = 130; +static const uint32_t KEYBOARD_MIN_HEIGHT = 130; MidiTimeAxisView::MidiTimeAxisView (PublicEditor& ed, Session* sess, Canvas& canvas) : AxisView(sess) // virtually inherited @@ -110,6 +111,7 @@ MidiTimeAxisView::MidiTimeAxisView (PublicEditor& ed, Session* sess, Canvas& can , _meter_color_mode_item(0) , _channel_color_mode_item(0) , _track_color_mode_item(0) + , _channel_selector (0) , _step_edit_item (0) , controller_menu (0) , _step_editor (0) @@ -246,28 +248,19 @@ MidiTimeAxisView::set_route (boost::shared_ptr<Route> rt) _midi_controls_box.set_border_width (10); if (!patch_manager.all_models().empty()) { - _channel_selector.set_border_width(2); - _channel_selector.show_all (); - - _midi_controls_box.resize(3, 2); - _midi_controls_box.attach(_channel_selector, 0, 2, 0, 1); - - _midi_controls_box.attach(*manage(new HSeparator()), 0, 2, 1, 2); + _midi_controls_box.resize(2, 2); _midnam_model_selector.set_size_request(22, 30); _midnam_model_selector.set_border_width(2); _midnam_model_selector.show (); - _midi_controls_box.attach(_midnam_model_selector, 0, 1, 2, 3); + _midi_controls_box.attach(_midnam_model_selector, 0, 1, 0, 1); _midnam_custom_device_mode_selector.set_size_request(10, 30); _midnam_custom_device_mode_selector.set_border_width(2); _midnam_custom_device_mode_selector.show (); - _midi_controls_box.attach(_midnam_custom_device_mode_selector, 0, 1, 3, 4); - } else { - _midi_controls_box.attach(_channel_selector, 0, 1, 0, 1); - _channel_selector.show_all (); - } + _midi_controls_box.attach(_midnam_custom_device_mode_selector, 0, 1, 1, 2); + } model_changed(); custom_device_mode_changed(); @@ -279,19 +272,11 @@ MidiTimeAxisView::set_route (boost::shared_ptr<Route> rt) controls_vbox.pack_start(_midi_controls_box, false, false); - // restore channel selector settings - _channel_selector.set_channel_mode(midi_track()->get_channel_mode(), - midi_track()->get_channel_mask()); - _channel_selector.mode_changed.connect( - sigc::mem_fun(*midi_track(), &MidiTrack::set_channel_mode)); - _channel_selector.mode_changed.connect( - sigc::mem_fun(*this, &MidiTimeAxisView::set_channel_mode)); - const string color_mode = gui_property ("color-mode"); if (!color_mode.empty()) { _color_mode = ColorMode (string_2_enum(color_mode, _color_mode)); - if (_color_mode == ChannelColors) { - _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors); + if (_channel_selector && _color_mode == ChannelColors) { + _channel_selector->set_channel_colors(CanvasNoteEvent::midi_channel_colors); } } @@ -334,6 +319,8 @@ MidiTimeAxisView::first_idle () MidiTimeAxisView::~MidiTimeAxisView () { + delete _channel_selector; + delete _piano_roll_header; _piano_roll_header = 0; @@ -468,11 +455,36 @@ MidiTimeAxisView::append_extra_display_menu_items () items.push_back (MenuElem (_("Note Range"), *range_menu)); items.push_back (MenuElem (_("Note Mode"), *build_note_mode_menu())); + items.push_back (MenuElem (_("Channel Selector"), + sigc::mem_fun(*this, &MidiTimeAxisView::toggle_channel_selector))); + color_mode_menu = build_color_mode_menu(); + if (color_mode_menu) { + items.push_back (MenuElem (_("Color Mode"), *color_mode_menu)); + } + items.push_back (SeparatorElem ()); } void +MidiTimeAxisView::toggle_channel_selector () +{ + if (!_channel_selector) { + _channel_selector = new MidiChannelSelectorWindow (midi_track()); + + if (_color_mode == ChannelColors) { + _channel_selector->set_channel_colors(CanvasNoteEvent::midi_channel_colors); + } else { + _channel_selector->set_default_channel_color (); + } + + _channel_selector->show_all (); + } else { + _channel_selector->cycle_visibility (); + } +} + +void MidiTimeAxisView::build_automation_action_menu (bool for_selection) { using namespace Menu_Helpers; @@ -493,7 +505,7 @@ MidiTimeAxisView::build_automation_action_menu (bool for_selection) MenuList& automation_items = automation_action_menu->items(); - uint16_t selected_channels = _channel_selector.get_selected_channels(); + uint16_t selected_channels = midi_track()->get_playback_channel_mask(); if (selected_channels != 0) { @@ -537,7 +549,7 @@ MidiTimeAxisView::build_automation_action_menu (bool for_selection) void MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param) { - const uint16_t selected_channels = _channel_selector.get_selected_channels(); + const uint16_t selected_channels = midi_track()->get_playback_channel_mask(); for (uint8_t chn = 0; chn < 16; chn++) { if (selected_channels & (0x0001 << chn)) { @@ -564,7 +576,7 @@ MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items, structure if there is more than 1 selected. */ - const uint16_t selected_channels = _channel_selector.get_selected_channels(); + const uint16_t selected_channels = midi_track()->get_playback_channel_mask(); int chn_cnt = 0; for (uint8_t chn = 0; chn < 16; chn++) { @@ -665,7 +677,7 @@ MidiTimeAxisView::add_single_channel_controller_item(Menu_Helpers::MenuList& ctl { using namespace Menu_Helpers; - const uint16_t selected_channels = _channel_selector.get_selected_channels(); + const uint16_t selected_channels = midi_track()->get_playback_channel_mask(); for (uint8_t chn = 0; chn < 16; chn++) { if (selected_channels & (0x0001 << chn)) { @@ -706,7 +718,7 @@ MidiTimeAxisView::add_multi_channel_controller_item(Menu_Helpers::MenuList& ctl_ { using namespace Menu_Helpers; - const uint16_t selected_channels = _channel_selector.get_selected_channels(); + const uint16_t selected_channels = midi_track()->get_playback_channel_mask(); Menu* chn_menu = manage (new Menu); MenuList& chn_items (chn_menu->items()); @@ -804,7 +816,7 @@ MidiTimeAxisView::build_controller_menu () combination covering the currently selected channels for this track */ - const uint16_t selected_channels = _channel_selector.get_selected_channels(); + const uint16_t selected_channels = midi_track()->get_playback_channel_mask(); /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected. @@ -982,10 +994,12 @@ MidiTimeAxisView::set_color_mode (ColorMode mode, bool force, bool redisplay, bo return; } - if (mode == ChannelColors) { - _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors); - } else { - _channel_selector.set_default_channel_color(); + if (_channel_selector) { + if (mode == ChannelColors) { + _channel_selector->set_channel_colors(CanvasNoteEvent::midi_channel_colors); + } else { + _channel_selector->set_default_channel_color(); + } } _color_mode = mode; @@ -1168,7 +1182,7 @@ MidiTimeAxisView::set_note_selection (uint8_t note) return; } - uint16_t chn_mask = _channel_selector.get_selected_channels(); + uint16_t chn_mask = midi_track()->get_playback_channel_mask(); if (_view->num_selected_regionviews() == 0) { _view->foreach_regionview ( @@ -1188,7 +1202,7 @@ MidiTimeAxisView::add_note_selection (uint8_t note) return; } - const uint16_t chn_mask = _channel_selector.get_selected_channels(); + const uint16_t chn_mask = midi_track()->get_playback_channel_mask(); if (_view->num_selected_regionviews() == 0) { _view->foreach_regionview ( @@ -1208,7 +1222,7 @@ MidiTimeAxisView::extend_note_selection (uint8_t note) return; } - const uint16_t chn_mask = _channel_selector.get_selected_channels(); + const uint16_t chn_mask = midi_track()->get_playback_channel_mask(); if (_view->num_selected_regionviews() == 0) { _view->foreach_regionview ( @@ -1228,7 +1242,7 @@ MidiTimeAxisView::toggle_note_selection (uint8_t note) return; } - const uint16_t chn_mask = _channel_selector.get_selected_channels(); + const uint16_t chn_mask = midi_track()->get_playback_channel_mask(); if (_view->num_selected_regionviews() == 0) { _view->foreach_regionview ( @@ -1272,7 +1286,7 @@ MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t) the right ones. */ - const uint16_t selected_channels = _channel_selector.get_selected_channels(); + const uint16_t selected_channels = midi_track()->get_playback_channel_mask(); bool changed = false; no_redraw = true; @@ -1392,7 +1406,7 @@ MidiTimeAxisView::stop_step_editing () uint8_t MidiTimeAxisView::get_channel_for_add () const { - uint16_t const chn_mask = _channel_selector.get_selected_channels (); + uint16_t const chn_mask = midi_track()->get_playback_channel_mask(); int chn_cnt = 0; uint8_t channel = 0; diff --git a/gtk2_ardour/midi_time_axis.h b/gtk2_ardour/midi_time_axis.h index 1f179baca6..e5fccd8c2f 100644 --- a/gtk2_ardour/midi_time_axis.h +++ b/gtk2_ardour/midi_time_axis.h @@ -39,7 +39,6 @@ #include "route_time_axis.h" #include "canvas.h" #include "midi_streamview.h" -#include "midi_channel_selector.h" namespace MIDI { namespace Name { @@ -62,6 +61,7 @@ class MidiScroomer; class PianoRollHeader; class StepEntry; class StepEditor; +class MidiChannelSelectorWindow; class MidiTimeAxisView : public RouteTimeAxisView { @@ -92,12 +92,6 @@ class MidiTimeAxisView : public RouteTimeAxisView void update_range(); - sigc::signal<void, ARDOUR::ChannelMode, uint16_t>& signal_channel_mode_changed() { - return _channel_selector.mode_changed; - } - - const MidiMultipleChannelSelector& channel_selector() { return _channel_selector; } - Gtk::CheckMenuItem* automation_child_menu_item (Evoral::Parameter); StepEditor* step_editor() { return _step_editor; } @@ -141,7 +135,7 @@ class MidiTimeAxisView : public RouteTimeAxisView Gtk::RadioMenuItem* _channel_color_mode_item; Gtk::RadioMenuItem* _track_color_mode_item; Gtk::Table _midi_controls_box; - MidiMultipleChannelSelector _channel_selector; + MidiChannelSelectorWindow* _channel_selector; Gtk::ComboBoxText _midnam_model_selector; Gtk::ComboBoxText _midnam_custom_device_mode_selector; @@ -157,6 +151,8 @@ class MidiTimeAxisView : public RouteTimeAxisView void add_single_channel_controller_item (Gtk::Menu_Helpers::MenuList& ctl_items, int ctl, const std::string& name); void add_multi_channel_controller_item (Gtk::Menu_Helpers::MenuList& ctl_items, int ctl, const std::string& name); void build_controller_menu (); + void toggle_channel_selector (); + void channel_selector_hidden (); void set_channel_mode (ARDOUR::ChannelMode, uint16_t); void set_note_selection (uint8_t note); diff --git a/gtk2_ardour/route_time_axis.cc b/gtk2_ardour/route_time_axis.cc index 041cbb59e1..784203bf6f 100644 --- a/gtk2_ardour/route_time_axis.cc +++ b/gtk2_ardour/route_time_axis.cc @@ -639,10 +639,6 @@ RouteTimeAxisView::build_display_menu () items.push_back (MenuElem (_("Mode"), *mode_menu)); } - color_mode_menu = build_color_mode_menu(); - if (color_mode_menu) { - items.push_back (MenuElem (_("Color Mode"), *color_mode_menu)); - } items.push_back (SeparatorElem()); diff --git a/libs/ardour/ardour/audio_buffer.h b/libs/ardour/ardour/audio_buffer.h index 5c04a7f43a..42aba607f9 100644 --- a/libs/ardour/ardour/audio_buffer.h +++ b/libs/ardour/ardour/audio_buffer.h @@ -46,6 +46,28 @@ public: } /** Read @a len frames @a src starting at @a src_offset into self starting at @ dst_offset*/ + void read_from (const Sample* src, framecnt_t len, framecnt_t dst_offset = 0, framecnt_t src_offset = 0) { + assert(src != 0); + assert(_capacity > 0); + assert(len <= _capacity); + memcpy(_data + dst_offset, src + src_offset, sizeof(Sample) * len); + _silent = false; + _written = true; + } + + void read_from_with_gain (const Sample* src, framecnt_t len, gain_t gain, framecnt_t dst_offset = 0, framecnt_t src_offset = 0) { + assert(src != 0); + assert(_capacity > 0); + assert(len <= _capacity); + src += src_offset; + for (framecnt_t n = 0; n < len; ++n) { + _data[dst_offset+n] = src[n] * gain; + } + _silent = false; + _written = true; + } + + /** Read @a len frames @a src starting at @a src_offset into self starting at @ dst_offset*/ void read_from (const Buffer& src, framecnt_t len, framecnt_t dst_offset = 0, framecnt_t src_offset = 0) { assert(&src != this); assert(_capacity > 0); @@ -82,6 +104,20 @@ public: _written = true; } + /** Acumulate (add) @a len frames @a src starting at @a src_offset into self starting at @a dst_offset */ + void accumulate_from (const Sample* src, framecnt_t len, framecnt_t dst_offset = 0, framecnt_t src_offset = 0) { + assert(_capacity > 0); + assert(len <= _capacity); + + Sample* const dst_raw = _data + dst_offset; + const Sample* const src_raw = src + src_offset; + + mix_buffers_no_gain(dst_raw, src_raw, len); + + _silent = false; + _written = true; + } + /** Acumulate (add) @a len frames @a src starting at @a src_offset into self starting at @dst_offset * scaling by @a gain_coeff */ void accumulate_with_gain_from (const AudioBuffer& src, framecnt_t len, gain_t gain_coeff, framecnt_t dst_offset = 0, framecnt_t src_offset = 0) { @@ -171,6 +207,8 @@ public: return _data + offset; } + bool check_silence (pframes_t, pframes_t&) const; + void prepare () { _written = false; _silent = false; } bool written() const { return _written; } void set_written(bool w) { _written = w; } diff --git a/libs/ardour/ardour/audio_diskstream.h b/libs/ardour/ardour/audio_diskstream.h index 455e89392b..cbc6b93fe0 100644 --- a/libs/ardour/ardour/audio_diskstream.h +++ b/libs/ardour/ardour/audio_diskstream.h @@ -151,7 +151,7 @@ class AudioDiskstream : public Diskstream protected: friend class AudioTrack; - int process (framepos_t transport_frame, pframes_t nframes, framecnt_t &); + int process (BufferSet&, framepos_t transport_frame, pframes_t nframes, framecnt_t &, bool need_disk_signal); bool commit (framecnt_t); private: diff --git a/libs/ardour/ardour/diskstream.h b/libs/ardour/ardour/diskstream.h index 81eb588d1a..85ca03caff 100644 --- a/libs/ardour/ardour/diskstream.h +++ b/libs/ardour/ardour/diskstream.h @@ -49,6 +49,7 @@ class Source; class Session; class Track; class Location; +class BufferSet; /** Parent class for classes which can stream data to and from disk. * These are used by Tracks to get playback and put recorded data. @@ -191,7 +192,7 @@ class Diskstream : public SessionObject, public PublicDiskstream protected: friend class Track; - virtual int process (framepos_t transport_frame, pframes_t nframes, framecnt_t &) = 0; + virtual int process (BufferSet&, framepos_t transport_frame, pframes_t nframes, framecnt_t &, bool need_disk_signal) = 0; virtual bool commit (framecnt_t) = 0; //private: diff --git a/libs/ardour/ardour/meter.h b/libs/ardour/ardour/meter.h index 773b04f386..eea240f821 100644 --- a/libs/ardour/ardour/meter.h +++ b/libs/ardour/ardour/meter.h @@ -44,7 +44,7 @@ class Metering { */ class PeakMeter : public Processor { public: - PeakMeter(Session& s) : Processor(s, "Meter") {} + PeakMeter(Session& s, const std::string& name); void meter(); void reset (); diff --git a/libs/ardour/ardour/midi_buffer.h b/libs/ardour/ardour/midi_buffer.h index 183ca7eea9..5ef5e4c845 100644 --- a/libs/ardour/ardour/midi_buffer.h +++ b/libs/ardour/ardour/midi_buffer.h @@ -55,38 +55,50 @@ public: template<typename BufferType, typename EventType> class iterator_base { public: - iterator_base<BufferType, EventType>(BufferType& b, framecnt_t o) : buffer(b), offset(o) {} + iterator_base<BufferType, EventType>(BufferType& b, framecnt_t o) + : buffer(&b), offset(o) {} + iterator_base<BufferType, EventType>(const iterator_base<BufferType,EventType>& o) + : buffer (o.buffer), offset(o.offset) {} + + inline iterator_base<BufferType,EventType> operator= (const iterator_base<BufferType,EventType>& o) { + if (&o != this) { + buffer = o.buffer; + offset = o.offset; + } + return *this; + } + inline EventType operator*() const { - uint8_t* ev_start = buffer._data + offset + sizeof(TimeType); + uint8_t* ev_start = buffer->_data + offset + sizeof(TimeType); int event_size = Evoral::midi_event_size(ev_start); assert(event_size >= 0); return EventType(EventTypeMap::instance().midi_event_type(*ev_start), - *((TimeType*)(buffer._data + offset)), + *((TimeType*)(buffer->_data + offset)), event_size, ev_start); } inline EventType operator*() { - uint8_t* ev_start = buffer._data + offset + sizeof(TimeType); + uint8_t* ev_start = buffer->_data + offset + sizeof(TimeType); int event_size = Evoral::midi_event_size(ev_start); assert(event_size >= 0); return EventType(EventTypeMap::instance().midi_event_type(*ev_start), - *((TimeType*)(buffer._data + offset)), + *((TimeType*)(buffer->_data + offset)), event_size, ev_start); } inline iterator_base<BufferType, EventType>& operator++() { - uint8_t* ev_start = buffer._data + offset + sizeof(TimeType); + uint8_t* ev_start = buffer->_data + offset + sizeof(TimeType); int event_size = Evoral::midi_event_size(ev_start); assert(event_size >= 0); offset += sizeof(TimeType) + event_size; return *this; } inline bool operator!=(const iterator_base<BufferType, EventType>& other) const { - return (&buffer != &other.buffer) || (offset != other.offset); + return (buffer != other.buffer) || (offset != other.offset); } inline bool operator==(const iterator_base<BufferType, EventType>& other) const { - return (&buffer == &other.buffer) && (offset == other.offset); + return (buffer == other.buffer) && (offset == other.offset); } - BufferType& buffer; + BufferType* buffer; size_t offset; }; @@ -99,6 +111,41 @@ public: const_iterator begin() const { return const_iterator(*this, 0); } const_iterator end() const { return const_iterator(*this, _size); } + iterator erase(const iterator& i) { + assert (i.buffer == this); + uint8_t* ev_start = _data + i.offset + sizeof (TimeType); + int event_size = Evoral::midi_event_size (ev_start); + + if (event_size < 0) { + /* unknown size, sysex: return end() */ + return end(); + } + + size_t total_data_deleted = sizeof(TimeType) + event_size; + + if (i.offset + total_data_deleted >= _size) { + _size = 0; + return end(); + } + + /* we need to avoid the temporary malloc that memmove would do, + so copy by hand. remember: this is small amounts of data ... + */ + size_t a, b; + for (a = i.offset, b = i.offset + total_data_deleted; b < _size; ++b, ++a) { + _data[a] = _data[b]; + } + + _size -= total_data_deleted; + + /* all subsequent iterators are now invalid, and the one we + * return should refer to the event we copied, which was after + * the one we just erased. + */ + + return iterator (*this, i.offset); + } + uint8_t* data() const { return _data; } /** diff --git a/libs/ardour/ardour/midi_diskstream.h b/libs/ardour/ardour/midi_diskstream.h index 309c275434..d6ad71863a 100644 --- a/libs/ardour/ardour/midi_diskstream.h +++ b/libs/ardour/ardour/midi_diskstream.h @@ -89,29 +89,6 @@ class MidiDiskstream : public Diskstream void set_note_mode (NoteMode m); - uint16_t get_channel_mask() { - uint16_t playback_mask = _playback_buf->get_channel_mask(); -#ifndef NDEBUG - uint16_t capture_mask = _capture_buf->get_channel_mask(); - assert(playback_mask == capture_mask); -#endif - return playback_mask; - } - - void set_channel_mode(ChannelMode mode, uint16_t mask) { - _playback_buf->set_channel_mode(mode, mask); - _capture_buf->set_channel_mode(mode, mask); - } - - ChannelMode get_channel_mode() { - ChannelMode playback_mode = _playback_buf->get_channel_mode(); -#ifndef NDEBUG - ChannelMode capture_mode = _capture_buf->get_channel_mode(); - assert(playback_mode == capture_mode); -#endif - return playback_mode; - } - /** Emitted when some MIDI data has been received for recording. * Parameter is the source that it is destined for. * A caller can get a copy of the data with get_gui_feed_buffer () @@ -147,7 +124,7 @@ class MidiDiskstream : public Diskstream protected: friend class MidiTrack; - int process (framepos_t transport_frame, pframes_t nframes, framecnt_t &); + int process (BufferSet&, framepos_t transport_frame, pframes_t nframes, framecnt_t &, bool need_diskstream); bool commit (framecnt_t nframes); static framecnt_t midi_readahead; diff --git a/libs/ardour/ardour/midi_ring_buffer.h b/libs/ardour/ardour/midi_ring_buffer.h index b3e532bdbe..d5c9947b9a 100644 --- a/libs/ardour/ardour/midi_ring_buffer.h +++ b/libs/ardour/ardour/midi_ring_buffer.h @@ -45,38 +45,16 @@ public: /** @param size Size in bytes. */ MidiRingBuffer(size_t size) - : Evoral::EventRingBuffer<T>(size) - , _channel_mask(0x0000FFFF) - {} + : Evoral::EventRingBuffer<T>(size) {} inline bool read_prefix(T* time, Evoral::EventType* type, uint32_t* size); inline bool read_contents(uint32_t size, uint8_t* buf); size_t read(MidiBuffer& dst, framepos_t start, framepos_t end, framecnt_t offset=0, bool stop_on_overflow_in_destination=false); - inline uint32_t write(T time, Evoral::EventType type, uint32_t size, const uint8_t* buf); void dump(std::ostream& dst); void flush (framepos_t start, framepos_t end); - /** Set the channel filtering mode. - * @param mask If mode is FilterChannels, each bit represents a midi channel: - * bit 0 = channel 0, bit 1 = channel 1 etc. the read and write methods will only - * process events whose channel bit is 1. - * If mode is ForceChannel, mask is simply a channel number which all events will - * be forced to while reading. - */ - void set_channel_mode(ChannelMode mode, uint16_t mask) { - g_atomic_int_set(&_channel_mask, (uint32_t(mode) << 16) | uint32_t(mask)); - } - - ChannelMode get_channel_mode() const { - return static_cast<ChannelMode>((g_atomic_int_get(&_channel_mask) & 0xFFFF0000) >> 16); - } - - uint16_t get_channel_mask() const { - return g_atomic_int_get(&_channel_mask) & 0x0000FFFF; - } - void reset_tracker (); void loop_resolve (MidiBuffer& dst, framepos_t); @@ -99,7 +77,6 @@ protected: } private: - volatile uint32_t _channel_mask; // 16 bits mode, 16 bits mask MidiStateTracker _tracker; }; @@ -137,38 +114,6 @@ MidiRingBuffer<T>::read_contents(uint32_t size, uint8_t* buf) return PBD::RingBufferNPT<uint8_t>::read(buf, size) == size; } -template<typename T> -inline uint32_t -MidiRingBuffer<T>::write(T time, Evoral::EventType type, uint32_t size, const uint8_t* buf) -{ - assert(size > 0); - uint8_t status = buf[0]; - - // Ignore event if it doesn't match channel filter - if (is_channel_event(status)) { - ChannelMode mode = get_channel_mode(); - if (mode == FilterChannels) { - const uint8_t channel = status & 0x0F; - if (!(get_channel_mask() & (1L << channel))) { - return 0; - } - } else if (mode == ForceChannel) { - uint8_t* tmpbuf = (uint8_t*) malloc(size); - assert(tmpbuf); - memcpy(tmpbuf, buf, size); - - tmpbuf[0] = (tmpbuf[0] & 0xF0) | (get_channel_mask() & 0x0F); - - uint32_t bytes_written = Evoral::EventRingBuffer<T>::write(time, type, size, tmpbuf); - free(tmpbuf); - return bytes_written; - } - } - - return Evoral::EventRingBuffer<T>::write(time, type, size, buf); -} - - } // namespace ARDOUR #endif // __ardour_midi_ring_buffer_h__ diff --git a/libs/ardour/ardour/midi_track.h b/libs/ardour/ardour/midi_track.h index 8134f5312a..be209bc0f6 100644 --- a/libs/ardour/ardour/midi_track.h +++ b/libs/ardour/ardour/midi_track.h @@ -102,11 +102,39 @@ public: PBD::Signal1<void,bool> StepEditStatusChange; boost::shared_ptr<SMFSource> write_source (uint32_t n = 0); - void set_channel_mode (ChannelMode, uint16_t); - ChannelMode get_channel_mode (); - uint16_t get_channel_mask (); + + /** Channel filtering mode. + * @param mask If mode is FilterChannels, each bit represents a midi channel: + * bit 0 = channel 0, bit 1 = channel 1 etc. the read and write methods will only + * process events whose channel bit is 1. + * If mode is ForceChannel, mask is simply a channel number which all events will + * be forced to while reading. + */ + void set_capture_channel_mode (ChannelMode mode, uint16_t mask); + void set_playback_channel_mode (ChannelMode mode, uint16_t mask); + void set_playback_channel_mask (uint16_t mask); + void set_capture_channel_mask (uint16_t mask); + + ChannelMode get_playback_channel_mode() const { + return static_cast<ChannelMode>((g_atomic_int_get(&_playback_channel_mask) & 0xffff0000) >> 16); + } + uint16_t get_playback_channel_mask() const { + return g_atomic_int_get(&_playback_channel_mask) & 0x0000ffff; + } + ChannelMode get_capture_channel_mode() const { + return static_cast<ChannelMode>((g_atomic_int_get(&_capture_channel_mask) & 0xffff0000) >> 16); + } + uint16_t get_capture_channel_mask() const { + return g_atomic_int_get(&_capture_channel_mask) & 0x0000ffff; + } + boost::shared_ptr<MidiPlaylist> midi_playlist (); + PBD::Signal0<void> PlaybackChannelMaskChanged; + PBD::Signal0<void> PlaybackChannelModeChanged; + PBD::Signal0<void> CaptureChannelMaskChanged; + PBD::Signal0<void> CaptureChannelModeChanged; + PBD::Signal1<void, boost::weak_ptr<MidiSource> > DataRecorded; boost::shared_ptr<MidiBuffer> get_gui_feed_buffer () const; @@ -123,6 +151,13 @@ protected: void act_on_mute (); private: + MidiRingBuffer<framepos_t> _immediate_events; + MidiRingBuffer<framepos_t> _step_edit_ring_buffer; + NoteMode _note_mode; + bool _step_editing; + bool _input_active; + uint32_t _playback_channel_mask; // 16 bits mode, 16 bits mask + uint32_t _capture_channel_mask; // 16 bits mode, 16 bits mask virtual boost::shared_ptr<Diskstream> diskstream_factory (XMLNode const &); @@ -133,11 +168,6 @@ private: void set_state_part_two (); void set_state_part_three (); - MidiRingBuffer<framepos_t> _immediate_events; - MidiRingBuffer<framepos_t> _step_edit_ring_buffer; - NoteMode _note_mode; - bool _step_editing; - bool _input_active; int no_roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, bool state_changing); void push_midi_input_to_step_edit_ringbuffer (framecnt_t nframes); @@ -147,6 +177,21 @@ private: void track_input_active (IOChange, void*); void map_input_active (bool); + + void filter_channels (BufferSet& bufs, ChannelMode mode, uint32_t mask); + + void _set_playback_channel_mode(ChannelMode mode, uint16_t mask) { + g_atomic_int_set(&_playback_channel_mask, (uint32_t(mode) << 16) | uint32_t(mask)); + } + void _set_playback_channel_mask (uint16_t mask) { + g_atomic_int_set(&_playback_channel_mask, (uint32_t(get_playback_channel_mode()) << 16) | uint32_t(mask)); + } + void _set_capture_channel_mode(ChannelMode mode, uint16_t mask) { + g_atomic_int_set(&_capture_channel_mask, (uint32_t(mode) << 16) | uint32_t(mask)); + } + void _set_capture_channel_mask (uint16_t mask) { + g_atomic_int_set(&_capture_channel_mask, (uint32_t(get_capture_channel_mode()) << 16) | uint32_t(mask)); + } }; } /* namespace ARDOUR*/ diff --git a/libs/ardour/ardour/route.h b/libs/ardour/ardour/route.h index 1a42999e6b..c25b6be950 100644 --- a/libs/ardour/ardour/route.h +++ b/libs/ardour/ardour/route.h @@ -436,6 +436,12 @@ class Route : public SessionObject, public Automatable, public RouteGroupMember, bool has_external_redirects() const; + /* can only be executed by a route for which is_monitor() is true + (i.e. the monitor out) + */ + void monitor_run (framepos_t start_frame, framepos_t end_frame, + pframes_t nframes, int declick); + protected: friend class Session; @@ -448,11 +454,13 @@ class Route : public SessionObject, public Automatable, public RouteGroupMember, protected: virtual framecnt_t check_initial_delay (framecnt_t nframes, framepos_t&) { return nframes; } - void passthru (framepos_t start_frame, framepos_t end_frame, + void fill_buffers_with_input (BufferSet& bufs, boost::shared_ptr<IO> io, pframes_t nframes); + + void passthru (BufferSet&, framepos_t start_frame, framepos_t end_frame, pframes_t nframes, int declick); virtual void write_out_of_band_data (BufferSet& /* bufs */, framepos_t /* start_frame */, framepos_t /* end_frame */, - framecnt_t /* nframes */) {} + framecnt_t /* nframes */) {} virtual void process_output_buffers (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, diff --git a/libs/ardour/ardour/track.h b/libs/ardour/ardour/track.h index 8493d5e4ae..7159261b51 100644 --- a/libs/ardour/ardour/track.h +++ b/libs/ardour/ardour/track.h @@ -33,6 +33,7 @@ class RouteGroup; class Source; class Region; class Diskstream; +class IO; class Track : public Route, public PublicDiskstream { diff --git a/libs/ardour/audio_buffer.cc b/libs/ardour/audio_buffer.cc index 8b2d5c744f..1fd0337dd1 100644 --- a/libs/ardour/audio_buffer.cc +++ b/libs/ardour/audio_buffer.cc @@ -67,4 +67,13 @@ AudioBuffer::resize (size_t size) cache_aligned_malloc ((void**) &_data, sizeof (Sample) * _capacity); } - +bool +AudioBuffer::check_silence (pframes_t nframes, pframes_t& n) const +{ + for (n = 0; n < _size && n < nframes; ++n) { + if (_data[n] != Sample (0)) { + return false; + } + } + return true; +} diff --git a/libs/ardour/audio_diskstream.cc b/libs/ardour/audio_diskstream.cc index dc9f427e87..24687c766f 100644 --- a/libs/ardour/audio_diskstream.cc +++ b/libs/ardour/audio_diskstream.cc @@ -408,7 +408,7 @@ AudioDiskstream::prepare_record_status(framepos_t capture_start_frame) * that someone can read playback_distance worth of data from. */ int -AudioDiskstream::process (framepos_t transport_frame, pframes_t nframes, framecnt_t& playback_distance) +AudioDiskstream::process (BufferSet& bufs, framepos_t transport_frame, pframes_t nframes, framecnt_t& playback_distance, bool need_disk_signal) { uint32_t n; boost::shared_ptr<ChannelList> c = channels.reader(); @@ -494,9 +494,9 @@ AudioDiskstream::process (framepos_t transport_frame, pframes_t nframes, framecn assert(ap); assert(rec_nframes <= (framecnt_t) ap->get_audio_buffer(nframes).capacity()); - Sample *buf = ap->get_audio_buffer (nframes).data (rec_offset); + Sample *buf = bufs.get_audio (n).data(rec_offset); memcpy (chaninfo->current_capture_buffer, buf, sizeof (Sample) * rec_nframes); - + } else { framecnt_t total = chaninfo->capture_vector.len[0] + chaninfo->capture_vector.len[1]; @@ -509,7 +509,7 @@ AudioDiskstream::process (framepos_t transport_frame, pframes_t nframes, framecn boost::shared_ptr<AudioPort> const ap = _io->audio (n); assert(ap); - Sample* buf = ap->get_audio_buffer(nframes).data (rec_offset); + Sample *buf = bufs.get_audio (n).data(rec_offset); framecnt_t first = chaninfo->capture_vector.len[0]; memcpy (chaninfo->capture_wrap_buffer, buf, sizeof (Sample) * first); @@ -657,6 +657,46 @@ AudioDiskstream::process (framepos_t transport_frame, pframes_t nframes, framecn _speed = _target_speed; } + if (need_disk_signal) { + + /* copy data over to buffer set */ + + size_t n_buffers = bufs.count().n_audio(); + size_t n_chans = c->size(); + gain_t scaling = 1.0f; + + if (n_chans > n_buffers) { + scaling = ((float) n_buffers)/n_chans; + } + + for (n = 0, chan = c->begin(); chan != c->end(); ++chan, ++n) { + + AudioBuffer& buf (bufs.get_audio (n%n_buffers)); + ChannelInfo* chaninfo (*chan); + + if (n < n_chans) { + if (scaling != 1.0f) { + buf.read_from_with_gain (chaninfo->current_playback_buffer, nframes, scaling); + } else { + buf.read_from (chaninfo->current_playback_buffer, nframes); + } + } else { + if (scaling != 1.0f) { + buf.accumulate_with_gain_from (chaninfo->current_playback_buffer, nframes, scaling); + } else { + buf.accumulate_from (chaninfo->current_playback_buffer, nframes); + } + } + } + + /* leave the MIDI count alone */ + ChanCount cnt (DataType::AUDIO, n_chans); + cnt.set (DataType::MIDI, bufs.count().n_midi()); + bufs.set_count (cnt); + + /* extra buffers will already be silent, so leave them alone */ + } + return 0; } diff --git a/libs/ardour/audio_track.cc b/libs/ardour/audio_track.cc index 90439f46e9..070a7453fb 100644 --- a/libs/ardour/audio_track.cc +++ b/libs/ardour/audio_track.cc @@ -316,8 +316,6 @@ AudioTrack::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_fram return 0; } - Sample* b; - Sample* tmpb; framepos_t transport_frame; boost::shared_ptr<AudioDiskstream> diskstream = audio_diskstream(); @@ -342,7 +340,9 @@ AudioTrack::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_fram to do nothing. */ - dret = diskstream->process (transport_frame, 0, playback_distance); + BufferSet bufs; /* empty set, no matter - nothing will happen */ + + dret = diskstream->process (bufs, transport_frame, 0, playback_distance, false); need_butler = diskstream->commit (playback_distance); return dret; } @@ -350,126 +350,22 @@ AudioTrack::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_fram _silent = false; _amp->apply_gain_automation(false); - if ((dret = diskstream->process (transport_frame, nframes, playback_distance)) != 0) { - need_butler = diskstream->commit (playback_distance); - silence (nframes); - return dret; - } - - /* special condition applies */ + BufferSet& bufs = _session.get_scratch_buffers (n_process_buffers ()); + fill_buffers_with_input (bufs, _input, nframes); + if (_meter_point == MeterInput) { - _input->process_input (_meter, start_frame, end_frame, nframes); + _meter->run (bufs, start_frame, end_frame, nframes, true); } - if (monitoring_state() == MonitoringInput) { - - passthru (start_frame, end_frame, nframes, false); - - } else if ((b = diskstream->playback_buffer(0)) != 0) { - - /* - XXX is it true that the earlier test on n_outputs() - means that we can avoid checking it again here? i think - so, because changing the i/o configuration of an IO - requires holding the AudioEngine lock, which we hold - while in the process() tree. - */ - - - /* copy the diskstream data to all output buffers */ - - size_t limit = input_streams ().n_audio(); - BufferSet& bufs = _session.get_scratch_buffers (); - const size_t blimit = bufs.count().n_audio(); - - uint32_t n; - uint32_t i; - - if (limit > blimit) { - - /* example case: auditioner configured for stereo output, - but loaded with an 8 channel file. there are only - 2 passthrough buffers, but n_process_buffers() will - return 8. - - arbitrary decision: map all channels in the diskstream - to the outputs available. - */ - - float scaling = limit/blimit; - - for (i = 0, n = 1; i < blimit; ++i, ++n) { - - /* first time through just copy a channel into - the output buffer. - */ - - Sample* bb = bufs.get_audio (i).data(); - - for (pframes_t xx = 0; xx < nframes; ++xx) { - bb[xx] = b[xx] * scaling; - } - - if (n < diskstream->n_channels().n_audio()) { - tmpb = diskstream->playback_buffer(n); - if (tmpb!=0) { - b = tmpb; - } - } - } - - for (;i < limit; ++i, ++n) { - - /* for all remaining channels, sum with existing - data in the output buffers - */ - - bufs.get_audio (i%blimit).accumulate_with_gain_from (b, nframes, 0, scaling); - - if (n < diskstream->n_channels().n_audio()) { - tmpb = diskstream->playback_buffer(n); - if (tmpb!=0) { - b = tmpb; - } - } - - } - - limit = blimit; - - } else { - for (i = 0, n = 1; i < limit; ++i, ++n) { - memcpy (bufs.get_audio (i).data(), b, sizeof (Sample) * nframes); - if (n < diskstream->n_channels().n_audio()) { - tmpb = diskstream->playback_buffer(n); - if (tmpb!=0) { - b = tmpb; - } - } - } - - /* try to leave any MIDI buffers alone */ - - ChanCount chn; - chn.set_audio (limit); - chn.set_midi (_input->n_ports().n_midi()); - bufs.set_count (chn); - } - - /* final argument: don't waste time with automation if we're recording or we've just stopped (yes it can happen) */ - - process_output_buffers ( - bufs, start_frame, end_frame, nframes, - declick, - (!diskstream->record_enabled() && _session.transport_rolling()) - ); - - } else { - /* problem with the diskstream; just be quiet for a bit */ + if ((dret = diskstream->process (bufs, transport_frame, nframes, playback_distance, (monitoring_state() == MonitoringDisk))) != 0) { + need_butler = diskstream->commit (playback_distance); silence (nframes); + return dret; } + process_output_buffers (bufs, start_frame, end_frame, nframes, declick, (!diskstream->record_enabled() && _session.transport_rolling())); + need_butler = diskstream->commit (playback_distance); return 0; diff --git a/libs/ardour/meter.cc b/libs/ardour/meter.cc index 3b49c97d27..38c5bb63de 100644 --- a/libs/ardour/meter.cc +++ b/libs/ardour/meter.cc @@ -19,6 +19,8 @@ #include <algorithm> #include <cmath> +#include "pbd/compose.h" + #include "ardour/audio_buffer.h" #include "ardour/buffer_set.h" #include "ardour/dB.h" @@ -33,6 +35,12 @@ using namespace ARDOUR; PBD::Signal0<void> Metering::Meter; +PeakMeter::PeakMeter (Session& s, const std::string& name) + : Processor (s, string_compose ("meter-%1", name)) +{ +} + + /** Get peaks from @a bufs * Input acceptance is lenient - the first n buffers from @a bufs will * be metered, where n was set by the last call to setup(), excess meters will @@ -55,7 +63,9 @@ PeakMeter::run (BufferSet& bufs, framepos_t /*start_frame*/, framepos_t /*end_fr // Meter MIDI in to the first n_midi peaks for (uint32_t i = 0; i < n_midi; ++i, ++n) { float val = 0.0f; - for (MidiBuffer::iterator e = bufs.get_midi(i).begin(); e != bufs.get_midi(i).end(); ++e) { + MidiBuffer& buf (bufs.get_midi(i)); + + for (MidiBuffer::iterator e = buf.begin(); e != buf.end(); ++e) { const Evoral::MIDIEvent<framepos_t> ev(*e, false); if (ev.is_note_on()) { const float this_vel = log(ev.buffer()[2] / 127.0 * (M_E*M_E-M_E) + M_E) - 1.0; diff --git a/libs/ardour/midi_diskstream.cc b/libs/ardour/midi_diskstream.cc index cf54bef82e..9c11e818ac 100644 --- a/libs/ardour/midi_diskstream.cc +++ b/libs/ardour/midi_diskstream.cc @@ -26,6 +26,7 @@ #include <fcntl.h> #include <cstdlib> #include <ctime> +#include <strings.h> // for ffs(3) #include <sys/stat.h> #include <sys/mman.h> @@ -320,7 +321,7 @@ get_location_times(const Location* location, } int -MidiDiskstream::process (framepos_t transport_frame, pframes_t nframes, framecnt_t& playback_distance) +MidiDiskstream::process (BufferSet& bufs, framepos_t transport_frame, pframes_t nframes, framecnt_t& playback_distance, bool need_disk_signal) { framecnt_t rec_offset = 0; framecnt_t rec_nframes = 0; @@ -389,8 +390,11 @@ MidiDiskstream::process (framepos_t transport_frame, pframes_t nframes, framecnt // Pump entire port buffer into the ring buffer (FIXME: split cycles?) MidiBuffer& buf = sp->get_midi_buffer(nframes); + ChannelMode mode = AllChannels; // _track->get_capture_channel_mode (); + uint32_t mask = 0xffff; // _track->get_capture_channel_mask (); + for (MidiBuffer::iterator i = buf.begin(); i != buf.end(); ++i) { - const Evoral::MIDIEvent<MidiBuffer::TimeType> ev(*i, false); + Evoral::MIDIEvent<MidiBuffer::TimeType> ev(*i, false); #ifndef NDEBUG if (DEBUG::MidiIO & PBD::debug_bits) { const uint8_t* __data = ev.buffer(); @@ -416,8 +420,31 @@ MidiDiskstream::process (framepos_t transport_frame, pframes_t nframes, framecnt probabl be implemented in the source instead of here. */ const framecnt_t loop_offset = _num_captured_loops * loop_length; - _capture_buf->write(transport_frame + loop_offset + ev.time(), - ev.type(), ev.size(), ev.buffer()); + + switch (mode) { + case AllChannels: + _capture_buf->write(transport_frame + loop_offset + ev.time(), + ev.type(), ev.size(), ev.buffer()); + break; + case FilterChannels: + if (ev.is_channel_event()) { + if ((1<<ev.channel()) & mask) { + _capture_buf->write(transport_frame + loop_offset + ev.time(), + ev.type(), ev.size(), ev.buffer()); + } + } else { + _capture_buf->write(transport_frame + loop_offset + ev.time(), + ev.type(), ev.size(), ev.buffer()); + } + break; + case ForceChannel: + if (ev.is_channel_event()) { + ev.set_channel (ffs(mask) - 1); + } + _capture_buf->write(transport_frame + loop_offset + ev.time(), + ev.type(), ev.size(), ev.buffer()); + break; + } } g_atomic_int_add(&_frames_pending_write, nframes); @@ -475,6 +502,18 @@ MidiDiskstream::process (framepos_t transport_frame, pframes_t nframes, framecnt } + if (need_disk_signal) { + /* copy the diskstream data to all output buffers */ + + MidiBuffer& mbuf (bufs.get_midi (0)); + get_playback (mbuf, nframes); + + /* leave the audio count alone */ + ChanCount cnt (DataType::MIDI, 1); + cnt.set (DataType::AUDIO, bufs.count().n_audio()); + bufs.set_count (cnt); + } + return 0; } @@ -1078,10 +1117,6 @@ MidiDiskstream::get_state () char buf[64]; LocaleGuard lg (X_("POSIX")); - node.add_property("channel-mode", enum_2_string(get_channel_mode())); - snprintf (buf, sizeof(buf), "0x%x", get_channel_mask()); - node.add_property("channel-mask", buf); - if (_write_source && _session.get_record_enabled()) { XMLNode* cs_child = new XMLNode (X_("CapturingSources")); @@ -1111,7 +1146,6 @@ MidiDiskstream::get_state () int MidiDiskstream::set_state (const XMLNode& node, int version) { - const XMLProperty* prop; XMLNodeList nlist = node.children(); XMLNodeIterator niter; XMLNode* capture_pending_node = 0; @@ -1131,26 +1165,10 @@ MidiDiskstream::set_state (const XMLNode& node, int version) return -1; } - ChannelMode channel_mode = AllChannels; - if ((prop = node.property ("channel-mode")) != 0) { - channel_mode = ChannelMode (string_2_enum(prop->value(), channel_mode)); - } - - unsigned int channel_mask = 0xFFFF; - if ((prop = node.property ("channel-mask")) != 0) { - sscanf (prop->value().c_str(), "0x%x", &channel_mask); - if (channel_mask & (~0xFFFF)) { - warning << _("MidiDiskstream: XML property channel-mask out of range") << endmsg; - } - } - - if (capture_pending_node) { use_pending_capture_data (*capture_pending_node); } - set_channel_mode (channel_mode, channel_mask); - in_set_state = false; return 0; diff --git a/libs/ardour/midi_port.cc b/libs/ardour/midi_port.cc index 51d364a3d9..72150f8cfc 100644 --- a/libs/ardour/midi_port.cc +++ b/libs/ardour/midi_port.cc @@ -108,7 +108,6 @@ MidiPort::get_midi_buffer (pframes_t nframes) return *_buffer; } - void MidiPort::cycle_end (pframes_t /*nframes*/) { diff --git a/libs/ardour/midi_ring_buffer.cc b/libs/ardour/midi_ring_buffer.cc index db3a4cb109..e4ae3f3ffe 100644 --- a/libs/ardour/midi_ring_buffer.cc +++ b/libs/ardour/midi_ring_buffer.cc @@ -17,6 +17,7 @@ */ #include "pbd/compose.h" +#include "pbd/enumwriter.h" #include "pbd/error.h" #include "ardour/debug.h" @@ -81,17 +82,6 @@ MidiRingBuffer<T>::read(MidiBuffer& dst, framepos_t start, framepos_t end, frame bool r = this->peek (&status, sizeof(uint8_t)); assert (r); // If this failed, buffer is corrupt, all hope is lost - // Ignore event if it doesn't match channel filter - if (is_channel_event(status) && get_channel_mode() == FilterChannels) { - const uint8_t channel = status & 0x0F; - if (!(get_channel_mask() & (1L << channel))) { - DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("MRB skipping event (%3 bytes) due to channel mask (mask = %1 chn = %2)\n", - get_channel_mask(), (int) channel, ev_size)); - this->increment_read_ptr (ev_size); // Advance read pointer to next event - continue; - } - } - /* lets see if we are going to be able to write this event into dst. */ uint8_t* write_loc = dst.reserve (ev_time, ev_size); @@ -130,10 +120,7 @@ MidiRingBuffer<T>::read(MidiBuffer& dst, framepos_t start, framepos_t end, frame } else if (is_note_off(write_loc[0])) { _tracker.remove (write_loc[1], write_loc[0] & 0xf); } - - if (is_channel_event(status) && get_channel_mode() == ForceChannel) { - write_loc[0] = (write_loc[0] & 0xF0) | (get_channel_mask() & 0x0F); - } + ++count; } else { cerr << "WARNING: error reading event contents from MIDI ring" << endl; diff --git a/libs/ardour/midi_track.cc b/libs/ardour/midi_track.cc index 65a42836a3..0f1b2b52af 100644 --- a/libs/ardour/midi_track.cc +++ b/libs/ardour/midi_track.cc @@ -17,6 +17,8 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include <strings.h> // for ffs(3) + #include "pbd/enumwriter.h" #include "pbd/convert.h" #include "evoral/midi_util.h" @@ -55,6 +57,8 @@ MidiTrack::MidiTrack (Session& sess, string name, Route::Flag flag, TrackMode mo , _note_mode(Sustained) , _step_editing (false) , _input_active (true) + , _playback_channel_mask(0x0000ffff) + , _capture_channel_mask(0x0000ffff) { } @@ -158,6 +162,38 @@ MidiTrack::set_state (const XMLNode& node, int version) set_input_active (string_is_affirmative (prop->value())); } + ChannelMode playback_channel_mode = AllChannels; + ChannelMode capture_channel_mode = AllChannels; + + if ((prop = node.property ("playback-channel-mode")) != 0) { + playback_channel_mode = ChannelMode (string_2_enum(prop->value(), playback_channel_mode)); + } + if ((prop = node.property ("capture-channel-mode")) != 0) { + playback_channel_mode = ChannelMode (string_2_enum(prop->value(), capture_channel_mode)); + } + if ((prop = node.property ("channel-mode")) != 0) { + /* 3.0 behaviour where capture and playback modes were not separated */ + playback_channel_mode = ChannelMode (string_2_enum(prop->value(), playback_channel_mode)); + capture_channel_mode = playback_channel_mode; + } + + unsigned int playback_channel_mask = 0xffff; + unsigned int capture_channel_mask = 0xffff; + + if ((prop = node.property ("playback-channel-mask")) != 0) { + sscanf (prop->value().c_str(), "0x%x", &playback_channel_mask); + } + if ((prop = node.property ("capture-channel-mask")) != 0) { + sscanf (prop->value().c_str(), "0x%x", &capture_channel_mask); + } + if ((prop = node.property ("channel-mask")) != 0) { + sscanf (prop->value().c_str(), "0x%x", &playback_channel_mask); + capture_channel_mask = playback_channel_mask; + } + + set_playback_channel_mode (playback_channel_mode, playback_channel_mask); + set_capture_channel_mode (capture_channel_mode, capture_channel_mask); + pending_state = const_cast<XMLNode*> (&node); if (_session.state_of_the_state() & Session::Loading) { @@ -196,6 +232,13 @@ MidiTrack::state(bool full_state) root.add_child_nocopy (*freeze_node); } + root.add_property("playback_channel-mode", enum_2_string(get_playback_channel_mode())); + root.add_property("capture_channel-mode", enum_2_string(get_capture_channel_mode())); + snprintf (buf, sizeof(buf), "0x%x", get_playback_channel_mask()); + root.add_property("playback-channel-mask", buf); + snprintf (buf, sizeof(buf), "0x%x", get_capture_channel_mask()); + root.add_property("capture-channel-mask", buf); + root.add_property ("note-mode", enum_2_string (_note_mode)); root.add_property ("step-editing", (_step_editing ? "yes" : "no")); root.add_property ("input-active", (_input_active ? "yes" : "no")); @@ -300,25 +343,36 @@ MidiTrack::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame playback distance to zero, thus causing diskstream::commit to do nothing. */ - dret = diskstream->process (transport_frame, 0, playback_distance); + BufferSet bufs; /* empty set - is OK, since nothing will happen */ + + dret = diskstream->process (bufs, transport_frame, 0, playback_distance, false); need_butler = diskstream->commit (playback_distance); return dret; } + BufferSet& bufs = _session.get_scratch_buffers (n_process_buffers()); + + fill_buffers_with_input (bufs, _input, nframes); + + if (_meter_point == MeterInput) { + _meter->run (bufs, start_frame, end_frame, nframes, true); + } + + /* filter captured data before the diskstream sees it */ + + filter_channels (bufs, get_capture_channel_mode(), get_capture_channel_mask()); _silent = false; - if ((dret = diskstream->process (transport_frame, nframes, playback_distance)) != 0) { + if ((dret = diskstream->process (bufs, transport_frame, nframes, playback_distance, (monitoring_state() == MonitoringDisk))) != 0) { need_butler = diskstream->commit (playback_distance); silence (nframes); return dret; } - /* special condition applies */ - - if (_meter_point == MeterInput) { - _input->process_input (_meter, start_frame, end_frame, nframes); - } + /* filter playback data before we do anything else */ + + filter_channels (bufs, get_playback_channel_mode(), get_playback_channel_mask ()); if (monitoring_state() == MonitoringInput) { @@ -334,47 +388,17 @@ MidiTrack::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame diskstream->flush_playback (start_frame, end_frame); - passthru (start_frame, end_frame, nframes, 0); - - } else { - - /* - XXX is it true that the earlier test on n_outputs() - means that we can avoid checking it again here? i think - so, because changing the i/o configuration of an IO - requires holding the AudioEngine lock, which we hold - while in the process() tree. - */ - - - /* copy the diskstream data to all output buffers */ - - BufferSet& bufs = _session.get_scratch_buffers (n_process_buffers()); - MidiBuffer& mbuf (bufs.get_midi (0)); - - /* we are a MIDI track, so we always start the chain with a - * single-MIDI-channel diskstream - */ - ChanCount c; - c.set_audio (0); - c.set_midi (1); - bufs.set_count (c); - - assert (nframes > 0); - - diskstream->get_playback (mbuf, nframes); - - /* append immediate messages to the first MIDI buffer (thus sending it to the first output port) */ - - write_out_of_band_data (bufs, start_frame, end_frame, nframes); - - /* final argument: don't waste time with automation if we're recording or we've just stopped (yes it can happen) */ + } - process_output_buffers ( - bufs, start_frame, end_frame, nframes, - declick, (!diskstream->record_enabled() && !_session.transport_stopped()) - ); - } + + /* append immediate messages to the first MIDI buffer (thus sending it to the first output port) */ + + write_out_of_band_data (bufs, start_frame, end_frame, nframes); + + /* final argument: don't waste time with automation if we're not recording or rolling */ + + process_output_buffers (bufs, start_frame, end_frame, nframes, + declick, (!diskstream->record_enabled() && !_session.transport_stopped())); for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { boost::shared_ptr<Delivery> d = boost::dynamic_pointer_cast<Delivery> (*i); @@ -457,6 +481,43 @@ MidiTrack::push_midi_input_to_step_edit_ringbuffer (framecnt_t nframes) } } +void +MidiTrack::filter_channels (BufferSet& bufs, ChannelMode mode, uint32_t mask) +{ + if (mode == AllChannels) { + return; + } + + MidiBuffer& buf (bufs.get_midi (0)); + + for (MidiBuffer::iterator e = buf.begin(); e != buf.end(); ) { + + Evoral::MIDIEvent<framepos_t> ev(*e, false); + + if (ev.is_channel_event()) { + switch (mode) { + case FilterChannels: + if (0 == ((1<<ev.channel()) & mask)) { + e = buf.erase (e); + } else { + ++e; + } + break; + case ForceChannel: + ev.set_channel (ffs (mask) - 1); + ++e; + break; + case AllChannels: + /* handled by the opening if() */ + ++e; + break; + } + } else { + ++e; + } + } +} + void MidiTrack::write_out_of_band_data (BufferSet& bufs, framepos_t /*start*/, framepos_t /*end*/, framecnt_t nframes) { @@ -637,21 +698,53 @@ MidiTrack::write_source (uint32_t) } void -MidiTrack::set_channel_mode (ChannelMode mode, uint16_t mask) +MidiTrack::set_playback_channel_mode(ChannelMode mode, uint16_t mask) { - midi_diskstream()->set_channel_mode (mode, mask); + ChannelMode old = get_playback_channel_mode (); + uint16_t old_mask = get_playback_channel_mask (); + + if (old != mode || mask != old_mask) { + _set_playback_channel_mode (mode, mask); + PlaybackChannelModeChanged (); + _session.set_dirty (); + } } -ChannelMode -MidiTrack::get_channel_mode () +void +MidiTrack::set_capture_channel_mode(ChannelMode mode, uint16_t mask) { - return midi_diskstream()->get_channel_mode (); + ChannelMode old = get_capture_channel_mode (); + uint16_t old_mask = get_capture_channel_mask (); + + if (old != mode || mask != old_mask) { + _set_capture_channel_mode (mode, mask); + CaptureChannelModeChanged (); + _session.set_dirty (); + } } -uint16_t -MidiTrack::get_channel_mask () +void +MidiTrack::set_playback_channel_mask (uint16_t mask) { - return midi_diskstream()->get_channel_mask (); + uint16_t old = get_playback_channel_mask(); + + if (old != mask) { + _set_playback_channel_mask (mask); + PlaybackChannelMaskChanged (); + _session.set_dirty (); + } +} + +void +MidiTrack::set_capture_channel_mask (uint16_t mask) +{ + uint16_t old = get_capture_channel_mask(); + + if (old != mask) { + _set_capture_channel_mask (mask); + CaptureChannelMaskChanged (); + _session.set_dirty (); + } } boost::shared_ptr<MidiPlaylist> @@ -739,7 +832,7 @@ MidiTrack::act_on_mute () if (muted()) { /* only send messages for channels we are using */ - uint16_t mask = get_channel_mask(); + uint16_t mask = get_playback_channel_mask(); for (uint8_t channel = 0; channel <= 0xF; channel++) { @@ -792,3 +885,4 @@ MidiTrack::monitoring_state () const } return ms; } + diff --git a/libs/ardour/return.cc b/libs/ardour/return.cc index 519f3ca494..921be6a53a 100644 --- a/libs/ardour/return.cc +++ b/libs/ardour/return.cc @@ -50,7 +50,7 @@ Return::Return (Session& s, bool internal) /* never muted */ _amp.reset (new Amp (_session)); - _meter.reset (new PeakMeter (_session)); + _meter.reset (new PeakMeter (_session, name())); } Return::~Return () diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index 1a90553be2..9f933e0a4a 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -37,6 +37,7 @@ #include "ardour/amp.h" #include "ardour/audio_buffer.h" +#include "ardour/audio_port.h" #include "ardour/audioengine.h" #include "ardour/buffer.h" #include "ardour/buffer_set.h" @@ -46,6 +47,8 @@ #include "ardour/internal_return.h" #include "ardour/internal_send.h" #include "ardour/meter.h" +#include "ardour/midi_buffer.h" +#include "ardour/midi_port.h" #include "ardour/monitor_processor.h" #include "ardour/pannable.h" #include "ardour/panner_shell.h" @@ -139,7 +142,7 @@ Route::init () they will be added to _processors by setup_invisible_processors () */ - _meter.reset (new PeakMeter (_session)); + _meter.reset (new PeakMeter (_session, _name)); _meter->set_display_to_user (false); _meter->activate (); @@ -454,8 +457,8 @@ Route::process_output_buffers (BufferSet& bufs, on a transition between monitoring states we get a de-clicking gain change in the _main_outs delivery. */ - _main_outs->no_outs_cuz_we_no_monitor (monitoring_state () == MonitoringSilence); + _main_outs->no_outs_cuz_we_no_monitor (monitoring_state () == MonitoringSilence); /* ------------------------------------------------------------------------------------------- GLOBAL DECLICK (for transport changes etc.) @@ -561,39 +564,26 @@ Route::n_process_buffers () } void -Route::passthru (framepos_t start_frame, framepos_t end_frame, pframes_t nframes, int declick) +Route::monitor_run (framepos_t start_frame, framepos_t end_frame, pframes_t nframes, int declick) { - BufferSet& bufs = _session.get_scratch_buffers (n_process_buffers()); + assert (is_monitor()); + BufferSet& bufs (_session.get_scratch_buffers (n_process_buffers())); + passthru (bufs, start_frame, end_frame, nframes, declick); +} +void +Route::passthru (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, pframes_t nframes, int declick) +{ _silent = false; - assert (bufs.available() >= input_streams()); - - if (_input->n_ports() == ChanCount::ZERO) { - silence_unlocked (nframes); - } - - bufs.set_count (input_streams()); - if (is_monitor() && _session.listening() && !_session.is_auditioning()) { /* control/monitor bus ignores input ports when something is feeding the listen "stream". data will "arrive" into the route from the intreturn processor element. */ - bufs.silence (nframes, 0); - - } else { - - for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) { - BufferSet::iterator o = bufs.begin(*t); - PortSet& ports (_input->ports()); - - for (PortSet::iterator i = ports.begin(*t); i != ports.end(*t); ++i, ++o) { - o->read_from (i->get_buffer(nframes), nframes); - } - } + bufs.silence (nframes, 0); } write_out_of_band_data (bufs, start_frame, end_frame, nframes); @@ -2985,6 +2975,7 @@ int Route::no_roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, bool session_state_changing) { Glib::Threads::RWLock::ReaderLock lm (_processor_lock, Glib::Threads::TRY_LOCK); + if (!lm.locked()) { return 0; } @@ -2997,6 +2988,7 @@ Route::no_roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, silence_unlocked (nframes); return 0; } + if (session_state_changing) { if (_session.transport_speed() != 0.0f) { /* we're rolling but some state is changing (e.g. our diskstream contents) @@ -3012,8 +3004,16 @@ Route::no_roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, */ } + BufferSet& bufs = _session.get_scratch_buffers (n_process_buffers()); + + fill_buffers_with_input (bufs, _input, nframes); + + if (_meter_point == MeterInput) { + _meter->run (bufs, start_frame, end_frame, nframes, true); + } + _amp->apply_gain_automation (false); - passthru (start_frame, end_frame, nframes, 0); + passthru (bufs, start_frame, end_frame, nframes, 0); return 0; } @@ -3043,7 +3043,15 @@ Route::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, in _silent = false; - passthru (start_frame, end_frame, nframes, declick); + BufferSet& bufs = _session.get_scratch_buffers (n_process_buffers()); + + fill_buffers_with_input (bufs, _input, nframes); + + if (_meter_point == MeterInput) { + _meter->run (bufs, start_frame, end_frame, nframes, true); + } + + passthru (bufs, start_frame, end_frame, nframes, declick); return 0; } @@ -4148,3 +4156,90 @@ Route::non_realtime_locate (framepos_t pos) } } } + +void +Route::fill_buffers_with_input (BufferSet& bufs, boost::shared_ptr<IO> io, pframes_t nframes) +{ + size_t n_buffers; + size_t i; + + /* MIDI + * + * We don't currently mix MIDI input together, so we don't need the + * complex logic of the audio case. + */ + + n_buffers = bufs.count().n_midi (); + + for (i = 0; i < n_buffers; ++i) { + + boost::shared_ptr<MidiPort> source_port = io->midi (i); + MidiBuffer& buf (bufs.get_midi (i)); + + if (source_port) { + buf.copy (source_port->get_midi_buffer(nframes)); + } else { + buf.silence (nframes); + } + } + + /* AUDIO */ + + n_buffers = bufs.count().n_audio(); + + size_t n_ports = io->n_ports().n_audio(); + float scaling = 1.0f; + + if (n_ports > n_buffers) { + scaling = ((float) n_buffers) / n_ports; + } + + for (i = 0; i < n_ports; ++i) { + + /* if there are more ports than buffers, map them onto buffers + * in a round-robin fashion + */ + + boost::shared_ptr<AudioPort> source_port = io->audio (i); + AudioBuffer& buf (bufs.get_audio (i%n_buffers)); + + + if (i < n_buffers) { + + /* first time through just copy a channel into + the output buffer. + */ + + buf.read_from (source_port->get_audio_buffer (nframes), nframes); + + if (scaling != 1.0f) { + buf.apply_gain (scaling, nframes); + } + + } else { + + /* on subsequent times around, merge data from + * the port with what is already there + */ + + if (scaling != 1.0f) { + buf.accumulate_with_gain_from (source_port->get_audio_buffer (nframes), nframes, 0, scaling); + } else { + buf.accumulate_from (source_port->get_audio_buffer (nframes), nframes); + } + } + } + + /* silence any remaining buffers */ + + for (; i < n_buffers; ++i) { + AudioBuffer& buf (bufs.get_audio (i)); + buf.silence (nframes); + } + + /* establish the initial setup of the buffer set, reflecting what was + copied into it. + */ + + bufs.set_count (io->n_ports()); +} diff --git a/libs/ardour/send.cc b/libs/ardour/send.cc index fb5c6b7b0c..107cf9862b 100644 --- a/libs/ardour/send.cc +++ b/libs/ardour/send.cc @@ -83,7 +83,7 @@ Send::Send (Session& s, boost::shared_ptr<Pannable> p, boost::shared_ptr<MuteMas boost_debug_shared_ptr_mark_interesting (this, "send"); _amp.reset (new Amp (_session)); - _meter.reset (new PeakMeter (_session)); + _meter.reset (new PeakMeter (_session, name())); add_control (_amp->gain_control ()); } diff --git a/libs/ardour/session_process.cc b/libs/ardour/session_process.cc index 9a4fabeb64..cd7daaf185 100644 --- a/libs/ardour/session_process.cc +++ b/libs/ardour/session_process.cc @@ -873,7 +873,7 @@ Session::process_audition (pframes_t nframes) /* if using a monitor section, run it because otherwise we don't hear anything */ if (auditioner->needs_monitor()) { - _monitor_out->passthru (_transport_frame, _transport_frame + nframes, nframes, false); + _monitor_out->monitor_run (_transport_frame, _transport_frame + nframes, nframes, false); } /* handle pending events */ diff --git a/libs/ardour/track.cc b/libs/ardour/track.cc index 52a960fce6..1effaa45b8 100644 --- a/libs/ardour/track.cc +++ b/libs/ardour/track.cc @@ -330,16 +330,21 @@ int Track::no_roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, bool session_state_changing) { Glib::Threads::RWLock::ReaderLock lm (_processor_lock, Glib::Threads::TRY_LOCK); + if (!lm.locked()) { return 0; } bool can_record = _session.actively_recording (); + /* no outputs? nothing to do ... what happens if we have sends etc. ? */ + if (n_outputs().n_total() == 0) { return 0; } + /* not active ... do the minimum possible by just outputting silence */ + if (!_active) { silence (nframes); return 0; @@ -393,14 +398,9 @@ Track::no_roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, } } - if (!_have_internal_generator && metering_state() == MeteringInput) { - _input->process_input (_meter, start_frame, end_frame, nframes); - } - _amp->apply_gain_automation (false); /* if have_internal_generator, or .. */ - //_input->process_input (_meter, start_frame, end_frame, nframes); if (be_silent) { @@ -408,10 +408,15 @@ Track::no_roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, } else { - /* we're sending signal, but we may still want to meter the input. - */ + BufferSet& bufs = _session.get_scratch_buffers (n_process_buffers()); + + fill_buffers_with_input (bufs, _input, nframes); - passthru (start_frame, end_frame, nframes, false); + if (_meter_point == MeterInput) { + _meter->run (bufs, start_frame, end_frame, nframes, true); + } + + passthru (bufs, start_frame, end_frame, nframes, false); } for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { @@ -447,7 +452,10 @@ Track::silent_roll (pframes_t nframes, framepos_t /*start_frame*/, framepos_t /* silence (nframes); framecnt_t playback_distance; - int const dret = _diskstream->process (_session.transport_frame(), nframes, playback_distance); + + BufferSet& bufs (_session.get_silent_buffers (n_process_buffers())); + + int const dret = _diskstream->process (bufs, _session.transport_frame(), nframes, playback_distance, false); need_butler = _diskstream->commit (playback_distance); return dret; } @@ -934,3 +942,4 @@ Track::metering_state () const { return _diskstream->record_enabled() ? MeteringInput : MeteringRoute; } + |