From d6edbf5ca2dd644754578fbe5bfd0e89834613e0 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Mon, 14 May 2012 18:35:19 +0000 Subject: [PATCH] merge 12096:12268 from svn+ssh://ardoursvn@subversion.ardour.org/ardour2/branches/3.0 git-svn-id: svn://localhost/ardour2/branches/3.0-SG@12269 d708f5d6-7413-0410-9779-e7cbd77b26cf --- gtk2_ardour/analysis_window.cc | 1 + gtk2_ardour/ardev_common.sh.in | 1 + gtk2_ardour/ardour_ui.cc | 6 +- gtk2_ardour/ardour_ui_ed.cc | 2 +- gtk2_ardour/ardour_window.cc | 2 +- gtk2_ardour/audio_clock.cc | 12 +- gtk2_ardour/audio_region_view.cc | 348 ++++++++- gtk2_ardour/audio_region_view.h | 20 +- gtk2_ardour/audio_streamview.cc | 17 + gtk2_ardour/audio_streamview.h | 2 + gtk2_ardour/automation_time_axis.cc | 66 ++ gtk2_ardour/automation_time_axis.h | 2 + gtk2_ardour/button_joiner.cc | 2 +- gtk2_ardour/canvas-simpleline.c | 4 +- gtk2_ardour/crossfade_edit.cc | 32 +- gtk2_ardour/curvetest.cc | 10 +- gtk2_ardour/editor.cc | 199 +++-- gtk2_ardour/editor.h | 10 + gtk2_ardour/editor_canvas_events.cc | 64 ++ gtk2_ardour/editor_drag.cc | 116 ++- gtk2_ardour/editor_drag.h | 25 + gtk2_ardour/editor_items.h | 2 + gtk2_ardour/editor_mouse.cc | 21 +- gtk2_ardour/editor_ops.cc | 7 +- gtk2_ardour/editor_route_groups.cc | 4 +- gtk2_ardour/editor_selection.cc | 61 +- gtk2_ardour/editor_snapshots.cc | 3 +- gtk2_ardour/export_format_selector.cc | 2 +- gtk2_ardour/group_tabs.cc | 2 +- gtk2_ardour/gtk_pianokeyboard.c | 2 +- .../{crossfade-in-S1.png => fadein-S1.png} | Bin .../{crossfade-in-S2.png => fadein-S2.png} | Bin ...nt-power.png => fadein-constant-power.png} | Bin ...de-in-fast-cut.png => fadein-fast-cut.png} | Bin ...ssfade-in-linear.png => fadein-linear.png} | Bin ...de-in-long-cut.png => fadein-long-cut.png} | Bin ...-in-short-cut.png => fadein-short-cut.png} | Bin ...de-in-slow-cut.png => fadein-slow-cut.png} | Bin .../{crossfade-out-S1.png => fadeout-S1.png} | Bin .../{crossfade-out-S2.png => fadeout-S2.png} | Bin ...t-power.png => fadeout-constant-power.png} | Bin ...-out-fast-cut.png => fadeout-fast-cut.png} | Bin ...fade-out-linear.png => fadeout-linear.png} | Bin ...-out-long-cut.png => fadeout-long-cut.png} | Bin ...ut-short-cut.png => fadeout-short-cut.png} | Bin ...-out-slow-cut.png => fadeout-slow-cut.png} | Bin gtk2_ardour/location_ui.cc | 22 +- gtk2_ardour/location_ui.h | 4 + gtk2_ardour/main.cc | 71 +- gtk2_ardour/midi_region_view.cc | 16 +- gtk2_ardour/midi_region_view.h | 1 - gtk2_ardour/plugin_selector.cc | 27 +- gtk2_ardour/port_matrix.cc | 4 +- gtk2_ardour/processor_box.cc | 2 +- gtk2_ardour/public_editor.h | 2 + gtk2_ardour/quantize_dialog.cc | 18 - gtk2_ardour/quantize_dialog.h | 3 - gtk2_ardour/rc_option_editor.cc | 13 +- gtk2_ardour/route_group_dialog.cc | 4 +- gtk2_ardour/route_time_axis.cc | 3 + gtk2_ardour/route_ui.cc | 4 +- gtk2_ardour/session_option_editor.cc | 29 +- gtk2_ardour/sfdb_freesound_mootcher.cc | 2 +- gtk2_ardour/shuttle_control.cc | 8 +- gtk2_ardour/startup.cc | 2 +- gtk2_ardour/stereo_panner.cc | 2 +- gtk2_ardour/tempo_dialog.cc | 8 +- gtk2_ardour/time_axis_view.h | 3 - gtk2_ardour/time_axis_view_item.cc | 42 +- gtk2_ardour/time_axis_view_item.h | 8 +- libs/ardour/ardour/audio_unit.h | 3 +- libs/ardour/ardour/audioregion.h | 32 +- libs/ardour/ardour/delivery.h | 6 +- libs/ardour/ardour/io.h | 2 +- libs/ardour/ardour/playlist.h | 22 +- libs/ardour/ardour/plugin.h | 29 +- libs/ardour/ardour/quantize.h | 3 +- libs/ardour/ardour/region.h | 9 + .../ardour/session_configuration_vars.h | 3 +- libs/ardour/ardour/types.h | 18 +- libs/ardour/audio_playlist.cc | 54 +- libs/ardour/audioregion.cc | 737 +++++++++++++----- libs/ardour/broadcast_info.cc | 2 +- libs/ardour/control_protocol_manager.cc | 15 +- libs/ardour/delivery.cc | 9 +- libs/ardour/enums.cc | 31 +- libs/ardour/filesystem_paths.cc | 10 - libs/ardour/graph.cc | 12 +- libs/ardour/internal_send.cc | 2 +- libs/ardour/io.cc | 4 +- libs/ardour/lv2_plugin.cc | 6 +- libs/ardour/midi_playlist.cc | 21 +- libs/ardour/midi_playlist_source.cc | 2 +- libs/ardour/midi_ui.cc | 2 +- libs/ardour/playlist.cc | 272 +++---- libs/ardour/plugin_insert.cc | 20 +- libs/ardour/quantize.cc | 3 +- libs/ardour/region.cc | 30 + libs/ardour/run-tests.sh | 1 + libs/ardour/session_metadata.cc | 2 +- libs/ardour/session_state.cc | 8 +- libs/ardour/tempo.cc | 5 +- libs/ardour/test/audio_region_test.cc | 13 +- libs/ardour/test/control_surfaces_test.cc | 23 + libs/ardour/test/control_surfaces_test.h | 11 + libs/ardour/test/test_needing_session.cc | 8 +- libs/ardour/wscript | 1 + libs/audiographer/tests/type_utils_test.cc | 2 + libs/evoral/evoral/ControlList.hpp | 1 + libs/evoral/evoral/Event.hpp | 2 +- libs/evoral/src/ControlList.cpp | 33 +- libs/evoral/src/Event.cpp | 8 +- libs/gtkmm2ext/cairo_packer.cc | 2 +- libs/gtkmm2ext/gtk_ui.cc | 27 +- libs/gtkmm2ext/gtkmm2ext/binding_proxy.h | 4 +- libs/gtkmm2ext/gtkmm2ext/gtk_ui.h | 16 +- libs/midi++2/ipmidi_port.cc | 6 +- libs/midi++2/jack_midi_port.cc | 4 +- libs/midi++2/midi++/ipmidi_port.h | 2 +- libs/midi++2/midi++/jack_midi_port.h | 2 +- libs/midi++2/midi++/port.h | 6 +- libs/pbd/base_ui.cc | 14 +- libs/pbd/convert.cc | 8 +- libs/pbd/crossthread.cc | 2 +- libs/pbd/pbd/base_ui.h | 10 +- libs/pbd/pbd/receiver.h | 2 +- libs/pbd/pbd/semaphore.h | 4 +- libs/pbd/pbd/signal.h.py | 214 +++++ libs/pbd/pbd/signals.h | 198 +++-- libs/pbd/signals.cc | 2 + libs/pbd/test/signals_test.cc | 48 ++ libs/pbd/test/signals_test.h | 4 + libs/pbd/test/xpath.cc | 14 +- libs/pbd/wscript | 8 +- libs/surfaces/control_protocol/basic_ui.cc | 1 - .../control_protocol/control_protocol.cc | 1 + .../control_protocol/control_protocol.h | 4 +- .../control_protocol/control_protocol/types.h | 6 +- libs/surfaces/mackie/button.cc | 186 ++--- libs/surfaces/mackie/device_info.cc | 24 +- libs/surfaces/mackie/device_info.h | 2 + libs/surfaces/mackie/device_profile.cc | 9 +- libs/surfaces/mackie/gui.cc | 14 +- libs/surfaces/mackie/gui.h | 3 + .../mackie/mackie_control_protocol.cc | 59 +- .../surfaces/mackie/mackie_control_protocol.h | 8 +- libs/surfaces/mackie/mcp_buttons.cc | 28 +- libs/surfaces/mackie/midi_byte_array.cc | 65 +- libs/surfaces/mackie/midi_byte_array.h | 3 - libs/surfaces/mackie/strip.cc | 16 +- libs/surfaces/mackie/strip.h | 2 +- libs/surfaces/mackie/surface.cc | 43 +- libs/surfaces/mackie/surface.h | 3 +- libs/surfaces/mackie/surface_port.cc | 14 +- libs/surfaces/osc/osc.cc | 1 - libs/surfaces/osc/wscript | 1 - libs/surfaces/wscript | 5 +- tools/linux_packaging/build | 42 +- tools/linux_packaging/noderun | 2 +- tools/linux_packaging/package | 2 +- tools/osx_packaging/osx_build | 4 + wscript | 30 +- 162 files changed, 2931 insertions(+), 1064 deletions(-) rename gtk2_ardour/icons/{crossfade-in-S1.png => fadein-S1.png} (100%) rename gtk2_ardour/icons/{crossfade-in-S2.png => fadein-S2.png} (100%) rename gtk2_ardour/icons/{crossfade-in-constant-power.png => fadein-constant-power.png} (100%) rename gtk2_ardour/icons/{crossfade-in-fast-cut.png => fadein-fast-cut.png} (100%) rename gtk2_ardour/icons/{crossfade-in-linear.png => fadein-linear.png} (100%) rename gtk2_ardour/icons/{crossfade-in-long-cut.png => fadein-long-cut.png} (100%) rename gtk2_ardour/icons/{crossfade-in-short-cut.png => fadein-short-cut.png} (100%) rename gtk2_ardour/icons/{crossfade-in-slow-cut.png => fadein-slow-cut.png} (100%) rename gtk2_ardour/icons/{crossfade-out-S1.png => fadeout-S1.png} (100%) rename gtk2_ardour/icons/{crossfade-out-S2.png => fadeout-S2.png} (100%) rename gtk2_ardour/icons/{crossfade-out-constant-power.png => fadeout-constant-power.png} (100%) rename gtk2_ardour/icons/{crossfade-out-fast-cut.png => fadeout-fast-cut.png} (100%) rename gtk2_ardour/icons/{crossfade-out-linear.png => fadeout-linear.png} (100%) rename gtk2_ardour/icons/{crossfade-out-long-cut.png => fadeout-long-cut.png} (100%) rename gtk2_ardour/icons/{crossfade-out-short-cut.png => fadeout-short-cut.png} (100%) rename gtk2_ardour/icons/{crossfade-out-slow-cut.png => fadeout-slow-cut.png} (100%) create mode 100644 libs/ardour/test/control_surfaces_test.cc create mode 100644 libs/ardour/test/control_surfaces_test.h create mode 100644 libs/pbd/pbd/signal.h.py diff --git a/gtk2_ardour/analysis_window.cc b/gtk2_ardour/analysis_window.cc index afa277e8e4..c4948f5092 100644 --- a/gtk2_ardour/analysis_window.cc +++ b/gtk2_ardour/analysis_window.cc @@ -334,6 +334,7 @@ AnalysisWindow::analyze_data (Gtk::Button */*button*/) n = length - x; } + memset (buf, 0, n * sizeof (Sample)); n = arv->audio_region()->read_at(buf, mixbuf, gain, arv->region()->position() + x, n, channel); if (n == 0) diff --git a/gtk2_ardour/ardev_common.sh.in b/gtk2_ardour/ardev_common.sh.in index 58e6c4e0ba..d8bdeb430d 100644 --- a/gtk2_ardour/ardev_common.sh.in +++ b/gtk2_ardour/ardev_common.sh.in @@ -8,6 +8,7 @@ export ARDOUR_PATH=$TOP/gtk2_ardour/icons:$TOP/gtk2_ardour/pixmaps:$TOP/build/gt export ARDOUR_SURFACES_PATH=$libs/surfaces/osc:$libs/surfaces/generic_midi:$libs/surfaces/tranzport:$libs/surfaces/powermate:$libs/surfaces/mackie export ARDOUR_PANNER_PATH=$libs/panners/2in2out:$libs/panners/1in2out:$libs/panners/vbap export ARDOUR_DATA_PATH=$TOP/gtk2_ardour:build/gtk2_ardour:. +export ARDOUR_MIDIMAPS_PATH=$TOP/midi_maps:. export ARDOUR_MCP_PATH=$TOP/mcp:. if test -d $HOME/gtk/inst ; then diff --git a/gtk2_ardour/ardour_ui.cc b/gtk2_ardour/ardour_ui.cc index e97745e946..8063164eb5 100644 --- a/gtk2_ardour/ardour_ui.cc +++ b/gtk2_ardour/ardour_ui.cc @@ -925,7 +925,7 @@ ARDOUR_UI::update_sample_rate (framecnt_t) if (!engine->connected()) { - snprintf (buf, sizeof (buf), _("disconnected")); + snprintf (buf, sizeof (buf), "%s", _("disconnected")); } else { @@ -1063,7 +1063,7 @@ ARDOUR_UI::update_disk_space() framecnt_t fr = _session->frame_rate(); if (frames == max_framecnt) { - snprintf (buf, sizeof (buf), _("Disk: 24hrs+")); + snprintf (buf, sizeof (buf), "%s", _("Disk: 24hrs+")); } else { rec_enabled_streams = 0; _session->foreach_route (this, &ARDOUR_UI::count_recenabled_streams); @@ -1079,7 +1079,7 @@ ARDOUR_UI::update_disk_space() hrs = frames / (fr * 3600); if (hrs > 24) { - snprintf (buf, sizeof (buf), _("Disk: >24 hrs")); + snprintf (buf, sizeof (buf), "%s", _("Disk: >24 hrs")); } else { frames -= hrs * fr * 3600; mins = frames / (fr * 60); diff --git a/gtk2_ardour/ardour_ui_ed.cc b/gtk2_ardour/ardour_ui_ed.cc index f5ee554c3f..35100e940b 100644 --- a/gtk2_ardour/ardour_ui_ed.cc +++ b/gtk2_ardour/ardour_ui_ed.cc @@ -706,7 +706,7 @@ ARDOUR_UI::float_big_clock (Gtk::Window* parent) } void -ARDOUR_UI::big_clock_size_allocate (Gtk::Allocation& alloc) +ARDOUR_UI::big_clock_size_allocate (Gtk::Allocation&) { if (!big_clock_resize_in_progress) { Glib::signal_idle().connect (sigc::bind (sigc::mem_fun (*this, &ARDOUR_UI::idle_big_clock_text_resizer), 0, 0)); diff --git a/gtk2_ardour/ardour_window.cc b/gtk2_ardour/ardour_window.cc index 94d3f55d12..8d3821fe87 100644 --- a/gtk2_ardour/ardour_window.cc +++ b/gtk2_ardour/ardour_window.cc @@ -36,7 +36,7 @@ ArdourWindow::ArdourWindow (string title) init (); } -ArdourWindow::ArdourWindow (Gtk::Window& parent, string title) +ArdourWindow::ArdourWindow (Gtk::Window& parent, string /*title*/) : Window () { init (); diff --git a/gtk2_ardour/audio_clock.cc b/gtk2_ardour/audio_clock.cc index a4cb473a72..76abb9d6bb 100644 --- a/gtk2_ardour/audio_clock.cc +++ b/gtk2_ardour/audio_clock.cc @@ -796,7 +796,7 @@ AudioClock::parse_as_timecode_distance (const std::string& str) } framecnt_t -AudioClock::parse_as_bbt_distance (const std::string& str) +AudioClock::parse_as_bbt_distance (const std::string&) { return 0; } @@ -1001,7 +1001,7 @@ AudioClock::set_frames (framepos_t when, bool /*force*/) } void -AudioClock::set_minsec (framepos_t when, bool force) +AudioClock::set_minsec (framepos_t when, bool /*force*/) { char buf[32]; framecnt_t left; @@ -1046,7 +1046,7 @@ AudioClock::set_minsec (framepos_t when, bool force) } void -AudioClock::set_timecode (framepos_t when, bool force) +AudioClock::set_timecode (framepos_t when, bool /*force*/) { char buf[32]; Timecode::Time TC; @@ -1112,7 +1112,7 @@ AudioClock::set_timecode (framepos_t when, bool force) } void -AudioClock::set_bbt (framepos_t when, bool force) +AudioClock::set_bbt (framepos_t when, bool /*force*/) { char buf[16]; Timecode::BBT_Time BBT; @@ -1740,7 +1740,7 @@ AudioClock::get_frame_step (Field field, framepos_t pos, int dir) } framepos_t -AudioClock::current_time (framepos_t pos) const +AudioClock::current_time (framepos_t) const { return last_when; } @@ -1795,7 +1795,7 @@ AudioClock::bbt_validate_edit (const string& str) } bool -AudioClock::timecode_validate_edit (const string& str) +AudioClock::timecode_validate_edit (const string&) { Timecode::Time TC; diff --git a/gtk2_ardour/audio_region_view.cc b/gtk2_ardour/audio_region_view.cc index 70966b1dff..6ab858f1c9 100644 --- a/gtk2_ardour/audio_region_view.cc +++ b/gtk2_ardour/audio_region_view.cc @@ -20,6 +20,8 @@ #include #include +#include + #include #include @@ -43,6 +45,7 @@ #include "waveview.h" #include "public_editor.h" #include "audio_region_editor.h" +#include "audio_streamview.h" #include "region_gain_line.h" #include "control_point.h" #include "ghostregion.h" @@ -73,13 +76,18 @@ AudioRegionView::AudioRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView , fade_in_handle(0) , fade_out_handle(0) , fade_position_line(0) + , start_xfade_in (0) + , start_xfade_out (0) + , start_xfade_rect (0) + , end_xfade_in (0) + , end_xfade_out (0) + , end_xfade_rect (0) , _amplitude_above_axis(1.0) , _flags(0) , fade_color(0) { } - AudioRegionView::AudioRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv, boost::shared_ptr r, double spu, Gdk::Color const & basic_color, bool recording, TimeAxisViewItem::Visibility visibility) : RegionView (parent, tv, r, spu, basic_color, recording, visibility) @@ -89,6 +97,12 @@ AudioRegionView::AudioRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView , fade_in_handle(0) , fade_out_handle(0) , fade_position_line(0) + , start_xfade_in (0) + , start_xfade_out (0) + , start_xfade_rect (0) + , end_xfade_in (0) + , end_xfade_out (0) + , end_xfade_rect (0) , _amplitude_above_axis(1.0) , _flags(0) , fade_color(0) @@ -102,6 +116,12 @@ AudioRegionView::AudioRegionView (const AudioRegionView& other, boost::shared_pt , fade_in_handle(0) , fade_out_handle(0) , fade_position_line(0) + , start_xfade_in (0) + , start_xfade_out (0) + , start_xfade_rect (0) + , end_xfade_in (0) + , end_xfade_out (0) + , end_xfade_rect (0) , _amplitude_above_axis (other._amplitude_above_axis) , _flags (other._flags) , fade_color(0) @@ -288,6 +308,7 @@ AudioRegionView::fade_out_changed () { reset_fade_out_shape (); } + void AudioRegionView::fade_in_active_changed () { @@ -534,10 +555,31 @@ AudioRegionView::reset_fade_in_shape () void AudioRegionView::reset_fade_in_shape_width (framecnt_t width) { + if (dragging()) { + return; + } + + if (audio_region()->fade_in_is_xfade()) { + if (fade_in_handle) { + fade_in_handle->hide (); + fade_in_shape->hide (); + } + redraw_start_xfade (); + return; + } else { + if (start_xfade_in) { + start_xfade_in->hide (); + start_xfade_out->hide (); + start_xfade_rect->hide (); + } + } + if (fade_in_handle == 0) { return; } + fade_in_handle->show (); + /* smallest size for a fade is 64 frames */ width = std::max ((framecnt_t) 64, width); @@ -621,10 +663,31 @@ AudioRegionView::reset_fade_out_shape () void AudioRegionView::reset_fade_out_shape_width (framecnt_t width) { + if (dragging()) { + return; + } + + if (audio_region()->fade_out_is_xfade()) { + if (fade_out_handle) { + fade_out_handle->hide (); + fade_out_shape->hide (); + } + redraw_end_xfade (); + return; + } else { + if (end_xfade_in) { + end_xfade_in->hide (); + end_xfade_out->hide (); + end_xfade_rect->hide (); + } + } + if (fade_out_handle == 0) { return; } + fade_out_handle->show (); + /* smallest size for a fade is 64 frames */ width = std::max ((framecnt_t) 64, width); @@ -1462,6 +1525,287 @@ void AudioRegionView::thaw_after_trim () { RegionView::thaw_after_trim (); - unhide_envelope (); + drag_end (); +} + +void +AudioRegionView::redraw_start_xfade () +{ + boost::shared_ptr ar (audio_region()); + + if (!ar->fade_in() || ar->fade_in()->empty()) { + return; + } + + if (!ar->fade_in_is_xfade()) { + if (start_xfade_in) { + start_xfade_in->hide (); + start_xfade_out->hide (); + start_xfade_rect->hide (); + } + return; + } + + redraw_start_xfade_to (ar, ar->fade_in()->back()->when); +} + +void +AudioRegionView::redraw_start_xfade_to (boost::shared_ptr ar, framecnt_t len) +{ + int32_t const npoints = trackview.editor().frame_to_pixel (len); + + if (npoints < 3) { + return; + } + + if (!start_xfade_in) { + start_xfade_in = new ArdourCanvas::Line (*group); + start_xfade_in->property_width_pixels() = 1; + start_xfade_in->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_GainLine.get(); + } + + if (!start_xfade_out) { + start_xfade_out = new ArdourCanvas::Line (*group); + start_xfade_out->property_width_pixels() = 1; + start_xfade_out->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_GainLine.get(); + } + + if (!start_xfade_rect) { + start_xfade_rect = new ArdourCanvas::SimpleRect (*group); + start_xfade_rect->property_draw() = true; + start_xfade_rect->property_fill() = true;; + start_xfade_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_ActiveCrossfade.get(); + start_xfade_rect->property_outline_pixels() = 0; + start_xfade_rect->signal_event().connect (sigc::bind (sigc::mem_fun (PublicEditor::instance(), &PublicEditor::canvas_start_xfade_event), start_xfade_rect, this)); + start_xfade_rect->set_data ("regionview", this); + } + + Points* points = get_canvas_points ("xfade edit redraw", npoints); + boost::scoped_ptr vec (new float[npoints]); + double effective_height = _height - NAME_HIGHLIGHT_SIZE - 1.0; + + ar->fade_in()->curve().get_vector (0, ar->fade_in()->back()->when, vec.get(), npoints); + + for (int i = 0, pci = 0; i < npoints; ++i) { + Gnome::Art::Point &p ((*points)[pci++]); + p.set_x (i); + p.set_y (1.0 + effective_height - (effective_height * vec.get()[i])); + } + + start_xfade_rect->property_x1() = ((*points)[0]).get_x(); + start_xfade_rect->property_y1() = 1.0; + start_xfade_rect->property_x2() = ((*points)[npoints-1]).get_x(); + start_xfade_rect->property_y2() = effective_height; + start_xfade_rect->show (); + start_xfade_rect->raise_to_top (); + + start_xfade_in->property_points() = *points; + start_xfade_in->show (); + start_xfade_in->raise_to_top (); + + /* fade out line */ + + boost::shared_ptr inverse = ar->inverse_fade_in(); + + if (!inverse) { + + for (int i = 0, pci = 0; i < npoints; ++i) { + Gnome::Art::Point &p ((*points)[pci++]); + p.set_x (i); + p.set_y (1.0 + effective_height - (effective_height * (1.0 - vec.get()[i]))); + } + + } else { + + inverse->curve().get_vector (0, inverse->back()->when, vec.get(), npoints); + + for (int i = 0, pci = 0; i < npoints; ++i) { + Gnome::Art::Point &p ((*points)[pci++]); + p.set_x (i); + p.set_y (1.0 + effective_height - (effective_height * vec.get()[i])); + } + } + + start_xfade_out->property_points() = *points; + start_xfade_out->show (); + start_xfade_out->raise_to_top (); + + delete points; +} + +void +AudioRegionView::redraw_end_xfade () +{ + boost::shared_ptr ar (audio_region()); + + if (!ar->fade_out() || ar->fade_out()->empty()) { + return; + } + + if (!ar->fade_out_is_xfade()) { + if (end_xfade_in) { + end_xfade_in->hide (); + end_xfade_out->hide (); + end_xfade_rect->hide (); + } + return; + } + + redraw_end_xfade_to (ar, ar->fade_out()->back()->when); +} + +void +AudioRegionView::redraw_end_xfade_to (boost::shared_ptr ar, framecnt_t len) +{ + int32_t const npoints = trackview.editor().frame_to_pixel (len); + + if (npoints < 3) { + return; + } + + if (!end_xfade_in) { + end_xfade_in = new ArdourCanvas::Line (*group); + end_xfade_in->property_width_pixels() = 1; + end_xfade_in->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_GainLine.get(); + } + + if (!end_xfade_out) { + end_xfade_out = new ArdourCanvas::Line (*group); + end_xfade_out->property_width_pixels() = 1; + end_xfade_out->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_GainLine.get(); + } + + if (!end_xfade_rect) { + end_xfade_rect = new ArdourCanvas::SimpleRect (*group); + end_xfade_rect->property_draw() = true; + end_xfade_rect->property_fill() = true;; + end_xfade_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_ActiveCrossfade.get(); + end_xfade_rect->property_outline_pixels() = 0; + end_xfade_rect->signal_event().connect (sigc::bind (sigc::mem_fun (PublicEditor::instance(), &PublicEditor::canvas_end_xfade_event), end_xfade_rect, this)); + end_xfade_rect->set_data ("regionview", this); + } + + Points* points = get_canvas_points ("xfade edit redraw", npoints); + boost::scoped_ptr vec (new float[npoints]); + + ar->fade_out()->curve().get_vector (0, ar->fade_out()->back()->when, vec.get(), npoints); + + double rend = trackview.editor().frame_to_pixel (_region->length() - len); + double effective_height = _height - NAME_HIGHLIGHT_SIZE - 1; + + for (int i = 0, pci = 0; i < npoints; ++i) { + Gnome::Art::Point &p ((*points)[pci++]); + p.set_x (rend + i); + p.set_y (1.0 + effective_height - (effective_height * vec.get()[i])); + } + + end_xfade_rect->property_x1() = ((*points)[0]).get_x(); + end_xfade_rect->property_y1() = 1; + end_xfade_rect->property_x2() = ((*points)[npoints-1]).get_x(); + end_xfade_rect->property_y2() = effective_height; + end_xfade_rect->show (); + end_xfade_rect->raise_to_top (); + + end_xfade_in->property_points() = *points; + end_xfade_in->show (); + end_xfade_in->raise_to_top (); + + /* fade in line */ + + boost::shared_ptr inverse = ar->inverse_fade_out (); + + if (!inverse) { + + for (int i = 0, pci = 0; i < npoints; ++i) { + Gnome::Art::Point &p ((*points)[pci++]); + p.set_x (rend + i); + p.set_y (1.0 + effective_height - (effective_height * (1.0 - vec.get()[i]))); + } + + } else { + + inverse->curve().get_vector (inverse->front()->when, inverse->back()->when, vec.get(), npoints); + + for (int i = 0, pci = 0; i < npoints; ++i) { + Gnome::Art::Point &p ((*points)[pci++]); + p.set_x (rend + i); + p.set_y (1.0 + effective_height - (effective_height * vec.get()[i])); + } + } + + end_xfade_out->property_points() = *points; + end_xfade_out->show (); + end_xfade_out->raise_to_top (); + + + delete points; +} + +void +AudioRegionView::hide_xfades () +{ + if (start_xfade_in) { + start_xfade_in->hide(); + } + if (start_xfade_out) { + start_xfade_out->hide(); + } + if (start_xfade_rect) { + start_xfade_rect->hide (); + } + if (end_xfade_in) { + end_xfade_in->hide(); + } + if (end_xfade_out) { + end_xfade_out->hide(); + } + if (end_xfade_rect) { + end_xfade_rect->hide (); + } +} + +void +AudioRegionView::show_xfades () +{ + if (start_xfade_in) { + start_xfade_in->show(); + } + if (start_xfade_out) { + start_xfade_out->show(); + } + if (start_xfade_rect) { + start_xfade_rect->show (); + } + if (end_xfade_in) { + end_xfade_in->show(); + } + if (end_xfade_out) { + end_xfade_out->show(); + } + if (end_xfade_rect) { + end_xfade_rect->show (); + } +} + +void +AudioRegionView::drag_start () +{ + TimeAxisViewItem::drag_start (); + AudioTimeAxisView* atav = dynamic_cast (&trackview); + + if (atav) { + AudioStreamView* av = atav->audio_view(); + if (av) { + /* this will hide our xfades too */ + av->hide_xfades_with (audio_region()); + } + } +} + +void +AudioRegionView::drag_end () +{ + TimeAxisViewItem::drag_end (); + /* fades will be redrawn if they changed */ } diff --git a/gtk2_ardour/audio_region_view.h b/gtk2_ardour/audio_region_view.h index 9dc7e507a1..233b53475b 100644 --- a/gtk2_ardour/audio_region_view.h +++ b/gtk2_ardour/audio_region_view.h @@ -117,6 +117,17 @@ class AudioRegionView : public RegionView void thaw_after_trim (); + void drag_start (); + void drag_end (); + + void redraw_start_xfade_to (boost::shared_ptr, framecnt_t); + void redraw_end_xfade_to (boost::shared_ptr, framecnt_t); + void redraw_start_xfade (); + void redraw_end_xfade (); + + void hide_xfades (); + void show_xfades (); + protected: /* this constructor allows derived types @@ -142,6 +153,14 @@ class AudioRegionView : public RegionView ArdourCanvas::SimpleRect* fade_out_handle; ///< fade out handle, or 0 ArdourCanvas::SimpleLine* fade_position_line; + ArdourCanvas::Line *start_xfade_in; + ArdourCanvas::Line *start_xfade_out; + ArdourCanvas::SimpleRect* start_xfade_rect; + + ArdourCanvas::Line *end_xfade_in; + ArdourCanvas::Line *end_xfade_out; + ArdourCanvas::SimpleRect* end_xfade_rect; + boost::shared_ptr gain_line; double _amplitude_above_axis; @@ -180,7 +199,6 @@ class AudioRegionView : public RegionView void transients_changed(); private: - void setup_fade_handle_positions (); /** A ScopedConnection for each PeaksReady callback (one per channel). Each member diff --git a/gtk2_ardour/audio_streamview.cc b/gtk2_ardour/audio_streamview.cc index e9ee8fe647..be8601170f 100644 --- a/gtk2_ardour/audio_streamview.cc +++ b/gtk2_ardour/audio_streamview.cc @@ -532,6 +532,23 @@ AudioStreamView::hide_all_fades () } } +void +AudioStreamView::hide_xfades_with (boost::shared_ptr ar) +{ + for (list::iterator i = region_views.begin(); i != region_views.end(); ++i) { + AudioRegionView* const arv = dynamic_cast(*i); + if (arv) { + switch (arv->region()->coverage (ar->position(), ar->last_frame())) { + case Evoral::OverlapNone: + break; + default: + arv->hide_xfades (); + break; + } + } + } +} + void AudioStreamView::color_handler () { diff --git a/gtk2_ardour/audio_streamview.h b/gtk2_ardour/audio_streamview.h index 4812903a35..cb3175798e 100644 --- a/gtk2_ardour/audio_streamview.h +++ b/gtk2_ardour/audio_streamview.h @@ -61,6 +61,8 @@ class AudioStreamView : public StreamView void show_all_fades (); void hide_all_fades (); + void hide_xfades_with (boost::shared_ptr ar); + RegionView* create_region_view (boost::shared_ptr, bool, bool); private: diff --git a/gtk2_ardour/automation_time_axis.cc b/gtk2_ardour/automation_time_axis.cc index 4b10ea5cb5..3332a066d9 100644 --- a/gtk2_ardour/automation_time_axis.cc +++ b/gtk2_ardour/automation_time_axis.cc @@ -900,3 +900,69 @@ AutomationTimeAxisView::parse_state_id ( return true; } + +void +AutomationTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op) +{ + list > lines; + if (_line) { + lines.push_back (_line); + } else if (_view) { + lines = _view->get_lines (); + } + + for (list >::iterator i = lines.begin(); i != lines.end(); ++i) { + cut_copy_clear_one (**i, selection, op); + } +} + +void +AutomationTimeAxisView::cut_copy_clear_one (AutomationLine& line, Selection& selection, CutCopyOp op) +{ + boost::shared_ptr what_we_got; + boost::shared_ptr alist (line.the_list()); + + XMLNode &before = alist->get_state(); + + /* convert time selection to automation list model coordinates */ + const Evoral::TimeConverter& tc = line.time_converter (); + double const start = tc.from (selection.time.front().start - tc.origin_b ()); + double const end = tc.from (selection.time.front().end - tc.origin_b ()); + + switch (op) { + case Delete: + if (alist->cut (start, end) != 0) { + _session->add_command(new MementoCommand(*alist.get(), &before, &alist->get_state())); + } + break; + + case Cut: + + if ((what_we_got = alist->cut (start, end)) != 0) { + _editor.get_cut_buffer().add (what_we_got); + _session->add_command(new MementoCommand(*alist.get(), &before, &alist->get_state())); + } + break; + case Copy: + if ((what_we_got = alist->copy (start, end)) != 0) { + _editor.get_cut_buffer().add (what_we_got); + } + break; + + case Clear: + if ((what_we_got = alist->cut (start, end)) != 0) { + _session->add_command(new MementoCommand(*alist.get(), &before, &alist->get_state())); + } + break; + } + + if (what_we_got) { + for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) { + double when = (*x)->when; + double val = (*x)->value; + line.model_to_view_coord (when, val); + (*x)->when = when; + (*x)->value = val; + } + } +} diff --git a/gtk2_ardour/automation_time_axis.h b/gtk2_ardour/automation_time_axis.h index 913cd1b467..d883b189c0 100644 --- a/gtk2_ardour/automation_time_axis.h +++ b/gtk2_ardour/automation_time_axis.h @@ -91,6 +91,7 @@ class AutomationTimeAxisView : public TimeAxisView { /* editing operations */ + void cut_copy_clear (Selection&, Editing::CutCopyOp); bool paste (ARDOUR::framepos_t, float times, Selection&, size_t nth); int set_state (const XMLNode&, int version); @@ -166,6 +167,7 @@ class AutomationTimeAxisView : public TimeAxisView { void build_display_menu (); + void cut_copy_clear_one (AutomationLine&, Selection&, Editing::CutCopyOp); bool paste_one (AutomationLine&, ARDOUR::framepos_t, float times, Selection&, size_t nth); void route_going_away (); diff --git a/gtk2_ardour/button_joiner.cc b/gtk2_ardour/button_joiner.cc index 86bb055d01..085d5d30b2 100644 --- a/gtk2_ardour/button_joiner.cc +++ b/gtk2_ardour/button_joiner.cc @@ -138,7 +138,7 @@ ButtonJoiner::on_size_allocate (Allocation& alloc) } bool -ButtonJoiner::on_button_release_event (GdkEventButton* ev) +ButtonJoiner::on_button_release_event (GdkEventButton*) { if (_action) { _action->activate (); diff --git a/gtk2_ardour/canvas-simpleline.c b/gtk2_ardour/canvas-simpleline.c index 39a1901f57..efff5034f9 100644 --- a/gtk2_ardour/canvas-simpleline.c +++ b/gtk2_ardour/canvas-simpleline.c @@ -389,8 +389,8 @@ gnome_canvas_simpleline_render (GnomeCanvasItem *item, } static void -gnome_canvas_simpleline_draw (GnomeCanvasItem *canvas, - GdkDrawable *drawable, +gnome_canvas_simpleline_draw (GnomeCanvasItem* canvas, + GdkDrawable* drawable, int x, int y, int width, int height) { diff --git a/gtk2_ardour/crossfade_edit.cc b/gtk2_ardour/crossfade_edit.cc index 65031917c7..3f6610d49d 100644 --- a/gtk2_ardour/crossfade_edit.cc +++ b/gtk2_ardour/crossfade_edit.cc @@ -889,7 +889,7 @@ CrossfadeEditor::build_presets () /* FADE IN */ - p = new Preset ("Linear (-6dB)", "crossfade-in-linear"); + p = new Preset ("Linear (-6dB)", "fadein-linear"); p->push_back (PresetPoint (0, 0)); p->push_back (PresetPoint (0.000000, 0.000000)); p->push_back (PresetPoint (0.166667, 0.166366)); @@ -900,7 +900,7 @@ CrossfadeEditor::build_presets () p->push_back (PresetPoint (1.000000, 1.000000)); fade_in_presets->push_back (p); - p = new Preset ("S(1)-curve", "crossfade-in-S1"); + p = new Preset ("S(1)-curve", "fadein-S1"); p->push_back (PresetPoint (0, 0)); p->push_back (PresetPoint (0.1, 0.01)); p->push_back (PresetPoint (0.2, 0.03)); @@ -909,7 +909,7 @@ CrossfadeEditor::build_presets () p->push_back (PresetPoint (1, 1)); fade_in_presets->push_back (p); - p = new Preset ("S(2)-curve", "crossfade-in-S2"); + p = new Preset ("S(2)-curve", "fadein-S2"); p->push_back (PresetPoint (0.0, 0.0)); p->push_back (PresetPoint (0.055, 0.222)); p->push_back (PresetPoint (0.163, 0.35)); @@ -918,7 +918,7 @@ CrossfadeEditor::build_presets () p->push_back (PresetPoint (1.0, 1.0)); fade_in_presets->push_back (p); - p = new Preset ("Constant Power (-3dB)", "crossfade-in-constant-power"); + p = new Preset ("Constant Power (-3dB)", "fadein-constant-power"); p->push_back (PresetPoint (0.000000, 0.000000)); p->push_back (PresetPoint (0.166667, 0.282192)); @@ -932,7 +932,7 @@ CrossfadeEditor::build_presets () if (!Profile->get_sae()) { - p = new Preset ("Short cut", "crossfade-in-short-cut"); + p = new Preset ("Short cut", "fadein-short-cut"); p->push_back (PresetPoint (0, 0)); p->push_back (PresetPoint (0.389401, 0.0333333)); p->push_back (PresetPoint (0.629032, 0.0861111)); @@ -942,7 +942,7 @@ CrossfadeEditor::build_presets () p->push_back (PresetPoint (1, 1)); fade_in_presets->push_back (p); - p = new Preset ("Slow cut", "crossfade-in-slow-cut"); + p = new Preset ("Slow cut", "fadein-slow-cut"); p->push_back (PresetPoint (0, 0)); p->push_back (PresetPoint (0.304147, 0.0694444)); p->push_back (PresetPoint (0.529954, 0.152778)); @@ -952,7 +952,7 @@ CrossfadeEditor::build_presets () p->push_back (PresetPoint (1, 1)); fade_in_presets->push_back (p); - p = new Preset ("Fast cut", "crossfade-in-fast-cut"); + p = new Preset ("Fast cut", "fadein-fast-cut"); p->push_back (PresetPoint (0, 0)); p->push_back (PresetPoint (0.0737327, 0.308333)); p->push_back (PresetPoint (0.246544, 0.658333)); @@ -962,7 +962,7 @@ CrossfadeEditor::build_presets () p->push_back (PresetPoint (1, 1)); fade_in_presets->push_back (p); - p = new Preset ("Long cut", "crossfade-in-long-cut"); + p = new Preset ("Long cut", "fadein-long-cut"); p->push_back (PresetPoint (0, 0)); p->push_back (PresetPoint (0.0207373, 0.197222)); p->push_back (PresetPoint (0.0645161, 0.525)); @@ -977,7 +977,7 @@ CrossfadeEditor::build_presets () /* FADE OUT */ // p = new Preset ("regout.xpm"); - p = new Preset ("Linear (-6dB cut)", "crossfade-out-linear"); + p = new Preset ("Linear (-6dB cut)", "fadeout-linear"); p->push_back (PresetPoint (0, 1)); p->push_back (PresetPoint (0.000000, 1.000000)); p->push_back (PresetPoint (0.166667, 0.833033)); @@ -988,7 +988,7 @@ CrossfadeEditor::build_presets () p->push_back (PresetPoint (1.000000, 0.000000)); fade_out_presets->push_back (p); - p = new Preset ("S(1)-Curve", "crossfade-out-S1"); + p = new Preset ("S(1)-Curve", "fadeout-S1"); p->push_back (PresetPoint (0, 1)); p->push_back (PresetPoint (0.1, 0.99)); p->push_back (PresetPoint (0.2, 0.97)); @@ -997,7 +997,7 @@ CrossfadeEditor::build_presets () p->push_back (PresetPoint (1, 0)); fade_out_presets->push_back (p); - p = new Preset ("S(2)-Curve", "crossfade-out-S2"); + p = new Preset ("S(2)-Curve", "fadeout-S2"); p->push_back (PresetPoint (0.0, 1.0)); p->push_back (PresetPoint (0.163, 0.678)); p->push_back (PresetPoint (0.055, 0.783)); @@ -1007,7 +1007,7 @@ CrossfadeEditor::build_presets () fade_out_presets->push_back (p); // p = new Preset ("linout.xpm"); - p = new Preset ("Constant Power (-3dB cut)", "crossfade-out-constant-power"); + p = new Preset ("Constant Power (-3dB cut)", "fadeout-constant-power"); p->push_back (PresetPoint (0.000000, 1.000000)); p->push_back (PresetPoint (0.166667, 0.948859)); p->push_back (PresetPoint (0.333333, 0.851507)); @@ -1019,7 +1019,7 @@ CrossfadeEditor::build_presets () if (!Profile->get_sae()) { // p = new Preset ("hiout.xpm"); - p = new Preset ("Short cut", "crossfade-out-short-cut"); + p = new Preset ("Short cut", "fadeout-short-cut"); p->push_back (PresetPoint (0, 1)); p->push_back (PresetPoint (0.305556, 1)); p->push_back (PresetPoint (0.548611, 0.991736)); @@ -1029,7 +1029,7 @@ CrossfadeEditor::build_presets () p->push_back (PresetPoint (1, 0)); fade_out_presets->push_back (p); - p = new Preset ("Slow cut", "crossfade-out-slow-cut"); + p = new Preset ("Slow cut", "fadeout-slow-cut"); p->push_back (PresetPoint (0, 1)); p->push_back (PresetPoint (0.228111, 0.988889)); p->push_back (PresetPoint (0.347926, 0.972222)); @@ -1039,7 +1039,7 @@ CrossfadeEditor::build_presets () p->push_back (PresetPoint (1, 0)); fade_out_presets->push_back (p); - p = new Preset ("Fast cut", "crossfade-out-fast-cut"); + p = new Preset ("Fast cut", "fadeout-fast-cut"); p->push_back (PresetPoint (0, 1)); p->push_back (PresetPoint (0.080645, 0.730556)); p->push_back (PresetPoint (0.277778, 0.289256)); @@ -1049,7 +1049,7 @@ CrossfadeEditor::build_presets () fade_out_presets->push_back (p); // p = new Preset ("loout.xpm"); - p = new Preset ("Long cut", "crossfade-out-long-cut"); + p = new Preset ("Long cut", "fadeout-long-cut"); p->push_back (PresetPoint (0, 1)); p->push_back (PresetPoint (0.023041, 0.697222)); p->push_back (PresetPoint (0.0553, 0.483333)); diff --git a/gtk2_ardour/curvetest.cc b/gtk2_ardour/curvetest.cc index 498ed45eba..b3431e3f39 100644 --- a/gtk2_ardour/curvetest.cc +++ b/gtk2_ardour/curvetest.cc @@ -32,6 +32,11 @@ using namespace PBD; int curvetest (string filename) { + // needed to initialize ID objects/counter used + // by Curve et al. + + PBD::ID::init (); + ifstream in (filename.c_str()); stringstream line; //Evoral::Parameter param(GainAutomation, -1.0, +1.0, 0.0); @@ -40,11 +45,6 @@ curvetest (string filename) double minx = DBL_MAX; double maxx = DBL_MIN; - // needed to initialize ID objects/counter used - // by Curve et al. - - PBD::ID::init (); - while (in) { double x, y; diff --git a/gtk2_ardour/editor.cc b/gtk2_ardour/editor.cc index b7f768adca..5e3810977c 100644 --- a/gtk2_ardour/editor.cc +++ b/gtk2_ardour/editor.cc @@ -548,7 +548,7 @@ Editor::Editor () add_notebook_page (_("Regions"), _regions->widget ()); add_notebook_page (_("Tracks & Busses"), _routes->widget ()); add_notebook_page (_("Snapshots"), _snapshots->widget ()); - add_notebook_page (_("Route Groups"), _route_groups->widget ()); + add_notebook_page (_("Track & Bus Groups"), _route_groups->widget ()); add_notebook_page (_("Ranges & Marks"), _locations->widget ()); _the_notebook.set_show_tabs (true); @@ -721,6 +721,7 @@ Editor::Editor () ControlProtocol::AddRouteToSelection.connect (*this, invalidator (*this), boost::bind (&Editor::control_select, this, _1, Selection::Add), gui_context()); ControlProtocol::RemoveRouteFromSelection.connect (*this, invalidator (*this), boost::bind (&Editor::control_select, this, _1, Selection::Toggle), gui_context()); ControlProtocol::SetRouteSelection.connect (*this, invalidator (*this), boost::bind (&Editor::control_select, this, _1, Selection::Set), gui_context()); + ControlProtocol::ToggleRouteSelection.connect (*this, invalidator (*this), boost::bind (&Editor::control_select, this, _1, Selection::Toggle), gui_context()); ControlProtocol::ClearRouteSelection.connect (*this, invalidator (*this), boost::bind (&Editor::control_unselect, this), gui_context()); BasicUI::AccessAction.connect (*this, invalidator (*this), boost::bind (&Editor::access_action, this, _1, _2), gui_context()); @@ -1340,6 +1341,101 @@ Editor::action_pre_activated (Glib::RefPtr const & a) } } +void +Editor::fill_xfade_menu (Menu_Helpers::MenuList& items, bool start) +{ + using namespace Menu_Helpers; + + void (Editor::*emf)(FadeShape); + std::map* images; + + if (start) { + images = &_xfade_in_images; + emf = &Editor::set_fade_in_shape; + } else { + images = &_xfade_out_images; + emf = &Editor::set_fade_out_shape; + } + + items.push_back ( + ImageMenuElem ( + _("Linear (for highly correlated material)"), + *(*images)[FadeLinear], + sigc::bind (sigc::mem_fun (*this, emf), FadeLinear) + ) + ); + + dynamic_cast(&items.back())->set_always_show_image (); + + items.push_back ( + ImageMenuElem ( + _("ConstantPower"), + *(*images)[FadeConstantPower], + sigc::bind (sigc::mem_fun (*this, emf), FadeConstantPower) + )); + + dynamic_cast(&items.back())->set_always_show_image (); + + items.push_back ( + ImageMenuElem ( + _("Symmetric"), + *(*images)[FadeSymmetric], + sigc::bind (sigc::mem_fun (*this, emf), FadeSymmetric) + ) + ); + + dynamic_cast(&items.back())->set_always_show_image (); + + items.push_back ( + ImageMenuElem ( + _("Slow"), + *(*images)[FadeSlow], + sigc::bind (sigc::mem_fun (*this, emf), FadeSlow) + )); + + dynamic_cast(&items.back())->set_always_show_image (); + + items.push_back ( + ImageMenuElem ( + _("Fast"), + *(*images)[FadeFast], + sigc::bind (sigc::mem_fun (*this, emf), FadeFast) + )); + + dynamic_cast(&items.back())->set_always_show_image (); +} + +/** Pop up a context menu for when the user clicks on a start crossfade */ +void +Editor::popup_xfade_in_context_menu (int button, int32_t time, ArdourCanvas::Item* item, ItemType item_type) +{ + using namespace Menu_Helpers; + + MenuList& items (xfade_in_context_menu.items()); + + if (items.empty()) { + fill_xfade_menu (items, true); + } + + xfade_in_context_menu.popup (button, time); +} + +/** Pop up a context menu for when the user clicks on an end crossfade */ +void +Editor::popup_xfade_out_context_menu (int button, int32_t time, ArdourCanvas::Item* item, ItemType item_type) +{ + using namespace Menu_Helpers; + + MenuList& items (xfade_out_context_menu.items()); + + if (items.empty()) { + fill_xfade_menu (items, false); + } + + xfade_out_context_menu.popup (button, time); +} + + /** Pop up a context menu for when the user clicks on a fade in or fade out */ void Editor::popup_fade_context_menu (int button, int32_t time, ArdourCanvas::Item* item, ItemType item_type) @@ -1386,7 +1482,16 @@ Editor::popup_fade_context_menu (int button, int32_t time, ArdourCanvas::Item* i items.push_back ( ImageMenuElem ( - _("Slowest"), + _("Slow"), + *_fade_in_images[FadeSlow], + sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeSlow) + )); + + dynamic_cast(&items.back())->set_always_show_image (); + + items.push_back ( + ImageMenuElem ( + _("Fast"), *_fade_in_images[FadeFast], sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeFast) )); @@ -1395,27 +1500,16 @@ Editor::popup_fade_context_menu (int button, int32_t time, ArdourCanvas::Item* i items.push_back ( ImageMenuElem ( - _("Slow"), - *_fade_in_images[FadeLogB], - sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLogB) - )); - - dynamic_cast(&items.back())->set_always_show_image (); - - items.push_back ( - ImageMenuElem ( - _("Fast"), - *_fade_in_images[FadeLogA], - sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLogA) - )); - - dynamic_cast(&items.back())->set_always_show_image (); - - items.push_back ( - ImageMenuElem ( - _("Fastest"), + _("Symmetric"), *_fade_in_images[FadeSlow], - sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeSlow) + sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeSymmetric) + )); + + items.push_back ( + ImageMenuElem ( + _("Constant Power"), + *_fade_in_images[FadeConstantPower], + sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeConstantPower) )); dynamic_cast(&items.back())->set_always_show_image (); @@ -1450,36 +1544,34 @@ Editor::popup_fade_context_menu (int button, int32_t time, ArdourCanvas::Item* i items.push_back ( ImageMenuElem ( - _("Slowest"), - *_fade_out_images[FadeFast], + _("Slow"), + *_fade_out_images[FadeSlow], sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeSlow) )); dynamic_cast(&items.back())->set_always_show_image (); - items.push_back ( - ImageMenuElem ( - _("Slow"), - *_fade_out_images[FadeLogB], - sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLogA) - )); - - dynamic_cast(&items.back())->set_always_show_image (); - items.push_back ( ImageMenuElem ( _("Fast"), - *_fade_out_images[FadeLogA], - sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLogB) + *_fade_out_images[FadeFast], + sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeFast) )); dynamic_cast(&items.back())->set_always_show_image (); items.push_back ( ImageMenuElem ( - _("Fastest"), + _("Symmetric"), *_fade_out_images[FadeSlow], - sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeFast) + sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeSymmetric) + )); + + items.push_back ( + ImageMenuElem ( + _("Constant Power"), + *_fade_out_images[FadeConstantPower], + sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeConstantPower) )); dynamic_cast(&items.back())->set_always_show_image (); @@ -5282,17 +5374,30 @@ Editor::update_region_layering_order_editor () void Editor::setup_fade_images () { - _fade_in_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("crossfade-in-linear"))); - _fade_in_images[FadeFast] = new Gtk::Image (get_icon_path (X_("crossfade-in-short-cut"))); - _fade_in_images[FadeLogB] = new Gtk::Image (get_icon_path (X_("crossfade-in-slow-cut"))); - _fade_in_images[FadeLogA] = new Gtk::Image (get_icon_path (X_("crossfade-in-fast-cut"))); - _fade_in_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("crossfade-in-long-cut"))); + _fade_in_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("fadein-linear"))); + _fade_in_images[FadeSymmetric] = new Gtk::Image (get_icon_path (X_("fadein-short-cut"))); + _fade_in_images[FadeFast] = new Gtk::Image (get_icon_path (X_("fadein-slow-cut"))); + _fade_in_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("fadein-fast-cut"))); + _fade_in_images[FadeConstantPower] = new Gtk::Image (get_icon_path (X_("fadein-long-cut"))); + + _fade_out_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("fadeout-linear"))); + _fade_out_images[FadeSymmetric] = new Gtk::Image (get_icon_path (X_("fadeout-short-cut"))); + _fade_out_images[FadeFast] = new Gtk::Image (get_icon_path (X_("fadeout-slow-cut"))); + _fade_out_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("fadeout-fast-cut"))); + _fade_out_images[FadeConstantPower] = new Gtk::Image (get_icon_path (X_("fadeout-long-cut"))); + + _xfade_in_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("fadeout-linear"))); + _xfade_in_images[FadeSymmetric] = new Gtk::Image (get_icon_path (X_("fadeout-short-cut"))); + _xfade_in_images[FadeFast] = new Gtk::Image (get_icon_path (X_("fadeout-slow-cut"))); + _xfade_in_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("fadeout-fast-cut"))); + _xfade_in_images[FadeConstantPower] = new Gtk::Image (get_icon_path (X_("fadeout-long-cut"))); + + _xfade_out_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("fadeout-linear"))); + _xfade_out_images[FadeSymmetric] = new Gtk::Image (get_icon_path (X_("fadeout-short-cut"))); + _xfade_out_images[FadeFast] = new Gtk::Image (get_icon_path (X_("fadeout-slow-cut"))); + _xfade_out_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("fadeout-fast-cut"))); + _xfade_out_images[FadeConstantPower] = new Gtk::Image (get_icon_path (X_("fadeout-long-cut"))); - _fade_out_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("crossfade-out-linear"))); - _fade_out_images[FadeFast] = new Gtk::Image (get_icon_path (X_("crossfade-out-short-cut"))); - _fade_out_images[FadeLogB] = new Gtk::Image (get_icon_path (X_("crossfade-out-slow-cut"))); - _fade_out_images[FadeLogA] = new Gtk::Image (get_icon_path (X_("crossfade-out-fast-cut"))); - _fade_out_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("crossfade-out-long-cut"))); } /** @return Gtk::manage()d menu item for a given action from `editor_actions' */ diff --git a/gtk2_ardour/editor.h b/gtk2_ardour/editor.h index 589b0ead6c..2e203e9c75 100644 --- a/gtk2_ardour/editor.h +++ b/gtk2_ardour/editor.h @@ -1337,6 +1337,12 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD Gtk::Menu fade_context_menu; void popup_fade_context_menu (int, int, ArdourCanvas::Item*, ItemType); + Gtk::Menu xfade_in_context_menu; + Gtk::Menu xfade_out_context_menu; + void popup_xfade_in_context_menu (int, int, ArdourCanvas::Item*, ItemType); + void popup_xfade_out_context_menu (int, int, ArdourCanvas::Item*, ItemType); + void fill_xfade_menu (Gtk::Menu_Helpers::MenuList& items, bool start); + void set_fade_in_shape (ARDOUR::FadeShape); void set_fade_out_shape (ARDOUR::FadeShape); @@ -1374,6 +1380,8 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD bool canvas_selection_rect_event (GdkEvent* event,ArdourCanvas::Item*, SelectionRect*); bool canvas_selection_start_trim_event (GdkEvent* event,ArdourCanvas::Item*, SelectionRect*); bool canvas_selection_end_trim_event (GdkEvent* event,ArdourCanvas::Item*, SelectionRect*); + bool canvas_start_xfade_event (GdkEvent* event,ArdourCanvas::Item*, AudioRegionView*); + bool canvas_end_xfade_event (GdkEvent* event,ArdourCanvas::Item*, AudioRegionView*); bool canvas_fade_in_event (GdkEvent* event,ArdourCanvas::Item*, AudioRegionView*); bool canvas_fade_in_handle_event (GdkEvent* event,ArdourCanvas::Item*, AudioRegionView*); bool canvas_fade_out_event (GdkEvent* event,ArdourCanvas::Item*, AudioRegionView*); @@ -2057,6 +2065,8 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD void setup_fade_images (); std::map _fade_in_images; std::map _fade_out_images; + std::map _xfade_in_images; + std::map _xfade_out_images; Gtk::MenuItem& action_menu_item (std::string const &); void action_pre_activated (Glib::RefPtr const &); diff --git a/gtk2_ardour/editor_canvas_events.cc b/gtk2_ardour/editor_canvas_events.cc index 72de353500..77bc5ec27b 100644 --- a/gtk2_ardour/editor_canvas_events.cc +++ b/gtk2_ardour/editor_canvas_events.cc @@ -348,6 +348,70 @@ Editor::canvas_automation_track_event (GdkEvent *event, ArdourCanvas::Item* item return ret; } +bool +Editor::canvas_start_xfade_event (GdkEvent *event, ArdourCanvas::Item* item, AudioRegionView *rv) +{ + if (!rv->sensitive()) { + return false; + } + + switch (event->type) { + case GDK_BUTTON_PRESS: + clicked_regionview = rv; + clicked_control_point = 0; + clicked_axisview = &rv->get_time_axis_view(); + clicked_routeview = dynamic_cast(clicked_axisview); + if (event->button.button == 3) { + return button_press_handler (item, event, StartCrossFadeItem); + } + break; + + case GDK_BUTTON_RELEASE: + if (event->button.button == 3) { + return button_release_handler (item, event, StartCrossFadeItem); + } + break; + + default: + break; + + } + + return typed_event (item, event, StartCrossFadeItem); +} + +bool +Editor::canvas_end_xfade_event (GdkEvent *event, ArdourCanvas::Item* item, AudioRegionView *rv) +{ + if (!rv->sensitive()) { + return false; + } + + switch (event->type) { + case GDK_BUTTON_PRESS: + clicked_regionview = rv; + clicked_control_point = 0; + clicked_axisview = &rv->get_time_axis_view(); + clicked_routeview = dynamic_cast(clicked_axisview); + if (event->button.button == 3) { + return button_press_handler (item, event, EndCrossFadeItem); + } + break; + + case GDK_BUTTON_RELEASE: + if (event->button.button == 3) { + return button_release_handler (item, event, EndCrossFadeItem); + } + break; + + default: + break; + + } + + return typed_event (item, event, EndCrossFadeItem); +} + bool Editor::canvas_fade_in_event (GdkEvent *event, ArdourCanvas::Item* item, AudioRegionView *rv) { diff --git a/gtk2_ardour/editor_drag.cc b/gtk2_ardour/editor_drag.cc index c418e85018..97590be1a9 100644 --- a/gtk2_ardour/editor_drag.cc +++ b/gtk2_ardour/editor_drag.cc @@ -663,7 +663,7 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move) if (first_move) { - rv->get_time_axis_view().hide_dependent_views (*rv); + rv->drag_start (); /* Absolutely no idea why this is necessary, but it is; without it, the region view disappears after the reparent. @@ -698,15 +698,15 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move) if (tv->view()->layer_display() == Stacked) { tv->view()->set_layer_display (Expanded); } - + /* We're only allowed to go -ve in layer on Expanded views */ if (tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) { this_delta_layer = - i->layer; } - + /* Set height */ rv->set_height (tv->view()->child_height ()); - + /* Update show/hidden status as the region view may have come from a hidden track, or have moved to one. */ @@ -1063,7 +1063,7 @@ RegionMoveDrag::finished_no_copy ( rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item()); rv->get_canvas_group()->property_y() = i->initial_y; - rv->get_time_axis_view().reveal_dependent_views (*rv); + rv->drag_end (); /* just change the model */ @@ -1277,7 +1277,7 @@ RegionMotionDrag::aborted (bool) assert (rtv); rv->get_canvas_group()->reparent (*rtv->view()->canvas_item()); rv->get_canvas_group()->property_y() = 0; - rv->get_time_axis_view().reveal_dependent_views (*rv); + rv->drag_end (); rv->fake_set_opaque (false); rv->move (-_total_x_delta, 0); rv->set_height (rtv->view()->child_height ()); @@ -1706,6 +1706,7 @@ TrimDrag::motion (GdkEvent* event, bool first_move) if (arv) { arv->temporarily_hide_envelope (); + arv->drag_start (); } boost::shared_ptr pl = rv->region()->playlist(); @@ -4360,7 +4361,7 @@ MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* r } void -MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress) +MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/) { framepos_t const p = _region_view->region()->position (); double const y = _region_view->midi_view()->y_position (); @@ -4393,7 +4394,7 @@ MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv) } void -MidiVerticalSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress) +MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/) { double const y = _region_view->midi_view()->y_position (); @@ -4516,7 +4517,7 @@ NoteCreateDrag::motion (GdkEvent* event, bool) } void -NoteCreateDrag::finished (GdkEvent* event, bool had_movement) +NoteCreateDrag::finished (GdkEvent*, bool had_movement) { if (!had_movement) { return; @@ -4550,3 +4551,100 @@ NoteCreateDrag::aborted (bool) { } + +/*------------*/ + +CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn) + : Drag (e, i) + , arv (rv) + , start (start_yn) +{ +} + +void +CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor) +{ + Drag::start_grab (event, cursor); +} + +void +CrossfadeEdgeDrag::motion (GdkEvent*, bool) +{ + double distance; + double new_length; + framecnt_t len; + + boost::shared_ptr ar (arv->audio_region()); + + if (start) { + distance = _drags->current_pointer_x() - grab_x(); + len = ar->fade_in()->back()->when; + } else { + distance = grab_x() - _drags->current_pointer_x(); + len = ar->fade_out()->back()->when; + } + + /* how long should it be ? */ + + new_length = len + _editor->unit_to_frame (distance); + + /* now check with the region that this is legal */ + + new_length = ar->verify_xfade_bounds (new_length, start); + + if (start) { + arv->redraw_start_xfade_to (ar, new_length); + } else { + arv->redraw_end_xfade_to (ar, new_length); + } +} + +void +CrossfadeEdgeDrag::finished (GdkEvent*, bool) +{ + double distance; + double new_length; + framecnt_t len; + + boost::shared_ptr ar (arv->audio_region()); + + if (start) { + distance = _drags->current_pointer_x() - grab_x(); + len = ar->fade_in()->back()->when; + } else { + distance = grab_x() - _drags->current_pointer_x(); + len = ar->fade_out()->back()->when; + } + + new_length = ar->verify_xfade_bounds (len + _editor->unit_to_frame (distance), start); + + _editor->begin_reversible_command ("xfade trim"); + ar->playlist()->clear_owned_changes (); + + if (start) { + ar->set_fade_in_length (new_length); + } else { + ar->set_fade_out_length (new_length); + } + + /* Adjusting the xfade may affect other regions in the playlist, so we need + to get undo Commands from the whole playlist rather than just the + region. + */ + + vector cmds; + ar->playlist()->rdiff (cmds); + _editor->session()->add_commands (cmds); + +} + +void +CrossfadeEdgeDrag::aborted (bool) +{ + if (start) { + arv->redraw_start_xfade (); + } else { + arv->redraw_end_xfade (); + } +} + diff --git a/gtk2_ardour/editor_drag.h b/gtk2_ardour/editor_drag.h index 0d457a39d9..2f81e06efc 100644 --- a/gtk2_ardour/editor_drag.h +++ b/gtk2_ardour/editor_drag.h @@ -972,5 +972,30 @@ private: bool _nothing_to_drag; }; +/** Drag of one edge of an xfade + */ +class CrossfadeEdgeDrag : public Drag +{ + public: + CrossfadeEdgeDrag (Editor*, AudioRegionView*, ArdourCanvas::Item*, bool start); + + void start_grab (GdkEvent*, Gdk::Cursor* c = 0); + void motion (GdkEvent*, bool); + void finished (GdkEvent*, bool); + void aborted (bool); + + bool y_movement_matters () const { + return false; + } + + virtual std::pair move_threshold () const { + return std::make_pair (4, 4); + } + + private: + AudioRegionView* arv; + bool start; +}; + #endif /* __gtk2_ardour_editor_drag_h_ */ diff --git a/gtk2_ardour/editor_items.h b/gtk2_ardour/editor_items.h index 36a5a9e36f..51db6dbc87 100644 --- a/gtk2_ardour/editor_items.h +++ b/gtk2_ardour/editor_items.h @@ -50,6 +50,8 @@ enum ItemType { FeatureLineItem, LeftFrameHandle, RightFrameHandle, + StartCrossFadeItem, + EndCrossFadeItem, #ifdef WITH_CMT MarkerViewItem, diff --git a/gtk2_ardour/editor_mouse.cc b/gtk2_ardour/editor_mouse.cc index bd5cdd3f8f..d7d1c34280 100644 --- a/gtk2_ardour/editor_mouse.cc +++ b/gtk2_ardour/editor_mouse.cc @@ -640,11 +640,12 @@ Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemTyp } break; - case FadeInHandleItem: case FadeInItem: case FadeOutHandleItem: case FadeOutItem: + case StartCrossFadeItem: + case EndCrossFadeItem: if (doing_object_stuff() || (mouse_mode != MouseRange && mouse_mode != MouseObject)) { set_selected_regionview_from_click (press, op); } else if (event->type == GDK_BUTTON_PRESS) { @@ -931,6 +932,14 @@ Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemT return true; } + case StartCrossFadeItem: + _drags->set (new CrossfadeEdgeDrag (this, reinterpret_cast(item->get_data("regionview")), item, true), event, 0); + break; + + case EndCrossFadeItem: + _drags->set (new CrossfadeEdgeDrag (this, reinterpret_cast(item->get_data("regionview")), item, false), event, 0); + break; + case FeatureLineItem: { if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) { @@ -1471,6 +1480,14 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT popup_fade_context_menu (1, event->button.time, item, item_type); break; + case StartCrossFadeItem: + popup_xfade_in_context_menu (1, event->button.time, item, item_type); + break; + + case EndCrossFadeItem: + popup_xfade_out_context_menu (1, event->button.time, item, item_type); + break; + case StreamItem: popup_track_context_menu (1, event->button.time, item_type, false); break; @@ -1486,7 +1503,7 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT case SelectionItem: popup_track_context_menu (1, event->button.time, item_type, true); break; - + case AutomationTrackItem: popup_track_context_menu (1, event->button.time, item_type, false); break; diff --git a/gtk2_ardour/editor_ops.cc b/gtk2_ardour/editor_ops.cc index bae511e031..a8f1d1f6d7 100644 --- a/gtk2_ardour/editor_ops.cc +++ b/gtk2_ardour/editor_ops.cc @@ -4708,8 +4708,7 @@ Editor::quantize_region () qd->hide (); if (r == Gtk::RESPONSE_OK) { - Quantize quant (*_session, Plain, - qd->snap_start(), qd->snap_end(), + Quantize quant (*_session, qd->snap_start(), qd->snap_end(), qd->start_grid_size(), qd->end_grid_size(), qd->strength(), qd->swing(), qd->threshold()); @@ -5250,6 +5249,10 @@ Editor::set_fade_out_active (bool yn) void Editor::toggle_region_fades (int dir) { + if (_ignore_region_action) { + return; + } + boost::shared_ptr ar; bool yn = false; diff --git a/gtk2_ardour/editor_route_groups.cc b/gtk2_ardour/editor_route_groups.cc index 1b72425032..2fd08e7609 100644 --- a/gtk2_ardour/editor_route_groups.cc +++ b/gtk2_ardour/editor_route_groups.cc @@ -380,7 +380,7 @@ EditorRouteGroups::button_press_event (GdkEventButton* ev) } void -EditorRouteGroups::row_change (const Gtk::TreeModel::Path& path, const Gtk::TreeModel::iterator& iter) +EditorRouteGroups::row_change (const Gtk::TreeModel::Path&, const Gtk::TreeModel::iterator& iter) { RouteGroup* group; @@ -487,7 +487,7 @@ EditorRouteGroups::groups_changed () } void -EditorRouteGroups::property_changed (RouteGroup* group, const PropertyChange& change) +EditorRouteGroups::property_changed (RouteGroup* group, const PropertyChange&) { _in_row_change = true; diff --git a/gtk2_ardour/editor_selection.cc b/gtk2_ardour/editor_selection.cc index fe93516063..20ab6673a8 100644 --- a/gtk2_ardour/editor_selection.cc +++ b/gtk2_ardour/editor_selection.cc @@ -1055,6 +1055,10 @@ Editor::sensitize_the_right_region_actions () bool have_envelope_inactive = false; bool have_non_unity_scale_amplitude = false; bool have_compound_regions = false; + bool have_inactive_fade_in = false; + bool have_inactive_fade_out = false; + bool have_active_fade_in = false; + bool have_active_fade_out = false; for (list::const_iterator i = rs.begin(); i != rs.end(); ++i) { @@ -1114,6 +1118,18 @@ Editor::sensitize_the_right_region_actions () if (ar->scale_amplitude() != 1) { have_non_unity_scale_amplitude = true; } + + if (ar->fade_in_active ()) { + have_active_fade_in = true; + } else { + have_inactive_fade_in = true; + } + + if (ar->fade_out_active ()) { + have_active_fade_out = true; + } else { + have_inactive_fade_out = true; + } } } @@ -1168,7 +1184,7 @@ Editor::sensitize_the_right_region_actions () if (have_envelope_active && !have_envelope_inactive) { Glib::RefPtr::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_active (); } else if (have_envelope_active && have_envelope_inactive) { - // _region_actions->get_action("toggle-region-gain-envelope-active")->set_inconsistent (); + // Glib::RefPtr::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_inconsistent (); } } else { @@ -1184,25 +1200,29 @@ Editor::sensitize_the_right_region_actions () _region_actions->get_action("reset-region-scale-amplitude")->set_sensitive (false); } - Glib::RefPtr::cast_dynamic (_region_actions->get_action("toggle-region-lock"))->set_active (have_locked && !have_unlocked); + Glib::RefPtr a = Glib::RefPtr::cast_dynamic (_region_actions->get_action("toggle-region-lock")); + a->set_active (have_locked && !have_unlocked); if (have_locked && have_unlocked) { - // _region_actions->get_action("toggle-region-lock")->set_inconsistent (); + // a->set_inconsistent (); } - Glib::RefPtr::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"))->set_active (have_position_lock_style_music && !have_position_lock_style_audio); + a = Glib::RefPtr::cast_dynamic (_region_actions->get_action("toggle-region-lock-style")); + a->set_active (have_position_lock_style_music && !have_position_lock_style_audio); if (have_position_lock_style_music && have_position_lock_style_audio) { - // _region_actions->get_action("toggle-region-lock-style")->set_inconsistent (); + // a->set_inconsistent (); } - Glib::RefPtr::cast_dynamic (_region_actions->get_action("toggle-region-mute"))->set_active (have_muted && !have_unmuted); + a = Glib::RefPtr::cast_dynamic (_region_actions->get_action("toggle-region-mute")); + a->set_active (have_muted && !have_unmuted); if (have_muted && have_unmuted) { - // _region_actions->get_action("toggle-region-mute")->set_inconsistent (); + // a->set_inconsistent (); } - Glib::RefPtr::cast_dynamic (_region_actions->get_action("toggle-opaque-region"))->set_active (have_opaque && !have_non_opaque); + a = Glib::RefPtr::cast_dynamic (_region_actions->get_action("toggle-opaque-region")); + a->set_active (have_opaque && !have_non_opaque); if (have_opaque && have_non_opaque) { - // _region_actions->get_action("toggle-opaque-region")->set_inconsistent (); + // a->set_inconsistent (); } if (!have_not_at_natural_position) { @@ -1216,6 +1236,29 @@ Editor::sensitize_the_right_region_actions () _region_actions->get_action("insert-region-from-region-list")->set_sensitive (true); } + a = Glib::RefPtr::cast_dynamic (_region_actions->get_action("toggle-region-fade-in")); + a->set_active (have_active_fade_in && !have_inactive_fade_in); + if (have_active_fade_in && have_inactive_fade_in) { + // a->set_inconsistent (); + } + + a = Glib::RefPtr::cast_dynamic (_region_actions->get_action("toggle-region-fade-out")); + a->set_active (have_active_fade_out && !have_inactive_fade_out); + + if (have_active_fade_out && have_inactive_fade_out) { + // a->set_inconsistent (); + } + + bool const have_active_fade = have_active_fade_in || have_active_fade_out; + bool const have_inactive_fade = have_inactive_fade_in || have_inactive_fade_out; + + a = Glib::RefPtr::cast_dynamic (_region_actions->get_action("toggle-region-fades")); + a->set_active (have_active_fade && !have_inactive_fade); + + if (have_active_fade && have_inactive_fade) { + // a->set_inconsistent (); + } + _ignore_region_action = false; _all_region_actions_sensitized = false; diff --git a/gtk2_ardour/editor_snapshots.cc b/gtk2_ardour/editor_snapshots.cc index d9c1489e82..c47c815984 100644 --- a/gtk2_ardour/editor_snapshots.cc +++ b/gtk2_ardour/editor_snapshots.cc @@ -121,7 +121,7 @@ EditorSnapshots::popup_context_menu (int button, int32_t time, std::string snaps add_item_with_sensitivity (items, MenuElem (_("Remove"), sigc::bind (sigc::mem_fun (*this, &EditorSnapshots::remove), snapshot_name)), modification_allowed); - add_item_with_sensitivity (items, MenuElem (_("Rename"), sigc::bind (sigc::mem_fun (*this, &EditorSnapshots::rename), snapshot_name)), modification_allowed); + add_item_with_sensitivity (items, MenuElem (_("Rename..."), sigc::bind (sigc::mem_fun (*this, &EditorSnapshots::rename), snapshot_name)), modification_allowed); _menu.popup (button, time); } @@ -134,6 +134,7 @@ EditorSnapshots::rename (std::string old_name) string new_name; prompter.set_name ("Prompter"); + prompter.set_title (_("Rename Snapshot")); prompter.add_button (Gtk::Stock::SAVE, Gtk::RESPONSE_ACCEPT); prompter.set_prompt (_("New name of snapshot")); prompter.set_initial_text (old_name); diff --git a/gtk2_ardour/export_format_selector.cc b/gtk2_ardour/export_format_selector.cc index d3ffce936a..146b31ab51 100644 --- a/gtk2_ardour/export_format_selector.cc +++ b/gtk2_ardour/export_format_selector.cc @@ -96,7 +96,7 @@ ExportFormatSelector::update_format_list () tree_it->set_value (format_cols.label, (*it)->description()); } - if (format_combo.get_active_row_number() == -1) { + if (format_combo.get_active_row_number() == -1 && format_combo.get_model()->children().size() > 0) { format_combo.set_active (0); } diff --git a/gtk2_ardour/group_tabs.cc b/gtk2_ardour/group_tabs.cc index 9fa9c8bf73..5dbeeffd09 100644 --- a/gtk2_ardour/group_tabs.cc +++ b/gtk2_ardour/group_tabs.cc @@ -180,7 +180,7 @@ GroupTabs::on_motion_notify_event (GdkEventMotion* ev) bool -GroupTabs::on_button_release_event (GdkEventButton* ev) +GroupTabs::on_button_release_event (GdkEventButton*) { if (_dragging == 0) { return false; diff --git a/gtk2_ardour/gtk_pianokeyboard.c b/gtk2_ardour/gtk_pianokeyboard.c index 7332299bfb..1e5768040e 100644 --- a/gtk2_ardour/gtk_pianokeyboard.c +++ b/gtk2_ardour/gtk_pianokeyboard.c @@ -527,7 +527,7 @@ piano_keyboard_expose(GtkWidget *widget, GdkEventExpose *event) } static void -piano_keyboard_size_request(GtkWidget* widget, GtkRequisition *requisition) +piano_keyboard_size_request(GtkWidget* w, GtkRequisition *requisition) { requisition->width = PIANO_KEYBOARD_DEFAULT_WIDTH; requisition->height = PIANO_KEYBOARD_DEFAULT_HEIGHT; diff --git a/gtk2_ardour/icons/crossfade-in-S1.png b/gtk2_ardour/icons/fadein-S1.png similarity index 100% rename from gtk2_ardour/icons/crossfade-in-S1.png rename to gtk2_ardour/icons/fadein-S1.png diff --git a/gtk2_ardour/icons/crossfade-in-S2.png b/gtk2_ardour/icons/fadein-S2.png similarity index 100% rename from gtk2_ardour/icons/crossfade-in-S2.png rename to gtk2_ardour/icons/fadein-S2.png diff --git a/gtk2_ardour/icons/crossfade-in-constant-power.png b/gtk2_ardour/icons/fadein-constant-power.png similarity index 100% rename from gtk2_ardour/icons/crossfade-in-constant-power.png rename to gtk2_ardour/icons/fadein-constant-power.png diff --git a/gtk2_ardour/icons/crossfade-in-fast-cut.png b/gtk2_ardour/icons/fadein-fast-cut.png similarity index 100% rename from gtk2_ardour/icons/crossfade-in-fast-cut.png rename to gtk2_ardour/icons/fadein-fast-cut.png diff --git a/gtk2_ardour/icons/crossfade-in-linear.png b/gtk2_ardour/icons/fadein-linear.png similarity index 100% rename from gtk2_ardour/icons/crossfade-in-linear.png rename to gtk2_ardour/icons/fadein-linear.png diff --git a/gtk2_ardour/icons/crossfade-in-long-cut.png b/gtk2_ardour/icons/fadein-long-cut.png similarity index 100% rename from gtk2_ardour/icons/crossfade-in-long-cut.png rename to gtk2_ardour/icons/fadein-long-cut.png diff --git a/gtk2_ardour/icons/crossfade-in-short-cut.png b/gtk2_ardour/icons/fadein-short-cut.png similarity index 100% rename from gtk2_ardour/icons/crossfade-in-short-cut.png rename to gtk2_ardour/icons/fadein-short-cut.png diff --git a/gtk2_ardour/icons/crossfade-in-slow-cut.png b/gtk2_ardour/icons/fadein-slow-cut.png similarity index 100% rename from gtk2_ardour/icons/crossfade-in-slow-cut.png rename to gtk2_ardour/icons/fadein-slow-cut.png diff --git a/gtk2_ardour/icons/crossfade-out-S1.png b/gtk2_ardour/icons/fadeout-S1.png similarity index 100% rename from gtk2_ardour/icons/crossfade-out-S1.png rename to gtk2_ardour/icons/fadeout-S1.png diff --git a/gtk2_ardour/icons/crossfade-out-S2.png b/gtk2_ardour/icons/fadeout-S2.png similarity index 100% rename from gtk2_ardour/icons/crossfade-out-S2.png rename to gtk2_ardour/icons/fadeout-S2.png diff --git a/gtk2_ardour/icons/crossfade-out-constant-power.png b/gtk2_ardour/icons/fadeout-constant-power.png similarity index 100% rename from gtk2_ardour/icons/crossfade-out-constant-power.png rename to gtk2_ardour/icons/fadeout-constant-power.png diff --git a/gtk2_ardour/icons/crossfade-out-fast-cut.png b/gtk2_ardour/icons/fadeout-fast-cut.png similarity index 100% rename from gtk2_ardour/icons/crossfade-out-fast-cut.png rename to gtk2_ardour/icons/fadeout-fast-cut.png diff --git a/gtk2_ardour/icons/crossfade-out-linear.png b/gtk2_ardour/icons/fadeout-linear.png similarity index 100% rename from gtk2_ardour/icons/crossfade-out-linear.png rename to gtk2_ardour/icons/fadeout-linear.png diff --git a/gtk2_ardour/icons/crossfade-out-long-cut.png b/gtk2_ardour/icons/fadeout-long-cut.png similarity index 100% rename from gtk2_ardour/icons/crossfade-out-long-cut.png rename to gtk2_ardour/icons/fadeout-long-cut.png diff --git a/gtk2_ardour/icons/crossfade-out-short-cut.png b/gtk2_ardour/icons/fadeout-short-cut.png similarity index 100% rename from gtk2_ardour/icons/crossfade-out-short-cut.png rename to gtk2_ardour/icons/fadeout-short-cut.png diff --git a/gtk2_ardour/icons/crossfade-out-slow-cut.png b/gtk2_ardour/icons/fadeout-slow-cut.png similarity index 100% rename from gtk2_ardour/icons/crossfade-out-slow-cut.png rename to gtk2_ardour/icons/fadeout-slow-cut.png diff --git a/gtk2_ardour/location_ui.cc b/gtk2_ardour/location_ui.cc index 60e0be5676..4ef413aef7 100644 --- a/gtk2_ardour/location_ui.cc +++ b/gtk2_ardour/location_ui.cc @@ -687,8 +687,9 @@ LocationEditRow::position_lock_style_changed (ARDOUR::Location*) } void -LocationEditRow::focus_name() { - name_entry.grab_focus(); +LocationEditRow::focus_name() +{ + name_entry.grab_focus (); } void @@ -862,9 +863,9 @@ LocationUI::location_redraw_ranges () } struct LocationSortByStart { - bool operator() (Location *a, Location *b) { - return a->start() < b->start(); - } + bool operator() (Location *a, Location *b) { + return a->start() < b->start(); + } }; void @@ -879,7 +880,7 @@ LocationUI::location_added (Location* location) loc.sort (LocationSortByStart ()); LocationEditRow* erow = manage (new LocationEditRow (_session, location)); - + erow->set_clock_group (*_clock_group); erow->remove_requested.connect (sigc::mem_fun (*this, &LocationUI::location_remove_requested)); @@ -910,6 +911,11 @@ LocationUI::location_added (Location* location) range_rows.show_all (); location_rows.show_all (); + + if (location == newest_location) { + newest_location = 0; + erow->focus_name(); + } } } @@ -959,10 +965,6 @@ LocationUI::map_locations (Locations::LocationList& locations) Box_Helpers::BoxList & loc_children = location_rows.children(); loc_children.push_back(Box_Helpers::Element(*erow, PACK_SHRINK, 1, PACK_START)); - if (location == newest_location) { - newest_location = 0; - erow->focus_name(); - } } else if (location->is_auto_punch()) { punch_edit_row.set_session (_session); punch_edit_row.set_location (location); diff --git a/gtk2_ardour/location_ui.h b/gtk2_ardour/location_ui.h index 5a6e8649c7..b850b89ddd 100644 --- a/gtk2_ardour/location_ui.h +++ b/gtk2_ardour/location_ui.h @@ -162,6 +162,10 @@ class LocationUI : public Gtk::HBox, public ARDOUR::SessionHandlePtr private: ARDOUR::LocationStack* locations; + /** set to the location that has just been created with the LocationUI `add' button + (if Config->get_name_new_markers() is true); if it is non-0, the name entry of + the location is given the focus by location_added(). + */ ARDOUR::Location *newest_location; void session_going_away (); diff --git a/gtk2_ardour/main.cc b/gtk2_ardour/main.cc index a853d7b61b..d7b7428b7e 100644 --- a/gtk2_ardour/main.cc +++ b/gtk2_ardour/main.cc @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -197,6 +198,7 @@ fixup_bundle_environment (int, char* []) export_search_path (dir_path, "ARDOUR_PANNER_PATH", "/../Panners"); export_search_path (dir_path, "ARDOUR_SURFACES_PATH", "/../Surfaces"); export_search_path (dir_path, "ARDOUR_MIDIMAPS_PATH", "/../MidiMaps"); + export_search_path (dir_path, "ARDOUR_MCP_PATH", "../MCP"); export_search_path (dir_path, "ARDOUR_EXPORT_FORMATS_PATH", "/../ExportFormats"); path = dir_path; @@ -289,6 +291,10 @@ fixup_bundle_environment (int, char* []) void fixup_bundle_environment (int /*argc*/, char* argv[]) { + /* THIS IS FOR LINUX - its just about the only place where its + * acceptable to build paths directly using '/'. + */ + if (!getenv ("ARDOUR_BUNDLED")) { return; } @@ -340,6 +346,7 @@ fixup_bundle_environment (int /*argc*/, char* argv[]) export_search_path (dir_path, "ARDOUR_PANNER_PATH", "/lib/panners"); export_search_path (dir_path, "ARDOUR_SURFACES_PATH", "/lib/surfaces"); export_search_path (dir_path, "ARDOUR_MIDIMAPS_PATH", "/share/midi_maps"); + export_search_path (dir_path, "ARDOUR_MCP_PATH", "/share/mcp"); export_search_path (dir_path, "ARDOUR_EXPORT_FORMATS_PATH", "/share/export"); path = dir_path; @@ -359,6 +366,22 @@ fixup_bundle_environment (int /*argc*/, char* argv[]) setenv ("GTK_LOCALEDIR", localedir, 1); } + /* Tell fontconfig where to find fonts.conf. Use the system version + if it exists, otherwise use the stuff we included in t + */ + + if (Glib::file_test ("/etc/fonts/fonts.conf", Glib::FILE_TEST_EXISTS)) { + setenv ("FONTCONFIG_FILE", "/etc/fonts/fonts.conf", 1); + setenv ("FONTCONFIG_PATH", "/etc/fonts", 1); + } else { + /* use the one included in the bundle */ + + path = Glib::build_filename (dir_path, "etc/fonts/fonts.conf"); + setenv ("FONTCONFIG_FILE", path.c_str(), 1); + path = Glib::build_filename (dir_path, "etc/fonts"); + setenv ("FONTCONFIG_PATH", "/etc/fonts", 1); + } + /* write a pango.rc file and tell pango to use it. we'd love to put this into the Ardour.app bundle and leave it there, but the user may not have write permission. so ... @@ -370,32 +393,32 @@ fixup_bundle_environment (int /*argc*/, char* argv[]) if (g_mkdir_with_parents (userconfigdir.c_str(), 0755) < 0) { error << string_compose (_("cannot create user ardour folder %1 (%2)"), userconfigdir, strerror (errno)) << endmsg; + return; + } + + Glib::ustring mpath; + + path = Glib::build_filename (userconfigdir, "pango.rc"); + + std::ofstream pangorc (path.c_str()); + if (!pangorc) { + error << string_compose (_("cannot open pango.rc file %1") , path) << endmsg; } else { - - Glib::ustring mpath; - - path = Glib::build_filename (userconfigdir, "pango.rc"); - - std::ofstream pangorc (path.c_str()); - if (!pangorc) { - error << string_compose (_("cannot open pango.rc file %1") , path) << endmsg; - } else { - mpath = Glib::build_filename (userconfigdir, "pango.modules"); - - pangorc << "[Pango]\nModuleFiles="; - pangorc << mpath << endl; - pangorc.close (); - } - - setenv ("PANGO_RC_FILE", path.c_str(), 1); - - /* similar for GDK pixbuf loaders, but there's no RC file required - to specify where it lives. - */ - - mpath = Glib::build_filename (userconfigdir, "gdk-pixbuf.loaders"); - setenv ("GDK_PIXBUF_MODULE_FILE", mpath.c_str(), 1); + mpath = Glib::build_filename (userconfigdir, "pango.modules"); + + pangorc << "[Pango]\nModuleFiles="; + pangorc << mpath << endl; + pangorc.close (); } + + setenv ("PANGO_RC_FILE", path.c_str(), 1); + + /* similar for GDK pixbuf loaders, but there's no RC file required + to specify where it lives. + */ + + mpath = Glib::build_filename (userconfigdir, "gdk-pixbuf.loaders"); + setenv ("GDK_PIXBUF_MODULE_FILE", mpath.c_str(), 1); } #endif diff --git a/gtk2_ardour/midi_region_view.cc b/gtk2_ardour/midi_region_view.cc index 154cd158dc..76db1a7b69 100644 --- a/gtk2_ardour/midi_region_view.cc +++ b/gtk2_ardour/midi_region_view.cc @@ -89,7 +89,6 @@ PBD::Signal1 MidiRegionView::SelectionCleared; MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv, boost::shared_ptr r, double spu, Gdk::Color const & basic_color) : RegionView (parent, tv, r, spu, basic_color) - , _force_channel(-1) , _last_channel_selection(0xFFFF) , _current_range_min(0) , _current_range_max(0) @@ -126,7 +125,6 @@ MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView & boost::shared_ptr r, double spu, Gdk::Color& basic_color, TimeAxisViewItem::Visibility visibility) : RegionView (parent, tv, r, spu, basic_color, false, visibility) - , _force_channel(-1) , _last_channel_selection(0xFFFF) , _current_range_min(0) , _current_range_max(0) @@ -171,7 +169,6 @@ MidiRegionView::parameter_changed (std::string const & p) MidiRegionView::MidiRegionView (const MidiRegionView& other) : sigc::trackable(other) , RegionView (other) - , _force_channel(-1) , _last_channel_selection(0xFFFF) , _current_range_min(0) , _current_range_max(0) @@ -206,7 +203,6 @@ MidiRegionView::MidiRegionView (const MidiRegionView& other) MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr region) : RegionView (other, boost::shared_ptr (region)) - , _force_channel(-1) , _last_channel_selection(0xFFFF) , _current_range_min(0) , _current_range_max(0) @@ -853,7 +849,7 @@ MidiRegionView::create_note_at (framepos_t t, double y, double length, bool snap view->update_note_range(new_note->note()); - MidiModel::NoteDiffCommand* cmd = _model->new_note_diff_command("add note"); + MidiModel::NoteDiffCommand* cmd = _model->new_note_diff_command(_("add note")); cmd->add (new_note); _model->apply_command(*trackview.session(), cmd); @@ -3109,15 +3105,9 @@ MidiRegionView::set_frame_color() void MidiRegionView::midi_channel_mode_changed(ChannelMode mode, uint16_t mask) { - switch (mode) { - case AllChannels: - case FilterChannels: - _force_channel = -1; - break; - case ForceChannel: - _force_channel = mask; + 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) { diff --git a/gtk2_ardour/midi_region_view.h b/gtk2_ardour/midi_region_view.h index e34c80de44..c3084aee38 100644 --- a/gtk2_ardour/midi_region_view.h +++ b/gtk2_ardour/midi_region_view.h @@ -363,7 +363,6 @@ private: void show_verbose_cursor (std::string const &, double, double) const; void show_verbose_cursor (boost::shared_ptr) const; - int8_t _force_channel; uint16_t _last_channel_selection; uint8_t _current_range_min; uint8_t _current_range_max; diff --git a/gtk2_ardour/plugin_selector.cc b/gtk2_ardour/plugin_selector.cc index 75040eca06..1ec07d1bbe 100644 --- a/gtk2_ardour/plugin_selector.cc +++ b/gtk2_ardour/plugin_selector.cc @@ -199,7 +199,7 @@ PluginSelector::~PluginSelector () } void -PluginSelector::row_activated(Gtk::TreeModel::Path path, Gtk::TreeViewColumn* col) +PluginSelector::row_activated(Gtk::TreeModel::Path, Gtk::TreeViewColumn*) { btn_add_clicked(); } @@ -319,15 +319,22 @@ PluginSelector::refiller (const PluginInfoList& plugs, const::std::string& filte newrow[plugin_columns.creator] = creator; - snprintf (buf, sizeof(buf), "%d", (*i)->n_inputs.n_audio()); - newrow[plugin_columns.audio_ins] = buf; - snprintf (buf, sizeof(buf), "%d", (*i)->n_inputs.n_midi()); - newrow[plugin_columns.midi_ins] = buf; - - snprintf (buf, sizeof(buf), "%d", (*i)->n_outputs.n_audio()); - newrow[plugin_columns.audio_outs] = buf; - snprintf (buf, sizeof(buf), "%d", (*i)->n_outputs.n_midi()); - newrow[plugin_columns.midi_outs] = buf; + if ((*i)->reconfigurable_io ()) { + newrow[plugin_columns.audio_ins] = _("variable"); + newrow[plugin_columns.midi_ins] = _("variable"); + newrow[plugin_columns.audio_outs] = _("variable"); + newrow[plugin_columns.midi_outs] = _("variable"); + } else { + snprintf (buf, sizeof(buf), "%d", (*i)->n_inputs.n_audio()); + newrow[plugin_columns.audio_ins] = buf; + snprintf (buf, sizeof(buf), "%d", (*i)->n_inputs.n_midi()); + newrow[plugin_columns.midi_ins] = buf; + + snprintf (buf, sizeof(buf), "%d", (*i)->n_outputs.n_audio()); + newrow[plugin_columns.audio_outs] = buf; + snprintf (buf, sizeof(buf), "%d", (*i)->n_outputs.n_midi()); + newrow[plugin_columns.midi_outs] = buf; + } newrow[plugin_columns.plugin] = *i; } diff --git a/gtk2_ardour/port_matrix.cc b/gtk2_ardour/port_matrix.cc index d942c99c89..a259f357ce 100644 --- a/gtk2_ardour/port_matrix.cc +++ b/gtk2_ardour/port_matrix.cc @@ -444,10 +444,8 @@ PortMatrix::popup_menu (BundleChannel column, BundleChannel row, uint32_t t) if (bc[dim].channel != -1) { add_remove_option (sub, w, bc[dim].channel); } else { - - snprintf (buf, sizeof (buf), _("Remove all")); sub.push_back ( - MenuElem (buf, sigc::bind (sigc::mem_fun (*this, &PortMatrix::remove_all_channels), w)) + MenuElem (_("Remove all"), sigc::bind (sigc::mem_fun (*this, &PortMatrix::remove_all_channels), w)) ); if (bc[dim].bundle->nchannels().n_total() > 1) { diff --git a/gtk2_ardour/processor_box.cc b/gtk2_ardour/processor_box.cc index e2d33b9891..1bba2c8af1 100644 --- a/gtk2_ardour/processor_box.cc +++ b/gtk2_ardour/processor_box.cc @@ -965,7 +965,7 @@ ProcessorBox::enter_notify (GdkEventCrossing*) } bool -ProcessorBox::leave_notify (GdkEventCrossing* ev) +ProcessorBox::leave_notify (GdkEventCrossing*) { return false; } diff --git a/gtk2_ardour/public_editor.h b/gtk2_ardour/public_editor.h index ee4c7162cb..ce625e1889 100644 --- a/gtk2_ardour/public_editor.h +++ b/gtk2_ardour/public_editor.h @@ -314,6 +314,8 @@ class PublicEditor : public Gtk::Window, public PBD::StatefulDestructible { virtual bool canvas_selection_rect_event (GdkEvent* event, ArdourCanvas::Item*, SelectionRect*) = 0; virtual bool canvas_selection_start_trim_event (GdkEvent* event, ArdourCanvas::Item*, SelectionRect*) = 0; virtual bool canvas_selection_end_trim_event (GdkEvent* event, ArdourCanvas::Item*, SelectionRect*) = 0; + virtual bool canvas_start_xfade_event (GdkEvent* event, ArdourCanvas::Item*, AudioRegionView*) = 0; + virtual bool canvas_end_xfade_event (GdkEvent* event, ArdourCanvas::Item*, AudioRegionView*) = 0; virtual bool canvas_fade_in_event (GdkEvent* event, ArdourCanvas::Item*, AudioRegionView*) = 0; virtual bool canvas_fade_in_handle_event (GdkEvent* event, ArdourCanvas::Item*, AudioRegionView*) = 0; virtual bool canvas_fade_out_event (GdkEvent* event, ArdourCanvas::Item*, AudioRegionView*) = 0; diff --git a/gtk2_ardour/quantize_dialog.cc b/gtk2_ardour/quantize_dialog.cc index 82b7ef3009..623a5d8033 100644 --- a/gtk2_ardour/quantize_dialog.cc +++ b/gtk2_ardour/quantize_dialog.cc @@ -46,20 +46,11 @@ static const gchar *_grid_strings[] = { 0 }; -static const gchar *_type_strings[] = { - N_("Grid"), - N_("Legato"), - N_("Groove"), - 0 -}; - std::vector QuantizeDialog::grid_strings; -std::vector QuantizeDialog::type_strings; QuantizeDialog::QuantizeDialog (PublicEditor& e) : ArdourDialog (_("Quantize"), false, false) , editor (e) - , type_label (_("Quantize Type")) , strength_adjustment (100.0, 0.0, 100.0, 1.0, 10.0) , strength_spinner (strength_adjustment) , strength_label (_("Strength")) @@ -74,7 +65,6 @@ QuantizeDialog::QuantizeDialog (PublicEditor& e) { if (grid_strings.empty()) { grid_strings = I18N (_grid_strings); - type_strings = I18N (_type_strings); } set_popdown_strings (start_grid_combo, grid_strings); @@ -82,20 +72,12 @@ QuantizeDialog::QuantizeDialog (PublicEditor& e) set_popdown_strings (end_grid_combo, grid_strings); end_grid_combo.set_active_text (grid_strings.front()); - set_popdown_strings (type_combo, type_strings); - type_combo.set_active_text (type_strings.front()); - Table* table = manage (new Table (6, 2)); table->set_spacings (12); table->set_border_width (12); int r = 0; - type_label.set_alignment (0, 0.5); - table->attach (type_label, 0, 1, r, r + 1); - table->attach (type_combo, 1, 2, r, r + 1); - ++r; - table->attach (snap_start_button, 0, 1, r, r + 1); table->attach (start_grid_combo, 1, 2, r, r + 1); ++r; diff --git a/gtk2_ardour/quantize_dialog.h b/gtk2_ardour/quantize_dialog.h index 97cb82c86e..8203875e98 100644 --- a/gtk2_ardour/quantize_dialog.h +++ b/gtk2_ardour/quantize_dialog.h @@ -45,7 +45,6 @@ class QuantizeDialog : public ArdourDialog QuantizeDialog (PublicEditor&); ~QuantizeDialog (); - ARDOUR::QuantizeType type() const; double start_grid_size() const; double end_grid_size() const; bool snap_start() const { return snap_start_button.get_active(); } @@ -57,10 +56,8 @@ class QuantizeDialog : public ArdourDialog private: PublicEditor& editor; - Gtk::ComboBoxText type_combo; Gtk::ComboBoxText start_grid_combo; Gtk::ComboBoxText end_grid_combo; - Gtk::Label type_label; Gtk::Adjustment strength_adjustment; Gtk::SpinButton strength_spinner; Gtk::Label strength_label; diff --git a/gtk2_ardour/rc_option_editor.cc b/gtk2_ardour/rc_option_editor.cc index 8bdc9f2610..ac9c11a5bb 100644 --- a/gtk2_ardour/rc_option_editor.cc +++ b/gtk2_ardour/rc_option_editor.cc @@ -722,7 +722,18 @@ private: if (!was_enabled) { ControlProtocolManager::instance().instantiate (*cpi); } else { + Gtk::Window* win = r[_model.editor]; + if (win) { + win->hide (); + } + ControlProtocolManager::instance().teardown (*cpi); + + if (win) { + delete win; + } + r[_model.editor] = 0; + cpi->requested = false; } } @@ -1573,7 +1584,7 @@ RCOptionEditor::RCOptionEditor () add_option (S_("Visual|Interface"), new BoolOption ( "use-own-plugin-gui", - _("Use plugins' own interface instead of a builtin one"), + _("Use plugins' own interfaces instead of Ardour's"), sigc::mem_fun (*_rc_config, &RCConfiguration::get_use_plugin_own_gui), sigc::mem_fun (*_rc_config, &RCConfiguration::set_use_plugin_own_gui) )); diff --git a/gtk2_ardour/route_group_dialog.cc b/gtk2_ardour/route_group_dialog.cc index eb74887771..3e2c497475 100644 --- a/gtk2_ardour/route_group_dialog.cc +++ b/gtk2_ardour/route_group_dialog.cc @@ -33,7 +33,7 @@ using namespace std; using namespace PBD; RouteGroupDialog::RouteGroupDialog (RouteGroup* g, bool creating_new) - : ArdourDialog (_("Route Group")) + : ArdourDialog (_("Track/bus Group")) , _group (g) , _initial_name (g->name ()) , _active (_("Active")) @@ -44,7 +44,7 @@ RouteGroupDialog::RouteGroupDialog (RouteGroup* g, bool creating_new) , _rec_enable (_("Record enable")) , _select (_("Selection")) , _edit (_("Editing")) - , _route_active (_("Route active state")) + , _route_active (_("Active state")) , _share_color (_("Color")) , _share_monitoring (_("Monitoring")) { diff --git a/gtk2_ardour/route_time_axis.cc b/gtk2_ardour/route_time_axis.cc index ef2cba7f9b..4d50a41272 100644 --- a/gtk2_ardour/route_time_axis.cc +++ b/gtk2_ardour/route_time_axis.cc @@ -2362,13 +2362,16 @@ RouteTimeAxisView::set_button_names () switch (Config->get_listen_position()) { case AfterFaderListen: solo_button->set_text (_("A")); + ARDOUR_UI::instance()->set_tip (*solo_button, _("After-fade listen (AFL)")); break; case PreFaderListen: solo_button->set_text (_("P")); + ARDOUR_UI::instance()->set_tip (*solo_button, _("Pre-fade listen (PFL)")); break; } } else { solo_button->set_text (_("s")); + ARDOUR_UI::instance()->set_tip (*solo_button, _("Solo")); } } mute_button->set_text (_("m")); diff --git a/gtk2_ardour/route_ui.cc b/gtk2_ardour/route_ui.cc index 8d8c4224b0..64ba5b067d 100644 --- a/gtk2_ardour/route_ui.cc +++ b/gtk2_ardour/route_ui.cc @@ -629,7 +629,7 @@ RouteUI::update_monitoring_display () } bool -RouteUI::monitor_input_press(GdkEventButton* ev) +RouteUI::monitor_input_press(GdkEventButton*) { return true; } @@ -641,7 +641,7 @@ RouteUI::monitor_input_release(GdkEventButton* ev) } bool -RouteUI::monitor_disk_press (GdkEventButton* ev) +RouteUI::monitor_disk_press (GdkEventButton*) { return true; } diff --git a/gtk2_ardour/session_option_editor.cc b/gtk2_ardour/session_option_editor.cc index 62d555c13d..b9f54f94e8 100644 --- a/gtk2_ardour/session_option_editor.cc +++ b/gtk2_ardour/session_option_editor.cc @@ -115,7 +115,7 @@ SessionOptionEditor::SessionOptionEditor (Session* s) ClockOption* co = new ClockOption ( "timecode-offset", - _("Timecode Offset"), + _("Timecode offset"), sigc::mem_fun (*_session_config, &SessionConfiguration::get_timecode_offset), sigc::mem_fun (*_session_config, &SessionConfiguration::set_timecode_offset) ); @@ -154,6 +154,19 @@ SessionOptionEditor::SessionOptionEditor (Session* s) add_option (_("Fades"), cfm); + ComboOption* cfc = new ComboOption ( + "xfade-choice", + _("Crossfade type"), + sigc::mem_fun (*_session_config, &SessionConfiguration::get_xfade_choice), + sigc::mem_fun (*_session_config, &SessionConfiguration::set_xfade_choice) + ); + + cfc->add (ConstantPowerMinus3dB, _("constant power (-3dB)")); + cfc->add (ConstantPowerMinus6dB, _("constant power (-6dB)")); + cfc->add (RegionFades, _("use existing region fade shape")); + + add_option (_("Fades"), cfc); + add_option (_("Fades"), new SpinOption ( _("short-xfade-seconds"), _("Short crossfade length"), @@ -179,20 +192,6 @@ SessionOptionEditor::SessionOptionEditor (Session* s) sigc::mem_fun (*_session_config, &SessionConfiguration::set_auto_xfade) )); - add_option (_("Fades"), new BoolOption ( - "xfades-active", - _("Crossfades active"), - sigc::mem_fun (*_session_config, &SessionConfiguration::get_xfades_active), - sigc::mem_fun (*_session_config, &SessionConfiguration::set_xfades_active) - )); - - add_option (_("Fades"), new BoolOption ( - "xfades-visible", - _("Crossfades visible"), - sigc::mem_fun (*_session_config, &SessionConfiguration::get_xfades_visible), - sigc::mem_fun (*_session_config, &SessionConfiguration::set_xfades_visible) - )); - add_option (_("Fades"), new BoolOption ( "use-region-fades", _("Region fades active"), diff --git a/gtk2_ardour/sfdb_freesound_mootcher.cc b/gtk2_ardour/sfdb_freesound_mootcher.cc index 7e13c4f68b..70d04abb66 100644 --- a/gtk2_ardour/sfdb_freesound_mootcher.cc +++ b/gtk2_ardour/sfdb_freesound_mootcher.cc @@ -369,7 +369,7 @@ std::string Mootcher::getAudioFile(std::string originalFileName, std::string ID, } //--------- -int Mootcher::progress_callback(void *caller, double dltotal, double dlnow, double ultotal, double ulnow) +int Mootcher::progress_callback(void *caller, double dltotal, double dlnow, double /*ultotal*/, double /*ulnow*/) { SoundFileBrowser *sfb = (SoundFileBrowser *) caller; diff --git a/gtk2_ardour/shuttle_control.cc b/gtk2_ardour/shuttle_control.cc index 3b5f8dbb78..abbd39ac16 100644 --- a/gtk2_ardour/shuttle_control.cc +++ b/gtk2_ardour/shuttle_control.cc @@ -522,7 +522,7 @@ ShuttleControl::on_expose_event (GdkEventExpose*) if (Config->get_shuttle_units() == Percentage) { if (speed == 1.0) { - snprintf (buf, sizeof (buf), _("Playing")); + snprintf (buf, sizeof (buf), "%s", _("Playing")); } else { if (speed < 0.0) { snprintf (buf, sizeof (buf), "<<< %d%%", (int) round (-speed * 100)); @@ -544,7 +544,7 @@ ShuttleControl::on_expose_event (GdkEventExpose*) } } else { - snprintf (buf, sizeof (buf), _("Stopped")); + snprintf (buf, sizeof (buf), "%s", _("Stopped")); } last_speed_displayed = speed; @@ -559,10 +559,10 @@ ShuttleControl::on_expose_event (GdkEventExpose*) switch (Config->get_shuttle_behaviour()) { case Sprung: - snprintf (buf, sizeof (buf), _("Sprung")); + snprintf (buf, sizeof (buf), "%s", _("Sprung")); break; case Wheel: - snprintf (buf, sizeof (buf), _("Wheel")); + snprintf (buf, sizeof (buf), "%s", _("Wheel")); break; } diff --git a/gtk2_ardour/startup.cc b/gtk2_ardour/startup.cc index 0a07e4ec98..220f406287 100644 --- a/gtk2_ardour/startup.cc +++ b/gtk2_ardour/startup.cc @@ -511,7 +511,7 @@ greater control in monitoring without affecting the mix.")); use_monitor_section_button.signal_toggled().connect (sigc::mem_fun (*this, &ArdourStartup::config_changed)); no_monitor_section_button.signal_toggled().connect (sigc::mem_fun (*this, &ArdourStartup::config_changed)); - monitor_section_label.set_markup(_("You can change this preference at any time, via the Preferences dialog). You can also add or remove the monitor section to/from any session.\n\n\ + monitor_section_label.set_markup(_("You can change this preference at any time via the Preferences dialog.\nYou can also add or remove the monitor section to/from any session.\n\n\ If you do not understand what this is about, just accept the default.")); monitor_section_label.set_alignment (0.0, 0.0); diff --git a/gtk2_ardour/stereo_panner.cc b/gtk2_ardour/stereo_panner.cc index bceb009b75..730ef58910 100644 --- a/gtk2_ardour/stereo_panner.cc +++ b/gtk2_ardour/stereo_panner.cc @@ -111,7 +111,7 @@ StereoPanner::set_drag_data () } bool -StereoPanner::on_expose_event (GdkEventExpose* ev) +StereoPanner::on_expose_event (GdkEventExpose*) { Glib::RefPtr win (get_window()); Glib::RefPtr gc (get_style()->get_base_gc (get_state())); diff --git a/gtk2_ardour/tempo_dialog.cc b/gtk2_ardour/tempo_dialog.cc index d3c4ef17b8..3557727394 100644 --- a/gtk2_ardour/tempo_dialog.cc +++ b/gtk2_ardour/tempo_dialog.cc @@ -36,7 +36,7 @@ using namespace Gtkmm2ext; using namespace ARDOUR; using namespace PBD; -TempoDialog::TempoDialog (TempoMap& map, framepos_t frame, const string & action) +TempoDialog::TempoDialog (TempoMap& map, framepos_t frame, const string&) : ArdourDialog (_("New Tempo")) , bpm_adjustment (60.0, 1.0, 999.9, 0.1, 1.0) , bpm_spinner (bpm_adjustment) @@ -51,7 +51,7 @@ TempoDialog::TempoDialog (TempoMap& map, framepos_t frame, const string & action init (when, tempo.beats_per_minute(), tempo.note_type(), true); } -TempoDialog::TempoDialog (TempoSection& section, const string & action) +TempoDialog::TempoDialog (TempoSection& section, const string&) : ArdourDialog ("Edit Tempo") , bpm_adjustment (60.0, 1.0, 999.9, 0.1, 1.0) , bpm_spinner (bpm_adjustment) @@ -251,7 +251,7 @@ TempoDialog::pulse_change () } -MeterDialog::MeterDialog (TempoMap& map, framepos_t frame, const string & action) +MeterDialog::MeterDialog (TempoMap& map, framepos_t frame, const string&) : ArdourDialog ("New Meter") { Timecode::BBT_Time when; @@ -262,7 +262,7 @@ MeterDialog::MeterDialog (TempoMap& map, framepos_t frame, const string & action init (when, meter.divisions_per_bar(), meter.note_divisor(), true); } -MeterDialog::MeterDialog (MeterSection& section, const string & action) +MeterDialog::MeterDialog (MeterSection& section, const string&) : ArdourDialog ("Edit Meter") { init (section.start(), section.divisions_per_bar(), section.note_divisor(), section.movable()); diff --git a/gtk2_ardour/time_axis_view.h b/gtk2_ardour/time_axis_view.h index 03c50e7ccb..ba69b3b154 100644 --- a/gtk2_ardour/time_axis_view.h +++ b/gtk2_ardour/time_axis_view.h @@ -160,9 +160,6 @@ class TimeAxisView : public virtual AxisView virtual void show_timestretch (framepos_t start, framepos_t end); virtual void hide_timestretch (); - virtual void hide_dependent_views (TimeAxisViewItem&) {} - virtual void reveal_dependent_views (TimeAxisViewItem&) {} - /* editing operations */ virtual void cut_copy_clear (Selection&, Editing::CutCopyOp) {} diff --git a/gtk2_ardour/time_axis_view_item.cc b/gtk2_ardour/time_axis_view_item.cc index c0a90f7111..0f184c1da7 100644 --- a/gtk2_ardour/time_axis_view_item.cc +++ b/gtk2_ardour/time_axis_view_item.cc @@ -52,7 +52,8 @@ using namespace Gtkmm2ext; Pango::FontDescription TimeAxisViewItem::NAME_FONT; const double TimeAxisViewItem::NAME_X_OFFSET = 15.0; -const double TimeAxisViewItem::GRAB_HANDLE_LENGTH = 6; +const double TimeAxisViewItem::GRAB_HANDLE_TOP = 6; +const double TimeAxisViewItem::GRAB_HANDLE_WIDTH = 5; int TimeAxisViewItem::NAME_HEIGHT; double TimeAxisViewItem::NAME_Y_OFFSET; @@ -102,6 +103,7 @@ TimeAxisViewItem::TimeAxisViewItem( , _height (1.0) , _recregion (recording) , _automation (automation) + , _dragging (false) { group = new ArdourCanvas::Group (parent); @@ -115,6 +117,7 @@ TimeAxisViewItem::TimeAxisViewItem (const TimeAxisViewItem& other) , trackview (other.trackview) , _recregion (other._recregion) , _automation (other._automation) + , _dragging (other._dragging) { Gdk::Color c; @@ -131,10 +134,8 @@ TimeAxisViewItem::TimeAxisViewItem (const TimeAxisViewItem& other) _selected = other._selected; - init ( - other.item_name, other.samples_per_unit, c, other.frame_position, - other.item_duration, other.visibility, other.wide_enough_for_name, other.high_enough_for_name - ); + init (other.item_name, other.samples_per_unit, c, other.frame_position, + other.item_duration, other.visibility, other.wide_enough_for_name, other.high_enough_for_name); } void @@ -215,9 +216,12 @@ TimeAxisViewItem::init ( /* create our grab handles used for trimming/duration etc */ if (!_recregion && !_automation) { - frame_handle_start = new ArdourCanvas::SimpleRect (*group, 0.0, TimeAxisViewItem::GRAB_HANDLE_LENGTH, 5.0, trackview.current_height()); + double top = TimeAxisViewItem::GRAB_HANDLE_TOP; + double width = TimeAxisViewItem::GRAB_HANDLE_WIDTH; + + frame_handle_start = new ArdourCanvas::SimpleRect (*group, 0.0, top, width, trackview.current_height()); frame_handle_start->property_outline_what() = 0x0; - frame_handle_end = new ArdourCanvas::SimpleRect (*group, 0.0, TimeAxisViewItem::GRAB_HANDLE_LENGTH, 5.0, trackview.current_height()); + frame_handle_end = new ArdourCanvas::SimpleRect (*group, 0.0, top, width, trackview.current_height()); frame_handle_end->property_outline_what() = 0x0; } else { frame_handle_start = frame_handle_end = 0; @@ -807,14 +811,6 @@ TimeAxisViewItem::set_samples_per_unit (double spu) void TimeAxisViewItem::reset_width_dependent_items (double pixel_width) { - if (pixel_width < GRAB_HANDLE_LENGTH * 2) { - - if (frame_handle_start) { - frame_handle_start->hide(); - frame_handle_end->hide(); - } - - } if (pixel_width < 2.0) { @@ -862,14 +858,20 @@ TimeAxisViewItem::reset_width_dependent_items (double pixel_width) } if (frame_handle_start) { - if (pixel_width < (2*TimeAxisViewItem::GRAB_HANDLE_LENGTH)) { + if (pixel_width < (3 * TimeAxisViewItem::GRAB_HANDLE_WIDTH)) { + /* + * there's less than GRAB_HANDLE_WIDTH of the region between + * the right-hand end of frame_handle_start and the left-hand + * end of frame_handle_end, so disable the handles + */ frame_handle_start->hide(); frame_handle_end->hide(); + } else { + frame_handle_start->show(); + frame_handle_end->property_x1() = pixel_width - (TimeAxisViewItem::GRAB_HANDLE_WIDTH); + frame_handle_end->property_x2() = pixel_width; + frame_handle_end->show(); } - frame_handle_start->show(); - frame_handle_end->property_x1() = pixel_width - (TimeAxisViewItem::GRAB_HANDLE_LENGTH); - frame_handle_end->show(); - frame_handle_end->property_x2() = pixel_width; } wide_enough_for_name = true; diff --git a/gtk2_ardour/time_axis_view_item.h b/gtk2_ardour/time_axis_view_item.h index 6b4bc09ec1..3985b8ffd8 100644 --- a/gtk2_ardour/time_axis_view_item.h +++ b/gtk2_ardour/time_axis_view_item.h @@ -78,6 +78,10 @@ class TimeAxisViewItem : public Selectable, public PBD::ScopedConnectionList double get_samples_per_unit(); + virtual void drag_start() { _dragging = true; } + virtual void drag_end() { _dragging = false; } + bool dragging() const { return _dragging; } + virtual void raise () { return; } virtual void raise_to_top () { return; } virtual void lower () { return; } @@ -93,7 +97,8 @@ class TimeAxisViewItem : public Selectable, public PBD::ScopedConnectionList static Pango::FontDescription NAME_FONT; static void set_constant_heights (); static const double NAME_X_OFFSET; - static const double GRAB_HANDLE_LENGTH; + static const double GRAB_HANDLE_TOP; + static const double GRAB_HANDLE_WIDTH; /* these are not constant, but vary with the pixel size of the font used to display the item name. @@ -239,6 +244,7 @@ class TimeAxisViewItem : public Selectable, public PBD::ScopedConnectionList Visibility visibility; bool _recregion; bool _automation; ///< true if this is an automation region view + bool _dragging; private: diff --git a/libs/ardour/ardour/audio_unit.h b/libs/ardour/ardour/audio_unit.h index 14665da301..36e82da802 100644 --- a/libs/ardour/ardour/audio_unit.h +++ b/libs/ardour/ardour/audio_unit.h @@ -104,7 +104,6 @@ class AUPlugin : public ARDOUR::Plugin bool has_editor () const; - bool reconfigurable_io() const { return true; } bool can_support_io_configuration (const ChanCount& in, ChanCount& out) const; ChanCount output_streams() const; ChanCount input_streams() const; @@ -241,6 +240,8 @@ class AUPluginInfo : public PluginInfo { AUPluginCachedInfo cache; + bool reconfigurable_io() const { return true; } + static PluginInfoList* discover (); static void get_names (CAComponentDescription&, std::string& name, std::string& maker); static std::string stringify_descriptor (const CAComponentDescription&); diff --git a/libs/ardour/ardour/audioregion.h b/libs/ardour/ardour/audioregion.h index 7236cd69a9..c03f32cb63 100644 --- a/libs/ardour/ardour/audioregion.h +++ b/libs/ardour/ardour/audioregion.h @@ -89,8 +89,15 @@ class AudioRegion : public Region bool fade_in_active () const { return _fade_in_active; } bool fade_out_active () const { return _fade_out_active; } + bool fade_in_is_xfade() const { return _fade_in_is_xfade; } + void set_fade_in_is_xfade (bool yn); + bool fade_out_is_xfade() const { return _fade_out_is_xfade; } + void set_fade_out_is_xfade (bool yn); + boost::shared_ptr fade_in() { return _fade_in; } + boost::shared_ptr inverse_fade_in() { return _inverse_fade_in; } boost::shared_ptr fade_out() { return _fade_out; } + boost::shared_ptr inverse_fade_out() { return _inverse_fade_out; } boost::shared_ptr envelope() { return _envelope; } Evoral::Range body_range () const; @@ -101,13 +108,6 @@ class AudioRegion : public Region /* Readable interface */ - enum ReadOps { - ReadOpsNone = 0x0, - ReadOpsOwnAutomation = 0x1, - ReadOpsOwnScaling = 0x2, - ReadOpsFades = 0x4 - }; - virtual framecnt_t read (Sample*, framepos_t pos, framecnt_t cnt, int channel) const; virtual framecnt_t readable_length() const { return length(); } @@ -142,6 +142,8 @@ class AudioRegion : public Region void set_default_fade_in (); void set_default_fade_out (); + + framecnt_t verify_xfade_bounds (framecnt_t, bool start); void set_envelope_active (bool yn); void set_default_envelope (); @@ -178,7 +180,6 @@ class AudioRegion : public Region private: friend class RegionFactory; - friend class Crossfade; AudioRegion (boost::shared_ptr); AudioRegion (const SourceList &); @@ -208,11 +209,7 @@ class AudioRegion : public Region void recompute_gain_at_end (); void recompute_gain_at_start (); - framecnt_t _read_at (const SourceList&, framecnt_t limit, - Sample *buf, Sample *mixdown_buffer, float *gain_buffer, - framepos_t position, framecnt_t cnt, - uint32_t chan_n = 0, - ReadOps readops = ReadOps (~0)) const; + framecnt_t read_from_sources (SourceList const &, framecnt_t, Sample *, framepos_t, framecnt_t, uint32_t) const; void recompute_at_start (); void recompute_at_end (); @@ -228,10 +225,19 @@ class AudioRegion : public Region Automatable _automatable; boost::shared_ptr _fade_in; + boost::shared_ptr _inverse_fade_in; boost::shared_ptr _fade_out; + boost::shared_ptr _inverse_fade_out; boost::shared_ptr _envelope; uint32_t _fade_in_suspended; uint32_t _fade_out_suspended; + /* This is not a Property because its not subject to user control, + or undo/redo. XXX this may prove to be a mistake. + */ + bool _fade_in_is_xfade; + bool _fade_out_is_xfade; + + boost::shared_ptr get_single_other_xfade_region (bool start) const; protected: /* default constructor for derived (compound) types */ diff --git a/libs/ardour/ardour/delivery.h b/libs/ardour/ardour/delivery.h index 4ee171c033..314b223538 100644 --- a/libs/ardour/ardour/delivery.h +++ b/libs/ardour/ardour/delivery.h @@ -89,7 +89,7 @@ public: /* Panning */ static int disable_panners (void); - static int reset_panners (void); + static void reset_panners (); boost::shared_ptr panner_shell() const { return _panshell; } boost::shared_ptr panner() const; @@ -115,9 +115,9 @@ public: boost::shared_ptr _mute_master; static bool panners_legal; - static PBD::Signal0 PannersLegal; + static PBD::Signal0 PannersLegal; - int panners_became_legal (); + void panners_became_legal (); PBD::ScopedConnection panner_legal_c; void output_changed (IOChange, void*); diff --git a/libs/ardour/ardour/io.h b/libs/ardour/ardour/io.h index 2451611b1b..1ba27a7449 100644 --- a/libs/ardour/ardour/io.h +++ b/libs/ardour/ardour/io.h @@ -152,7 +152,7 @@ class IO : public SessionObject, public Latent typedef bool result_type; template - bool operator() (Iter first, Iter last) const { + result_type operator() (Iter first, Iter last) const { bool r = false; while (first != last) { if (*first) { diff --git a/libs/ardour/ardour/playlist.h b/libs/ardour/ardour/playlist.h index be848d381b..a853c501f0 100644 --- a/libs/ardour/ardour/playlist.h +++ b/libs/ardour/ardour/playlist.h @@ -39,6 +39,7 @@ #include "pbd/stateful.h" #include "pbd/statefuldestructible.h" #include "pbd/sequence_property.h" +#include "pbd/stacktrace.h" #include "evoral/types.hpp" @@ -234,7 +235,10 @@ public: protected: struct RegionLock { RegionLock (Playlist *pl, bool do_block_notify = true) : playlist (pl), block_notify (do_block_notify) { - playlist->region_lock.lock(); + if (!playlist->region_lock.trylock()) { + std::cerr << "Lock for playlist " << pl->name() << " already held\n"; + PBD::stacktrace (std::cerr, 10); + } if (block_notify) { playlist->delay_notifications(); } @@ -249,8 +253,6 @@ public: bool block_notify; }; - friend class RegionLock; - RegionListProperty regions; /* the current list of regions in the playlist */ std::set > all_regions; /* all regions ever added to this playlist */ PBD::ScopedConnectionList region_state_changed_connections; @@ -258,11 +260,6 @@ public: int _sort_id; mutable gint block_notifications; mutable gint ignore_state_changes; -#ifdef HAVE_GLIB_THREADS_RECMUTEX - mutable Glib::Threads::RecMutex region_lock; -#else - mutable Glib::RecMutex region_lock; -#endif std::set > pending_adds; std::set > pending_removes; RegionList pending_bounds; @@ -309,6 +306,8 @@ public: void _set_sort_id (); + boost::shared_ptr regions_touched_locked (framepos_t start, framepos_t end); + void notify_region_removed (boost::shared_ptr); void notify_region_added (boost::shared_ptr); void notify_layering_changed (); @@ -373,9 +372,12 @@ public: */ virtual void pre_uncombine (std::vector >&, boost::shared_ptr) {} -private: + private: + friend class RegionLock; + mutable Glib::Mutex region_lock; - void setup_layering_indices (RegionList const &) const; + private: + void setup_layering_indices (RegionList const &); void coalesce_and_check_crossfades (std::list >); boost::shared_ptr find_regions_at (framepos_t); }; diff --git a/libs/ardour/ardour/plugin.h b/libs/ardour/ardour/plugin.h index 069a579ff7..cfdf0e8121 100644 --- a/libs/ardour/ardour/plugin.h +++ b/libs/ardour/ardour/plugin.h @@ -67,6 +67,18 @@ class PluginInfo { virtual PluginPtr load (Session& session) = 0; virtual bool is_instrument() const; + /* NOTE: this block of virtual methods looks like the interface + to a Processor, but Plugin does not inherit from Processor. + It is therefore not required that these precisely match + the interface, but it is likely that they will evolve together. + */ + + /* this returns true if the plugin can change its inputs or outputs on demand. + LADSPA, LV2 and VST plugins cannot do this. AudioUnits can. + */ + + virtual bool reconfigurable_io() const { return false; } + protected: friend class PluginManager; uint32_t index; @@ -118,8 +130,8 @@ class Plugin : public PBD::StatefulDestructible, public Latent virtual uint32_t parameter_count () const = 0; virtual float default_value (uint32_t port) = 0; virtual float get_parameter(uint32_t which) const = 0; - virtual std::string get_docs() const { return ""; } - virtual std::string get_parameter_docs(uint32_t which) const { return ""; } + virtual std::string get_docs () const { return ""; } + virtual std::string get_parameter_docs (uint32_t /*which*/) const { return ""; } virtual int get_parameter_descriptor (uint32_t which, ParameterDescriptor&) const = 0; virtual uint32_t nth_parameter (uint32_t which, bool& ok) const = 0; @@ -203,19 +215,6 @@ class Plugin : public PBD::StatefulDestructible, public Latent /** Emitted when any parameter changes */ PBD::Signal2 ParameterChanged; - /* NOTE: this block of virtual methods looks like the interface - to a Processor, but Plugin does not inherit from Processor. - It is therefore not required that these precisely match - the interface, but it is likely that they will evolve together. - */ - - /* this returns true if the plugin can change its inputs or outputs on demand. - LADSPA, LV2 and VST plugins cannot do this. AudioUnits can. - */ - - virtual bool reconfigurable_io() const { return false; } - - /* this is only called if reconfigurable_io() returns true */ virtual bool configure_io (ChanCount /*in*/, ChanCount /*out*/) { return true; } /* specific types of plugins can overload this. As of September 2008, only diff --git a/libs/ardour/ardour/quantize.h b/libs/ardour/ardour/quantize.h index e1ca2b2395..e56927767e 100644 --- a/libs/ardour/ardour/quantize.h +++ b/libs/ardour/ardour/quantize.h @@ -30,8 +30,7 @@ class Session; class Quantize : public MidiOperator { public: - Quantize (ARDOUR::Session&, QuantizeType type, - bool snap_start, bool snap_end, + Quantize (ARDOUR::Session&, bool snap_start, bool snap_end, double start_grid, double end_grid, float strength, float swing, float threshold); ~Quantize (); diff --git a/libs/ardour/ardour/region.h b/libs/ardour/ardour/region.h index 788a8d90c9..1d7894fac5 100644 --- a/libs/ardour/ardour/region.h +++ b/libs/ardour/ardour/region.h @@ -141,6 +141,15 @@ class Region framepos_t first_frame () const { return _position; } framepos_t last_frame () const { return _position + _length - 1; } + /** Return the earliest possible value of _position given the + * value of _start within the region's sources + */ + framepos_t earliest_possible_position () const; + /** Return the last possible value of _last_frame given the + * value of _startin the regions's sources + */ + framepos_t latest_possible_frame () const; + Evoral::Range last_range () const { return Evoral::Range (_last_position, _last_position + _last_length - 1); } diff --git a/libs/ardour/ardour/session_configuration_vars.h b/libs/ardour/ardour/session_configuration_vars.h index a6147f9140..489105e5ac 100644 --- a/libs/ardour/ardour/session_configuration_vars.h +++ b/libs/ardour/ardour/session_configuration_vars.h @@ -26,10 +26,9 @@ *****************************************************/ CONFIG_VARIABLE (CrossfadeModel, xfade_model, "xfade-model", FullCrossfade) +CONFIG_VARIABLE (CrossfadeChoice, xfade_choice, "xfade-choice", ConstantPowerMinus3dB) CONFIG_VARIABLE (bool, auto_xfade, "auto-xfade", true) CONFIG_VARIABLE (float, short_xfade_seconds, "short-xfade-seconds", 0.015) -CONFIG_VARIABLE (bool, xfades_active, "xfades-active", true) -CONFIG_VARIABLE (bool, xfades_visible, "xfades-visible", true) CONFIG_VARIABLE (uint32_t, destructive_xfade_msecs, "destructive-xfade-msecs", 2) CONFIG_VARIABLE (bool, use_region_fades, "use-region-fades", true) CONFIG_VARIABLE (bool, show_region_fades, "show-region-fades", true) diff --git a/libs/ardour/ardour/types.h b/libs/ardour/ardour/types.h index b3c1666dbb..6ac9ebfe70 100644 --- a/libs/ardour/ardour/types.h +++ b/libs/ardour/ardour/types.h @@ -406,6 +406,12 @@ namespace ARDOUR { ShortCrossfade }; + enum CrossfadeChoice { + RegionFades, + ConstantPowerMinus3dB, + ConstantPowerMinus6dB, + }; + enum ListenPosition { AfterFaderListen, PreFaderListen @@ -504,12 +510,6 @@ namespace ARDOUR { Rectified }; - enum QuantizeType { - Plain, - Legato, - Groove - }; - struct CleanupReport { std::vector paths; size_t space; @@ -557,8 +557,8 @@ namespace ARDOUR { FadeLinear, FadeFast, FadeSlow, - FadeLogA, - FadeLogB + FadeConstantPower, + FadeSymmetric, }; } // namespace ARDOUR @@ -579,6 +579,7 @@ std::istream& operator>>(std::istream& o, ARDOUR::RemoteModel& sf); std::istream& operator>>(std::istream& o, ARDOUR::ListenPosition& sf); std::istream& operator>>(std::istream& o, ARDOUR::InsertMergePolicy& sf); std::istream& operator>>(std::istream& o, ARDOUR::CrossfadeModel& sf); +std::istream& operator>>(std::istream& o, ARDOUR::CrossfadeChoice& sf); std::istream& operator>>(std::istream& o, ARDOUR::SyncSource& sf); std::istream& operator>>(std::istream& o, ARDOUR::ShuttleBehaviour& sf); std::istream& operator>>(std::istream& o, ARDOUR::ShuttleUnits& sf); @@ -599,6 +600,7 @@ std::ostream& operator<<(std::ostream& o, const ARDOUR::RemoteModel& sf); std::ostream& operator<<(std::ostream& o, const ARDOUR::ListenPosition& sf); std::ostream& operator<<(std::ostream& o, const ARDOUR::InsertMergePolicy& sf); std::ostream& operator<<(std::ostream& o, const ARDOUR::CrossfadeModel& sf); +std::ostream& operator<<(std::ostream& o, const ARDOUR::CrossfadeChoice& sf); std::ostream& operator<<(std::ostream& o, const ARDOUR::SyncSource& sf); std::ostream& operator<<(std::ostream& o, const ARDOUR::ShuttleBehaviour& sf); std::ostream& operator<<(std::ostream& o, const ARDOUR::ShuttleUnits& sf); diff --git a/libs/ardour/audio_playlist.cc b/libs/ardour/audio_playlist.cc index ebebc62c33..9b433d160b 100644 --- a/libs/ardour/audio_playlist.cc +++ b/libs/ardour/audio_playlist.cc @@ -186,16 +186,12 @@ AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, fr its OK to block (for short intervals). */ -#ifdef HAVE_GLIB_THREADS_RECMUTEX - Glib::Threads::RecMutex::Lock lm (region_lock); -#else - Glib::RecMutex::Lock rm (region_lock); -#endif + Playlist::RegionLock rl (this, false); /* Find all the regions that are involved in the bit we are reading, and sort them by descending layer and ascending position. */ - boost::shared_ptr all = regions_touched (start, start + cnt - 1); + boost::shared_ptr all = regions_touched_locked (start, start + cnt - 1); all->sort (ReadSorter ()); /* This will be a list of the bits of our read range that we have @@ -290,7 +286,6 @@ AudioPlaylist::check_crossfades (Evoral::Range range) continue; } - boost::shared_ptr top; boost::shared_ptr bottom; @@ -317,8 +312,9 @@ AudioPlaylist::check_crossfades (Evoral::Range range) */ if (done_start.find (top) == done_start.end() && done_end.find (bottom) == done_end.end ()) { - /* Top's fade-in will cause an implicit fade-out of bottom */ + /* Top's fade-in will cause an implicit fade-out of bottom */ + framecnt_t len = 0; switch (_session.config.get_xfade_model()) { case FullCrossfade: @@ -328,11 +324,27 @@ AudioPlaylist::check_crossfades (Evoral::Range range) len = _session.config.get_short_xfade_seconds() * _session.frame_rate(); break; } - - top->set_fade_in_length (len); + top->set_fade_in_active (true); + top->set_fade_in_is_xfade (true); + + /* XXX may 2012: -3dB and -6dB curves + * are the same right now + */ + + switch (_session.config.get_xfade_choice ()) { + case ConstantPowerMinus3dB: + top->set_fade_in (FadeConstantPower, len); + break; + case ConstantPowerMinus6dB: + top->set_fade_in (FadeConstantPower, len); + break; + case RegionFades: + top->set_fade_in_length (len); + break; + } + done_start.insert (top); - done_end.insert (bottom); } } else if (c == Evoral::OverlapEnd) { @@ -349,17 +361,29 @@ AudioPlaylist::check_crossfades (Evoral::Range range) framecnt_t len = 0; switch (_session.config.get_xfade_model()) { case FullCrossfade: - len = bottom->last_frame () - top->first_frame (); + len = top->last_frame () - bottom->first_frame (); break; case ShortCrossfade: len = _session.config.get_short_xfade_seconds() * _session.frame_rate(); break; } - - top->set_fade_out_length (len); + top->set_fade_out_active (true); + top->set_fade_out_is_xfade (true); + + switch (_session.config.get_xfade_choice ()) { + case ConstantPowerMinus3dB: + top->set_fade_out (FadeConstantPower, len); + break; + case ConstantPowerMinus6dB: + top->set_fade_out (FadeConstantPower, len); + break; + case RegionFades: + top->set_fade_out_length (len); + break; + } + done_end.insert (top); - done_start.insert (bottom); } } } diff --git a/libs/ardour/audioregion.cc b/libs/ardour/audioregion.cc index e4a7504ec1..739987dc56 100644 --- a/libs/ardour/audioregion.cc +++ b/libs/ardour/audioregion.cc @@ -65,6 +65,90 @@ namespace ARDOUR { } } +static const double VERY_SMALL_SIGNAL = 0.0000001; //-140dB + +/* Curve manipulations */ + +static void +reverse_curve (boost::shared_ptr dst, boost::shared_ptr src) +{ + size_t len = src->back()->when; + + for (Evoral::ControlList::const_iterator it = src->begin(); it!=src->end(); it++) { + dst->add ( len - (*it)->when, (*it)->value ); + } +} + +static void +generate_inverse_power_curve (boost::shared_ptr dst, boost::shared_ptr src) +{ + //calc inverse curve using sum of squares + for (Evoral::ControlList::const_iterator it = src->begin(); it!=src->end(); ++it ) { + float value = (*it)->value; + value = 1 - powf(value,2); + value = sqrtf(value); + dst->fast_simple_add ( (*it)->when, value ); + } +} + +/* +static void +generate_inverse_coefficient_curve (boost::shared_ptr dst, boost::shared_ptr src) +{ + //calc inverse gain coefficient curve + for (Evoral::ControlList::const_iterator it = src->begin(); it!=src->end(); ++it ) { + float value = 1.0 - (*it)->value; + dst->fast_simple_add ( (*it)->when, value ); + } +} +*/ + +static void +generate_db_fade (boost::shared_ptr dst, double len, int num_steps, float dB_drop) +{ + dst->fast_simple_add (0, 1); + + //generate a fade-out curve by successively applying a gain drop + float fade_speed = dB_to_coefficient(dB_drop / (float) num_steps); + for (int i = 1; i < (num_steps-1); i++) { + float coeff = 1.0; + for (int j = 0; j < i; j++) { + coeff *= fade_speed; + } + dst->fast_simple_add (len*(double)i/(double)num_steps, coeff); + } + + dst->fast_simple_add (len, VERY_SMALL_SIGNAL); +} + +static void +merge_curves (boost::shared_ptr dst, + boost::shared_ptr curve1, + boost::shared_ptr curve2) +{ + Evoral::ControlList::EventList::size_type size = curve1->size(); + + //curve lengths must match for now + if (size != curve2->size()) { + return; + } + + Evoral::ControlList::const_iterator c1 = curve1->begin(); + int count = 0; + for (Evoral::ControlList::const_iterator c2 = curve2->begin(); c2!=curve2->end(); c2++ ) { + float v1 = accurate_coefficient_to_dB((*c1)->value); + float v2 = accurate_coefficient_to_dB((*c2)->value); + + double interp = v1 * ( 1.0-( (double)count / (double)size) ); + interp += v2 * ( (double)count / (double)size ); + + interp = dB_to_coefficient(interp); + dst->add ( (*c1)->when, interp ); + c1++; + count++; + } +} + void AudioRegion::make_property_quarks () { @@ -133,10 +217,14 @@ AudioRegion::AudioRegion (Session& s, framepos_t start, framecnt_t len, std::str , AUDIOREGION_STATE_DEFAULT , _automatable (s) , _fade_in (new AutomationList(Evoral::Parameter(FadeInAutomation))) + , _inverse_fade_in (new AutomationList(Evoral::Parameter(FadeInAutomation))) , _fade_out (new AutomationList(Evoral::Parameter(FadeOutAutomation))) + , _inverse_fade_out (new AutomationList(Evoral::Parameter(FadeOutAutomation))) , _envelope (new AutomationList(Evoral::Parameter(EnvelopeAutomation))) , _fade_in_suspended (0) , _fade_out_suspended (0) + , _fade_in_is_xfade (false) + , _fade_out_is_xfade (false) { init (); assert (_sources.size() == _master_sources.size()); @@ -148,10 +236,14 @@ AudioRegion::AudioRegion (const SourceList& srcs) , AUDIOREGION_STATE_DEFAULT , _automatable(srcs[0]->session()) , _fade_in (new AutomationList(Evoral::Parameter(FadeInAutomation))) + , _inverse_fade_in (new AutomationList(Evoral::Parameter(FadeInAutomation))) , _fade_out (new AutomationList(Evoral::Parameter(FadeOutAutomation))) + , _inverse_fade_out (new AutomationList(Evoral::Parameter(FadeOutAutomation))) , _envelope (new AutomationList(Evoral::Parameter(EnvelopeAutomation))) , _fade_in_suspended (0) , _fade_out_suspended (0) + , _fade_in_is_xfade (false) + , _fade_out_is_xfade (false) { init (); assert (_sources.size() == _master_sources.size()); @@ -162,13 +254,17 @@ AudioRegion::AudioRegion (boost::shared_ptr other) , AUDIOREGION_COPY_STATE (other) , _automatable (other->session()) , _fade_in (new AutomationList (*other->_fade_in)) + , _inverse_fade_in (new AutomationList(*other->_inverse_fade_in)) , _fade_out (new AutomationList (*other->_fade_out)) + , _inverse_fade_out (new AutomationList (*other->_inverse_fade_out)) /* As far as I can see, the _envelope's times are relative to region position, and have nothing to do with sources (and hence _start). So when we copy the envelope, we just use the supplied offset. */ , _envelope (new AutomationList (*other->_envelope, 0, other->_length)) , _fade_in_suspended (0) , _fade_out_suspended (0) + , _fade_in_is_xfade (false) + , _fade_out_is_xfade (false) { /* don't use init here, because we got fade in/out from the other region */ @@ -186,13 +282,17 @@ AudioRegion::AudioRegion (boost::shared_ptr other, framecnt_t , AUDIOREGION_COPY_STATE (other) , _automatable (other->session()) , _fade_in (new AutomationList (*other->_fade_in)) + , _inverse_fade_in (new AutomationList(*other->_inverse_fade_in)) , _fade_out (new AutomationList (*other->_fade_out)) + , _inverse_fade_out (new AutomationList (*other->_inverse_fade_out)) /* As far as I can see, the _envelope's times are relative to region position, and have nothing to do with sources (and hence _start). So when we copy the envelope, we just use the supplied offset. */ , _envelope (new AutomationList (*other->_envelope, offset, other->_length)) , _fade_in_suspended (0) , _fade_out_suspended (0) + , _fade_in_is_xfade (false) + , _fade_out_is_xfade (false) { /* don't use init here, because we got fade in/out from the other region */ @@ -210,10 +310,14 @@ AudioRegion::AudioRegion (boost::shared_ptr other, const Sour , AUDIOREGION_COPY_STATE (other) , _automatable (other->session()) , _fade_in (new AutomationList (*other->_fade_in)) + , _inverse_fade_in (new AutomationList(*other->_inverse_fade_in)) , _fade_out (new AutomationList (*other->_fade_out)) + , _inverse_fade_out (new AutomationList (*other->_inverse_fade_out)) , _envelope (new AutomationList (*other->_envelope)) , _fade_in_suspended (0) , _fade_out_suspended (0) + , _fade_in_is_xfade (false) + , _fade_out_is_xfade (false) { /* make-a-sort-of-copy-with-different-sources constructor (used by audio filter) */ @@ -231,10 +335,14 @@ AudioRegion::AudioRegion (SourceList& srcs) , AUDIOREGION_STATE_DEFAULT , _automatable(srcs[0]->session()) , _fade_in (new AutomationList(Evoral::Parameter(FadeInAutomation))) + , _inverse_fade_in (new AutomationList(Evoral::Parameter(FadeInAutomation))) , _fade_out (new AutomationList(Evoral::Parameter(FadeOutAutomation))) + , _inverse_fade_out (new AutomationList(Evoral::Parameter(FadeOutAutomation))) , _envelope (new AutomationList(Evoral::Parameter(EnvelopeAutomation))) , _fade_in_suspended (0) , _fade_out_suspended (0) + , _fade_in_is_xfade (false) + , _fade_out_is_xfade (false) { init (); @@ -341,45 +449,43 @@ AudioRegion::read_peaks (PeakData *buf, framecnt_t npeaks, framecnt_t offset, fr } } +/** @param buf Buffer to write data to (existing data will be overwritten). + * @param pos Position to read from as an offset from the region position. + * @param cnt Number of frames to read. + * @param channel Channel to read from. + */ framecnt_t -AudioRegion::read (Sample* buf, framepos_t timeline_position, framecnt_t cnt, int channel) const +AudioRegion::read (Sample* buf, framepos_t pos, framecnt_t cnt, int channel) const { /* raw read, no fades, no gain, nada */ - /* XXX: xfade: passes no mixbuf... */ - return _read_at (_sources, _length, buf, 0, 0, _position + timeline_position, cnt, channel, ReadOps (0)); + return read_from_sources (_sources, _length, buf, _position + pos, cnt, channel); } framecnt_t -AudioRegion::read_at (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, - framepos_t position, framecnt_t cnt, uint32_t chan_n) const -{ - /* regular diskstream/butler read complete with fades etc */ - return _read_at (_sources, _length, buf, mixdown_buffer, gain_buffer, - position, cnt, chan_n, ReadOps (~0)); -} - -framecnt_t -AudioRegion::master_read_at (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, +AudioRegion::master_read_at (Sample *buf, Sample* /*mixdown_buffer*/, float* /*gain_buffer*/, framepos_t position, framecnt_t cnt, uint32_t chan_n) const { /* do not read gain/scaling/fades and do not count this disk i/o in statistics */ assert (cnt >= 0); - - return _read_at (_master_sources, _master_sources.front()->length(_master_sources.front()->timeline_position()), - buf, mixdown_buffer, gain_buffer, position, cnt, chan_n, ReadOps (0)); + return read_from_sources ( + _master_sources, _master_sources.front()->length (_master_sources.front()->timeline_position()), + buf, position, cnt, chan_n + ); } -/** @param position Position within the session to read from. +/** @param buf Buffer to mix data into. + * @param mixdown_buffer Scratch buffer for audio data. + * @param gain_buffer Scratch buffer for gain data. + * @param position Position within the session to read from. * @param cnt Number of frames to read. + * @param chan_n Channel number to read. */ framecnt_t -AudioRegion::_read_at (const SourceList& srcs, framecnt_t limit, - Sample *buf, Sample *mixdown_buffer, float *gain_buffer, - framepos_t position, - framecnt_t cnt, - uint32_t chan_n, - ReadOps rops) const +AudioRegion::read_at (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, + framepos_t position, + framecnt_t cnt, + uint32_t chan_n) const { /* We are reading data from this region into buf (possibly via mixdown_buffer). The caller has verified that we cover the desired section. @@ -395,7 +501,7 @@ AudioRegion::_read_at (const SourceList& srcs, framecnt_t limit, return 0; } - if (muted() && rops != ReadOpsNone) { + if (muted()) { return 0; /* read nothing */ } @@ -407,11 +513,11 @@ AudioRegion::_read_at (const SourceList& srcs, framecnt_t limit, assert (position >= _position); frameoffset_t const internal_offset = position - _position; - if (internal_offset >= limit) { + if (internal_offset >= _length) { return 0; /* read nothing */ } - if ((to_read = min (cnt, limit - internal_offset)) == 0) { + if ((to_read = min (cnt, _length - internal_offset)) == 0) { return 0; /* read nothing */ } @@ -431,52 +537,49 @@ AudioRegion::_read_at (const SourceList& srcs, framecnt_t limit, framecnt_t fade_interval_start = 0; - if (rops & ReadOpsFades) { - - /* Fade in */ + /* Fade in */ + + if (_fade_in_active && _session.config.get_use_region_fades()) { - if (_fade_in_active && _session.config.get_use_region_fades()) { - - framecnt_t fade_in_length = (framecnt_t) _fade_in->back()->when; - - /* see if this read is within the fade in */ - - if (internal_offset < fade_in_length) { - fade_in_limit = min (to_read, fade_in_length - internal_offset); - } + framecnt_t fade_in_length = (framecnt_t) _fade_in->back()->when; + + /* see if this read is within the fade in */ + + if (internal_offset < fade_in_length) { + fade_in_limit = min (to_read, fade_in_length - internal_offset); } - - /* Fade out */ - - if (_fade_out_active && _session.config.get_use_region_fades()) { - - /* see if some part of this read is within the fade out */ + } + + /* Fade out */ + + if (_fade_out_active && _session.config.get_use_region_fades()) { + + /* see if some part of this read is within the fade out */ /* ................. >| REGION - limit + _length { } FADE fade_out_length ^ - limit - fade_out_length + _length - fade_out_length |--------------| ^internal_offset ^internal_offset + to_read we need the intersection of [internal_offset,internal_offset+to_read] with - [limit - fade_out_length, limit] + [_length - fade_out_length, _length] */ - fade_interval_start = max (internal_offset, limit - framecnt_t (_fade_out->back()->when)); - framecnt_t fade_interval_end = min(internal_offset + to_read, limit); - - if (fade_interval_end > fade_interval_start) { - /* (part of the) the fade out is in this buffer */ - fade_out_limit = fade_interval_end - fade_interval_start; - fade_out_offset = fade_interval_start - internal_offset; - } + fade_interval_start = max (internal_offset, _length - framecnt_t (_fade_out->back()->when)); + framecnt_t fade_interval_end = min(internal_offset + to_read, _length.val()); + + if (fade_interval_end > fade_interval_start) { + /* (part of the) the fade out is in this buffer */ + fade_out_limit = fade_interval_end - fade_interval_start; + fade_out_offset = fade_interval_start - internal_offset; } } @@ -486,10 +589,146 @@ AudioRegion::_read_at (const SourceList& srcs, framecnt_t limit, must always mix. */ + if (read_from_sources (_sources, _length, mixdown_buffer, position, to_read, chan_n) != to_read) { + return 0; + } + + /* APPLY REGULAR GAIN CURVES AND SCALING TO mixdown_buffer */ + + if (envelope_active()) { + _envelope->curve().get_vector (internal_offset, internal_offset + to_read, gain_buffer, to_read); + + if (_scale_amplitude != 1.0f) { + for (framecnt_t n = 0; n < to_read; ++n) { + mixdown_buffer[n] *= gain_buffer[n] * _scale_amplitude; + } + } else { + for (framecnt_t n = 0; n < to_read; ++n) { + mixdown_buffer[n] *= gain_buffer[n]; + } + } + } else if (_scale_amplitude != 1.0f) { + apply_gain_to_buffer (mixdown_buffer, to_read, _scale_amplitude); + } + + + /* APPLY FADES TO THE DATA IN mixdown_buffer AND MIX THE RESULTS INTO + * buf. The key things to realize here: (1) the fade being applied is + * (as of April 26th 2012) just the inverse of the fade in curve (2) + * "buf" contains data from lower regions already. So this operation + * fades out the existing material. + */ + + if (fade_in_limit != 0) { + if (_inverse_fade_in) { + + /* explicit inverse fade in curve (e.g. for constant + * power), so we have to fetch it. + */ + + _inverse_fade_in->curve().get_vector (internal_offset, internal_offset + fade_in_limit, gain_buffer, fade_in_limit); + + /* Fade the data from lower layers out */ + for (framecnt_t n = 0; n < fade_in_limit; ++n) { + buf[n] *= gain_buffer[n]; + } + + /* refill gain buffer with the fade in */ + + _fade_in->curve().get_vector (internal_offset, internal_offset + fade_in_limit, gain_buffer, fade_in_limit); + + } else { + + /* no explicit inverse fade in, so just use (1 - fade + * in) for the fade out of lower layers + */ + + _fade_in->curve().get_vector (internal_offset, internal_offset + fade_in_limit, gain_buffer, fade_in_limit); + + for (framecnt_t n = 0; n < fade_in_limit; ++n) { + buf[n] *= 1 - gain_buffer[n]; + } + } + + /* Mix our newly-read data in, with the fade */ + for (framecnt_t n = 0; n < fade_in_limit; ++n) { + buf[n] += mixdown_buffer[n] * gain_buffer[n]; + } + } + + if (fade_out_limit != 0) { + + framecnt_t const curve_offset = fade_interval_start - (_length - _fade_out->back()->when); + + if (_inverse_fade_out) { + + _inverse_fade_out->curve().get_vector (curve_offset, curve_offset + fade_out_limit, gain_buffer, fade_out_limit); + + /* Fade the data from lower levels out */ + for (framecnt_t n = 0, m = fade_out_offset; n < fade_out_limit; ++n, ++m) { + buf[m] *= gain_buffer[n]; + } + + /* fetch the actual fade out */ + + _fade_out->curve().get_vector (curve_offset, curve_offset + fade_out_limit, gain_buffer, fade_out_limit); + + } else { + + /* no explicit inverse fade out, so just use (1 - fade + * out) for the fade in of lower layers + */ + + _fade_out->curve().get_vector (curve_offset, curve_offset + fade_out_limit, gain_buffer, fade_out_limit); + + for (framecnt_t n = 0, m = fade_out_offset; n < fade_out_limit; ++n, ++m) { + buf[m] *= 1 - gain_buffer[n]; + } + } + + /* Mix our newly-read data out, with the fade */ + for (framecnt_t n = 0, m = fade_out_offset; n < fade_out_limit; ++n, ++m) { + buf[m] += mixdown_buffer[m] * gain_buffer[n]; + } + } + + + /* MIX THE REGION BODY FROM mixdown_buffer INTO buf */ + + mix_buffers_no_gain (buf + fade_in_limit, mixdown_buffer + fade_in_limit, to_read - fade_in_limit - fade_out_limit); + + return to_read; +} + +/** Read data directly from one of our sources, accounting for the situation when the track has a different channel + * count to the region. + * + * @param srcs Source list to get our source from. + * @param limit Furthest that we should read, as an offset from the region position. + * @param buf Buffer to write data into (existing contents of the buffer will be overwritten) + * @param position Position to read from, in session frames. + * @param cnt Number of frames to read. + * @param chan_n Channel to read from. + * @return Number of frames read. + */ + +framecnt_t +AudioRegion::read_from_sources (SourceList const & srcs, framecnt_t limit, Sample* buf, framepos_t position, framecnt_t cnt, uint32_t chan_n) const +{ + frameoffset_t const internal_offset = position - _position; + if (internal_offset >= limit) { + return 0; + } + + framecnt_t const to_read = min (cnt, limit - internal_offset); + if (to_read == 0) { + return 0; + } + if (chan_n < n_channels()) { boost::shared_ptr src = boost::dynamic_pointer_cast (srcs[chan_n]); - if (src->read (mixdown_buffer, _start + internal_offset, to_read) != to_read) { + if (src->read (buf, _start + internal_offset, to_read) != to_read) { return 0; /* "read nothing" */ } @@ -506,67 +745,12 @@ AudioRegion::_read_at (const SourceList& srcs, framecnt_t limit, uint32_t channel = n_channels() % chan_n; boost::shared_ptr src = boost::dynamic_pointer_cast (srcs[channel]); - if (src->read (mixdown_buffer, _start + internal_offset, to_read) != to_read) { + if (src->read (buf, _start + internal_offset, to_read) != to_read) { return 0; /* "read nothing" */ } } } - /* APPLY REGULAR GAIN CURVES AND SCALING TO mixdown_buffer */ - - if ((rops & ReadOpsOwnAutomation) && envelope_active()) { - _envelope->curve().get_vector (internal_offset, internal_offset + to_read, gain_buffer, to_read); - - if ((rops & ReadOpsOwnScaling) && _scale_amplitude != 1.0f) { - for (framecnt_t n = 0; n < to_read; ++n) { - mixdown_buffer[n] *= gain_buffer[n] * _scale_amplitude; - } - } else { - for (framecnt_t n = 0; n < to_read; ++n) { - mixdown_buffer[n] *= gain_buffer[n]; - } - } - } else if ((rops & ReadOpsOwnScaling) && _scale_amplitude != 1.0f) { - apply_gain_to_buffer (mixdown_buffer, to_read, _scale_amplitude); - } - - - /* APPLY FADES TO THE DATA IN mixdown_buffer AND MIX THE RESULTS INTO buf */ - - if (fade_in_limit != 0) { - _fade_in->curve().get_vector (internal_offset, internal_offset + fade_in_limit, gain_buffer, fade_in_limit); - - /* Fade the current data out */ - for (framecnt_t n = 0; n < fade_in_limit; ++n) { - buf[n] *= 1 - gain_buffer[n]; - } - - /* Mix our newly-read data in, with the fade */ - for (framecnt_t n = 0; n < fade_in_limit; ++n) { - buf[n] += mixdown_buffer[n] * gain_buffer[n]; - } - } - - if (fade_out_limit != 0) { - framecnt_t const curve_offset = fade_interval_start - (limit - _fade_out->back()->when); - _fade_out->curve().get_vector (curve_offset, curve_offset + fade_out_limit, gain_buffer, fade_out_limit); - - /* Fade the current data in */ - for (framecnt_t n = 0, m = fade_out_offset; n < fade_out_limit; ++n, ++m) { - buf[m] *= 1 - gain_buffer[n]; - } - - /* Mix our newly-read data in, with the fade */ - for (framecnt_t n = 0, m = fade_out_offset; n < fade_out_limit; ++n, ++m) { - buf[m] += mixdown_buffer[m] * gain_buffer[n]; - } - } - - - /* MIX THE REGION BODY FROM mixdown_buffer INTO buf */ - - mix_buffers_no_gain (buf + fade_in_limit, mixdown_buffer + fade_in_limit, to_read - fade_in_limit - fade_out_limit); - return to_read; } @@ -605,6 +789,7 @@ AudioRegion::state () } child = node.add_child (X_("FadeIn")); + child->add_property ("is-xfade", (_fade_in_is_xfade ? "yes" : "no")); if (_default_fade_in) { child->add_property ("default", "yes"); @@ -612,7 +797,13 @@ AudioRegion::state () child->add_child_nocopy (_fade_in->get_state ()); } + if (_inverse_fade_in) { + child = node.add_child (X_("InvFadeIn")); + child->add_child_nocopy (_inverse_fade_in->get_state ()); + } + child = node.add_child (X_("FadeOut")); + child->add_property ("is-xfade", (_fade_out_is_xfade ? "yes" : "no")); if (_default_fade_out) { child->add_property ("default", "yes"); @@ -620,6 +811,11 @@ AudioRegion::state () child->add_child_nocopy (_fade_out->get_state ()); } + if (_inverse_fade_out) { + child = node.add_child (X_("InvFadeOut")); + child->add_child_nocopy (_inverse_fade_out->get_state ()); + } + return node; } @@ -677,7 +873,7 @@ AudioRegion::_set_state (const XMLNode& node, int version, PropertyChange& what_ _fade_in->clear (); - if ((prop = child->property ("default")) != 0 || (prop = child->property ("steepness")) != 0) { + if (((prop = child->property ("default")) != 0 && string_is_affirmative (prop->value())) || (prop = child->property ("steepness")) != 0) { set_default_fade_in (); } else { XMLNode* grandchild = child->child ("AutomationList"); @@ -686,6 +882,12 @@ AudioRegion::_set_state (const XMLNode& node, int version, PropertyChange& what_ } } + if ((prop = child->property ("is-xfade")) != 0) { + _fade_in_is_xfade = string_is_affirmative (prop->value()); + } else { + _fade_in_is_xfade = false; + } + if ((prop = child->property ("active")) != 0) { if (string_is_affirmative (prop->value())) { set_fade_in_active (true); @@ -698,7 +900,7 @@ AudioRegion::_set_state (const XMLNode& node, int version, PropertyChange& what_ _fade_out->clear (); - if ((prop = child->property ("default")) != 0 || (prop = child->property ("steepness")) != 0) { + if (((prop = child->property ("default")) != 0 && (string_is_affirmative (prop->value()))) || (prop = child->property ("steepness")) != 0) { set_default_fade_out (); } else { XMLNode* grandchild = child->child ("AutomationList"); @@ -707,7 +909,13 @@ AudioRegion::_set_state (const XMLNode& node, int version, PropertyChange& what_ } } - if ((prop = child->property ("active")) != 0) { + if ((prop = child->property ("is-xfade")) != 0) { + _fade_out_is_xfade = string_is_affirmative (prop->value()); + } else { + _fade_out_is_xfade = false; + } + + if ((prop = child->property ("active")) != 0) { if (string_is_affirmative (prop->value())) { set_fade_out_active (true); } else { @@ -715,6 +923,16 @@ AudioRegion::_set_state (const XMLNode& node, int version, PropertyChange& what_ } } + } else if (child->name() == "InvFadeIn") { + XMLNode* grandchild = child->child ("AutomationList"); + if (grandchild) { + _inverse_fade_in->set_state (*grandchild, version); + } + } else if (child->name() == "InvFadeOut") { + XMLNode* grandchild = child->child ("AutomationList"); + if (grandchild) { + _inverse_fade_out->set_state (*grandchild, version); + } } } @@ -757,6 +975,7 @@ AudioRegion::set_fade_in (boost::shared_ptr f) _fade_in->freeze (); *_fade_in = *f; _fade_in->thaw (); + _default_fade_in = false; send_change (PropertyChange (Properties::fade_in)); } @@ -764,57 +983,64 @@ AudioRegion::set_fade_in (boost::shared_ptr f) void AudioRegion::set_fade_in (FadeShape shape, framecnt_t len) { + boost::shared_ptr c1 (new Evoral::ControlList (FadeInAutomation)); + boost::shared_ptr c2 (new Evoral::ControlList (FadeInAutomation)); + boost::shared_ptr c3 (new Evoral::ControlList (FadeInAutomation)); + _fade_in->freeze (); _fade_in->clear (); + _inverse_fade_in->clear (); switch (shape) { case FadeLinear: _fade_in->fast_simple_add (0.0, 0.0); _fade_in->fast_simple_add (len, 1.0); + reverse_curve (_inverse_fade_in, _fade_in); break; case FadeFast: - _fade_in->fast_simple_add (0, 0); - _fade_in->fast_simple_add (len * 0.389401, 0.0333333); - _fade_in->fast_simple_add (len * 0.629032, 0.0861111); - _fade_in->fast_simple_add (len * 0.829493, 0.233333); - _fade_in->fast_simple_add (len * 0.9447, 0.483333); - _fade_in->fast_simple_add (len * 0.976959, 0.697222); - _fade_in->fast_simple_add (len, 1); + generate_db_fade (_fade_in, len, 10, -60); + reverse_curve (c1, _fade_in); + _fade_in->copy_events (*c1); + generate_inverse_power_curve (_inverse_fade_in, _fade_in); break; case FadeSlow: - _fade_in->fast_simple_add (0, 0); - _fade_in->fast_simple_add (len * 0.0207373, 0.197222); - _fade_in->fast_simple_add (len * 0.0645161, 0.525); - _fade_in->fast_simple_add (len * 0.152074, 0.802778); - _fade_in->fast_simple_add (len * 0.276498, 0.919444); - _fade_in->fast_simple_add (len * 0.481567, 0.980556); - _fade_in->fast_simple_add (len * 0.767281, 1); - _fade_in->fast_simple_add (len, 1); + generate_db_fade (c1, len, 10, -1); // start off with a slow fade + generate_db_fade (c2, len, 10, -80); // end with a fast fade + merge_curves (_fade_in, c1, c2); + generate_inverse_power_curve (_inverse_fade_in, _fade_in); break; - case FadeLogA: - _fade_in->fast_simple_add (0, 0); - _fade_in->fast_simple_add (len * 0.0737327, 0.308333); - _fade_in->fast_simple_add (len * 0.246544, 0.658333); - _fade_in->fast_simple_add (len * 0.470046, 0.886111); - _fade_in->fast_simple_add (len * 0.652074, 0.972222); - _fade_in->fast_simple_add (len * 0.771889, 0.988889); - _fade_in->fast_simple_add (len, 1); + case FadeConstantPower: + for (int i = 0; i < 9; ++i) { + float dist = (float) i / 10.0f; + _fade_in->fast_simple_add (len*dist, sin (dist*M_PI/2)); + } + _fade_in->fast_simple_add (len, 1.0); + reverse_curve (_inverse_fade_in, _fade_in); break; + + case FadeSymmetric: + // starts kind of like a constant power but has a slower fadeout + // however it is NOT constant power and there will be a level drop in the middle of the crossfade + c1->fast_simple_add (0.0, 1.0); + for ( int i = 1; i < 9; i++ ) { + float dist = (float)i/10.0; + c1->fast_simple_add ((len * dist), cos(dist*M_PI/10.0)); + } + c1->fast_simple_add (len, VERY_SMALL_SIGNAL); - case FadeLogB: - _fade_in->fast_simple_add (0, 0); - _fade_in->fast_simple_add (len * 0.304147, 0.0694444); - _fade_in->fast_simple_add (len * 0.529954, 0.152778); - _fade_in->fast_simple_add (len * 0.725806, 0.333333); - _fade_in->fast_simple_add (len * 0.847926, 0.558333); - _fade_in->fast_simple_add (len * 0.919355, 0.730556); - _fade_in->fast_simple_add (len, 1); + //curve 2 is a slow fade at end + generate_db_fade (c2, len, 10, -30 ); + + merge_curves (c3, c1, c2); + reverse_curve (_fade_in, c3); + reverse_curve (_inverse_fade_in, _fade_in ); break; } + _default_fade_in = false; _fade_in->thaw (); send_change (PropertyChange (Properties::fade_in)); } @@ -825,6 +1051,7 @@ AudioRegion::set_fade_out (boost::shared_ptr f) _fade_out->freeze (); *_fade_out = *f; _fade_out->thaw (); + _default_fade_out = false; send_change (PropertyChange (Properties::fade_in)); } @@ -832,57 +1059,65 @@ AudioRegion::set_fade_out (boost::shared_ptr f) void AudioRegion::set_fade_out (FadeShape shape, framecnt_t len) { + boost::shared_ptr c1 (new Evoral::ControlList (FadeOutAutomation)); + boost::shared_ptr c2 (new Evoral::ControlList (FadeOutAutomation)); + _fade_out->freeze (); _fade_out->clear (); + _inverse_fade_out->clear (); switch (shape) { - case FadeFast: - _fade_out->fast_simple_add (len * 0, 1); - _fade_out->fast_simple_add (len * 0.023041, 0.697222); - _fade_out->fast_simple_add (len * 0.0553, 0.483333); - _fade_out->fast_simple_add (len * 0.170507, 0.233333); - _fade_out->fast_simple_add (len * 0.370968, 0.0861111); - _fade_out->fast_simple_add (len * 0.610599, 0.0333333); - _fade_out->fast_simple_add (len * 1, 0); - break; - - case FadeLogA: - _fade_out->fast_simple_add (len * 0, 1); - _fade_out->fast_simple_add (len * 0.228111, 0.988889); - _fade_out->fast_simple_add (len * 0.347926, 0.972222); - _fade_out->fast_simple_add (len * 0.529954, 0.886111); - _fade_out->fast_simple_add (len * 0.753456, 0.658333); - _fade_out->fast_simple_add (len * 0.9262673, 0.308333); - _fade_out->fast_simple_add (len * 1, 0); - break; - - case FadeSlow: - _fade_out->fast_simple_add (len * 0, 1); - _fade_out->fast_simple_add (len * 0.305556, 1); - _fade_out->fast_simple_add (len * 0.548611, 0.991736); - _fade_out->fast_simple_add (len * 0.759259, 0.931129); - _fade_out->fast_simple_add (len * 0.918981, 0.68595); - _fade_out->fast_simple_add (len * 0.976852, 0.22865); - _fade_out->fast_simple_add (len * 1, 0); - break; - - case FadeLogB: - _fade_out->fast_simple_add (len * 0, 1); - _fade_out->fast_simple_add (len * 0.080645, 0.730556); - _fade_out->fast_simple_add (len * 0.277778, 0.289256); - _fade_out->fast_simple_add (len * 0.470046, 0.152778); - _fade_out->fast_simple_add (len * 0.695853, 0.0694444); - _fade_out->fast_simple_add (len * 1, 0); - break; - case FadeLinear: - _fade_out->fast_simple_add (len * 0, 1); - _fade_out->fast_simple_add (len * 1, 0); + _fade_out->fast_simple_add (0.0, 1.0); + _fade_out->fast_simple_add (len, VERY_SMALL_SIGNAL); + reverse_curve (_inverse_fade_out, _fade_out); + break; + + case FadeFast: + generate_db_fade (_fade_out, len, 10, -60 ); + generate_inverse_power_curve (_inverse_fade_out, _fade_out); + break; + + case FadeSlow: + generate_db_fade (c1, len, 10, -1 ); //start off with a slow fade + generate_db_fade (c2, len, 10, -80 ); //end with a fast fade + merge_curves (_fade_out, c1, c2); + generate_inverse_power_curve (_inverse_fade_out, _fade_out); + break; + + case FadeConstantPower: + //constant-power fades use a sin/cos relationship + //the cutoff is abrupt but it has the benefit of being symmetrical + _fade_out->fast_simple_add (0.0, 1.0); + for (int i = 1; i < 9; i++ ) { + float dist = (float)i/10.0; + _fade_out->fast_simple_add ((len * dist), cos(dist*M_PI/2)); + } + _fade_out->fast_simple_add (len, VERY_SMALL_SIGNAL); + reverse_curve (_inverse_fade_out, _fade_out); + break; + + case FadeSymmetric: + //starts kind of like a constant power but has a slower fadeout + //however it is NOT constant power and there will be a level drop in the middle of the crossfade + c1->fast_simple_add (0.0, 1.0); + for ( int i = 1; i < 9; i++ ) { + float dist = (float)i/10.0; + c1->fast_simple_add ((len * dist), cos(dist*M_PI/10.0)); //cheesy way of making a flat line + } + c1->fast_simple_add (len, VERY_SMALL_SIGNAL); + + //curve 2 is a slow fade at end + generate_db_fade (c2, len, 10, -30); + + merge_curves (_fade_out, c1, c2); + reverse_curve (_inverse_fade_out, _fade_out); break; } + _default_fade_out = false; _fade_out->thaw (); - send_change (PropertyChange (Properties::fade_in)); + send_change (PropertyChange (Properties::fade_out)); } void @@ -891,10 +1126,32 @@ AudioRegion::set_fade_in_length (framecnt_t len) if (len > _length) { len = _length - 1; } + + if (len < 64) { + len = 64; + } bool changed = _fade_in->extend_to (len); if (changed) { + if (_inverse_fade_in) { + _inverse_fade_in->extend_to (len); + } + + if (_session.config.get_xfade_model() == FullCrossfade && + _session.config.get_auto_xfade() && + _fade_in_is_xfade) { + + /* trim a single other region below us to the new start + of the fade. + */ + + boost::shared_ptr other = get_single_other_xfade_region (true); + if (other) { + other->trim_end (position() + len); + } + } + _default_fade_in = false; send_change (PropertyChange (Properties::fade_in)); } @@ -907,10 +1164,33 @@ AudioRegion::set_fade_out_length (framecnt_t len) len = _length - 1; } + if (len < 64) { + len = 64; + } + bool changed = _fade_out->extend_to (len); if (changed) { + + if (_inverse_fade_out) { + _inverse_fade_out->extend_to (len); + } _default_fade_out = false; + + if (_session.config.get_xfade_model() == FullCrossfade && + _session.config.get_auto_xfade() && + _fade_out_is_xfade) { + + /* trim a single other region below us to the new start + of the fade. + */ + + boost::shared_ptr other = get_single_other_xfade_region (false); + if (other) { + other->trim_front (last_frame() - len); + } + } + send_change (PropertyChange (Properties::fade_out)); } } @@ -952,6 +1232,7 @@ void AudioRegion::set_default_fade_in () { _fade_in_suspended = 0; + _fade_in_is_xfade = false; set_fade_in (FadeLinear, 64); } @@ -959,6 +1240,7 @@ void AudioRegion::set_default_fade_out () { _fade_out_suspended = 0; + _fade_out_is_xfade = false; set_fade_out (FadeLinear, 64); } @@ -1527,6 +1809,85 @@ AudioRegion::body_range () const return Evoral::Range (first_frame() + _fade_in->back()->when, last_frame() - _fade_out->back()->when); } +void +AudioRegion::set_fade_in_is_xfade (bool yn) +{ + _fade_in_is_xfade = yn; +} + +void +AudioRegion::set_fade_out_is_xfade (bool yn) +{ + _fade_out_is_xfade = yn; +} + +boost::shared_ptr +AudioRegion::get_single_other_xfade_region (bool start) const +{ + boost::shared_ptr pl (playlist()); + + if (!pl) { + /* not currently in a playlist - xfade length is unbounded + (and irrelevant) + */ + return boost::shared_ptr (); + } + + boost::shared_ptr rl; + + if (start) { + rl = pl->regions_at (position()); + } else { + rl = pl->regions_at (last_frame()); + } + + RegionList::iterator i; + boost::shared_ptr other; + uint32_t n = 0; + + /* count and find the other region in a single pass through the list */ + + for (i = rl->begin(); i != rl->end(); ++i) { + if ((*i).get() != this) { + other = *i; + } + ++n; + } + + if (n != 2) { + /* zero or multiple regions stacked here - don't care about xfades */ + return boost::shared_ptr (); + } + + return other; +} + +framecnt_t +AudioRegion::verify_xfade_bounds (framecnt_t len, bool start) +{ + boost::shared_ptr other = get_single_other_xfade_region (start); + framecnt_t maxlen; + + if (!other) { + /* zero or > 2 regions here, don't care about len */ + return len; + } + + /* we overlap a single region. clamp the length of an xfade to + the maximum possible duration of the overlap (if the other + region were trimmed appropriately). + */ + + if (start) { + maxlen = other->latest_possible_frame() - position(); + } else { + maxlen = last_frame() - other->earliest_possible_position(); + } + + return min (maxlen, len); + +} + extern "C" { int region_read_peaks_from_c (void *arg, uint32_t npeaks, uint32_t start, uint32_t cnt, intptr_t data, uint32_t n_chan, double samples_per_unit) diff --git a/libs/ardour/broadcast_info.cc b/libs/ardour/broadcast_info.cc index af760629ff..d31104d545 100644 --- a/libs/ardour/broadcast_info.cc +++ b/libs/ardour/broadcast_info.cc @@ -81,7 +81,7 @@ BroadcastInfo::set_originator (std::string const & str) } void -BroadcastInfo::set_originator_ref_from_session (Session const & session) +BroadcastInfo::set_originator_ref_from_session (Session const & /*session*/) { _has_info = true; diff --git a/libs/ardour/control_protocol_manager.cc b/libs/ardour/control_protocol_manager.cc index 6aa0c51da2..59001dce14 100644 --- a/libs/ardour/control_protocol_manager.cc +++ b/libs/ardour/control_protocol_manager.cc @@ -19,6 +19,8 @@ #include +#include + #include "pbd/compose.h" #include "pbd/file_utils.h" #include "pbd/error.h" @@ -169,6 +171,8 @@ ControlProtocolManager::teardown (ControlProtocolInfo& cpi) } cpi.protocol = 0; + delete cpi.state; + cpi.state = 0; dlclose (cpi.descriptor->module); return 0; } @@ -200,10 +204,10 @@ ControlProtocolManager::discover_control_protocols () Glib::PatternSpec dylib_extension_pattern("*.dylib"); find_matching_files_in_search_path (control_protocol_search_path (), - so_extension_pattern, cp_modules); + so_extension_pattern, cp_modules); find_matching_files_in_search_path (control_protocol_search_path (), - dylib_extension_pattern, cp_modules); + dylib_extension_pattern, cp_modules); DEBUG_TRACE (DEBUG::ControlProtocols, string_compose (_("looking for control protocols in %1"), control_protocol_search_path().to_string())); @@ -218,6 +222,13 @@ ControlProtocolManager::control_protocol_discover (string path) { ControlProtocolDescriptor* descriptor; + /* don't load shared objects that are just symlinks to the real thing. + */ + + if (Glib::file_test (path, Glib::FILE_TEST_IS_SYMLINK)) { + return 0; + } + if ((descriptor = get_descriptor (path)) != 0) { if (!descriptor->probe (descriptor)) { diff --git a/libs/ardour/delivery.cc b/libs/ardour/delivery.cc index 2fc8d82e47..86ce419340 100644 --- a/libs/ardour/delivery.cc +++ b/libs/ardour/delivery.cc @@ -47,7 +47,7 @@ using namespace std; using namespace PBD; using namespace ARDOUR; -PBD::Signal0 Delivery::PannersLegal; +PBD::Signal0 Delivery::PannersLegal; bool Delivery::panners_legal = false; /* deliver to an existing IO object */ @@ -402,7 +402,7 @@ Delivery::reset_panner () } } -int +void Delivery::panners_became_legal () { if (_panshell) { @@ -414,7 +414,6 @@ Delivery::panners_became_legal () } panner_legal_c.disconnect (); - return 0; } void @@ -438,11 +437,11 @@ Delivery::disable_panners () return 0; } -int +void Delivery::reset_panners () { panners_legal = true; - return *PannersLegal (); + PannersLegal (); } void diff --git a/libs/ardour/enums.cc b/libs/ardour/enums.cc index 4559ed457d..c0d6107639 100644 --- a/libs/ardour/enums.cc +++ b/libs/ardour/enums.cc @@ -73,6 +73,7 @@ setup_enum_writer () RemoteModel _RemoteModel; DenormalModel _DenormalModel; CrossfadeModel _CrossfadeModel; + CrossfadeChoice _CrossfadeChoice; InsertMergePolicy _InsertMergePolicy; ListenPosition _ListenPosition; SampleFormat _SampleFormat; @@ -121,7 +122,6 @@ setup_enum_writer () MidiModel::PatchChangeDiffCommand::Property _MidiModel_PatchChangeDiffCommand_Property; WaveformScale _WaveformScale; WaveformShape _WaveformShape; - QuantizeType _QuantizeType; Session::PostTransportWork _Session_PostTransportWork; Session::SlaveState _Session_SlaveState; MTC_Status _MIDI_MTC_Status; @@ -257,6 +257,11 @@ setup_enum_writer () REGISTER_ENUM (ShortCrossfade); REGISTER (_CrossfadeModel); + REGISTER_ENUM (RegionFades); + REGISTER_ENUM (ConstantPowerMinus3dB); + REGISTER_ENUM (ConstantPowerMinus6dB); + REGISTER (_CrossfadeChoice); + REGISTER_ENUM (InsertMergeReject); REGISTER_ENUM (InsertMergeRelax); REGISTER_ENUM (InsertMergeReplace); @@ -409,8 +414,8 @@ setup_enum_writer () REGISTER_ENUM (FadeLinear); REGISTER_ENUM (FadeFast); REGISTER_ENUM (FadeSlow); - REGISTER_ENUM (FadeLogA); - REGISTER_ENUM (FadeLogB); + REGISTER_ENUM (FadeConstantPower); + REGISTER_ENUM (FadeSymmetric); REGISTER (_FadeShape); REGISTER_CLASS_ENUM (Diskstream, Recordable); @@ -569,11 +574,6 @@ setup_enum_writer () REGISTER_ENUM(Rectified); REGISTER(_WaveformShape); - REGISTER_ENUM(Plain); - REGISTER_ENUM(Legato); - REGISTER_ENUM(Groove); - REGISTER(_QuantizeType); - REGISTER_ENUM(AudioTime); REGISTER_ENUM(MusicTime); REGISTER(_PositionLockStyle); @@ -732,6 +732,21 @@ std::ostream& operator<<(std::ostream& o, const CrossfadeModel& var) std::string s = enum_2_string (var); return o << s; } + +std::istream& operator>>(std::istream& o, CrossfadeChoice& var) +{ + std::string s; + o >> s; + var = (CrossfadeChoice) string_2_enum (s, var); + return o; +} + +std::ostream& operator<<(std::ostream& o, const CrossfadeChoice& var) +{ + std::string s = enum_2_string (var); + return o << s; +} + std::istream& operator>>(std::istream& o, SyncSource& var) { std::string s; diff --git a/libs/ardour/filesystem_paths.cc b/libs/ardour/filesystem_paths.cc index 11ddc88502..2d14494764 100644 --- a/libs/ardour/filesystem_paths.cc +++ b/libs/ardour/filesystem_paths.cc @@ -30,8 +30,6 @@ #include "i18n.h" -#define WITH_STATIC_PATHS 1 - using namespace PBD; namespace ARDOUR { @@ -109,11 +107,7 @@ ardour_search_path () SearchPath system_config_search_path () { -#ifdef WITH_STATIC_PATHS - SearchPath config_path(string(CONFIG_DIR)); -#else SearchPath config_path(system_config_directories()); -#endif config_path.add_subdirectory_to_paths("ardour3"); @@ -123,11 +117,7 @@ system_config_search_path () SearchPath system_data_search_path () { -#ifdef WITH_STATIC_PATHS - SearchPath data_path(string(DATA_DIR)); -#else SearchPath data_path(system_data_directories()); -#endif data_path.add_subdirectory_to_paths("ardour3"); diff --git a/libs/ardour/graph.cc b/libs/ardour/graph.cc index 556748e2de..300271c21a 100644 --- a/libs/ardour/graph.cc +++ b/libs/ardour/graph.cc @@ -108,14 +108,18 @@ Graph::reset_thread_list () drop_threads (); } - if (AudioEngine::instance()->create_process_thread (boost::bind (&Graph::main_thread, this), &a_thread, 100000) == 0) { - _thread_list.push_back (a_thread); + if (AudioEngine::instance()->create_process_thread (boost::bind (&Graph::main_thread, this), &a_thread, 100000) != 0) { + throw failed_constructor (); } + _thread_list.push_back (a_thread); + for (uint32_t i = 1; i < num_threads; ++i) { - if (AudioEngine::instance()->create_process_thread (boost::bind (&Graph::helper_thread, this), &a_thread, 100000) == 0) { - _thread_list.push_back (a_thread); + if (AudioEngine::instance()->create_process_thread (boost::bind (&Graph::helper_thread, this), &a_thread, 100000) != 0) { + throw failed_constructor (); } + + _thread_list.push_back (a_thread); } } diff --git a/libs/ardour/internal_send.cc b/libs/ardour/internal_send.cc index b4d89fb986..dfb0204c8c 100644 --- a/libs/ardour/internal_send.cc +++ b/libs/ardour/internal_send.cc @@ -359,7 +359,7 @@ InternalSend::set_can_pan (bool yn) } void -InternalSend::cycle_start (pframes_t nframes) +InternalSend::cycle_start (pframes_t /*nframes*/) { for (BufferSet::audio_iterator b = mixbufs.audio_begin(); b != mixbufs.audio_end(); ++b) { b->prepare (); diff --git a/libs/ardour/io.cc b/libs/ardour/io.cc index a278852e77..26432c66ef 100644 --- a/libs/ardour/io.cc +++ b/libs/ardour/io.cc @@ -256,8 +256,8 @@ IO::remove_port (boost::shared_ptr port, void* src) ChanCount after = before; after.set (port->type(), after.get (port->type()) - 1); - bool const r = PortCountChanging (after); /* EMIT SIGNAL */ - if (r) { + boost::optional const r = PortCountChanging (after); /* EMIT SIGNAL */ + if (r.get_value_or (false)) { return -1; } diff --git a/libs/ardour/lv2_plugin.cc b/libs/ardour/lv2_plugin.cc index 0b3bc3cd18..46c09a1227 100644 --- a/libs/ardour/lv2_plugin.cc +++ b/libs/ardour/lv2_plugin.cc @@ -175,7 +175,8 @@ LV2Plugin::LV2Plugin (AudioEngine& engine, Session& session, void* c_plugin, framecnt_t rate) - : Plugin(engine, session) + : Plugin (engine, session) + , Workee () , _impl(new Impl()) , _features(NULL) , _worker(NULL) @@ -185,7 +186,8 @@ LV2Plugin::LV2Plugin (AudioEngine& engine, } LV2Plugin::LV2Plugin (const LV2Plugin& other) - : Plugin(other) + : Plugin (other) + , Workee () , _impl(new Impl()) , _features(NULL) , _worker(NULL) diff --git a/libs/ardour/midi_playlist.cc b/libs/ardour/midi_playlist.cc index 7e3d70bfe0..81b14dd933 100644 --- a/libs/ardour/midi_playlist.cc +++ b/libs/ardour/midi_playlist.cc @@ -107,11 +107,8 @@ MidiPlaylist::read (Evoral::EventSink& dst, framepos_t start, framec its OK to block (for short intervals). */ -#ifdef HAVE_GLIB_THREADS_RECMUTEX - Glib::Threads::RecMutex::Lock rm (region_lock); -#else - Glib::RecMutex::Lock rm (region_lock); -#endif + Playlist::RegionLock rl (this, false); + DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("++++++ %1 .. %2 +++++++ %3 trackers +++++++++++++++++\n", start, start + dur, _note_trackers.size())); @@ -298,11 +295,8 @@ MidiPlaylist::read (Evoral::EventSink& dst, framepos_t start, framec void MidiPlaylist::clear_note_trackers () { -#ifdef HAVE_GLIB_THREADS_RECMUTEX - Glib::Threads::RecMutex::Lock rm (region_lock); -#else - Glib::RecMutex::Lock rm (region_lock); -#endif + Playlist::RegionLock rl (this, false); + for (NoteTrackers::iterator n = _note_trackers.begin(); n != _note_trackers.end(); ++n) { delete n->second; } @@ -407,12 +401,7 @@ MidiPlaylist::contained_automation() its OK to block (for short intervals). */ -#ifdef HAVE_GLIB_THREADS_RECMUTEX - Glib::Threads::RecMutex::Lock rm (region_lock); -#else - Glib::RecMutex::Lock rm (region_lock); -#endif - + Playlist::RegionLock rl (this, false); set ret; for (RegionList::const_iterator r = regions.begin(); r != regions.end(); ++r) { diff --git a/libs/ardour/midi_playlist_source.cc b/libs/ardour/midi_playlist_source.cc index 208b2b0460..957531f520 100644 --- a/libs/ardour/midi_playlist_source.cc +++ b/libs/ardour/midi_playlist_source.cc @@ -142,7 +142,7 @@ MidiPlaylistSource::read_unlocked (Evoral::EventSink& dst, } framecnt_t -MidiPlaylistSource::write_unlocked (MidiRingBuffer& dst, +MidiPlaylistSource::write_unlocked (MidiRingBuffer&, framepos_t, framecnt_t) { diff --git a/libs/ardour/midi_ui.cc b/libs/ardour/midi_ui.cc index 770a371457..78da32e427 100644 --- a/libs/ardour/midi_ui.cc +++ b/libs/ardour/midi_ui.cc @@ -45,7 +45,7 @@ MidiControlUI* MidiControlUI::_instance = 0; #include "pbd/abstract_ui.cc" /* instantiate the template */ MidiControlUI::MidiControlUI (Session& s) - : AbstractUI (_("midiui")) + : AbstractUI (X_("midiui")) , _session (s) { MIDI::Manager::instance()->PortsChanged.connect_same_thread (rebind_connection, boost::bind (&MidiControlUI::change_midi_ports, this)); diff --git a/libs/ardour/playlist.cc b/libs/ardour/playlist.cc index cb40b4df91..1b546ce4ed 100644 --- a/libs/ardour/playlist.cc +++ b/libs/ardour/playlist.cc @@ -32,6 +32,7 @@ #include "pbd/failed_constructor.h" #include "pbd/stateful_diff_command.h" #include "pbd/xml++.h" +#include "pbd/stacktrace.h" #include "ardour/debug.h" #include "ardour/playlist.h" @@ -608,11 +609,7 @@ Playlist::flush_notifications (bool from_undo) */ } - if ( - ((regions_changed || pending_contents_change) && !in_set_state) || - pending_layering - ) { - + if (((regions_changed || pending_contents_change) && !in_set_state) || pending_layering) { relayer (); } @@ -1761,6 +1758,12 @@ boost::shared_ptr Playlist::regions_touched (framepos_t start, framepos_t end) { RegionLock rlock (this); + return regions_touched_locked (start, end); +} + +boost::shared_ptr +Playlist::regions_touched_locked (framepos_t start, framepos_t end) +{ boost::shared_ptr rlist (new RegionList); for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) { @@ -1772,126 +1775,125 @@ Playlist::regions_touched (framepos_t start, framepos_t end) return rlist; } - framepos_t - Playlist::find_next_transient (framepos_t from, int dir) - { - RegionLock rlock (this); - AnalysisFeatureList points; - AnalysisFeatureList these_points; +framepos_t +Playlist::find_next_transient (framepos_t from, int dir) +{ + RegionLock rlock (this); + AnalysisFeatureList points; + AnalysisFeatureList these_points; + + for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) { + if (dir > 0) { + if ((*i)->last_frame() < from) { + continue; + } + } else { + if ((*i)->first_frame() > from) { + continue; + } + } + + (*i)->get_transients (these_points); + + /* add first frame, just, err, because */ + + these_points.push_back ((*i)->first_frame()); + + points.insert (points.end(), these_points.begin(), these_points.end()); + these_points.clear (); + } + + if (points.empty()) { + return -1; + } + + TransientDetector::cleanup_transients (points, _session.frame_rate(), 3.0); + bool reached = false; + + if (dir > 0) { + for (AnalysisFeatureList::iterator x = points.begin(); x != points.end(); ++x) { + if ((*x) >= from) { + reached = true; + } + + if (reached && (*x) > from) { + return *x; + } + } + } else { + for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) { + if ((*x) <= from) { + reached = true; + } + + if (reached && (*x) < from) { + return *x; + } + } + } + + return -1; +} - for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) { - if (dir > 0) { - if ((*i)->last_frame() < from) { - continue; - } - } else { - if ((*i)->first_frame() > from) { - continue; - } - } - - (*i)->get_transients (these_points); - - /* add first frame, just, err, because */ - - these_points.push_back ((*i)->first_frame()); - - points.insert (points.end(), these_points.begin(), these_points.end()); - these_points.clear (); - } - - if (points.empty()) { - return -1; - } - - TransientDetector::cleanup_transients (points, _session.frame_rate(), 3.0); - bool reached = false; - - if (dir > 0) { - for (AnalysisFeatureList::iterator x = points.begin(); x != points.end(); ++x) { - if ((*x) >= from) { - reached = true; - } - - if (reached && (*x) > from) { - return *x; - } - } - } else { - for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) { - if ((*x) <= from) { - reached = true; - } - - if (reached && (*x) < from) { - return *x; - } - } - } - - return -1; - } - - boost::shared_ptr - Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir) - { - RegionLock rlock (this); - boost::shared_ptr ret; - framepos_t closest = max_framepos; - - bool end_iter = false; - - for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) { - - if(end_iter) break; - - frameoffset_t distance; - boost::shared_ptr r = (*i); - framepos_t pos = 0; - - switch (point) { - case Start: - pos = r->first_frame (); - break; - case End: - pos = r->last_frame (); - break; - case SyncPoint: - pos = r->sync_position (); - break; - } - - switch (dir) { - case 1: /* forwards */ - - if (pos > frame) { - if ((distance = pos - frame) < closest) { - closest = distance; - ret = r; - end_iter = true; - } - } - - break; - - default: /* backwards */ - - if (pos < frame) { - if ((distance = frame - pos) < closest) { - closest = distance; - ret = r; - } - } - else { - end_iter = true; - } - - break; - } - } - - return ret; - } +boost::shared_ptr +Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir) +{ + RegionLock rlock (this); + boost::shared_ptr ret; + framepos_t closest = max_framepos; + + bool end_iter = false; + + for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) { + + if(end_iter) break; + + frameoffset_t distance; + boost::shared_ptr r = (*i); + framepos_t pos = 0; + + switch (point) { + case Start: + pos = r->first_frame (); + break; + case End: + pos = r->last_frame (); + break; + case SyncPoint: + pos = r->sync_position (); + break; + } + + switch (dir) { + case 1: /* forwards */ + + if (pos > frame) { + if ((distance = pos - frame) < closest) { + closest = distance; + ret = r; + end_iter = true; + } + } + + break; + + default: /* backwards */ + + if (pos < frame) { + if ((distance = frame - pos) < closest) { + closest = distance; + ret = r; + } + } else { + end_iter = true; + } + + break; + } + } + + return ret; +} framepos_t Playlist::find_next_region_boundary (framepos_t frame, int dir) @@ -2276,15 +2278,16 @@ Playlist::set_layer (boost::shared_ptr region, double new_layer) } void -Playlist::setup_layering_indices (RegionList const & regions) const +Playlist::setup_layering_indices (RegionList const & regions) { uint64_t j = 0; + list > xf; + for (RegionList::const_iterator k = regions.begin(); k != regions.end(); ++k) { (*k)->set_layering_index (j++); } } - /** Take the layering indices of each of our regions, compute the layers * that they should be on, and write the layers back to the regions. */ @@ -2400,7 +2403,7 @@ Playlist::relayer () notify_layering_changed (); /* This relayer() may have been called as a result of a region removal, in which - case we need to setup layering indices so account for the one that has just + case we need to setup layering indices to account for the one that has just gone away. */ setup_layering_indices (copy); @@ -2411,6 +2414,7 @@ Playlist::raise_region (boost::shared_ptr region) { set_layer (region, region->layer() + 1.5); relayer (); + check_crossfades (region->range ()); } void @@ -2418,6 +2422,7 @@ Playlist::lower_region (boost::shared_ptr region) { set_layer (region, region->layer() - 1.5); relayer (); + check_crossfades (region->range ()); } void @@ -2425,6 +2430,7 @@ Playlist::raise_region_to_top (boost::shared_ptr region) { set_layer (region, DBL_MAX); relayer (); + check_crossfades (region->range ()); } void @@ -2432,6 +2438,7 @@ Playlist::lower_region_to_bottom (boost::shared_ptr region) { set_layer (region, -0.5); relayer (); + check_crossfades (region->range ()); } void @@ -2577,8 +2584,6 @@ Playlist::shuffle (boost::shared_ptr region, int dir) _shuffling = true; - Evoral::Range old_range = region->range (); - { RegionLock rlock (const_cast (this)); @@ -2677,12 +2682,6 @@ Playlist::shuffle (boost::shared_ptr region, int dir) if (moved) { relayer (); - - list > xf; - xf.push_back (old_range); - xf.push_back (region->range ()); - coalesce_and_check_crossfades (xf); - notify_contents_changed(); } @@ -3096,6 +3095,9 @@ Playlist::set_orig_track_id (const PBD::ID& id) _orig_track_id = id; } +/** Take a list of ranges, coalesce any that can be coalesced, then call + * check_crossfades for each one. + */ void Playlist::coalesce_and_check_crossfades (list > ranges) { diff --git a/libs/ardour/plugin_insert.cc b/libs/ardour/plugin_insert.cc index 796ec9420c..20180b285b 100644 --- a/libs/ardour/plugin_insert.cc +++ b/libs/ardour/plugin_insert.cc @@ -138,12 +138,14 @@ PluginInsert::output_streams() const { assert (!_plugins.empty()); - if (_plugins.front()->reconfigurable_io()) { + PluginInfoPtr info = _plugins.front()->get_info(); + + if (info->reconfigurable_io()) { ChanCount out = _plugins.front()->output_streams (); // DEBUG_TRACE (DEBUG::Processors, string_compose ("Plugin insert, reconfigur(able) output streams = %1\n", out)); return out; } else { - ChanCount out = _plugins.front()->get_info()->n_outputs; + ChanCount out = info->n_outputs; // DEBUG_TRACE (DEBUG::Processors, string_compose ("Plugin insert, static output streams = %1 for %2 plugins\n", out, _plugins.size())); out.set_audio (out.n_audio() * _plugins.size()); out.set_midi (out.n_midi() * _plugins.size()); @@ -158,11 +160,13 @@ PluginInsert::input_streams() const ChanCount in; - if (_plugins.front()->reconfigurable_io()) { + PluginInfoPtr info = _plugins.front()->get_info(); + + if (info->reconfigurable_io()) { assert (_plugins.size() == 1); in = _plugins.front()->input_streams(); } else { - in = _plugins[0]->get_info()->n_inputs; + in = info->n_inputs; } DEBUG_TRACE (DEBUG::Processors, string_compose ("Plugin insert, input streams = %1, match using %2\n", in, _match.method)); @@ -712,7 +716,9 @@ PluginInsert::can_support_io_configuration (const ChanCount& in, ChanCount& out) PluginInsert::Match PluginInsert::private_can_support_io_configuration (ChanCount const & in, ChanCount& out) const { - if (_plugins.front()->reconfigurable_io()) { + PluginInfoPtr info = _plugins.front()->get_info(); + + if (info->reconfigurable_io()) { /* Plugin has flexible I/O, so delegate to it */ bool const r = _plugins.front()->can_support_io_configuration (in, out); if (!r) { @@ -722,8 +728,8 @@ PluginInsert::private_can_support_io_configuration (ChanCount const & in, ChanCo return Match (Delegate, 1); } - ChanCount inputs = _plugins[0]->get_info()->n_inputs; - ChanCount outputs = _plugins[0]->get_info()->n_outputs; + ChanCount inputs = info->n_inputs; + ChanCount outputs = info->n_outputs; bool no_inputs = true; for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) { diff --git a/libs/ardour/quantize.cc b/libs/ardour/quantize.cc index 7ede60b578..563b7bf4e7 100644 --- a/libs/ardour/quantize.cc +++ b/libs/ardour/quantize.cc @@ -40,8 +40,7 @@ using namespace ARDOUR; * 0.25 = quantize to beats/4, etc. */ -Quantize::Quantize (Session& s, QuantizeType /* type */, - bool snap_start, bool snap_end, +Quantize::Quantize (Session& s, bool snap_start, bool snap_end, double start_grid, double end_grid, float strength, float swing, float threshold) : session (s) diff --git a/libs/ardour/region.cc b/libs/ardour/region.cc index efaa104d35..a4a1584792 100644 --- a/libs/ardour/region.cc +++ b/libs/ardour/region.cc @@ -1654,3 +1654,33 @@ Region::set_start_internal (framecnt_t s) { _start = s; } + +framepos_t +Region::earliest_possible_position () const +{ + if (_start > _position) { + return 0; + } else { + return _position - _start; + } +} + +framecnt_t +Region::latest_possible_frame () const +{ + framecnt_t minlen = max_framecnt; + + for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) { + /* non-audio regions have a length that may vary based on their + * position, so we have to pass it in the call. + */ + minlen = min (minlen, (*i)->length (_position)); + } + + /* the latest possible last frame is determined by the current + * position, plus the shortest source extent past _start. + */ + + return _position + (minlen - _start) - 1; +} + diff --git a/libs/ardour/run-tests.sh b/libs/ardour/run-tests.sh index 1af623c3f2..1097804994 100755 --- a/libs/ardour/run-tests.sh +++ b/libs/ardour/run-tests.sh @@ -16,6 +16,7 @@ libs='libs' export LD_LIBRARY_PATH=$libs/audiographer:$libs/vamp-sdk:$libs/surfaces:$libs/surfaces/control_protocol:$libs/ardour:$libs/midi++2:$libs/pbd:$libs/rubberband:$libs/soundtouch:$libs/gtkmm2ext:$libs/appleutility:$libs/taglib:$libs/evoral:$libs/evoral/src/libsmf:$libs/timecode:/usr/local/lib:/usr/local/lib64:$LD_LIBRARY_PATH export ARDOUR_PANNER_PATH=$libs/panners/2in2out:$libs/panners/1in2out:$libs/panners/vbap +export ARDOUR_SURFACES_PATH=$libs/surfaces/osc:$libs/surfaces/generic_midi:$libs/surfaces/tranzport:$libs/surfaces/powermate:$libs/surfaces/mackie if [ "$1" == "--debug" ]; then gdb ./libs/ardour/run-tests diff --git a/libs/ardour/session_metadata.cc b/libs/ardour/session_metadata.cc index 00f5d31ed8..e1eb166bb6 100644 --- a/libs/ardour/session_metadata.cc +++ b/libs/ardour/session_metadata.cc @@ -182,7 +182,7 @@ SessionMetadata::get_state () } int -SessionMetadata::set_state (const XMLNode & state, int version_num) +SessionMetadata::set_state (const XMLNode & state, int /*version_num*/) { const XMLNodeList & children = state.children(); string name; diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc index 48e7f206e7..2f67915ce5 100644 --- a/libs/ardour/session_state.cc +++ b/libs/ardour/session_state.cc @@ -926,8 +926,12 @@ Session::load_state (string snapshot_name) Stateful::loading_state_version = 1000; } else { if (prop->value().find ('.')) { - /* old school version format - lock at 3000 */ - Stateful::loading_state_version = 3000; + /* old school version format */ + if (prop->value()[0] == '2') { + Stateful::loading_state_version = 2000; + } else { + Stateful::loading_state_version = 3000; + } } else { Stateful::loading_state_version = atoi (prop->value()); } diff --git a/libs/ardour/tempo.cc b/libs/ardour/tempo.cc index df3dddbd6d..798ced17b4 100644 --- a/libs/ardour/tempo.cc +++ b/libs/ardour/tempo.cc @@ -19,11 +19,10 @@ #include #include +#include #include -#include - #include #include "pbd/xml++.h" #include "evoral/types.hpp" @@ -1195,7 +1194,7 @@ TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir) } framecnt_t -TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int dir) +TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int /*dir*/) { if (bbt.bars == 0 && bbt.beats == 0 && bbt.ticks == 0) { return 0; diff --git a/libs/ardour/test/audio_region_test.cc b/libs/ardour/test/audio_region_test.cc index 72e886b4e1..cd1986b19b 100644 --- a/libs/ardour/test/audio_region_test.cc +++ b/libs/ardour/test/audio_region_test.cc @@ -23,19 +23,10 @@ AudioRegionTest::readTest () /* Simple read: 256 frames from start of region, no fades */ - /* gbuf should be ignored; set it to 0 to ensure that it is */ - for (int i = 0; i < N; ++i) { - gbuf[i] = 0; - } - ar->set_position (P); ar->set_length (1024); - for (int i = 0; i < N; ++i) { - buf[i] = 0; - } - - ar->_read_at (ar->_sources, ar->_length, buf, mbuf, gbuf, P, 256, 0, AudioRegion::ReadOps (0)); + ar->read_from_sources (ar->_sources, ar->_length, buf, P, 256, 0); check_staircase (buf, 0, 256); for (int i = 0; i < N; ++i) { @@ -43,7 +34,7 @@ AudioRegionTest::readTest () } /* Offset read: 256 frames from 128 frames into the region, no fades */ - ar->_read_at (ar->_sources, ar->_length, buf, mbuf, gbuf, P + 128, 256, 0, AudioRegion::ReadOps (0)); + ar->read_from_sources (ar->_sources, ar->_length, buf, P + 128, 256, 0); check_staircase (buf, 128, 256); /* Simple read with a fade-in: 256 frames from start of region, with fades */ diff --git a/libs/ardour/test/control_surfaces_test.cc b/libs/ardour/test/control_surfaces_test.cc new file mode 100644 index 0000000000..82127a2524 --- /dev/null +++ b/libs/ardour/test/control_surfaces_test.cc @@ -0,0 +1,23 @@ +#include "control_surfaces_test.h" +#include "ardour/control_protocol_manager.h" +#include "ardour/session.h" + +CPPUNIT_TEST_SUITE_REGISTRATION (ControlSurfacesTest); + +using namespace std; +using namespace ARDOUR; + +void +ControlSurfacesTest::instantiateAndTeardownTest () +{ + cout << "HELLO!\n"; + _session->new_audio_track (1, 2, Normal, 0, 1, "Test"); + + ControlProtocolManager& m = ControlProtocolManager::instance (); + cout << "CST: Test " << m.control_protocol_info.size() << "\n"; + for (list::iterator i = m.control_protocol_info.begin(); i != m.control_protocol_info.end(); ++i) { + cout << "CST: Test " << (*i)->name << "\n"; + m.instantiate (**i); + m.teardown (**i); + } +} diff --git a/libs/ardour/test/control_surfaces_test.h b/libs/ardour/test/control_surfaces_test.h new file mode 100644 index 0000000000..97662c6ea3 --- /dev/null +++ b/libs/ardour/test/control_surfaces_test.h @@ -0,0 +1,11 @@ +#include "test_needing_session.h" + +class ControlSurfacesTest : public TestNeedingSession +{ + CPPUNIT_TEST_SUITE (ControlSurfacesTest); + CPPUNIT_TEST (instantiateAndTeardownTest); + CPPUNIT_TEST_SUITE_END (); + +public: + void instantiateAndTeardownTest (); +}; diff --git a/libs/ardour/test/test_needing_session.cc b/libs/ardour/test/test_needing_session.cc index e2da42c475..625d5735b2 100644 --- a/libs/ardour/test/test_needing_session.cc +++ b/libs/ardour/test/test_needing_session.cc @@ -6,6 +6,8 @@ #include "ardour/audioengine.h" #include "test_needing_session.h" +extern void setup_libpbd_enums (); + using namespace std; using namespace ARDOUR; using namespace PBD; @@ -57,13 +59,16 @@ TestNeedingSession::setUp () init (false, true); SessionEvent::create_per_thread_pool ("test", 512); + setup_libpbd_enums (); + test_receiver.listen_to (error); test_receiver.listen_to (info); test_receiver.listen_to (fatal); test_receiver.listen_to (warning); AudioEngine* engine = new AudioEngine ("test", ""); - MIDI::Manager::create (engine->jack ()); + init_post_engine (); + CPPUNIT_ASSERT (engine->start () == 0); _session = new Session (*engine, test_session_path, "test_session"); @@ -74,6 +79,7 @@ void TestNeedingSession::tearDown () { AudioEngine::instance()->remove_session (); + AudioEngine::instance()->stop (true); delete _session; diff --git a/libs/ardour/wscript b/libs/ardour/wscript index c493f830e7..8d1c679f0f 100644 --- a/libs/ardour/wscript +++ b/libs/ardour/wscript @@ -436,6 +436,7 @@ def build(bld): test/framepos_minus_beats_test.cc test/playlist_layering_test.cc test/playlist_read_test.cc + test/control_surfaces_test.cc test/testrunner.cc '''.split() diff --git a/libs/audiographer/tests/type_utils_test.cc b/libs/audiographer/tests/type_utils_test.cc index af9e923d1b..a2b2c96f11 100644 --- a/libs/audiographer/tests/type_utils_test.cc +++ b/libs/audiographer/tests/type_utils_test.cc @@ -38,6 +38,7 @@ class TypeUtilsTest : public CppUnit::TestFixture void testZeroFillNonPod() { + /* does not compile on OS X Lion unsigned frames = 10; NonPodType buf[frames]; TypeUtils::zero_fill (buf, frames); @@ -45,6 +46,7 @@ class TypeUtilsTest : public CppUnit::TestFixture for (unsigned i = 0; i < frames; ++i) { CPPUNIT_ASSERT (zero == buf[i]); } + */ } void testMoveBackward() diff --git a/libs/evoral/evoral/ControlList.hpp b/libs/evoral/evoral/ControlList.hpp index 30b9fca430..c54c231ca7 100644 --- a/libs/evoral/evoral/ControlList.hpp +++ b/libs/evoral/evoral/ControlList.hpp @@ -95,6 +95,7 @@ public: ControlList& operator= (const ControlList&); bool operator== (const ControlList&); + void copy_events (const ControlList&); virtual void freeze(); virtual void thaw (); diff --git a/libs/evoral/evoral/Event.hpp b/libs/evoral/evoral/Event.hpp index da8036c8f6..5c287fd714 100644 --- a/libs/evoral/evoral/Event.hpp +++ b/libs/evoral/evoral/Event.hpp @@ -60,7 +60,7 @@ struct Event { const Event& operator=(const Event& copy); - void set(uint8_t* buf, uint32_t size, Time t); + void set(const uint8_t* buf, uint32_t size, Time t); inline bool operator==(const Event& other) const { if (_type != other._type) diff --git a/libs/evoral/src/ControlList.cpp b/libs/evoral/src/ControlList.cpp index 9c38f67b29..9a820294ef 100644 --- a/libs/evoral/src/ControlList.cpp +++ b/libs/evoral/src/ControlList.cpp @@ -80,9 +80,7 @@ ControlList::ControlList (const ControlList& other) _search_cache.first = _events.end(); _sort_pending = false; - for (const_iterator i = other._events.begin(); i != other._events.end(); ++i) { - _events.push_back (new ControlEvent (**i)); - } + copy_events (other); mark_dirty (); } @@ -106,9 +104,7 @@ ControlList::ControlList (const ControlList& other, double start, double end) boost::shared_ptr section = const_cast(&other)->copy (start, end); if (!section->empty()) { - for (iterator i = section->begin(); i != section->end(); ++i) { - _events.push_back (new ControlEvent ((*i)->when, (*i)->value)); - } + copy_events (*(section.get())); } mark_dirty (); @@ -147,23 +143,30 @@ ControlList::operator= (const ControlList& other) { if (this != &other) { - _events.clear (); - - for (const_iterator i = other._events.begin(); i != other._events.end(); ++i) { - _events.push_back (new ControlEvent (**i)); - } - _min_yval = other._min_yval; _max_yval = other._max_yval; _default_value = other._default_value; - - mark_dirty (); - maybe_signal_changed (); + + copy_events (other); } return *this; } +void +ControlList::copy_events (const ControlList& other) +{ + { + Glib::Mutex::Lock lm (_lock); + _events.clear (); + for (const_iterator i = other.begin(); i != other.end(); ++i) { + _events.push_back (new ControlEvent ((*i)->when, (*i)->value)); + } + mark_dirty (); + } + maybe_signal_changed (); +} + void ControlList::create_curve() { diff --git a/libs/evoral/src/Event.cpp b/libs/evoral/src/Event.cpp index d0e0e8ac3a..e315d811e0 100644 --- a/libs/evoral/src/Event.cpp +++ b/libs/evoral/src/Event.cpp @@ -118,7 +118,7 @@ Event::operator=(const Event& copy) template void -Event::set(uint8_t* buf, uint32_t size, Timestamp t) +Event::set (const uint8_t* buf, uint32_t size, Timestamp t) { if (_owns_buf) { if (_size < size) { @@ -126,7 +126,11 @@ Event::set(uint8_t* buf, uint32_t size, Timestamp t) } memcpy (_buf, buf, size); } else { - _buf = buf; + /* XXX this is really dangerous given the + const-ness of buf. The API should really + intervene here. + */ + _buf = const_cast (buf); } _original_time = t; diff --git a/libs/gtkmm2ext/cairo_packer.cc b/libs/gtkmm2ext/cairo_packer.cc index 70f51281dd..6145f7dd50 100644 --- a/libs/gtkmm2ext/cairo_packer.cc +++ b/libs/gtkmm2ext/cairo_packer.cc @@ -3,7 +3,7 @@ #include "gtkmm2ext/cairo_packer.h" void -CairoPacker::draw_background (Gtk::Widget& w, GdkEventExpose* ev) +CairoPacker::draw_background (Gtk::Widget& w, GdkEventExpose*) { int x, y; Gtk::Widget* window_parent; diff --git a/libs/gtkmm2ext/gtk_ui.cc b/libs/gtkmm2ext/gtk_ui.cc index 255b18b2fb..11b06b78ff 100644 --- a/libs/gtkmm2ext/gtk_ui.cc +++ b/libs/gtkmm2ext/gtk_ui.cc @@ -64,6 +64,8 @@ BaseUI::RequestType Gtkmm2ext::AddTimeout = BaseUI::new_request_type(); UI::UI (string namestr, int *argc, char ***argv) : AbstractUI (namestr) + , _receiver (*this) + { theMain = new Main (argc, argv); @@ -252,10 +254,10 @@ UI::load_rcfile (string path, bool themechange) void UI::run (Receiver &old_receiver) { - listen_to (error); - listen_to (info); - listen_to (warning); - listen_to (fatal); + _receiver.listen_to (error); + _receiver.listen_to (info); + _receiver.listen_to (warning); + _receiver.listen_to (fatal); /* stop the old receiver (text/console) once we hit the first idle */ @@ -266,7 +268,7 @@ UI::run (Receiver &old_receiver) theMain->run (); _active = false; stopping (); - hangup (); + _receiver.hangup (); return; } @@ -356,7 +358,7 @@ UI::set_tip (Widget *w, const gchar *tip, const gchar *hlp) if (!abbrev.empty()) { replace_all (abbrev, "<", ""); replace_all (abbrev, ">", "-"); - msg.append("\n\nKey: ").append (abbrev); + msg.append(_("\n\nKey: ")).append (abbrev); } } } @@ -565,10 +567,15 @@ UI::process_error_message (Transmitter::Channel chn, const char *str) handle_fatal (str); } else { - display_message (prefix, prefix_len, ptag, mtag, str); - - if (!errors->is_visible() && chn != Transmitter::Info) { - show_errors (); + if (!ptag || !mtag) { + /* oops, message sent before we set up tags - don't crash */ + cerr << prefix << str << endl; + } else { + display_message (prefix, prefix_len, ptag, mtag, str); + + if (!errors->is_visible() && chn != Transmitter::Info) { + show_errors (); + } } } diff --git a/libs/gtkmm2ext/gtkmm2ext/binding_proxy.h b/libs/gtkmm2ext/gtkmm2ext/binding_proxy.h index 69d1ebdaad..b541582f6f 100644 --- a/libs/gtkmm2ext/gtkmm2ext/binding_proxy.h +++ b/libs/gtkmm2ext/gtkmm2ext/binding_proxy.h @@ -24,7 +24,7 @@ #include #include #include -#include +#include "pbd/signals.h" namespace PBD { class Controllable; @@ -50,7 +50,7 @@ class BindingProxy : public sigc::trackable boost::shared_ptr controllable; guint bind_button; guint bind_statemask; - boost::signals2::scoped_connection learning_connection; + PBD::ScopedConnection learning_connection; void learning_finished (); bool prompter_hiding (GdkEventAny *); }; diff --git a/libs/gtkmm2ext/gtkmm2ext/gtk_ui.h b/libs/gtkmm2ext/gtkmm2ext/gtk_ui.h index bba3fb6fdf..4bf82fb1aa 100644 --- a/libs/gtkmm2ext/gtkmm2ext/gtk_ui.h +++ b/libs/gtkmm2ext/gtkmm2ext/gtk_ui.h @@ -88,8 +88,21 @@ struct UIRequest : public BaseUI::BaseRequestObject { } }; -class UI : public Receiver, public AbstractUI +class UI : public AbstractUI { + private: + class MyReceiver : public Receiver { + public: + MyReceiver (UI& ui) : _ui (ui) {} + void receive (Transmitter::Channel chn, const char *msg) { + _ui.receive (chn, msg); + } + private: + UI& _ui; + }; + + MyReceiver _receiver; + public: UI (std::string name, int *argc, char **argv[]); virtual ~UI (); @@ -183,6 +196,7 @@ class UI : public Receiver, public AbstractUI bool color_picked; void do_request (UIRequest*); + }; } /* namespace */ diff --git a/libs/midi++2/ipmidi_port.cc b/libs/midi++2/ipmidi_port.cc index 50777634f1..7e30752c38 100644 --- a/libs/midi++2/ipmidi_port.cc +++ b/libs/midi++2/ipmidi_port.cc @@ -246,11 +246,11 @@ IPMIDIPort::open_sockets (int base_port, const string& ifname) } int -IPMIDIPort::write (byte* msg, size_t msglen, timestamp_t /* ignored */) { +IPMIDIPort::write (const byte* msg, size_t msglen, timestamp_t /* ignored */) { if (sockout) { Glib::Mutex::Lock lm (write_lock); - if (::sendto (sockout, (char *) msg, msglen, 0, (struct sockaddr *) &addrout, sizeof(struct sockaddr_in)) < 0) { + if (::sendto (sockout, (const char *) msg, msglen, 0, (struct sockaddr *) &addrout, sizeof(struct sockaddr_in)) < 0) { ::perror("sendto"); return -1; } @@ -260,7 +260,7 @@ IPMIDIPort::write (byte* msg, size_t msglen, timestamp_t /* ignored */) { } int -IPMIDIPort::read (byte* buf, size_t bufsize) +IPMIDIPort::read (byte* /*buf*/, size_t /*bufsize*/) { /* nothing to do here - all handled by parse() */ return 0; diff --git a/libs/midi++2/jack_midi_port.cc b/libs/midi++2/jack_midi_port.cc index 729fc789d4..05eebc5aa1 100644 --- a/libs/midi++2/jack_midi_port.cc +++ b/libs/midi++2/jack_midi_port.cc @@ -76,7 +76,7 @@ JackMIDIPort::JackMIDIPort (const XMLNode& node, jack_client_t* jack_client) } void -JackMIDIPort::init (string const & name, Flags flags) +JackMIDIPort::init (const string& /*name*/, Flags /*flags*/) { if (!create_port ()) { _ok = true; @@ -206,7 +206,7 @@ JackMIDIPort::drain (int check_interval_usecs) } int -JackMIDIPort::write(byte * msg, size_t msglen, timestamp_t timestamp) +JackMIDIPort::write (const byte * msg, size_t msglen, timestamp_t timestamp) { int ret = 0; diff --git a/libs/midi++2/midi++/ipmidi_port.h b/libs/midi++2/midi++/ipmidi_port.h index 7df9642321..0441626565 100644 --- a/libs/midi++2/midi++/ipmidi_port.h +++ b/libs/midi++2/midi++/ipmidi_port.h @@ -55,7 +55,7 @@ class IPMIDIPort : public Port { XMLNode& get_state () const; void set_state (const XMLNode&); - int write (byte *msg, size_t msglen, timestamp_t timestamp); + int write (const byte *msg, size_t msglen, timestamp_t timestamp); int read (byte *buf, size_t bufsize); void parse (framecnt_t timestamp); int selectable () const; diff --git a/libs/midi++2/midi++/jack_midi_port.h b/libs/midi++2/midi++/jack_midi_port.h index e381120a99..b8d208c77a 100644 --- a/libs/midi++2/midi++/jack_midi_port.h +++ b/libs/midi++2/midi++/jack_midi_port.h @@ -54,7 +54,7 @@ class JackMIDIPort : public Port { void cycle_end (); void parse (framecnt_t timestamp); - int write (byte *msg, size_t msglen, timestamp_t timestamp); + int write (const byte *msg, size_t msglen, timestamp_t timestamp); int read (byte *buf, size_t bufsize); void drain (int check_interval_usecs); int selectable () const { return xthread.selectable(); } diff --git a/libs/midi++2/midi++/port.h b/libs/midi++2/midi++/port.h index a2315f7284..03abd1a41c 100644 --- a/libs/midi++2/midi++/port.h +++ b/libs/midi++2/midi++/port.h @@ -57,7 +57,7 @@ class Port { // FIXME: make Manager a friend of port so these can be hidden? /* Only for use by MidiManager. Don't ever call this. */ - virtual void cycle_start (pframes_t nframes) {} + virtual void cycle_start (pframes_t) {} /* Only for use by MidiManager. Don't ever call this. */ virtual void cycle_end () {} @@ -67,7 +67,7 @@ class Port { * @param timestamp Time stamp in frames of this message (relative to cycle start) * @return number of bytes successfully written */ - virtual int write (byte *msg, size_t msglen, timestamp_t timestamp) = 0; + virtual int write (const byte *msg, size_t msglen, timestamp_t timestamp) = 0; /** Read raw bytes from a port. * @param buf memory to store read data in @@ -82,7 +82,7 @@ class Port { * executes any part of a JACK process callback (will * simply return immediately in that situation). */ - virtual void drain (int check_interval_usecs) {} + virtual void drain (int /* check_interval_usecs */) {} /** Write a message to port. * @return true on success. diff --git a/libs/pbd/base_ui.cc b/libs/pbd/base_ui.cc index 6c21549e8b..14a3ef644d 100644 --- a/libs/pbd/base_ui.cc +++ b/libs/pbd/base_ui.cc @@ -76,9 +76,19 @@ BaseUI::main_thread () DEBUG_TRACE (DEBUG::EventLoop, string_compose ("%1: event loop running in thread %2\n", name(), pthread_self())); set_event_loop_for_thread (this); thread_init (); + _main_loop->get_context()->signal_idle().connect (sigc::mem_fun (*this, &BaseUI::signal_running)); _main_loop->run (); } +bool +BaseUI::signal_running () +{ + Glib::Mutex::Lock lm (_run_lock); + _running.signal (); + + return false; // don't call it again +} + void BaseUI::run () { @@ -91,13 +101,15 @@ BaseUI::run () /* glibmm hack - drop the refptr to the IOSource now before it can hurt */ request_channel.drop_ios (); + Glib::Mutex::Lock lm (_run_lock); run_loop_thread = Thread::create (mem_fun (*this, &BaseUI::main_thread), true); + _running.wait (_run_lock); } void BaseUI::quit () { - if (_main_loop->is_running()) { + if (_main_loop && _main_loop->is_running()) { _main_loop->quit (); run_loop_thread->join (); } diff --git a/libs/pbd/convert.cc b/libs/pbd/convert.cc index 07407e2fad..1787d3b70d 100644 --- a/libs/pbd/convert.cc +++ b/libs/pbd/convert.cc @@ -28,6 +28,8 @@ #endif #include +#include + #include "pbd/convert.h" #include "i18n.h" @@ -264,15 +266,15 @@ string_is_affirmative (const std::string& str) return false; } - /* the use of g_strncasecmp() is solely to get around issues with + /* the use of g_ascii_strncasecmp() is solely to get around issues with * charsets posed by trying to use C++ for the same * comparison. switching a std::string to its lower- or upper-case * version has several issues, but handled by default * in the way we desire when doing it in C. */ - return str == "1" || str == "y" || str == "Y" || (!g_strncasecmp(str.c_str(), "yes", str.length())) || - (!g_strncasecmp(str.c_str(), "true", str.length())); + return str == "1" || str == "y" || str == "Y" || (!g_ascii_strncasecmp(str.c_str(), "yes", str.length())) || + (!g_ascii_strncasecmp(str.c_str(), "true", str.length())); } /** A wrapper for dgettext that takes a msgid of the form Context|Text. diff --git a/libs/pbd/crossthread.cc b/libs/pbd/crossthread.cc index 0fbd1034bd..2ffede5163 100644 --- a/libs/pbd/crossthread.cc +++ b/libs/pbd/crossthread.cc @@ -104,7 +104,7 @@ CrossThreadChannel::drain (int fd) { /* drain selectable fd */ char buf[64]; - while (::read (fd, buf, sizeof (buf)) > 0) {} + while (::read (fd, buf, sizeof (buf)) > 0) {}; } int diff --git a/libs/pbd/pbd/base_ui.h b/libs/pbd/pbd/base_ui.h index 414c9970d9..65a9f27b49 100644 --- a/libs/pbd/pbd/base_ui.h +++ b/libs/pbd/pbd/base_ui.h @@ -41,7 +41,7 @@ */ -class BaseUI : virtual public sigc::trackable, public PBD::EventLoop +class BaseUI : public sigc::trackable, public PBD::EventLoop { public: BaseUI (const std::string& name); @@ -76,6 +76,14 @@ class BaseUI : virtual public sigc::trackable, public PBD::EventLoop Glib::RefPtr _main_loop; Glib::Thread* run_loop_thread; + Glib::Mutex _run_lock; + Glib::Cond _running; + + /* this signals _running from within the event loop, + from an idle callback + */ + + bool signal_running (); /** Derived UI objects can implement thread_init() * which will be called by the event loop thread diff --git a/libs/pbd/pbd/receiver.h b/libs/pbd/pbd/receiver.h index 5e32c7d67f..32fb84fa38 100644 --- a/libs/pbd/pbd/receiver.h +++ b/libs/pbd/pbd/receiver.h @@ -28,7 +28,7 @@ class strstream; -class Receiver : virtual public sigc::trackable +class Receiver : public sigc::trackable { public: Receiver (); diff --git a/libs/pbd/pbd/semaphore.h b/libs/pbd/pbd/semaphore.h index f1b07ea4f5..e54063a15a 100644 --- a/libs/pbd/pbd/semaphore.h +++ b/libs/pbd/pbd/semaphore.h @@ -81,7 +81,7 @@ private: inline Semaphore::Semaphore(unsigned initial) { - if (semaphore_create(mach_task_self(), &sem->sem, SYNC_POLICY_FIFO, 0)) { + if (semaphore_create(mach_task_self(), &_sem, SYNC_POLICY_FIFO, initial)) { throw failed_constructor(); } } @@ -148,7 +148,7 @@ Semaphore::wait() inline bool Semaphore::try_wait() { - return WaitForSingleObject(sem->sem, 0) == WAIT_OBJECT_0; + return WaitForSingleObject(_sem, 0) == WAIT_OBJECT_0; } #else /* !defined(__APPLE__) && !defined(_WIN32) */ diff --git a/libs/pbd/pbd/signal.h.py b/libs/pbd/pbd/signal.h.py new file mode 100644 index 0000000000..70a1a2d88c --- /dev/null +++ b/libs/pbd/pbd/signal.h.py @@ -0,0 +1,214 @@ +#!/usr/bin/python + +import sys + +if len(sys.argv) < 2: + print 'Syntax: %s ' % sys.argv[0] + sys.exit(1) + +f = open(sys.argv[1], 'w') + +print >>f,""" +/** THIS FILE IS AUTOGENERATED: DO NOT EDIT. + * + * This file is generated by signals.h.py. + */ + +#include +#include +#include +#include +#include +#include +#include "pbd/stacktrace.h" + +namespace PBD { + +class Connection; + +class SignalBase : public boost::enable_shared_from_this +{ +public: + virtual ~SignalBase () {} + virtual void disconnect (boost::shared_ptr) = 0; + +protected: + boost::mutex _mutex; +}; + +class Connection : public boost::enable_shared_from_this +{ +public: + Connection (boost::shared_ptr b) : _signal (b) {} + + void disconnect () + { + if (_signal) { + _signal->disconnect (shared_from_this ()); + } + } + +private: + boost::shared_ptr _signal; +}; + +template +class OptionalLastValue +{ +public: + typedef boost::optional result_type; + + template + result_type operator() (Iter first, Iter last) const { + result_type r; + while (first != last) { + r = *first; + ++first; + } + + return r; + } +}; +""" + +def comma_separated(n, prefix = ""): + r = "" + for i in range(0, len(n)): + if i > 0: + r += ", " + r += "%s%s" % (prefix, n[i]) + return r + +def simple_signal(f, n, v): + + An = [] + for i in range(0, n): + An.append("A%d" % (i + 1)) + + if v: + print >>f,"template <%s>" % comma_separated(An, "typename ") + print >>f,"class SimpleSignal%d<%s> : public SignalBase" % (n, comma_separated(["void"] + An)) + else: + print >>f,"template <%s>" % comma_separated(["R"] + An + ["C = OptionalLastValue "], "typename ") + print >>f,"class SimpleSignal%d : public SignalBase" % n + + print >>f,"{" + print >>f,"public:" + print >>f,"" + if v: + print >>f,"\ttypedef boost::function slot_function_type;" % comma_separated(An) + print >>f,"\ttypedef void result_type;" + else: + print >>f,"\ttypedef boost::function slot_function_type;" % comma_separated(An) + print >>f,"\ttypedef boost::optional result_type;" + + print >>f,"private:" + print >>f,"" + + print >>f,""" + typedef std::map, slot_function_type> Slots; + Slots _slots; +""" + + print >>f,"public:" + print >>f,"" + print >>f,""" + boost::shared_ptr connect (slot_function_type f) + { + boost::shared_ptr c (new Connection (shared_from_this ())); + boost::mutex::scoped_lock lm (_mutex); + _slots[c] = f; + return c; + } +""" + + Anan = [] + for a in An: + Anan.append('%s %s' % (a, a.lower())) + + an = [] + for a in An: + an.append(a.lower()) + + if v: + print >>f,"\tvoid emit (%s)" % comma_separated(Anan) + else: + print >>f,"\ttypename C::result_type emit (%s)" % comma_separated(Anan) + print >>f,"\t{" + print >>f,"\t\tSlots s;" + print >>f,"\t\t{" + print >>f,"\t\t\tboost::mutex::scoped_lock lm (_mutex);" + print >>f,"\t\t\ts = _slots;" + print >>f,"\t\t}" + if not v: + print >>f,"\t\tstd::list r;" + if n == 0 and v: + print >>f,""" +#if __GNUC__ > 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ >= 6)) + for (typename Slots::iterator i = s.begin(); i != s.end(); ++i) { +#else + for (Slots::iterator i = s.begin(); i != s.end(); ++i) { +#endif +""" + else: + print >>f,"\t\tfor (typename Slots::iterator i = s.begin(); i != s.end(); ++i) {" + + print >>f,""" + bool still_there = false; + { + boost::mutex::scoped_lock lm (_mutex); + still_there = _slots.find (i->first) != _slots.end (); + } + + if (still_there) {""" + if v: + print >>f,"\t\t\t\t(i->second)(%s);" % comma_separated(an) + else: + print >>f,"\t\t\t\tr.push_back ((i->second)(%s));" % comma_separated(an) + print >>f,"\t\t\t}" + print >>f,"\t\t}" + if not v: + print >>f,"\t\tC c;" + print >>f,"\t\treturn c (r.begin(), r.end());" + print >>f,"\t}" + + print >>f,""" + bool empty () { + boost::mutex::scoped_lock lm (_mutex); + return _slots.empty (); + } +""" + + + if v: + tp = comma_separated(["void"] + An) + else: + tp = comma_separated(["R"] + An + ["C"]) + + print >>f,"\tstatic boost::shared_ptr > create ()" % (n, tp) + print >>f,"\t{" + print >>f,"\t\treturn boost::shared_ptr > (new SimpleSignal%d<%s>);" % (n, tp, n, tp) + print >>f,"\t}" + + print >>f,"" + print >>f,"private:" + print >>f,"" + print >>f,"\tfriend class Connection;" + print >>f,"" + print >>f,"\tSimpleSignal%d () {}" % n + + print >>f,""" + void disconnect (boost::shared_ptr c) + { + boost::mutex::scoped_lock lm (_mutex); + _slots.erase (c); + } +}; +""" + + +for i in range(0, 6): + simple_signal(f, i, False) + simple_signal(f, i, True) + +print >>f,"}" diff --git a/libs/pbd/pbd/signals.h b/libs/pbd/pbd/signals.h index 1acc6022b0..7cdbcef414 100644 --- a/libs/pbd/pbd/signals.h +++ b/libs/pbd/pbd/signals.h @@ -23,23 +23,47 @@ #include #include -#include #include #include #include #include "pbd/event_loop.h" +#include "pbd/signal.h" namespace PBD { -typedef boost::signals2::connection UnscopedConnection; -typedef boost::signals2::scoped_connection ScopedConnection; +typedef boost::shared_ptr UnscopedConnection; +class ScopedConnection +{ +public: + ScopedConnection () {} + ScopedConnection (UnscopedConnection c) : _c (c) {} + ~ScopedConnection () { + disconnect (); + } + void disconnect () + { + if (_c) { + _c->disconnect (); + } + } + + ScopedConnection& operator= (UnscopedConnection const & o) + { + _c = o; + return *this; + } + +private: + UnscopedConnection _c; +}; + class ScopedConnectionList : public boost::noncopyable { public: ScopedConnectionList(); - ~ScopedConnectionList (); + virtual ~ScopedConnectionList (); void add_connection (const UnscopedConnection& c); void drop_connections (); @@ -73,19 +97,59 @@ class ScopedConnectionList : public boost::noncopyable template class Signal0 { public: - Signal0 () {} - typedef boost::signals2::signal SignalType; + typedef SimpleSignal0 SignalType; + + Signal0 () : _signal (SignalType::create ()) {} + + /** Arrange for @a slot to be executed whenever this signal is emitted. + Store the connection that represents this arrangement in @a c. + + NOTE: @a slot will be executed in the same thread that the signal is + emitted in. + */ void connect_same_thread (ScopedConnection& c, const typename SignalType::slot_function_type& slot) { - c = _signal.connect (slot); + c = _signal->connect (slot); } + /** Arrange for @a slot to be executed whenever this signal is emitted. + Add the connection that represents this arrangement to @a clist. + + NOTE: @a slot will be executed in the same thread that the signal is + emitted in. + */ + void connect_same_thread (ScopedConnectionList& clist, const typename SignalType::slot_function_type& slot) { - clist.add_connection (_signal.connect (slot)); + clist.add_connection (_signal->connect (slot)); } + /** Arrange for @a slot to be executed in the context of @a event_loop + whenever this signal is emitted. Add the connection that represents + this arrangement to @a clist. + + If the event loop/thread in which @a slot will be executed will + outlive the lifetime of any object referenced in @a slot, + then an InvalidationRecord should be passed, allowing + any request sent to the @a event_loop and not executed + before the object is destroyed to be marked invalid. + + "outliving the lifetime" doesn't have a specific, detailed meaning, + but is best illustrated by two contrasting examples: + + 1) the main GUI event loop/thread - this will outlive more or + less all objects in the application, and thus when arranging for + @a slot to be called in that context, an invalidation record is + highly advisable. + + 2) a secondary event loop/thread which will be destroyed along + with the objects that are typically referenced by @a slot. + Assuming that the event loop is stopped before the objects are + destroyed, there is no reason to pass in an invalidation record, + and MISSING_INVALIDATOR may be used. + */ + void connect (ScopedConnectionList& clist, PBD::EventLoop::InvalidationRecord* ir, const typename SignalType::slot_function_type& slot, @@ -93,9 +157,14 @@ public: if (ir) { ir->event_loop = event_loop; } - clist.add_connection (_signal.connect (boost::bind (&EventLoop::call_slot, event_loop, ir, slot))); + clist.add_connection (_signal->connect (boost::bind (&EventLoop::call_slot, event_loop, ir, slot))); } - + + /** See notes for the ScopedConnectionList variant of this function. This + * differs in that it stores the connection to the signal in a single + * ScopedConnection rather than a ScopedConnectionList. + */ + void connect (ScopedConnection& c, PBD::EventLoop::InvalidationRecord* ir, const typename SignalType::slot_function_type& slot, @@ -103,33 +172,42 @@ public: if (ir) { ir->event_loop = event_loop; } - c = _signal.connect (boost::bind (&EventLoop::call_slot, event_loop, ir, slot)); + c = _signal->connect (boost::bind (&EventLoop::call_slot, event_loop, ir, slot)); } + + /** Emit this signal. This will cause all slots connected to it be executed + in the order that they were connected (cross-thread issues may alter + the precise execution time of cross-thread slots). + */ typename SignalType::result_type operator()() { - return _signal (); + return _signal->emit (); } - bool empty() const { return _signal.empty(); } + /** Return true if there is nothing connected to this signal, false + * otherwise. + */ + + bool empty() const { return _signal->empty(); } private: - SignalType _signal; + boost::shared_ptr _signal; }; -template > +template > class Signal1 { public: - Signal1 () {} - typedef boost::signals2::signal SignalType; + typedef SimpleSignal1 SignalType; + Signal1 () : _signal (SignalType::create()) {} void connect_same_thread (ScopedConnectionList& clist, const typename SignalType::slot_function_type& slot) { - clist.add_connection (_signal.connect (slot)); + clist.add_connection (_signal->connect (slot)); } void connect_same_thread (ScopedConnection& c, const typename SignalType::slot_function_type& slot) { - c = _signal.connect (slot); + c = _signal->connect (slot); } static void compositor (typename boost::function f, EventLoop* event_loop, EventLoop::InvalidationRecord* ir, A arg) { @@ -143,7 +221,7 @@ public: if (ir) { ir->event_loop = event_loop; } - clist.add_connection (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1))); + clist.add_connection (_signal->connect (boost::bind (&compositor, slot, event_loop, ir, _1))); } void connect (ScopedConnection& c, @@ -153,34 +231,34 @@ public: if (ir) { ir->event_loop = event_loop; } - c = _signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1)); + c = _signal->connect (boost::bind (&compositor, slot, event_loop, ir, _1)); } typename SignalType::result_type operator()(A arg1) { - return _signal (arg1); + return _signal->emit (arg1); } - bool empty() const { return _signal.empty(); } + bool empty() const { return _signal->empty(); } private: - SignalType _signal; + boost::shared_ptr _signal; }; template class Signal2 { public: - Signal2 () {} - typedef boost::signals2::signal SignalType; + typedef SimpleSignal2 SignalType; + Signal2 () : _signal (SignalType::create()) {} void connect_same_thread (ScopedConnectionList& clist, const typename SignalType::slot_function_type& slot) { - clist.add_connection (_signal.connect (slot)); + clist.add_connection (_signal->connect (slot)); } void connect_same_thread (ScopedConnection& c, const typename SignalType::slot_function_type& slot) { - c = _signal.connect (slot); + c = _signal->connect (slot); } static void compositor (typename boost::function f, PBD::EventLoop* event_loop, @@ -196,7 +274,7 @@ public: if (ir) { ir->event_loop = event_loop; } - clist.add_connection (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2))); + clist.add_connection (_signal->connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2))); } void connect (ScopedConnection& c, @@ -206,33 +284,33 @@ public: if (ir) { ir->event_loop = event_loop; } - c = _signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2)); + c = _signal->connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2)); } typename SignalType::result_type operator()(A1 arg1, A2 arg2) { - return _signal (arg1, arg2); + return _signal->emit (arg1, arg2); } - bool empty() const { return _signal.empty(); } + bool empty() const { return _signal->empty(); } private: - SignalType _signal; + boost::shared_ptr _signal; }; template class Signal3 { public: - Signal3 () {} - typedef boost::signals2::signal SignalType; + typedef SimpleSignal3 SignalType; + Signal3 () : _signal (SignalType::create()) {} void connect_same_thread (ScopedConnectionList& clist, const typename SignalType::slot_function_type& slot) { - clist.add_connection (_signal.connect (slot)); + clist.add_connection (_signal->connect (slot)); } void connect_same_thread (ScopedConnection& c, const typename SignalType::slot_function_type& slot) { - c = _signal.connect (slot); + c = _signal->connect (slot); } static void compositor (typename boost::function f, PBD::EventLoop* event_loop, @@ -248,7 +326,7 @@ public: if (ir) { ir->event_loop = event_loop; } - clist.add_connection (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3))); + clist.add_connection (_signal->connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3))); } void connect (ScopedConnection& c, @@ -258,33 +336,33 @@ public: if (ir) { ir->event_loop = event_loop; } - c = _signal.connect (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3))); + c = _signal->connect (_signal->connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3))); } typename SignalType::result_type operator()(A1 arg1, A2 arg2, A3 arg3) { - return _signal (arg1, arg2, arg3); + return _signal->emit (arg1, arg2, arg3); } - bool empty() const { return _signal.empty(); } + bool empty() const { return _signal->empty(); } private: - SignalType _signal; + boost::shared_ptr _signal; }; template class Signal4 { public: - Signal4 () {} - typedef boost::signals2::signal SignalType; + typedef SimpleSignal4 SignalType; + Signal4 () : _signal (SignalType::create()) {} void connect_same_thread (ScopedConnectionList& clist, const typename SignalType::slot_function_type& slot) { - clist.add_connection (_signal.connect (slot)); + clist.add_connection (_signal->connect (slot)); } void connect_same_thread (ScopedConnection& c, const typename SignalType::slot_function_type& slot) { - c = _signal.connect (slot); + c = _signal->connect (slot); } static void compositor (typename boost::function f, PBD::EventLoop* event_loop, @@ -300,7 +378,7 @@ public: if (ir) { ir->event_loop = event_loop; } - clist.add_connection (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3, _4))); + clist.add_connection (_signal->connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3, _4))); } void connect (ScopedConnection& c, @@ -310,33 +388,33 @@ public: if (ir) { ir->event_loop = event_loop; } - c = _signal.connect (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3, _4))); + c = _signal->connect (_signal->connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3, _4))); } typename SignalType::result_type operator()(A1 arg1, A2 arg2, A3 arg3, A4 arg4) { - return _signal (arg1, arg2, arg3, arg4); + return _signal->emit (arg1, arg2, arg3, arg4); } - bool empty() const { return _signal.empty(); } + bool empty() const { return _signal->empty(); } private: - SignalType _signal; + boost::shared_ptr _signal; }; template class Signal5 { public: - Signal5 () {} - typedef boost::signals2::signal SignalType; + typedef SimpleSignal5 SignalType; + Signal5 () : _signal (SignalType::create()) {} void connect_same_thread (ScopedConnectionList& clist, const typename SignalType::slot_function_type& slot) { - clist.add_connection (_signal.connect (slot)); + clist.add_connection (_signal->connect (slot)); } void connect_same_thread (ScopedConnection& c, const typename SignalType::slot_function_type& slot) { - c = _signal.connect (slot); + c = _signal->connect (slot); } static void compositor (typename boost::function f, PBD::EventLoop* event_loop, @@ -352,7 +430,7 @@ public: if (ir) { ir->event_loop = event_loop; } - clist.add_connection (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3, _4, _5))); + clist.add_connection (_signal->connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3, _4, _5))); } void connect (ScopedConnection& c, @@ -362,17 +440,17 @@ public: if (ir) { ir->event_loop = event_loop; } - c = _signal.connect (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3, _4, _5))); + c = _signal->connect (_signal->connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3, _4, _5))); } typename SignalType::result_type operator()(A1 arg1, A2 arg2, A3 arg3, A4 arg4, A5 arg5) { - return _signal (arg1, arg2, arg3, arg4, arg5); + return _signal->emit (arg1, arg2, arg3, arg4, arg5); } - bool empty() const { return _signal.empty(); } + bool empty() const { return _signal->empty(); } private: - SignalType _signal; + boost::shared_ptr _signal; }; } /* namespace */ diff --git a/libs/pbd/signals.cc b/libs/pbd/signals.cc index 62cabff091..b37c6e211a 100644 --- a/libs/pbd/signals.cc +++ b/libs/pbd/signals.cc @@ -18,6 +18,7 @@ */ #include "pbd/signals.h" +#include "pbd/demangle.h" using namespace PBD; @@ -29,6 +30,7 @@ ScopedConnectionList::ScopedConnectionList() ScopedConnectionList::~ScopedConnectionList() { + std::cout << "~ScopedConnectionList " << this << " " << PBD::demangled_name (*this) << "\n"; drop_connections (); } diff --git a/libs/pbd/test/signals_test.cc b/libs/pbd/test/signals_test.cc index f635ea63da..9ca1bf536b 100644 --- a/libs/pbd/test/signals_test.cc +++ b/libs/pbd/test/signals_test.cc @@ -1,6 +1,8 @@ #include "signals_test.h" #include "pbd/signals.h" +using namespace std; + CPPUNIT_TEST_SUITE_REGISTRATION (SignalsTest); class Emitter { @@ -12,10 +14,30 @@ public: PBD::Signal0 Fred; }; +static int N = 0; + void receiver () { + ++N; +} +void +SignalsTest::testEmission () +{ + Emitter* e = new Emitter; + PBD::ScopedConnection c; + e->Fred.connect_same_thread (c, boost::bind (&receiver)); + + N = 0; + e->emit (); + e->emit (); + CPPUNIT_ASSERT_EQUAL (2, N); + + e->Fred.connect_same_thread (c, boost::bind (&receiver)); + N = 0; + e->emit (); + CPPUNIT_ASSERT_EQUAL (2, N); } void @@ -31,3 +53,29 @@ SignalsTest::testDestruction () CPPUNIT_ASSERT (true); } +class Receiver : public PBD::ScopedConnectionList +{ +public: + Receiver (Emitter* e) { + e->Fred.connect_same_thread (*this, boost::bind (&Receiver::receiver, this)); + } + + void receiver () { + cout << "Receiver::receiver\n"; + ++N; + } +}; + +void +SignalsTest::testScopedConnectionList () +{ + Emitter* e = new Emitter; + Receiver* r = new Receiver (e); + + N = 0; + e->emit (); + delete r; + e->emit (); + + CPPUNIT_ASSERT_EQUAL (1, N); +} diff --git a/libs/pbd/test/signals_test.h b/libs/pbd/test/signals_test.h index 9a66564705..8beb02ab1e 100644 --- a/libs/pbd/test/signals_test.h +++ b/libs/pbd/test/signals_test.h @@ -4,9 +4,13 @@ class SignalsTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE (SignalsTest); + CPPUNIT_TEST (testEmission); CPPUNIT_TEST (testDestruction); + CPPUNIT_TEST (testScopedConnectionList); CPPUNIT_TEST_SUITE_END (); public: + void testEmission (); void testDestruction (); + void testScopedConnectionList (); }; diff --git a/libs/pbd/test/xpath.cc b/libs/pbd/test/xpath.cc index 15062c6132..f0e976eabd 100644 --- a/libs/pbd/test/xpath.cc +++ b/libs/pbd/test/xpath.cc @@ -13,12 +13,12 @@ static string const prefix = "../libs/pbd/test/"; void XPathTest::testMisc () { - cout << "Test 1: RosegardenPatchFile.xml: Find all banks in the file" << endl; +// cout << "Test 1: RosegardenPatchFile.xml: Find all banks in the file" << endl; XMLTree doc(prefix + "RosegardenPatchFile.xml"); // "//bank" gives as last element an empty element libxml bug???? boost::shared_ptr result = doc.find("//bank[@name]"); - cout << "Found " << result->size() << " banks" << endl; +// cout << "Found " << result->size() << " banks" << endl; assert(result->size() == 8); // int counter = 1; for(XMLSharedNodeList::const_iterator i = result->begin(); i != result->end(); ++i) { @@ -31,7 +31,7 @@ XPathTest::testMisc () } } - cout << endl << endl << "Test 2: RosegardenPatchFile.xml: Find all programs whose program name contains 'Latin'" << endl; +// cout << endl << endl << "Test 2: RosegardenPatchFile.xml: Find all programs whose program name contains 'Latin'" << endl; result = doc.find("/rosegarden-data/studio/device/bank/program[contains(@name, 'Latin')]"); assert(result->size() == 5); @@ -53,7 +53,7 @@ XPathTest::testMisc () // "' with id: " << (*i)->property("id")->value() << endl; } - cout << endl << endl << "Test 4: TestSession.ardour: Find all elements with an 'id' and 'name' attribute" << endl; +// cout << endl << endl << "Test 4: TestSession.ardour: Find all elements with an 'id' and 'name' attribute" << endl; result = doc2.find("//*[@id and @name]"); @@ -65,7 +65,7 @@ XPathTest::testMisc () // "' and name: " << (*i)->property("name")->value() << endl; } - cout << endl << endl << "Test 5: ProtoolsPatchFile.midnam: Get Banks and Patches for 'Name Set 1'" << endl; +// cout << endl << endl << "Test 5: ProtoolsPatchFile.midnam: Get Banks and Patches for 'Name Set 1'" << endl; // We have to allocate a new document here, or we get segfaults XMLTree doc3(prefix + "ProtoolsPatchFile.midnam"); @@ -81,7 +81,7 @@ XPathTest::testMisc () } } - cout << endl << endl << "Test 5: ProtoolsPatchFile.midnam: Find attribute nodes" << endl; +// cout << endl << endl << "Test 5: ProtoolsPatchFile.midnam: Find attribute nodes" << endl; result = doc3.find("//@Value"); for(XMLSharedNodeList::const_iterator i = result->begin(); i != result->end(); ++i) { @@ -90,7 +90,7 @@ XPathTest::testMisc () // << " value: " << node->attribute_value() << endl; } - cout << endl << endl << "Test 6: ProtoolsPatchFile.midnam: Find available channels on 'Name Set 1'" << endl; +// cout << endl << endl << "Test 6: ProtoolsPatchFile.midnam: Find available channels on 'Name Set 1'" << endl; result = doc3.find( "//ChannelNameSet[@Name = 'Name Set 1']//AvailableChannel[@Available = 'true']/@Channel"); diff --git a/libs/pbd/wscript b/libs/pbd/wscript index b050544524..7e48f40019 100644 --- a/libs/pbd/wscript +++ b/libs/pbd/wscript @@ -54,6 +54,10 @@ def configure(conf): # autowaf.check_header(conf, 'cxx', 'boost/uuid/uuid.hpp') def build(bld): + + # Make signal.h using signal.h.py + bld(rule = 'python ${SRC} ${TGT}', source = 'pbd/signal.h.py', target = 'pbd/signal.h') + # Library obj = bld(features = 'cxx cxxshlib') obj.source = ''' @@ -142,7 +146,9 @@ def build(bld): testobj.includes = obj.includes + ['test', '../pbd'] testobj.uselib = 'CPPUNIT XML SNDFILE' testobj.use = 'libpbd' - + testobj.name = 'libpbd-tests' + if sys.platform != 'darwin': + testobj.linkflags = ['-lrt'] def shutdown(): autowaf.shutdown() diff --git a/libs/surfaces/control_protocol/basic_ui.cc b/libs/surfaces/control_protocol/basic_ui.cc index 2c514f188e..2ed82cd8c3 100644 --- a/libs/surfaces/control_protocol/basic_ui.cc +++ b/libs/surfaces/control_protocol/basic_ui.cc @@ -107,7 +107,6 @@ BasicUI::add_marker (const std::string& markername) void BasicUI::rewind () { - std::cerr << "request transport speed of " << session->transport_speed() - 1.5 << std::endl; session->request_transport_speed (session->transport_speed() - 1.5); } diff --git a/libs/surfaces/control_protocol/control_protocol.cc b/libs/surfaces/control_protocol/control_protocol.cc index ec58dc2f21..e0eb57fc87 100644 --- a/libs/surfaces/control_protocol/control_protocol.cc +++ b/libs/surfaces/control_protocol/control_protocol.cc @@ -47,6 +47,7 @@ PBD::Signal0 ControlProtocol::VerticalZoomOutSelected; PBD::Signal1 ControlProtocol::TrackSelectionChanged; PBD::Signal1 ControlProtocol::AddRouteToSelection; PBD::Signal1 ControlProtocol::SetRouteSelection; +PBD::Signal1 ControlProtocol::ToggleRouteSelection; PBD::Signal1 ControlProtocol::RemoveRouteFromSelection; PBD::Signal0 ControlProtocol::ClearRouteSelection; PBD::Signal0 ControlProtocol::StepTracksDown; diff --git a/libs/surfaces/control_protocol/control_protocol/control_protocol.h b/libs/surfaces/control_protocol/control_protocol/control_protocol.h index b75a04db2a..155c9236da 100644 --- a/libs/surfaces/control_protocol/control_protocol/control_protocol.h +++ b/libs/surfaces/control_protocol/control_protocol/control_protocol.h @@ -39,7 +39,8 @@ class Route; class Session; class Bundle; -class ControlProtocol : public PBD::Stateful, public PBD::ScopedConnectionList, public BasicUI { +class ControlProtocol : public PBD::Stateful, public PBD::ScopedConnectionList, public BasicUI +{ public: ControlProtocol (Session&, std::string name); virtual ~ControlProtocol(); @@ -78,6 +79,7 @@ class ControlProtocol : public PBD::Stateful, public PBD::ScopedConnectionList, static PBD::Signal1 AddRouteToSelection; static PBD::Signal1 SetRouteSelection; + static PBD::Signal1 ToggleRouteSelection; static PBD::Signal1 RemoveRouteFromSelection; static PBD::Signal0 ClearRouteSelection; diff --git a/libs/surfaces/control_protocol/control_protocol/types.h b/libs/surfaces/control_protocol/control_protocol/types.h index 0f81d87d0a..9e96383659 100644 --- a/libs/surfaces/control_protocol/control_protocol/types.h +++ b/libs/surfaces/control_protocol/control_protocol/types.h @@ -26,9 +26,11 @@ namespace ARDOUR { class Route; + + typedef std::vector > RouteNotificationList; + typedef boost::shared_ptr RouteNotificationListPtr; - typedef std::vector > RouteNotificationList; - typedef boost::shared_ptr RouteNotificationListPtr; + typedef std::vector > StrongRouteNotificationList; } #endif /* __ardour_control_protocol_types_h__ */ diff --git a/libs/surfaces/mackie/button.cc b/libs/surfaces/mackie/button.cc index dc26429a63..836e7390df 100644 --- a/libs/surfaces/mackie/button.cc +++ b/libs/surfaces/mackie/button.cc @@ -39,102 +39,102 @@ Button::factory (Surface& surface, Button::ID bid, int id, const std::string& na int Button::name_to_id (const std::string& name) { - if (!g_strcasecmp (name.c_str(), "IO")) { return IO; } - if (!g_strcasecmp (name.c_str(), "Sends")) { return Sends; } - if (!g_strcasecmp (name.c_str(), "Pan")) { return Pan; } - if (!g_strcasecmp (name.c_str(), "Plugin")) { return Plugin; } - if (!g_strcasecmp (name.c_str(), "Eq")) { return Eq; } - if (!g_strcasecmp (name.c_str(), "Dyn")) { return Dyn; } - if (!g_strcasecmp (name.c_str(), "Left")) { return Left; } - if (!g_strcasecmp (name.c_str(), "Right")) { return Right; } - if (!g_strcasecmp (name.c_str(), "ChannelLeft")) { return ChannelLeft; } - if (!g_strcasecmp (name.c_str(), "ChannelRight")) { return ChannelRight; } - if (!g_strcasecmp (name.c_str(), "Flip")) { return Flip; } - if (!g_strcasecmp (name.c_str(), "Edit")) { return Edit; } - if (!g_strcasecmp (name.c_str(), "Name/Value")) { return NameValue; } - if (!g_strcasecmp (name.c_str(), "Timecode/Beats")) { return TimecodeBeats; } - if (!g_strcasecmp (name.c_str(), "F1")) { return F1; } - if (!g_strcasecmp (name.c_str(), "F2")) { return F2; } - if (!g_strcasecmp (name.c_str(), "F3")) { return F3; } - if (!g_strcasecmp (name.c_str(), "F4")) { return F4; } - if (!g_strcasecmp (name.c_str(), "F5")) { return F5; } - if (!g_strcasecmp (name.c_str(), "F6")) { return F6; } - if (!g_strcasecmp (name.c_str(), "F7")) { return F7; } - if (!g_strcasecmp (name.c_str(), "F8")) { return F8; } - if (!g_strcasecmp (name.c_str(), "F9")) { return F9; } - if (!g_strcasecmp (name.c_str(), "F10")) { return F10; } - if (!g_strcasecmp (name.c_str(), "F11")) { return F11; } - if (!g_strcasecmp (name.c_str(), "F12")) { return F12; } - if (!g_strcasecmp (name.c_str(), "F13")) { return F13; } - if (!g_strcasecmp (name.c_str(), "F14")) { return F14; } - if (!g_strcasecmp (name.c_str(), "F15")) { return F15; } - if (!g_strcasecmp (name.c_str(), "F16")) { return F16; } - if (!g_strcasecmp (name.c_str(), "Shift")) { return Shift; } - if (!g_strcasecmp (name.c_str(), "Option")) { return Option; } - if (!g_strcasecmp (name.c_str(), "Ctrl")) { return Ctrl; } - if (!g_strcasecmp (name.c_str(), "CmdAlt")) { return CmdAlt; } - if (!g_strcasecmp (name.c_str(), "On")) { return On; } - if (!g_strcasecmp (name.c_str(), "RecReady")) { return RecReady; } - if (!g_strcasecmp (name.c_str(), "Undo")) { return Undo; } - if (!g_strcasecmp (name.c_str(), "Save")) { return Save; } - if (!g_strcasecmp (name.c_str(), "Touch")) { return Touch; } - if (!g_strcasecmp (name.c_str(), "Redo")) { return Redo; } - if (!g_strcasecmp (name.c_str(), "Marker")) { return Marker; } - if (!g_strcasecmp (name.c_str(), "Enter")) { return Enter; } - if (!g_strcasecmp (name.c_str(), "Cancel")) { return Cancel; } - if (!g_strcasecmp (name.c_str(), "Mixer")) { return Mixer; } - if (!g_strcasecmp (name.c_str(), "FrmLeft")) { return FrmLeft; } - if (!g_strcasecmp (name.c_str(), "FrmRight")) { return FrmRight; } - if (!g_strcasecmp (name.c_str(), "Loop")) { return Loop; } - if (!g_strcasecmp (name.c_str(), "PunchIn")) { return PunchIn; } - if (!g_strcasecmp (name.c_str(), "PunchOut")) { return PunchOut; } - if (!g_strcasecmp (name.c_str(), "Home")) { return Home; } - if (!g_strcasecmp (name.c_str(), "End")) { return End; } - if (!g_strcasecmp (name.c_str(), "Rewind")) { return Rewind; } - if (!g_strcasecmp (name.c_str(), "Ffwd")) { return Ffwd; } - if (!g_strcasecmp (name.c_str(), "Stop")) { return Stop; } - if (!g_strcasecmp (name.c_str(), "Play")) { return Play; } - if (!g_strcasecmp (name.c_str(), "Record")) { return Record; } - if (!g_strcasecmp (name.c_str(), "CursorUp")) { return CursorUp; } - if (!g_strcasecmp (name.c_str(), "CursorDown")) { return CursorDown; } - if (!g_strcasecmp (name.c_str(), "CursorLeft")) { return CursorLeft; } - if (!g_strcasecmp (name.c_str(), "CursorRight")) { return CursorRight; } - if (!g_strcasecmp (name.c_str(), "Zoom")) { return Zoom; } - if (!g_strcasecmp (name.c_str(), "Scrub")) { return Scrub; } - if (!g_strcasecmp (name.c_str(), "UserA")) { return UserA; } - if (!g_strcasecmp (name.c_str(), "UserB")) { return UserB; } - if (!g_strcasecmp (name.c_str(), "Snapshot")) { return Snapshot; } - if (!g_strcasecmp (name.c_str(), "Read")) { return Read; } - if (!g_strcasecmp (name.c_str(), "Write")) { return Write; } - if (!g_strcasecmp (name.c_str(), "FdrGroup")) { return FdrGroup; } - if (!g_strcasecmp (name.c_str(), "ClearSolo")) { return ClearSolo; } - if (!g_strcasecmp (name.c_str(), "Track")) { return Track; } - if (!g_strcasecmp (name.c_str(), "Send")) { return Send; } - if (!g_strcasecmp (name.c_str(), "MidiTracks")) { return MidiTracks; } - if (!g_strcasecmp (name.c_str(), "Inputs")) { return Inputs; } - if (!g_strcasecmp (name.c_str(), "AudioTracks")) { return AudioTracks; } - if (!g_strcasecmp (name.c_str(), "AudioInstruments")) { return AudioInstruments; } - if (!g_strcasecmp (name.c_str(), "Aux")) { return Aux; } - if (!g_strcasecmp (name.c_str(), "Busses")) { return Busses; } - if (!g_strcasecmp (name.c_str(), "Outputs")) { return Outputs; } - if (!g_strcasecmp (name.c_str(), "User")) { return User; } - if (!g_strcasecmp (name.c_str(), "Trim")) { return Trim; } - if (!g_strcasecmp (name.c_str(), "Latch")) { return Latch; } - if (!g_strcasecmp (name.c_str(), "Grp")) { return Grp; } - if (!g_strcasecmp (name.c_str(), "Nudge")) { return Nudge; } - if (!g_strcasecmp (name.c_str(), "Drop")) { return Drop; } - if (!g_strcasecmp (name.c_str(), "Replace")) { return Replace; } - if (!g_strcasecmp (name.c_str(), "Click")) { return Click; } - if (!g_strcasecmp (name.c_str(), "View")) { return View; } + if (!g_ascii_strcasecmp (name.c_str(), "IO")) { return IO; } + if (!g_ascii_strcasecmp (name.c_str(), "Sends")) { return Sends; } + if (!g_ascii_strcasecmp (name.c_str(), "Pan")) { return Pan; } + if (!g_ascii_strcasecmp (name.c_str(), "Plugin")) { return Plugin; } + if (!g_ascii_strcasecmp (name.c_str(), "Eq")) { return Eq; } + if (!g_ascii_strcasecmp (name.c_str(), "Dyn")) { return Dyn; } + if (!g_ascii_strcasecmp (name.c_str(), "Left")) { return Left; } + if (!g_ascii_strcasecmp (name.c_str(), "Right")) { return Right; } + if (!g_ascii_strcasecmp (name.c_str(), "ChannelLeft")) { return ChannelLeft; } + if (!g_ascii_strcasecmp (name.c_str(), "ChannelRight")) { return ChannelRight; } + if (!g_ascii_strcasecmp (name.c_str(), "Flip")) { return Flip; } + if (!g_ascii_strcasecmp (name.c_str(), "Edit")) { return Edit; } + if (!g_ascii_strcasecmp (name.c_str(), "Name/Value")) { return NameValue; } + if (!g_ascii_strcasecmp (name.c_str(), "Timecode/Beats")) { return TimecodeBeats; } + if (!g_ascii_strcasecmp (name.c_str(), "F1")) { return F1; } + if (!g_ascii_strcasecmp (name.c_str(), "F2")) { return F2; } + if (!g_ascii_strcasecmp (name.c_str(), "F3")) { return F3; } + if (!g_ascii_strcasecmp (name.c_str(), "F4")) { return F4; } + if (!g_ascii_strcasecmp (name.c_str(), "F5")) { return F5; } + if (!g_ascii_strcasecmp (name.c_str(), "F6")) { return F6; } + if (!g_ascii_strcasecmp (name.c_str(), "F7")) { return F7; } + if (!g_ascii_strcasecmp (name.c_str(), "F8")) { return F8; } + if (!g_ascii_strcasecmp (name.c_str(), "F9")) { return F9; } + if (!g_ascii_strcasecmp (name.c_str(), "F10")) { return F10; } + if (!g_ascii_strcasecmp (name.c_str(), "F11")) { return F11; } + if (!g_ascii_strcasecmp (name.c_str(), "F12")) { return F12; } + if (!g_ascii_strcasecmp (name.c_str(), "F13")) { return F13; } + if (!g_ascii_strcasecmp (name.c_str(), "F14")) { return F14; } + if (!g_ascii_strcasecmp (name.c_str(), "F15")) { return F15; } + if (!g_ascii_strcasecmp (name.c_str(), "F16")) { return F16; } + if (!g_ascii_strcasecmp (name.c_str(), "Shift")) { return Shift; } + if (!g_ascii_strcasecmp (name.c_str(), "Option")) { return Option; } + if (!g_ascii_strcasecmp (name.c_str(), "Ctrl")) { return Ctrl; } + if (!g_ascii_strcasecmp (name.c_str(), "CmdAlt")) { return CmdAlt; } + if (!g_ascii_strcasecmp (name.c_str(), "On")) { return On; } + if (!g_ascii_strcasecmp (name.c_str(), "RecReady")) { return RecReady; } + if (!g_ascii_strcasecmp (name.c_str(), "Undo")) { return Undo; } + if (!g_ascii_strcasecmp (name.c_str(), "Save")) { return Save; } + if (!g_ascii_strcasecmp (name.c_str(), "Touch")) { return Touch; } + if (!g_ascii_strcasecmp (name.c_str(), "Redo")) { return Redo; } + if (!g_ascii_strcasecmp (name.c_str(), "Marker")) { return Marker; } + if (!g_ascii_strcasecmp (name.c_str(), "Enter")) { return Enter; } + if (!g_ascii_strcasecmp (name.c_str(), "Cancel")) { return Cancel; } + if (!g_ascii_strcasecmp (name.c_str(), "Mixer")) { return Mixer; } + if (!g_ascii_strcasecmp (name.c_str(), "FrmLeft")) { return FrmLeft; } + if (!g_ascii_strcasecmp (name.c_str(), "FrmRight")) { return FrmRight; } + if (!g_ascii_strcasecmp (name.c_str(), "Loop")) { return Loop; } + if (!g_ascii_strcasecmp (name.c_str(), "PunchIn")) { return PunchIn; } + if (!g_ascii_strcasecmp (name.c_str(), "PunchOut")) { return PunchOut; } + if (!g_ascii_strcasecmp (name.c_str(), "Home")) { return Home; } + if (!g_ascii_strcasecmp (name.c_str(), "End")) { return End; } + if (!g_ascii_strcasecmp (name.c_str(), "Rewind")) { return Rewind; } + if (!g_ascii_strcasecmp (name.c_str(), "Ffwd")) { return Ffwd; } + if (!g_ascii_strcasecmp (name.c_str(), "Stop")) { return Stop; } + if (!g_ascii_strcasecmp (name.c_str(), "Play")) { return Play; } + if (!g_ascii_strcasecmp (name.c_str(), "Record")) { return Record; } + if (!g_ascii_strcasecmp (name.c_str(), "CursorUp")) { return CursorUp; } + if (!g_ascii_strcasecmp (name.c_str(), "CursorDown")) { return CursorDown; } + if (!g_ascii_strcasecmp (name.c_str(), "CursorLeft")) { return CursorLeft; } + if (!g_ascii_strcasecmp (name.c_str(), "CursorRight")) { return CursorRight; } + if (!g_ascii_strcasecmp (name.c_str(), "Zoom")) { return Zoom; } + if (!g_ascii_strcasecmp (name.c_str(), "Scrub")) { return Scrub; } + if (!g_ascii_strcasecmp (name.c_str(), "UserA")) { return UserA; } + if (!g_ascii_strcasecmp (name.c_str(), "UserB")) { return UserB; } + if (!g_ascii_strcasecmp (name.c_str(), "Snapshot")) { return Snapshot; } + if (!g_ascii_strcasecmp (name.c_str(), "Read")) { return Read; } + if (!g_ascii_strcasecmp (name.c_str(), "Write")) { return Write; } + if (!g_ascii_strcasecmp (name.c_str(), "FdrGroup")) { return FdrGroup; } + if (!g_ascii_strcasecmp (name.c_str(), "ClearSolo")) { return ClearSolo; } + if (!g_ascii_strcasecmp (name.c_str(), "Track")) { return Track; } + if (!g_ascii_strcasecmp (name.c_str(), "Send")) { return Send; } + if (!g_ascii_strcasecmp (name.c_str(), "MidiTracks")) { return MidiTracks; } + if (!g_ascii_strcasecmp (name.c_str(), "Inputs")) { return Inputs; } + if (!g_ascii_strcasecmp (name.c_str(), "AudioTracks")) { return AudioTracks; } + if (!g_ascii_strcasecmp (name.c_str(), "AudioInstruments")) { return AudioInstruments; } + if (!g_ascii_strcasecmp (name.c_str(), "Aux")) { return Aux; } + if (!g_ascii_strcasecmp (name.c_str(), "Busses")) { return Busses; } + if (!g_ascii_strcasecmp (name.c_str(), "Outputs")) { return Outputs; } + if (!g_ascii_strcasecmp (name.c_str(), "User")) { return User; } + if (!g_ascii_strcasecmp (name.c_str(), "Trim")) { return Trim; } + if (!g_ascii_strcasecmp (name.c_str(), "Latch")) { return Latch; } + if (!g_ascii_strcasecmp (name.c_str(), "Grp")) { return Grp; } + if (!g_ascii_strcasecmp (name.c_str(), "Nudge")) { return Nudge; } + if (!g_ascii_strcasecmp (name.c_str(), "Drop")) { return Drop; } + if (!g_ascii_strcasecmp (name.c_str(), "Replace")) { return Replace; } + if (!g_ascii_strcasecmp (name.c_str(), "Click")) { return Click; } + if (!g_ascii_strcasecmp (name.c_str(), "View")) { return View; } /* Strip buttons */ - if (!g_strcasecmp (name.c_str(), "RecEnable")) { return RecEnable; } - if (!g_strcasecmp (name.c_str(), "Solo")) { return Solo; } - if (!g_strcasecmp (name.c_str(), "Mute")) { return Mute; } - if (!g_strcasecmp (name.c_str(), "Select")) { return Select; } - if (!g_strcasecmp (name.c_str(), "VSelect")) { return VSelect; } - if (!g_strcasecmp (name.c_str(), "FaderTouch")) { return FaderTouch; } + if (!g_ascii_strcasecmp (name.c_str(), "RecEnable")) { return RecEnable; } + if (!g_ascii_strcasecmp (name.c_str(), "Solo")) { return Solo; } + if (!g_ascii_strcasecmp (name.c_str(), "Mute")) { return Mute; } + if (!g_ascii_strcasecmp (name.c_str(), "Select")) { return Select; } + if (!g_ascii_strcasecmp (name.c_str(), "VSelect")) { return VSelect; } + if (!g_ascii_strcasecmp (name.c_str(), "FaderTouch")) { return FaderTouch; } return -1; } diff --git a/libs/surfaces/mackie/device_info.cc b/libs/surfaces/mackie/device_info.cc index e9e5d1b04b..5646650eeb 100644 --- a/libs/surfaces/mackie/device_info.cc +++ b/libs/surfaces/mackie/device_info.cc @@ -50,6 +50,7 @@ DeviceInfo::DeviceInfo() , _has_touch_sense_faders (true) , _uses_logic_control_buttons (false) , _uses_ipmidi (false) + , _no_handshake (false) , _name (X_("Mackie Control Universal Pro")) { mackie_control_buttons (); @@ -281,6 +282,14 @@ DeviceInfo::set_state (const XMLNode& node, int /* version */) _uses_ipmidi = false; } + if ((child = node.child ("NoHandShake")) != 0) { + if ((prop = child->property ("value")) != 0) { + _no_handshake = string_is_affirmative (prop->value()); + } + } else { + _no_handshake = false; + } + if ((child = node.child ("LogicControlButtons")) != 0) { if ((prop = child->property ("value")) != 0) { _uses_logic_control_buttons = string_is_affirmative (prop->value()); @@ -395,6 +404,12 @@ DeviceInfo::has_jog_wheel () const return _has_jog_wheel; } +bool +DeviceInfo::no_handshake () const +{ + return _no_handshake; +} + bool DeviceInfo::has_touch_sense_faders () const { @@ -405,7 +420,7 @@ static const char * const devinfo_env_variable_name = "ARDOUR_MCP_PATH"; static const char* const devinfo_dir_name = "mcp"; static const char* const devinfo_suffix = ".device"; -static sys::path +static SearchPath system_devinfo_search_path () { bool devinfo_path_defined = false; @@ -418,12 +433,7 @@ system_devinfo_search_path () SearchPath spath (system_data_search_path()); spath.add_subdirectory_to_paths(devinfo_dir_name); - // just return the first directory in the search path that exists - SearchPath::const_iterator i = std::find_if(spath.begin(), spath.end(), sys::exists); - - if (i == spath.end()) return sys::path(); - - return *i; + return spath; } static sys::path diff --git a/libs/surfaces/mackie/device_info.h b/libs/surfaces/mackie/device_info.h index 4366cd09b1..294bc091ed 100644 --- a/libs/surfaces/mackie/device_info.h +++ b/libs/surfaces/mackie/device_info.h @@ -67,6 +67,7 @@ class DeviceInfo bool has_jog_wheel () const; bool has_touch_sense_faders() const; bool uses_ipmidi() const; + bool no_handshake() const; const std::string& name() const; static std::map device_info; @@ -86,6 +87,7 @@ class DeviceInfo bool _has_touch_sense_faders; bool _uses_logic_control_buttons; bool _uses_ipmidi; + bool _no_handshake; std::string _name; std::map _global_buttons; diff --git a/libs/surfaces/mackie/device_profile.cc b/libs/surfaces/mackie/device_profile.cc index 110038ca2b..d7667ce721 100644 --- a/libs/surfaces/mackie/device_profile.cc +++ b/libs/surfaces/mackie/device_profile.cc @@ -55,7 +55,7 @@ static const char * const devprofile_env_variable_name = "ARDOUR_MCP_PATH"; static const char* const devprofile_dir_name = "mcp"; static const char* const devprofile_suffix = ".profile"; -static sys::path +static SearchPath system_devprofile_search_path () { bool devprofile_path_defined = false; @@ -68,12 +68,7 @@ system_devprofile_search_path () SearchPath spath (system_data_search_path()); spath.add_subdirectory_to_paths(devprofile_dir_name); - // just return the first directory in the search path that exists - SearchPath::const_iterator i = std::find_if(spath.begin(), spath.end(), sys::exists); - - if (i == spath.end()) return sys::path(); - - return *i; + return spath; } static sys::path diff --git a/libs/surfaces/mackie/gui.cc b/libs/surfaces/mackie/gui.cc index 8728b4a88c..e27a354305 100644 --- a/libs/surfaces/mackie/gui.cc +++ b/libs/surfaces/mackie/gui.cc @@ -74,13 +74,14 @@ MackieControlProtocolGUI::MackieControlProtocolGUI (MackieControlProtocol& p) , recalibrate_fader_button (_("Recalibrate Faders")) , ipmidi_base_port_adjustment (_cp.ipmidi_base(), 0, 32767, 1, 1000) , ipmidi_base_port_spinner (ipmidi_base_port_adjustment) + , discover_button (_("Discover Mackie Devices")) { Gtk::Label* l; Gtk::Alignment* align; set_border_width (12); - Gtk::Table* table = Gtk::manage (new Gtk::Table (2, 8)); + Gtk::Table* table = Gtk::manage (new Gtk::Table (2, 9)); table->set_row_spacings (4); table->set_col_spacings (6); l = manage (new Gtk::Label (_("Device Type:"))); @@ -144,6 +145,10 @@ MackieControlProtocolGUI::MackieControlProtocolGUI (MackieControlProtocol& p) ipmidi_base_port_spinner.set_sensitive (_cp.device_info().uses_ipmidi()); ipmidi_base_port_adjustment.signal_value_changed().connect (sigc::mem_fun (*this, &MackieControlProtocolGUI::ipmidi_spinner_changed)); + + table->attach (discover_button, 1, 2, 8, 9, AttachOptions(FILL|EXPAND), AttachOptions (0)); + discover_button.signal_clicked().connect (sigc::mem_fun (*this, &MackieControlProtocolGUI::discover_clicked)); + vector profiles; profiles.push_back ("default"); @@ -510,3 +515,10 @@ MackieControlProtocolGUI::ipmidi_spinner_changed () cerr << "Set IP MIDI base to " << ipmidi_base_port_spinner.get_value() << endl; _cp.set_ipmidi_base ((int16_t) lrintf (ipmidi_base_port_spinner.get_value())); } + +void +MackieControlProtocolGUI::discover_clicked () +{ + /* this should help to get things started */ + _cp.midi_connectivity_established (); +} diff --git a/libs/surfaces/mackie/gui.h b/libs/surfaces/mackie/gui.h index 9af0c21cab..42ee338f99 100644 --- a/libs/surfaces/mackie/gui.h +++ b/libs/surfaces/mackie/gui.h @@ -105,5 +105,8 @@ class MackieControlProtocolGUI : public Gtk::Notebook Gtk::Button recalibrate_fader_button; Gtk::Adjustment ipmidi_base_port_adjustment; Gtk::SpinButton ipmidi_base_port_spinner; + Gtk::Button discover_button; + + void discover_clicked (); }; diff --git a/libs/surfaces/mackie/mackie_control_protocol.cc b/libs/surfaces/mackie/mackie_control_protocol.cc index c1f5317976..f8aada29bc 100644 --- a/libs/surfaces/mackie/mackie_control_protocol.cc +++ b/libs/surfaces/mackie/mackie_control_protocol.cc @@ -113,7 +113,7 @@ MackieControlProtocol::MackieControlProtocol (Session& session) DeviceInfo::reload_device_info (); DeviceProfile::reload_device_profiles (); - TrackSelectionChanged.connect (gui_connections, MISSING_INVALIDATOR, boost::bind (&MackieControlProtocol::gui_track_selection_changed, this, _1), this); + TrackSelectionChanged.connect (gui_connections, MISSING_INVALIDATOR, boost::bind (&MackieControlProtocol::gui_track_selection_changed, this, _1, true), this); _instance = this; @@ -124,6 +124,9 @@ MackieControlProtocol::~MackieControlProtocol() { DEBUG_TRACE (DEBUG::MackieControl, "MackieControlProtocol::~MackieControlProtocol\n"); + drop_connections (); + tear_down_gui (); + _active = false; /* stop event loop */ @@ -316,8 +319,10 @@ MackieControlProtocol::switch_banks (uint32_t initial, bool force) uint32_t strip_cnt = n_strips (false); // do not include locked strips // in this count - if (sorted.size() <= strip_cnt && !force) { - /* no banking - not enough routes to fill all strips */ + if (sorted.size() <= strip_cnt && _current_initial_bank == 0 && !force) { + /* no banking - not enough routes to fill all strips and we're + * not at the first one. + */ return; } @@ -352,6 +357,10 @@ MackieControlProtocol::switch_banks (uint32_t initial, bool force) /* reset this to get the right display of view mode after the switch */ set_view_mode (_view_mode); + + /* make sure selection is correct */ + + _gui_track_selection_changed (&_last_selected_routes, false); /* current bank has not been saved */ session->set_dirty(); @@ -1198,10 +1207,39 @@ MackieControlProtocol::force_special_route_to_strip (boost::shared_ptr r, } void -MackieControlProtocol::gui_track_selection_changed (ARDOUR::RouteNotificationListPtr rl) +MackieControlProtocol::gui_track_selection_changed (ARDOUR::RouteNotificationListPtr rl, bool save_list) { + _gui_track_selection_changed (rl.get(), save_list); +} + +void +MackieControlProtocol::_gui_track_selection_changed (ARDOUR::RouteNotificationList* rl, bool save_list) +{ + + /* We need to keep a list of the most recently selected routes around, + but we are not allowed to keep shared_ptr unless we want to + handle the complexities of route deletion. So instead, the GUI sends + us a notification using weak_ptr, which we keep a copy + of. For efficiency's sake, however, we convert the weak_ptr's into + shared_ptr before passing them to however many surfaces (and + thus strips) that we have. + */ + + StrongRouteNotificationList srl; + + for (ARDOUR::RouteNotificationList::const_iterator i = rl->begin(); i != rl->end(); ++i) { + boost::shared_ptr r = (*i).lock(); + if (r) { + srl.push_back (r); + } + } + for (Surfaces::iterator s = surfaces.begin(); s != surfaces.end(); ++s) { - (*s)->gui_selection_changed (rl); + (*s)->gui_selection_changed (srl); + } + + if (save_list) { + _last_selected_routes = *rl; } } @@ -1241,10 +1279,15 @@ MackieControlProtocol::select_range () if (!routes.empty()) { for (RouteList::iterator r = routes.begin(); r != routes.end(); ++r) { - if (r == routes.begin()) { - SetRouteSelection ((*r)->remote_control_id()); + + if (_modifier_state == MODIFIER_CONTROL) { + ToggleRouteSelection ((*r)->remote_control_id ()); } else { - AddRouteToSelection ((*r)->remote_control_id()); + if (r == routes.begin()) { + SetRouteSelection ((*r)->remote_control_id()); + } else { + AddRouteToSelection ((*r)->remote_control_id()); + } } } } diff --git a/libs/surfaces/mackie/mackie_control_protocol.h b/libs/surfaces/mackie/mackie_control_protocol.h index 0df770b36e..0fd5d8c8a2 100644 --- a/libs/surfaces/mackie/mackie_control_protocol.h +++ b/libs/surfaces/mackie/mackie_control_protocol.h @@ -195,6 +195,8 @@ class MackieControlProtocol int16_t ipmidi_base() const { return _ipmidi_base; } void set_ipmidi_base (int16_t); + + void midi_connectivity_established (); protected: // shut down the surface @@ -233,7 +235,6 @@ class MackieControlProtocol int stop (); void thread_init (); - void midi_connectivity_established (); bool route_is_locked_to_strip (boost::shared_ptr) const; @@ -286,6 +287,8 @@ class MackieControlProtocol int16_t _ipmidi_base; bool needs_ipmidi_restart; + ARDOUR::RouteNotificationList _last_selected_routes; + void create_surfaces (); bool periodic(); void build_gui (); @@ -293,7 +296,8 @@ class MackieControlProtocol void clear_ports (); void force_special_route_to_strip (boost::shared_ptr r, uint32_t surface, uint32_t strip_number); void build_button_map (); - void gui_track_selection_changed (ARDOUR::RouteNotificationListPtr); + void gui_track_selection_changed (ARDOUR::RouteNotificationListPtr, bool save_list); + void _gui_track_selection_changed (ARDOUR::RouteNotificationList*, bool save_list); void ipmidi_restart (); /* BUTTON HANDLING */ diff --git a/libs/surfaces/mackie/mcp_buttons.cc b/libs/surfaces/mackie/mcp_buttons.cc index 6ded9a9d20..c864b5b871 100644 --- a/libs/surfaces/mackie/mcp_buttons.cc +++ b/libs/surfaces/mackie/mcp_buttons.cc @@ -102,16 +102,13 @@ MackieControlProtocol::left_press (Button &) DEBUG_TRACE (DEBUG::MackieControl, string_compose ("bank left with current initial = %1 nstrips = %2 tracks/busses = %3\n", _current_initial_bank, strip_cnt, route_cnt)); - if (route_cnt && route_cnt > strip_cnt) { - if (_current_initial_bank > strip_cnt) { - switch_banks (_current_initial_bank - strip_cnt); - } else { - switch_banks (0); - } - - return on; + if (_current_initial_bank > strip_cnt) { + switch_banks (_current_initial_bank - strip_cnt); + } else { + switch_banks (0); } - return off; + + return on; } LedState @@ -128,15 +125,12 @@ MackieControlProtocol::right_press (Button &) uint32_t route_cnt = sorted.size(); DEBUG_TRACE (DEBUG::MackieControl, string_compose ("bank right with current initial = %1 nstrips = %2 tracks/busses = %3\n", - _current_initial_bank, strip_cnt, sorted.size())); + _current_initial_bank, strip_cnt, route_cnt)); - if (route_cnt && route_cnt > strip_cnt) { - uint32_t new_initial = std::min (_current_initial_bank + strip_cnt, route_cnt - 1); - switch_banks (new_initial); - return on; - } + uint32_t new_initial = std::min (_current_initial_bank + strip_cnt, route_cnt - 1); + switch_banks (new_initial); - return off; + return on; } LedState @@ -503,7 +497,7 @@ MackieControlProtocol::play_press (Button &) again, jump back to where we started last time */ - transport_play (session->transport_rolling() == 1.0); + transport_play (session->transport_speed() == 1.0); return none; } diff --git a/libs/surfaces/mackie/midi_byte_array.cc b/libs/surfaces/mackie/midi_byte_array.cc index 1c27c82be1..1e94e781c0 100644 --- a/libs/surfaces/mackie/midi_byte_array.cc +++ b/libs/surfaces/mackie/midi_byte_array.cc @@ -28,80 +28,67 @@ using namespace std; -MidiByteArray::MidiByteArray( size_t size, MIDI::byte array[] ) -: std::vector() +MidiByteArray::MidiByteArray (size_t size, MIDI::byte array[]) + : std::vector() { - for ( size_t i = 0; i < size; ++i ) + for (size_t i = 0; i < size; ++i) { - push_back( array[i] ); + push_back (array[i]); } } -MidiByteArray::MidiByteArray( size_t count, MIDI::byte first, ... ) -: vector() +MidiByteArray::MidiByteArray (size_t count, MIDI::byte first, ...) + : vector() { - push_back( first ); + push_back (first); va_list var_args; - va_start( var_args, first ); - for ( size_t i = 1; i < count; ++i ) + va_start (var_args, first); + for (size_t i = 1; i < count; ++i) { - MIDI::byte b = va_arg( var_args, int ); - push_back( b ); + MIDI::byte b = va_arg (var_args, int); + push_back (b); } - va_end( var_args ); + va_end (var_args); } -boost::shared_array MidiByteArray::bytes() const -{ - MIDI::byte * buf = new MIDI::byte[size()]; - const_iterator it = begin(); - for( MIDI::byte * ptr = buf; it != end(); ++it ) - { - *ptr++ = *it; - } - return boost::shared_array( buf ); -} -void MidiByteArray::copy( size_t count, MIDI::byte * arr ) +void MidiByteArray::copy (size_t count, MIDI::byte * arr) { - for( size_t i = 0; i < count; ++i ) - { - push_back( arr[i] ); + for (size_t i = 0; i < count; ++i) { + push_back (arr[i]); } } -MidiByteArray & operator << ( MidiByteArray & mba, const MIDI::byte & b ) +MidiByteArray & operator << (MidiByteArray & mba, const MIDI::byte & b) { - mba.push_back( b ); + mba.push_back (b); return mba; } -MidiByteArray & operator << ( MidiByteArray & mba, const MidiByteArray & barr ) +MidiByteArray & operator << (MidiByteArray & mba, const MidiByteArray & barr) { - back_insert_iterator bit( mba ); - copy( barr.begin(), barr.end(), bit ); + back_insert_iterator bit (mba); + copy (barr.begin(), barr.end(), bit); return mba; } -ostream & operator << ( ostream & os, const MidiByteArray & mba ) +ostream & operator << (ostream & os, const MidiByteArray & mba) { os << "["; char fill = os.fill('0'); - for( MidiByteArray::const_iterator it = mba.begin(); it != mba.end(); ++it ) - { - if ( it != mba.begin() ) os << " "; + for (MidiByteArray::const_iterator it = mba.begin(); it != mba.end(); ++it) { + if (it != mba.begin()) os << " "; os << hex << setw(2) << (int)*it; } - os.fill( fill ); + os.fill (fill); os << dec; os << "]"; return os; } -MidiByteArray & operator << ( MidiByteArray & mba, const std::string & st ) +MidiByteArray & operator << (MidiByteArray & mba, const std::string & st) { - for ( string::const_iterator it = st.begin(); it != st.end(); ++it ) - { + for (string::const_iterator it = st.begin(); it != st.end(); ++it) { mba << *it; } return mba; diff --git a/libs/surfaces/mackie/midi_byte_array.h b/libs/surfaces/mackie/midi_byte_array.h index 7176367189..372e48ab90 100644 --- a/libs/surfaces/mackie/midi_byte_array.h +++ b/libs/surfaces/mackie/midi_byte_array.h @@ -57,9 +57,6 @@ public: */ MidiByteArray( size_t count, MIDI::byte first, ... ); - /// return smart pointer to a copy of the bytes - boost::shared_array bytes() const; - /// copy the given number of bytes from the given array void copy( size_t count, MIDI::byte arr[] ); }; diff --git a/libs/surfaces/mackie/strip.cc b/libs/surfaces/mackie/strip.cc index af859ab41d..9e4c72eab4 100644 --- a/libs/surfaces/mackie/strip.cc +++ b/libs/surfaces/mackie/strip.cc @@ -130,7 +130,7 @@ Strip::add (Control & control) } void -Strip::set_route (boost::shared_ptr r, bool with_messages) +Strip::set_route (boost::shared_ptr r, bool /*with_messages*/) { if (_controls_locked) { return; @@ -419,7 +419,7 @@ Strip::notify_panner_width_changed (bool force_update) } void -Strip::select_event (Button& button, ButtonState bs) +Strip::select_event (Button&, ButtonState bs) { DEBUG_TRACE (DEBUG::MackieControl, "select button\n"); @@ -454,15 +454,16 @@ Strip::select_event (Button& button, ButtonState bs) } void -Strip::vselect_event (Button& button, ButtonState bs) +Strip::vselect_event (Button&, ButtonState bs) { if (bs == press) { int ms = _surface->mcp().modifier_state(); if (ms & MackieControlProtocol::MODIFIER_SHIFT) { - boost::shared_ptr ac = button.control (); + boost::shared_ptr ac = _vpot->control (); + if (ac) { /* reset to default/normal value */ @@ -470,6 +471,7 @@ Strip::vselect_event (Button& button, ButtonState bs) } } else { + DEBUG_TRACE (DEBUG::MackieControl, "switching to next pot mode\n"); next_pot_mode (); } @@ -478,7 +480,7 @@ Strip::vselect_event (Button& button, ButtonState bs) } void -Strip::fader_touch_event (Button& button, ButtonState bs) +Strip::fader_touch_event (Button&, ButtonState bs) { DEBUG_TRACE (DEBUG::MackieControl, string_compose ("fader touch, press ? %1\n", (bs == press))); @@ -754,9 +756,9 @@ Strip::unlock_controls () } void -Strip::gui_selection_changed (ARDOUR::RouteNotificationListPtr rl) +Strip::gui_selection_changed (const ARDOUR::StrongRouteNotificationList& rl) { - for (ARDOUR::RouteNotificationList::iterator i = rl->begin(); i != rl->end(); ++i) { + for (ARDOUR::StrongRouteNotificationList::const_iterator i = rl.begin(); i != rl.end(); ++i) { if ((*i) == _route) { _surface->write (_select->set_state (on)); return; diff --git a/libs/surfaces/mackie/strip.h b/libs/surfaces/mackie/strip.h index d7e80c95e5..99e51ae6e1 100644 --- a/libs/surfaces/mackie/strip.h +++ b/libs/surfaces/mackie/strip.h @@ -82,7 +82,7 @@ public: void unlock_controls (); bool locked() const { return _controls_locked; } - void gui_selection_changed (ARDOUR::RouteNotificationListPtr); + void gui_selection_changed (const ARDOUR::StrongRouteNotificationList&); private: Button* _solo; diff --git a/libs/surfaces/mackie/surface.cc b/libs/surfaces/mackie/surface.cc index fecc4d5a33..7617156589 100644 --- a/libs/surfaces/mackie/surface.cc +++ b/libs/surfaces/mackie/surface.cc @@ -299,9 +299,14 @@ Surface::handle_midi_pitchbend_message (MIDI::Parser&, MIDI::pitchbend_t pb, uin * when we connected to the per-channel pitchbend events. */ + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("handle_midi pitchbend on port %3, fader = %1 value = %2\n", fader_id, pb, _number)); + if (_mcp.device_info().no_handshake()) { + turn_it_on (); + } + Fader* fader = faders[fader_id]; if (fader) { @@ -324,6 +329,10 @@ Surface::handle_midi_note_on_message (MIDI::Parser &, MIDI::EventTwoBytes* ev) { DEBUG_TRACE (DEBUG::MackieControl, string_compose ("SurfacePort::handle_note_on %1 = %2\n", (int) ev->note_number, (int) ev->velocity)); + if (_mcp.device_info().no_handshake()) { + turn_it_on (); + } + Button* button = buttons[ev->note_number]; if (button) { @@ -348,6 +357,10 @@ Surface::handle_midi_controller_message (MIDI::Parser &, MIDI::EventTwoBytes* ev { DEBUG_TRACE (DEBUG::MackieControl, string_compose ("SurfacePort::handle_midi_controller %1 = %2\n", (int) ev->controller_number, (int) ev->value)); + if (_mcp.device_info().no_handshake()) { + turn_it_on (); + } + Pot* pot = pots[ev->controller_number]; // bit 6 gives the sign @@ -387,6 +400,10 @@ Surface::handle_midi_sysex (MIDI::Parser &, MIDI::byte * raw_bytes, size_t count DEBUG_TRACE (DEBUG::MackieControl, string_compose ("handle_midi_sysex: %1\n", bytes)); + if (_mcp.device_info().no_handshake()) { + turn_it_on (); + } + /* always save the device type ID so that our outgoing sysex messages * are correct */ @@ -406,12 +423,7 @@ Surface::handle_midi_sysex (MIDI::Parser &, MIDI::byte * raw_bytes, size_t count write_sysex (host_connection_query (bytes)); } else { if (!_active) { - _active = true; - zero_controls (); - for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) { - (*s)->notify_all (); - } - update_view_mode_display (); + turn_it_on (); } } break; @@ -493,8 +505,21 @@ Surface::host_connection_confirmation (const MidiByteArray & bytes) return MidiByteArray (2, 0x13, 0x00); } +void +Surface::turn_it_on () +{ + if (!_active) { + _active = true; + zero_controls (); + for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) { + (*s)->notify_all (); + } + update_view_mode_display (); + } +} + void -Surface::handle_port_inactive (SurfacePort * port) +Surface::handle_port_inactive (SurfacePort*) { _active = false; } @@ -793,7 +818,7 @@ Surface::update_view_mode_display () } void -Surface::gui_selection_changed (ARDOUR::RouteNotificationListPtr routes) +Surface::gui_selection_changed (const ARDOUR::StrongRouteNotificationList& routes) { for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) { (*s)->gui_selection_changed (routes); @@ -820,7 +845,7 @@ Surface::next_jog_mode () } void -Surface::set_jog_mode (JogWheel::Mode m) +Surface::set_jog_mode (JogWheel::Mode) { } diff --git a/libs/surfaces/mackie/surface.h b/libs/surfaces/mackie/surface.h index 9f3ac0b8c6..2034dd4096 100644 --- a/libs/surfaces/mackie/surface.h +++ b/libs/surfaces/mackie/surface.h @@ -146,7 +146,7 @@ public: void update_view_mode_display (); void update_flip_mode_display (); - void gui_selection_changed (ARDOUR::RouteNotificationListPtr); + void gui_selection_changed (const ARDOUR::StrongRouteNotificationList&); MackieControlProtocol& mcp() const { return _mcp; } @@ -174,6 +174,7 @@ public: void init_strips (uint32_t n); void setup_master (); void master_gain_changed (); + void turn_it_on (); }; } diff --git a/libs/surfaces/mackie/surface_port.cc b/libs/surfaces/mackie/surface_port.cc index bf5e7417ab..9d0296a9a4 100644 --- a/libs/surfaces/mackie/surface_port.cc +++ b/libs/surfaces/mackie/surface_port.cc @@ -117,11 +117,19 @@ SurfacePort::write (const MidiByteArray & mba) DEBUG_TRACE (DEBUG::MackieControl, string_compose ("port %1 write %2\n", output_port().name(), mba)); - int count = output_port().write (mba.bytes().get(), mba.size(), 0); + if (mba[0] != 0xf0 && mba.size() > 3) { + std::cerr << "TOO LONG WRITE: " << mba << std::endl; + } + + /* this call relies on std::vector using contiguous storage. not + * actually guaranteed by the standard, but way, way beyond likely. + */ - if (count != (int)mba.size()) { + int count = output_port().write (&mba[0], mba.size(), 0); - if (errno == 0) { + if (count != (int) mba.size()) { + + if (errno == 0) { cout << "port overflow on " << output_port().name() << ". Did not write all of " << mba << endl; diff --git a/libs/surfaces/osc/osc.cc b/libs/surfaces/osc/osc.cc index bde72169b5..afff3cdcd8 100644 --- a/libs/surfaces/osc/osc.cc +++ b/libs/surfaces/osc/osc.cc @@ -29,7 +29,6 @@ #include #include -#include #include #include diff --git a/libs/surfaces/osc/wscript b/libs/surfaces/osc/wscript index 1ab9bf42a7..c2c12616c1 100644 --- a/libs/surfaces/osc/wscript +++ b/libs/surfaces/osc/wscript @@ -17,7 +17,6 @@ def options(opt): def configure(conf): autowaf.configure(conf) - autowaf.check_pkg(conf, 'liblo', uselib_store='LO', linkflags='-llo') def build(bld): obj = bld(features = 'cxx cxxshlib') diff --git a/libs/surfaces/wscript b/libs/surfaces/wscript index 5595eed44c..f281247208 100644 --- a/libs/surfaces/wscript +++ b/libs/surfaces/wscript @@ -48,9 +48,8 @@ def configure(conf): if Options.options.tranzport and conf.is_defined('HAVE_USB'): conf.define('BUILD_TRANZPORT', 1) - #conf.check_cc (lib='libusb', header_name='libusb.h', function_name='usb_interrupt_write', define_name='BUILD_TRANZPORT') conf.check_cc (header_name='linux/input.h', define_name='BUILD_POWERMATE',mandatory=False) - conf.check_cc (lib='lo', header_name='lo/lo.h', function_name='lo_server_new', define_name='BUILD_OSC',mandatory=False) + autowaf.check_pkg (conf, 'liblo', mandatory=False, uselib_store="LO", atleast_version="0.24") if Options.options.wiimote: conf.check_cc (header_name='cwiid.h', define_name='HAVE_CWIID_H') @@ -67,7 +66,7 @@ def build(bld): bld.recurse('control_protocol') bld.recurse('generic_midi') bld.recurse('mackie') - if bld.is_defined ('BUILD_OSC'): + if bld.is_defined ('HAVE_LO'): bld.recurse('osc') if bld.is_defined('BUILD_POWERMATE'): bld.recurse('powermate') diff --git a/tools/linux_packaging/build b/tools/linux_packaging/build index 75e32f7f60..f8d1843352 100755 --- a/tools/linux_packaging/build +++ b/tools/linux_packaging/build @@ -85,7 +85,7 @@ if test x$STRIP != xall -a x$STRIP != xnone -a x$STRIP != xsome ; then fi release_version=`grep -m 1 '^VERSION' ../../wscript | awk '{print $3}' | sed "s/'//g"` -svn_version=`grep -m 1 'svn_revision =' ../../libs/ardour/svn_revision.cc | cut -d"\"" -f 2` +svn_version=`grep -m 1 'svn_revision =' ../../libs/ardour/svn_revision.cc | cut -d"'" -f 2` echo "Version is $release_version / $svn_version" info_string="$release_version/$svn_version built on `hostname` by `whoami` on `date`" echo "Info string is $info_string" @@ -150,6 +150,7 @@ Panners=$APPLIB/panners ExportFormats=$Shared/export Locale=$Shared/locale MidiMaps=$Shared/midi_maps +MackieControl=$Shared/mcp Modules=$Libraries/modules Loaders=$Libraries/loaders @@ -186,6 +187,7 @@ mkdir -p $Shared mkdir -p $Locale mkdir -p $Surfaces mkdir -p $MidiMaps +mkdir -p $MackieControl mkdir -p $ExportFormats mkdir -p $Panners mkdir -p $Shared/templates @@ -278,6 +280,25 @@ else echo "Skipping NLS support" fi +### Find fontconfig ### +FCROOT=`pkg-config --libs-only-L fontconfig | sed -e "s/-L//" -e "s/[[:space:]]//g"` +if [ ! -z "$FCROOT" ]; then + echo "Found FCOOT using pkg-config" + FCETC=`dirname $FCROOT`/etc +elif [ -d /usr/lib/gtk-2.0 ]; then + FCETC="/etc" +elif [ -d /usr/local/lib/gtk-2.0 ]; then + FCETC="/usr/local/etc" +else + echo "" + echo "!!! ERROR !!! - Unable to locate fontconfig directory. Packager will exit" + echo "" + exit 1 +fi + +echo "Copying Fontconfig files to $Etc ..." +cp -r $FCETC/fonts $Etc + ### Find gtk ### GTKROOT=`pkg-config --libs-only-L gtk+-2.0 | sed -e "s/-L//" -e "s/[[:space:]]//g"` if [ ! -z "$GTKROOT" ]; then @@ -402,15 +423,21 @@ gdk-pixbuf-query-loaders | sed "s?$GDKPIXBUFLIB/?@ROOTDIR@/?" > $Etc/gdk-pixbuf. # this one is special - we will set GTK_PATH to $Libraries/clearlooks if [ ! -e ${GTKLIB}/engines/libclearlooks.so ]; then + if [ ! -e $BUILD_ROOT/libs/clearlooks-newer/libclearlooks.so ] ; then echo "" echo "!!! ERROR !!! - not able to locate libclearlooks.so" echo "" echo "Packager with exit" exit 1 + else + clearlooks_so=$BUILD_ROOT/libs/clearlooks-newer/libclearlooks.so + fi +else + clearlooks_so=${GTKLIB}/engines/libclearlooks.so fi echo "Copying clearlooks ..." -cp ${GTKLIB}/engines/libclearlooks.so $Libraries +cp $clearlooks_so $Libraries mkdir -p $Libraries/clearlooks/engines (cd $Libraries/clearlooks/engines && ln -s ../../libclearlooks* libclearlooks.so ) @@ -439,6 +466,13 @@ for x in $BUILD_ROOT/../midi_maps/*.map ; do echo Copied MIDI map $x done +# MackieControl data +# got to be careful with names here +for x in $BUILD_ROOT/../mcp/*.device $BUILD_ROOT/../mcp/*.profile ; do + cp "$x" $MackieControl + echo Copied Mackie Control file $x +done + # ExportFormats # got to be careful with names here for x in $BUILD_ROOT/../export/*.preset $BUILD_ROOT/../export/*.format ; do @@ -536,10 +570,14 @@ echo # strip libraries if test x$STRIP = xall ; then echo Stripping all libraries + # Must be writable so that we can strip + find $APPLIB/ -name "*.so*" | xargs chmod u+w + # and strip ... find $APPLIB/ -name "*.so*" | xargs strip elif test x$STRIP = xsome ; then echo Stripping dependent libraries for l in $deplibs ; do + chmod u+w $APPLIB/$l strip $APPLIB/$l done fi diff --git a/tools/linux_packaging/noderun b/tools/linux_packaging/noderun index c52a2dbae4..eed4f6917c 100644 --- a/tools/linux_packaging/noderun +++ b/tools/linux_packaging/noderun @@ -11,7 +11,7 @@ fi cd $BASE || exit 1 svn update || exit 1 -./waf configure --strict --noconfirm || exit 1 +./waf configure --strict --noconfirm --also-libdir=$HOME/a3/inst/lib --also-include=$HOME/gtk/inst/include || exit 1 ./waf || exit 1 cd tools/linux_packaging || exit 1 ./build --public --strip some || exit 1 diff --git a/tools/linux_packaging/package b/tools/linux_packaging/package index 407ba1ca7f..673f1439d1 100755 --- a/tools/linux_packaging/package +++ b/tools/linux_packaging/package @@ -63,7 +63,7 @@ if [ x$DEBUG = xT ]; then fi release_version=`grep -m 1 '^VERSION' ../../wscript | awk '{print $3}' | sed "s/'//g"` -svn_version=`grep -m 1 'svn_revision =' ../../libs/ardour/svn_revision.cc | cut -d"\"" -f 2` +svn_version=`grep -m 1 'svn_revision =' ../../libs/ardour/svn_revision.cc | cut -d"'" -f 2` X86_BUNDLE="${APPNAME}_x86-${release_version}_${svn_version}" X86_64_BUNDLE="${APPNAME}_x86_64-${release_version}_${svn_version}" diff --git a/tools/osx_packaging/osx_build b/tools/osx_packaging/osx_build index b4f78f9c6a..4a4e8847d3 100755 --- a/tools/osx_packaging/osx_build +++ b/tools/osx_packaging/osx_build @@ -101,6 +101,7 @@ Plugins=$APPROOT/Plugins Surfaces=$APPROOT/Surfaces Panners=$APPROOT/Panners MidiMaps=$APPROOT/MidiMaps +MCP=$APPROOT/MCP ExportFormats=$APPROOT/ExportFormats Templates=$APPROOT/Templates Shared=$Resources/share @@ -306,6 +307,9 @@ done) # MIDI maps cp ../../midi_maps/*.map $MidiMaps +# Mackie support files +cp ../../mcp/*.{device,profile} $MCP + # VAMP plugins that we use cp $BUILD_ROOT/libs/vamp-plugins/libardourvampplugins.dylib $Frameworks diff --git a/wscript b/wscript index 8e44fed6b0..21375904e2 100644 --- a/wscript +++ b/wscript @@ -416,7 +416,9 @@ def options(opt): opt.add_option('--boost-include', type='string', action='store', dest='boost_include', default='', help='directory where Boost header files can be found') opt.add_option('--also-include', type='string', action='store', dest='also_include', default='', - help='additional include directory where header files can be found') + help='additional include directory where header files can be found (split multiples with commas)') + opt.add_option('--also-libdir', type='string', action='store', dest='also_libdir', default='', + help='additional include directory where shared libraries can be found (split multiples with commas)') opt.add_option('--wine-include', type='string', action='store', dest='wine_include', default='/usr/include/wine/windows', help='directory where Wine\'s Windows header files can be found') opt.add_option('--noconfirm', action='store_true', default=False, dest='noconfirm', @@ -476,6 +478,10 @@ def configure(conf): conf.define ('TOP_MENUBAR',1) conf.define ('GTKOSX',1) + # + # need this on OS X to pick up long long variants of several math functions + # + conf.env.append_value('CXXFLAGS_APPLEUTILITY', '-I../libs') # # Define OSX as a uselib to use when compiling @@ -534,6 +540,9 @@ def configure(conf): conf.env.append_value('CXXFLAGS', '-I' + Options.options.also_include) conf.env.append_value('CFLAGS', '-I' + Options.options.also_include) + if Options.options.also_libdir != '': + conf.env.append_value('LDFLAGS', '-L' + Options.options.also_libdir) + autowaf.check_header(conf, 'cxx', 'boost/signals2.hpp', mandatory = True) if Options.options.boost_sp_debug: @@ -554,14 +563,7 @@ def configure(conf): autowaf.check_pkg(conf, 'glibmm-2.4', uselib_store='GLIBMM', atleast_version='2.14.0') autowaf.check_pkg(conf, 'sndfile', uselib_store='SNDFILE', atleast_version='1.0.18') autowaf.check_pkg(conf, 'giomm-2.4', uselib_store='GIOMM', atleast_version='2.2') - - conf.check_cxx(fragment = '#include \nstatic Glib::Threads::RecMutex foo;\nint main () {}', - uselib = ['GLIBMM'], - msg = 'Checking for Glib::Threads::RecMutex', - mandatory = False, - okmsg = 'yes', - errmsg = 'no; using deprecated API', - define_name = 'HAVE_GLIB_THREADS_RECMUTEX') + autowaf.check_pkg(conf, 'libcurl', uselib_store='CURL', atleast_version='7.0.0') for i in children: sub_config_and_use(conf, i) @@ -570,7 +572,6 @@ def configure(conf): conf.env['INCLUDES_FLAC'] = [] conf.check_cc(function_name='dlopen', header_name='dlfcn.h', linkflags='-ldl', uselib_store='DL') - conf.check_cc(function_name='curl_global_init', header_name='curl/curl.h', linkflags='-lcurl', uselib_store='CURL') # Tell everyone that this is a waf build @@ -737,3 +738,12 @@ def i18n_po(bld): def i18n_mo(bld): bld.recurse (i18n_children) + +def install_not_supported(bld): + print 'Installing Ardour 3 is currently unsupported. Run it via the command ./ardev from within the gtk2_ardour directory.' + sys.exit (1) + +from waflib import Build +class install(Build.InstallContext): + cmd = 'install' + fun = 'install_not_supported'