From 9c8b57aab087e32c2dd68f5d364a40453b77da36 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Thu, 26 Apr 2012 17:06:21 +0000 Subject: [PATCH] SG: merge 3.0 from 11783-12096 git-svn-id: svn://localhost/ardour2/branches/3.0-SG@12097 d708f5d6-7413-0410-9779-e7cbd77b26cf --- .gitignore | 6 +- doc/eventloop.svg | 451 ++++ doc/region_read.svg | 623 +++++ gtk2_ardour/ardev_common.sh.in | 1 + gtk2_ardour/ardour.menus.in | 2 +- gtk2_ardour/ardour_button.cc | 2 + gtk2_ardour/ardour_ui.cc | 149 +- gtk2_ardour/ardour_ui_dialogs.cc | 14 +- gtk2_ardour/ardour_ui_options.cc | 2 +- gtk2_ardour/au_pluginui.mm | 10 +- gtk2_ardour/audio_streamview.cc | 301 +-- gtk2_ardour/audio_streamview.h | 27 - gtk2_ardour/audio_time_axis.cc | 65 - gtk2_ardour/audio_time_axis.h | 5 - gtk2_ardour/automation_line.cc | 534 +--- gtk2_ardour/automation_line.h | 42 +- gtk2_ardour/automation_range.h | 51 - gtk2_ardour/automation_time_axis.cc | 186 +- gtk2_ardour/automation_time_axis.h | 6 - gtk2_ardour/axis_view.cc | 5 +- gtk2_ardour/bundle_manager.cc | 2 +- gtk2_ardour/button_joiner.cc | 75 +- gtk2_ardour/button_joiner.h | 3 +- gtk2_ardour/control_point.cc | 4 + gtk2_ardour/control_point.h | 20 +- gtk2_ardour/crossfade_edit.cc | 4 +- gtk2_ardour/crossfade_view.cc | 302 --- gtk2_ardour/editor.cc | 343 +-- gtk2_ardour/editor.h | 40 +- gtk2_ardour/editor_actions.cc | 2 - gtk2_ardour/editor_canvas_events.cc | 122 - gtk2_ardour/editor_drag.cc | 19 +- gtk2_ardour/editor_drag.h | 2 +- gtk2_ardour/editor_markers.cc | 10 +- gtk2_ardour/editor_mouse.cc | 3 +- gtk2_ardour/editor_ops.cc | 159 +- gtk2_ardour/editor_regions.cc | 8 +- gtk2_ardour/editor_route_groups.cc | 6 +- gtk2_ardour/editor_routes.cc | 10 +- gtk2_ardour/editor_selection.cc | 72 +- gtk2_ardour/editor_summary.cc | 6 +- gtk2_ardour/editor_xpms | 12 +- gtk2_ardour/engine_dialog.cc | 3 + gtk2_ardour/export_format_dialog.cc | 24 +- gtk2_ardour/gain_meter.cc | 4 +- gtk2_ardour/generic_pluginui.cc | 8 + gtk2_ardour/imageframe_time_axis.cc | 2 +- gtk2_ardour/imageframe_time_axis_group.cc | 2 +- gtk2_ardour/imageframe_time_axis_view.cc | 2 +- gtk2_ardour/imageframe_view.cc | 2 +- gtk2_ardour/keyeditor.cc | 4 - gtk2_ardour/level_meter.cc | 4 +- gtk2_ardour/location_ui.cc | 18 +- gtk2_ardour/lv2_plugin_ui.cc | 48 +- gtk2_ardour/lv2_plugin_ui.h | 6 + gtk2_ardour/lv2_ui.h | 241 -- gtk2_ardour/marker_time_axis_view.cc | 2 +- gtk2_ardour/midi_list_editor.cc | 8 +- gtk2_ardour/midi_region_view.cc | 22 +- gtk2_ardour/midi_selection.cc | 4 +- gtk2_ardour/midi_time_axis.cc | 4 +- gtk2_ardour/mixer_strip.cc | 8 +- gtk2_ardour/mixer_ui.cc | 20 +- gtk2_ardour/mnemonic-us.bindings.in | 1 + gtk2_ardour/monitor_section.cc | 16 +- gtk2_ardour/monitor_section.h | 1 + gtk2_ardour/mouse_cursors.cc | 8 +- gtk2_ardour/option_editor.cc | 2 +- gtk2_ardour/playlist_selector.cc | 20 +- gtk2_ardour/plugin_eq_gui.cc | 2 +- gtk2_ardour/plugin_selector.cc | 8 +- gtk2_ardour/plugin_selector.h | 2 +- gtk2_ardour/plugin_ui.cc | 30 +- gtk2_ardour/plugin_ui.h | 3 + gtk2_ardour/point_selection.h | 5 +- gtk2_ardour/port_group.cc | 4 +- gtk2_ardour/port_matrix.cc | 2 +- gtk2_ardour/processor_box.cc | 10 +- gtk2_ardour/public_editor.h | 2 - gtk2_ardour/rc_option_editor.cc | 13 +- gtk2_ardour/rc_option_editor.h | 1 - gtk2_ardour/region_editor.cc | 4 +- gtk2_ardour/region_gain_line.cc | 7 +- gtk2_ardour/region_selection.cc | 4 +- gtk2_ardour/region_view.cc | 8 +- gtk2_ardour/return_ui.cc | 2 +- gtk2_ardour/rhythm_ferret.cc | 4 +- gtk2_ardour/route_params_ui.cc | 6 +- gtk2_ardour/route_params_ui.h | 2 +- gtk2_ardour/route_processor_selection.cc | 2 +- gtk2_ardour/route_time_axis.cc | 9 +- gtk2_ardour/route_ui.cc | 14 +- gtk2_ardour/selection.cc | 120 +- gtk2_ardour/selection.h | 4 +- gtk2_ardour/send_ui.cc | 2 +- gtk2_ardour/session_metadata_dialog.cc | 72 +- gtk2_ardour/session_metadata_dialog.h | 4 +- gtk2_ardour/session_option_editor.cc | 16 - gtk2_ardour/sfdb_freesound_mootcher.cc | 56 +- gtk2_ardour/sfdb_ui.cc | 209 +- gtk2_ardour/sfdb_ui.h | 10 +- gtk2_ardour/shuttle_control.cc | 2 +- gtk2_ardour/splash.cc | 2 +- gtk2_ardour/startup.cc | 236 +- gtk2_ardour/startup.h | 19 +- gtk2_ardour/step_editor.cc | 2 +- gtk2_ardour/stereo_panner.cc | 3 +- gtk2_ardour/streamview.cc | 8 +- gtk2_ardour/streamview.h | 2 +- gtk2_ardour/strip_silence_dialog.cc | 2 +- gtk2_ardour/tempo_lines.cc | 9 +- gtk2_ardour/theme_manager.cc | 2 +- gtk2_ardour/time_axis_view.cc | 12 +- gtk2_ardour/time_axis_view.h | 1 - gtk2_ardour/time_axis_view_item.cc | 4 +- gtk2_ardour/time_axis_view_item.h | 2 +- gtk2_ardour/time_info_box.cc | 9 +- gtk2_ardour/time_selection.cc | 6 +- gtk2_ardour/window_proxy.cc | 5 +- gtk2_ardour/wscript | 2 - libs/ardour/amp.cc | 2 + libs/ardour/ardour/audioplaylist.h | 68 +- libs/ardour/ardour/audioregion.h | 15 +- libs/ardour/ardour/audiosource.h | 2 +- libs/ardour/ardour/auditioner.h | 1 - libs/ardour/ardour/automation_control.h | 6 +- libs/ardour/ardour/configuration_variable.h | 3 +- libs/ardour/ardour/control_protocol_manager.h | 3 +- libs/ardour/ardour/crossfade.h | 180 -- libs/ardour/ardour/crossfade_binder.h | 52 - libs/ardour/ardour/delivery.h | 2 +- libs/ardour/ardour/diskstream.h | 6 +- libs/ardour/ardour/importable_source.h | 2 +- libs/ardour/ardour/location.h | 2 +- libs/ardour/ardour/lv2_plugin.h | 35 +- libs/ardour/ardour/midi_playlist.h | 6 - libs/ardour/ardour/midi_playlist_source.h | 4 +- libs/ardour/ardour/midi_port.h | 2 +- libs/ardour/ardour/midi_region.h | 3 +- libs/ardour/ardour/midi_source.h | 6 +- libs/ardour/ardour/midi_track.h | 2 +- libs/ardour/ardour/midi_ui.h | 5 +- libs/ardour/ardour/panner.h | 3 - libs/ardour/ardour/playlist.h | 27 +- libs/ardour/ardour/plugin.h | 2 + libs/ardour/ardour/port.h | 2 +- libs/ardour/ardour/rc_configuration_vars.h | 3 +- libs/ardour/ardour/region.h | 25 +- libs/ardour/ardour/resampled_source.h | 2 +- libs/ardour/ardour/route.h | 13 +- libs/ardour/ardour/session.h | 10 +- .../ardour/session_configuration_vars.h | 2 - libs/ardour/ardour/session_metadata.h | 29 +- libs/ardour/ardour/slave.h | 4 +- libs/ardour/ardour/sndfileimportable.h | 2 +- libs/ardour/ardour/source.h | 2 +- libs/ardour/ardour/tempo.h | 2 +- libs/ardour/ardour/track.h | 16 +- libs/ardour/ardour/types.h | 23 +- libs/ardour/ardour/utils.h | 2 - libs/ardour/ardour/worker.h | 90 + libs/ardour/audio_diskstream.cc | 2 +- libs/ardour/audio_playlist.cc | 993 ++------ libs/ardour/audio_playlist_importer.cc | 8 +- libs/ardour/audio_track_importer.cc | 21 +- libs/ardour/audioengine.cc | 24 +- libs/ardour/audioregion.cc | 212 +- libs/ardour/audiosource.cc | 7 +- libs/ardour/auditioner.cc | 23 - libs/ardour/automation_list.cc | 9 - libs/ardour/beats_frames_converter.cc | 4 +- libs/ardour/broadcast_info.cc | 5 +- libs/ardour/butler.cc | 3 - libs/ardour/control_protocol_manager.cc | 33 +- libs/ardour/crossfade.cc | 1008 -------- libs/ardour/crossfade_binder.cc | 63 - libs/ardour/delivery.cc | 4 +- libs/ardour/diskstream.cc | 14 +- libs/ardour/enums.cc | 8 - libs/ardour/event_type_map.cc | 3 + libs/ardour/globals.cc | 76 - libs/ardour/graph.cc | 1 + libs/ardour/import.cc | 2 +- libs/ardour/interpolation.cc | 18 +- libs/ardour/lv2/lv2plug.in/ns/ext/atom/atom.h | 238 -- .../ardour/lv2/lv2plug.in/ns/ext/atom/forge.h | 618 ----- libs/ardour/lv2/lv2plug.in/ns/ext/atom/util.h | 424 ---- .../lv2plug.in/ns/ext/event/event-helpers.h | 243 -- .../lv2/lv2plug.in/ns/ext/event/event.h | 260 -- .../lv2/lv2plug.in/ns/ext/state/state.h | 359 --- .../lv2/lv2plug.in/ns/ext/uri-map/uri-map.h | 88 - libs/ardour/lv2/lv2plug.in/ns/ext/urid/urid.h | 115 - libs/ardour/lv2_plugin.cc | 333 ++- libs/ardour/midi_buffer.cc | 1 + libs/ardour/midi_clock_slave.cc | 1 + libs/ardour/midi_diskstream.cc | 2 +- libs/ardour/midi_playlist.cc | 40 +- libs/ardour/midi_playlist_source.cc | 4 +- libs/ardour/midi_port.cc | 5 +- libs/ardour/midi_region.cc | 10 +- libs/ardour/midi_source.cc | 2 +- libs/ardour/midi_state_tracker.cc | 2 +- libs/ardour/midi_track.cc | 39 +- libs/ardour/midi_ui.cc | 5 + libs/ardour/mute_master.cc | 3 +- libs/ardour/playlist.cc | 310 +-- libs/ardour/plugin_insert.cc | 9 +- libs/ardour/rc_configuration.cc | 7 +- libs/ardour/region.cc | 14 +- libs/ardour/region_factory.cc | 9 +- libs/ardour/route.cc | 25 +- libs/ardour/session.cc | 41 +- libs/ardour/session_butler.cc | 1 - libs/ardour/session_command.cc | 15 - libs/ardour/session_metadata.cc | 135 +- libs/ardour/session_midi.cc | 8 +- libs/ardour/session_process.cc | 6 +- libs/ardour/session_state.cc | 80 +- libs/ardour/session_vst.cc | 4 + libs/ardour/sndfilesource.cc | 6 +- libs/ardour/source.cc | 2 +- libs/ardour/strip_silence.cc | 3 +- libs/ardour/tempo.cc | 18 +- libs/ardour/test/audio_region_test.cc | 85 + libs/ardour/test/audio_region_test.h | 15 + libs/ardour/test/interpolation_test.cc | 16 +- libs/ardour/test/midi_clock_slave_test.h | 2 +- libs/ardour/test/playlist_read_test.cc | 206 ++ libs/ardour/test/playlist_read_test.h | 30 + libs/ardour/test/tempo_test.cc | 9 - libs/ardour/test/test_globals.cc | 4 + libs/ardour/test/test_globals.h | 3 + .../test/test_needing_playlist_and_regions.cc | 20 +- libs/ardour/ticker.cc | 9 +- libs/ardour/track.cc | 45 +- libs/ardour/utils.cc | 20 - libs/ardour/worker.cc | 115 + libs/ardour/wscript | 17 +- libs/clearlooks-newer/cairo-support.c | 2 +- libs/evoral/evoral/ControlList.hpp | 10 +- libs/evoral/evoral/Range.hpp | 219 ++ libs/evoral/evoral/types.hpp | 17 - libs/evoral/run-tests.sh | 4 +- libs/evoral/src/ControlList.cpp | 82 +- libs/evoral/src/Curve.cpp | 41 +- libs/evoral/test/RangeTest.cpp | 84 + libs/evoral/test/RangeTest.hpp | 19 + libs/evoral/wscript | 1 + libs/gtkmm2ext/actions.cc | 59 +- libs/gtkmm2ext/gtk_ui.cc | 2 +- libs/gtkmm2ext/gtkmm2ext/actions.h | 1 + libs/gtkmm2ext/gtkmm2ext/dndvbox.h | 7 +- libs/gtkmm2ext/gtkmm2ext/keyboard.h | 2 + libs/gtkmm2ext/keyboard.cc | 17 +- libs/midi++2/channel.cc | 3 +- libs/midi++2/ipmidi_port.cc | 293 +++ libs/midi++2/jack_midi_port.cc | 463 ++++ libs/midi++2/manager.cc | 23 +- libs/midi++2/midi++/channel.h | 4 +- libs/midi++2/midi++/ipmidi_port.h | 79 + libs/midi++2/midi++/jack_midi_port.h | 103 + libs/midi++2/midi++/manager.h | 12 +- libs/midi++2/midi++/mmc.h | 12 +- libs/midi++2/midi++/parser.h | 4 +- libs/midi++2/midi++/port.h | 93 +- libs/midi++2/mmc.cc | 23 +- libs/midi++2/parser.cc | 3 +- libs/midi++2/port.cc | 361 +-- libs/midi++2/wscript | 2 + libs/panners/1in2out/panner_1in2out.cc | 6 +- libs/panners/2in2out/panner_2in2out.cc | 8 +- libs/pbd/base_ui.cc | 4 +- libs/pbd/convert.cc | 20 + libs/pbd/debug.cc | 2 + libs/pbd/event_loop.cc | 27 +- libs/pbd/pbd/abstract_ui.cc | 141 +- libs/pbd/pbd/base_ui.h | 27 + libs/pbd/pbd/convert.h | 2 + libs/pbd/pbd/crossthread.h | 54 +- libs/pbd/pbd/debug.h | 2 + libs/pbd/pbd/event_loop.h | 9 + libs/pbd/pbd/ringbufferNPT.h | 1 + libs/pbd/pbd/semaphore.h | 198 ++ libs/rubberband/src/StretcherImpl.cpp | 4 + libs/surfaces/control_protocol/basic_ui.cc | 18 +- .../control_protocol/control_protocol.cc | 41 +- .../control_protocol/basic_ui.h | 2 +- .../control_protocol/control_protocol.h | 37 +- .../control_protocol/control_protocol/types.h | 34 + .../generic_midi_control_protocol.cc | 25 +- libs/surfaces/generic_midi/midifunction.cc | 3 +- libs/surfaces/mackie/README | 18 - libs/surfaces/mackie/bcf_surface.cc | 42 - libs/surfaces/mackie/bcf_surface.h | 34 - libs/surfaces/mackie/bcf_surface_generated.cc | 1461 ----------- libs/surfaces/mackie/button.cc | 241 ++ libs/surfaces/mackie/button.h | 163 ++ libs/surfaces/mackie/control_group.h | 42 + libs/surfaces/mackie/controls.cc | 242 +- libs/surfaces/mackie/controls.h | 310 +-- libs/surfaces/mackie/device_info.cc | 500 ++++ libs/surfaces/mackie/device_info.h | 104 + libs/surfaces/mackie/device_profile.cc | 364 +++ libs/surfaces/mackie/device_profile.h | 72 + libs/surfaces/mackie/dummy_port.cc | 58 - libs/surfaces/mackie/dummy_port.h | 60 - libs/surfaces/mackie/fader.cc | 57 + libs/surfaces/mackie/fader.h | 31 + libs/surfaces/mackie/gui.cc | 484 +++- libs/surfaces/mackie/gui.h | 109 + libs/surfaces/mackie/interface.cc | 11 +- libs/surfaces/mackie/jog.cc | 36 + libs/surfaces/mackie/jog.h | 45 + libs/surfaces/mackie/jog_wheel.cc | 50 + libs/surfaces/mackie/jog_wheel.h | 34 + libs/surfaces/mackie/led.cc | 64 + .../generate-button-handlers-h.erb => led.h} | 55 +- libs/surfaces/mackie/mackie_button_handler.cc | 711 ------ libs/surfaces/mackie/mackie_button_handler.h | 233 -- .../mackie/mackie_control_protocol.cc | 2145 +++++++---------- .../surfaces/mackie/mackie_control_protocol.h | 541 +++-- .../mackie/mackie_control_protocol_poll.cc | 54 - libs/surfaces/mackie/mackie_jog_wheel.cc | 196 -- libs/surfaces/mackie/mackie_jog_wheel.h | 102 - libs/surfaces/mackie/mackie_midi_builder.cc | 272 --- libs/surfaces/mackie/mackie_midi_builder.h | 104 - libs/surfaces/mackie/mackie_port.cc | 467 ---- libs/surfaces/mackie/mackie_port.h | 124 - libs/surfaces/mackie/mackie_surface.cc | 18 - libs/surfaces/mackie/mackie_surface.h | 32 - .../mackie/mackie_surface_generated.cc | 1507 ------------ libs/surfaces/mackie/mcp_buttons.cc | 1224 ++++++++++ libs/surfaces/mackie/meter.cc | 98 + libs/surfaces/mackie/meter.h | 52 + libs/surfaces/mackie/pot.cc | 66 + libs/surfaces/mackie/pot.h | 52 + libs/surfaces/mackie/route_signal.cc | 103 - libs/surfaces/mackie/route_signal.h | 92 - libs/surfaces/mackie/scripts/bank.rb | 32 - libs/surfaces/mackie/scripts/bcf-controls.csv | 95 - libs/surfaces/mackie/scripts/controls.rb | 216 -- libs/surfaces/mackie/scripts/dump.rb | 11 - .../scripts/generate-button-handlers-cc.erb | 59 - .../mackie/scripts/generate-surface.rb | 26 - libs/surfaces/mackie/scripts/host.rb | 106 - .../mackie/scripts/mackie-controls.csv | 93 - libs/surfaces/mackie/scripts/mackie-dump.midi | Bin 495 -> 0 bytes libs/surfaces/mackie/scripts/mackie.rb | 119 - libs/surfaces/mackie/scripts/parse.rb | 61 - libs/surfaces/mackie/scripts/signals.rb | 137 -- libs/surfaces/mackie/scripts/simple_host.rb | 128 - .../mackie/scripts/surface-cc-template.erb | 107 - .../mackie/scripts/surface-h-template.erb | 27 - libs/surfaces/mackie/scripts/test_controls.rb | 9 - libs/surfaces/mackie/scripts/transform.rb | 26 - libs/surfaces/mackie/scripts/write.rb | 10 - libs/surfaces/mackie/strip.cc | 1061 ++++++++ libs/surfaces/mackie/strip.h | 151 ++ libs/surfaces/mackie/surface.cc | 859 ++++++- libs/surfaces/mackie/surface.h | 192 +- libs/surfaces/mackie/surface_port.cc | 262 +- libs/surfaces/mackie/surface_port.h | 79 +- libs/surfaces/mackie/timer.h | 30 +- libs/surfaces/mackie/types.h | 30 +- libs/surfaces/mackie/wscript | 25 +- libs/surfaces/osc/osc.cc | 6 +- libs/surfaces/osc/osc_route_observer.cc | 5 +- libs/surfaces/powermate/powermate.cc | 2 +- libs/taglib/taglib/mpeg/id3v1/id3v1tag.cpp | 3 +- libs/taglib/taglib/mpeg/id3v2/id3v2tag.cpp | 3 +- libs/taglib/taglib/ogg/flac/oggflacfile.cpp | 3 +- libs/taglib/taglib/ogg/oggpage.cpp | 6 +- .../taglib/ogg/speex/speexproperties.cpp | 6 +- .../taglib/ogg/vorbis/vorbisproperties.cpp | 6 +- libs/taglib/taglib/toolkit/tfile.cpp | 3 +- libs/taglib/taglib/toolkit/tstring.cpp | 6 +- mcp/mcpro.device | 13 + mcp/mcproxt.device | 13 + mcp/nucleus.device | 15 + mcp/paul-nucleus.profile | 19 + patches/shared_ptr.patch | 275 +++ tools/linux_packaging/build | 8 +- tools/linux_packaging/package | 2 +- tools/linux_packaging/stage2.run | 4 +- wscript | 9 +- 385 files changed, 14193 insertions(+), 18198 deletions(-) create mode 100644 doc/eventloop.svg create mode 100644 doc/region_read.svg delete mode 100644 gtk2_ardour/automation_range.h delete mode 100644 gtk2_ardour/crossfade_view.cc delete mode 100644 gtk2_ardour/lv2_ui.h delete mode 100644 libs/ardour/ardour/crossfade.h delete mode 100644 libs/ardour/ardour/crossfade_binder.h create mode 100644 libs/ardour/ardour/worker.h delete mode 100644 libs/ardour/crossfade.cc delete mode 100644 libs/ardour/crossfade_binder.cc delete mode 100644 libs/ardour/lv2/lv2plug.in/ns/ext/atom/atom.h delete mode 100644 libs/ardour/lv2/lv2plug.in/ns/ext/atom/forge.h delete mode 100644 libs/ardour/lv2/lv2plug.in/ns/ext/atom/util.h delete mode 100644 libs/ardour/lv2/lv2plug.in/ns/ext/event/event-helpers.h delete mode 100644 libs/ardour/lv2/lv2plug.in/ns/ext/event/event.h delete mode 100644 libs/ardour/lv2/lv2plug.in/ns/ext/state/state.h delete mode 100644 libs/ardour/lv2/lv2plug.in/ns/ext/uri-map/uri-map.h delete mode 100644 libs/ardour/lv2/lv2plug.in/ns/ext/urid/urid.h create mode 100644 libs/ardour/test/audio_region_test.cc create mode 100644 libs/ardour/test/audio_region_test.h create mode 100644 libs/ardour/test/playlist_read_test.cc create mode 100644 libs/ardour/test/playlist_read_test.h create mode 100644 libs/ardour/test/test_globals.cc create mode 100644 libs/ardour/test/test_globals.h create mode 100644 libs/ardour/worker.cc create mode 100644 libs/evoral/evoral/Range.hpp create mode 100644 libs/evoral/test/RangeTest.cpp create mode 100644 libs/evoral/test/RangeTest.hpp create mode 100644 libs/midi++2/ipmidi_port.cc create mode 100644 libs/midi++2/jack_midi_port.cc create mode 100644 libs/midi++2/midi++/ipmidi_port.h create mode 100644 libs/midi++2/midi++/jack_midi_port.h create mode 100644 libs/pbd/pbd/semaphore.h create mode 100644 libs/surfaces/control_protocol/control_protocol/types.h delete mode 100644 libs/surfaces/mackie/README delete mode 100644 libs/surfaces/mackie/bcf_surface.cc delete mode 100644 libs/surfaces/mackie/bcf_surface.h delete mode 100644 libs/surfaces/mackie/bcf_surface_generated.cc create mode 100644 libs/surfaces/mackie/button.cc create mode 100644 libs/surfaces/mackie/button.h create mode 100644 libs/surfaces/mackie/control_group.h create mode 100644 libs/surfaces/mackie/device_info.cc create mode 100644 libs/surfaces/mackie/device_info.h create mode 100644 libs/surfaces/mackie/device_profile.cc create mode 100644 libs/surfaces/mackie/device_profile.h delete mode 100644 libs/surfaces/mackie/dummy_port.cc delete mode 100644 libs/surfaces/mackie/dummy_port.h create mode 100644 libs/surfaces/mackie/fader.cc create mode 100644 libs/surfaces/mackie/fader.h create mode 100644 libs/surfaces/mackie/gui.h create mode 100644 libs/surfaces/mackie/jog.cc create mode 100644 libs/surfaces/mackie/jog.h create mode 100644 libs/surfaces/mackie/jog_wheel.cc create mode 100644 libs/surfaces/mackie/jog_wheel.h create mode 100644 libs/surfaces/mackie/led.cc rename libs/surfaces/mackie/{scripts/generate-button-handlers-h.erb => led.h} (50%) delete mode 100644 libs/surfaces/mackie/mackie_button_handler.cc delete mode 100644 libs/surfaces/mackie/mackie_button_handler.h delete mode 100644 libs/surfaces/mackie/mackie_jog_wheel.cc delete mode 100644 libs/surfaces/mackie/mackie_jog_wheel.h delete mode 100644 libs/surfaces/mackie/mackie_midi_builder.cc delete mode 100644 libs/surfaces/mackie/mackie_midi_builder.h delete mode 100644 libs/surfaces/mackie/mackie_port.cc delete mode 100644 libs/surfaces/mackie/mackie_port.h delete mode 100644 libs/surfaces/mackie/mackie_surface.cc delete mode 100644 libs/surfaces/mackie/mackie_surface.h delete mode 100644 libs/surfaces/mackie/mackie_surface_generated.cc create mode 100644 libs/surfaces/mackie/mcp_buttons.cc create mode 100644 libs/surfaces/mackie/meter.cc create mode 100644 libs/surfaces/mackie/meter.h create mode 100644 libs/surfaces/mackie/pot.cc create mode 100644 libs/surfaces/mackie/pot.h delete mode 100644 libs/surfaces/mackie/route_signal.cc delete mode 100644 libs/surfaces/mackie/route_signal.h delete mode 100644 libs/surfaces/mackie/scripts/bank.rb delete mode 100644 libs/surfaces/mackie/scripts/bcf-controls.csv delete mode 100644 libs/surfaces/mackie/scripts/controls.rb delete mode 100755 libs/surfaces/mackie/scripts/dump.rb delete mode 100644 libs/surfaces/mackie/scripts/generate-button-handlers-cc.erb delete mode 100755 libs/surfaces/mackie/scripts/generate-surface.rb delete mode 100755 libs/surfaces/mackie/scripts/host.rb delete mode 100644 libs/surfaces/mackie/scripts/mackie-controls.csv delete mode 100644 libs/surfaces/mackie/scripts/mackie-dump.midi delete mode 100644 libs/surfaces/mackie/scripts/mackie.rb delete mode 100644 libs/surfaces/mackie/scripts/parse.rb delete mode 100644 libs/surfaces/mackie/scripts/signals.rb delete mode 100644 libs/surfaces/mackie/scripts/simple_host.rb delete mode 100644 libs/surfaces/mackie/scripts/surface-cc-template.erb delete mode 100644 libs/surfaces/mackie/scripts/surface-h-template.erb delete mode 100755 libs/surfaces/mackie/scripts/test_controls.rb delete mode 100644 libs/surfaces/mackie/scripts/transform.rb delete mode 100644 libs/surfaces/mackie/scripts/write.rb create mode 100644 libs/surfaces/mackie/strip.cc create mode 100644 libs/surfaces/mackie/strip.h create mode 100644 mcp/mcpro.device create mode 100644 mcp/mcproxt.device create mode 100644 mcp/nucleus.device create mode 100644 mcp/paul-nucleus.profile create mode 100644 patches/shared_ptr.patch diff --git a/.gitignore b/.gitignore index b86506eb98..9e84d0792a 100644 --- a/.gitignore +++ b/.gitignore @@ -125,4 +125,8 @@ gtk2_ardour/po/*.mo libs/ardour/po/*.mo gtk2_ardour/*.pot libs/ardour/libardour.pot - +.lock-wafbuild +GPATH +GRTAGS +GSYMS +GTAGS diff --git a/doc/eventloop.svg b/doc/eventloop.svg new file mode 100644 index 0000000000..09683c5efc --- /dev/null +++ b/doc/eventloop.svg @@ -0,0 +1,451 @@ + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + UI Event Loop Thread + CrossThreadChannel + RT Thread + Freeze Thread + Step One:AbstractUI::get_request()AbstractUI::send_request() + Generic non-RT-safe request queue + RT-safe per-thread request queues + + + + while (1) if (channel.data_available()) { channel.drain(); handle_ui_requests(); } + + + + J. Random Thread + + + Step Two:CrossThreadChannel::wakeup() + + + + + + AbstractUI IS-A BaseUI IS-A Event Loop + + diff --git a/doc/region_read.svg b/doc/region_read.svg new file mode 100644 index 0000000000..3f89819cdd --- /dev/null +++ b/doc/region_read.svg @@ -0,0 +1,623 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + _position + (session frames) + + + + + fade_in_length + position + + + + cnt + to_read + and + + fade_interval_start + + + + + fade_interval_end + + fade_in_limit + + + + fade_out_offset + fade_out_limit + + + + internal_offset + + Region + + Range being read + + Fade + + diff --git a/gtk2_ardour/ardev_common.sh.in b/gtk2_ardour/ardev_common.sh.in index dc5926aae9..58e6c4e0ba 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_MCP_PATH=$TOP/mcp:. if test -d $HOME/gtk/inst ; then export GTK_PATH=~/.ardour3:$libs/clearlooks-newer diff --git a/gtk2_ardour/ardour.menus.in b/gtk2_ardour/ardour.menus.in index 83a4200ceb..aca08444df 100644 --- a/gtk2_ardour/ardour.menus.in +++ b/gtk2_ardour/ardour.menus.in @@ -10,6 +10,7 @@ + @@ -213,7 +214,6 @@ - diff --git a/gtk2_ardour/ardour_button.cc b/gtk2_ardour/ardour_button.cc index 942d678bfb..1d98a7b84f 100644 --- a/gtk2_ardour/ardour_button.cc +++ b/gtk2_ardour/ardour_button.cc @@ -75,10 +75,12 @@ ArdourButton::ArdourButton (Element e) ArdourButton::ArdourButton (const std::string& str, Element e) : _elements (e) + , _tweaks (Tweaks (0)) , _text_width (0) , _text_height (0) , _diameter (11.0) , _corner_radius (9.0) + , _corner_mask (0xf) , edge_pattern (0) , active_pattern (0) , inactive_pattern (0) diff --git a/gtk2_ardour/ardour_ui.cc b/gtk2_ardour/ardour_ui.cc index d9142b8b1a..e97745e946 100644 --- a/gtk2_ardour/ardour_ui.cc +++ b/gtk2_ardour/ardour_ui.cc @@ -168,20 +168,8 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[]) , _feedback_exists (false) { - using namespace Gtk::Menu_Helpers; - Gtkmm2ext::init(); - -#ifdef TOP_MENUBAR - // _auto_display_errors = false; - /* - * This was commented out as it wasn't defined - * in A3 IIRC. If this is not needed it should - * be completely removed. - */ -#endif - about = 0; splash = 0; _startup = 0; @@ -252,7 +240,7 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[]) /* handle dialog requests */ - ARDOUR::Session::Dialog.connect (forever_connections, MISSING_INVALIDATOR, ui_bind (&ARDOUR_UI::session_dialog, this, _1), gui_context()); + ARDOUR::Session::Dialog.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::session_dialog, this, _1), gui_context()); /* handle pending state with a dialog (PROBLEM: needs to return a value and thus cannot be x-thread) */ @@ -264,12 +252,12 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[]) /* handle requests to quit (coming from JACK session) */ - ARDOUR::Session::Quit.connect (forever_connections, MISSING_INVALIDATOR, ui_bind (&ARDOUR_UI::finish, this), gui_context ()); + ARDOUR::Session::Quit.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::finish, this), gui_context ()); /* tell the user about feedback */ - ARDOUR::Session::FeedbackDetected.connect (forever_connections, MISSING_INVALIDATOR, ui_bind (&ARDOUR_UI::feedback_detected, this), gui_context ()); - ARDOUR::Session::SuccessfulGraphSort.connect (forever_connections, MISSING_INVALIDATOR, ui_bind (&ARDOUR_UI::successful_graph_sort, this), gui_context ()); + ARDOUR::Session::FeedbackDetected.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::feedback_detected, this), gui_context ()); + ARDOUR::Session::SuccessfulGraphSort.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::successful_graph_sort, this), gui_context ()); /* handle requests to deal with missing files */ @@ -360,7 +348,7 @@ ARDOUR_UI::run_startup (bool should_be_new, string load_template) _startup->set_new_only (should_be_new); if (!load_template.empty()) { - _startup->set_load_template( load_template ); + _startup->set_load_template (load_template); } _startup->present (); @@ -397,7 +385,7 @@ ARDOUR_UI::create_engine () engine->Stopped.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::engine_stopped, this), gui_context()); engine->Running.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::engine_running, this), gui_context()); - engine->SampleRateChanged.connect (forever_connections, MISSING_INVALIDATOR, ui_bind (&ARDOUR_UI::update_sample_rate, this, _1), gui_context()); + engine->SampleRateChanged.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::update_sample_rate, this, _1), gui_context()); engine->Halted.connect_same_thread (forever_connections, boost::bind (&ARDOUR_UI::engine_halted, this, _1, false)); @@ -483,7 +471,7 @@ ARDOUR_UI::post_engine () update_cpu_load (); update_sample_rate (engine->frame_rate()); - Config->ParameterChanged.connect (forever_connections, MISSING_INVALIDATOR, ui_bind (&ARDOUR_UI::parameter_changed, this, _1), gui_context()); + Config->ParameterChanged.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::parameter_changed, this, _1), gui_context()); boost::function pc (boost::bind (&ARDOUR_UI::parameter_changed, this, _1)); Config->map_parameters (pc); @@ -530,7 +518,6 @@ ARDOUR_UI::configure_timeout () return true; } else { have_configure_timeout = false; - cerr << "config event-driven save\n"; save_ardour_state (); return false; } @@ -578,7 +565,6 @@ ARDOUR_UI::set_transport_controllable_state (const XMLNode& node) if ((prop = node.property ("shuttle")) != 0) { shuttle_box->controllable()->set_id (prop->value()); } - } XMLNode& @@ -796,10 +782,10 @@ ARDOUR_UI::finish() if (save_state_canfail ("")) { /* failed - don't quit */ MessageDialog msg (*editor, - _("\ -Ardour was unable to save your session.\n\n\ + string_compose (_("\ +%1 was unable to save your session.\n\n\ If you still wish to quit, please use the\n\n\ -\"Just quit\" option.")); +\"Just quit\" option."), PROGRAM_NAME)); pop_back_splash(msg); msg.run (); return; @@ -1112,15 +1098,6 @@ ARDOUR_UI::update_disk_space() } disk_space_label.set_markup (buf); - - // An attempt to make the disk space label flash red when space has run out. - - if (frames < fr * 60 * 5) { - /* disk_space_box.style ("disk_space_label_empty"); */ - } else { - /* disk_space_box.style ("disk_space_label"); */ - } - } gint @@ -1155,7 +1132,7 @@ ARDOUR_UI::redisplay_recent_sessions () recent_session_display.set_model (recent_session_model); return; } - // + // sort them alphabetically sort (rs.begin(), rs.end(), cmp); @@ -1174,12 +1151,12 @@ ARDOUR_UI::redisplay_recent_sessions () vector* states; vector item; - string fullpath = (*i).to_string(); + string fullpath = i->to_string(); /* remove any trailing / */ - if (fullpath[fullpath.length()-1] == '/') { - fullpath = fullpath.substr (0, fullpath.length()-1); + if (fullpath[fullpath.length() - 1] == '/') { + fullpath = fullpath.substr (0, fullpath.length() - 1); } /* check whether session still exists */ @@ -1249,7 +1226,6 @@ ARDOUR_UI::build_session_selector () recent_session_display.show(); scroller->show(); - //session_selector_window->get_vbox()->show(); } void @@ -1416,11 +1392,7 @@ ARDOUR_UI::session_add_midi_route (bool disk, RouteGroup* route_group, uint32_t } } - } /*else { - if ((route = _session->new_midi_route ()) == 0) { - error << _("could not create new midi bus") << endmsg; - } - }*/ + } } catch (...) { @@ -1612,7 +1584,6 @@ ARDOUR_UI::transport_record (bool roll) _session->disable_record (false, true); } } - //cerr << "ARDOUR_UI::transport_record () called roll = " << roll << " _session->record_status() = " << _session->record_status() << endl; } void @@ -1787,35 +1758,34 @@ ARDOUR_UI::transport_rewind (int option) void ARDOUR_UI::transport_forward (int option) { - float current_transport_speed; - - if (_session) { - current_transport_speed = _session->transport_speed(); - - if (current_transport_speed <= 0.0f) { - switch (option) { - case 0: - _session->request_transport_speed (1.0f); - break; - case 1: - _session->request_transport_speed (4.0f); - break; - case -1: - _session->request_transport_speed (0.5f); - break; - } - } else { - /* speed up */ - _session->request_transport_speed (current_transport_speed * 1.5f); + if (!_session) { + return; + } + + float current_transport_speed = _session->transport_speed(); + + if (current_transport_speed <= 0.0f) { + switch (option) { + case 0: + _session->request_transport_speed (1.0f); + break; + case 1: + _session->request_transport_speed (4.0f); + break; + case -1: + _session->request_transport_speed (0.5f); + break; } - + } else { + /* speed up */ + _session->request_transport_speed (current_transport_speed * 1.5f); } } void ARDOUR_UI::toggle_record_enable (uint32_t rid) { - if (_session == 0) { + if (!_session) { return; } @@ -1829,9 +1799,6 @@ ARDOUR_UI::toggle_record_enable (uint32_t rid) t->set_record_enabled (!t->record_enabled(), this); } } - if (_session == 0) { - return; - } } void @@ -2032,7 +1999,6 @@ ARDOUR_UI::stop_clocking () gint ARDOUR_UI::_blink (void *arg) - { ((ARDOUR_UI *) arg)->blink (); return TRUE; @@ -2424,27 +2390,9 @@ ARDOUR_UI::idle_load (const std::string& path) /* /path/to/foo/foo.ardour => /path/to/foo, foo */ load_session (Glib::path_get_dirname (path), basename_nosuffix (path)); } + } else { - ARDOUR_COMMAND_LINE::session_name = path; - - /* - * new_session_dialog doens't exist in A3 - * Try to remove all references to it to - * see if it will compile. NOTE: this will - * likely cause a runtime issue is my somewhat - * uneducated guess. - */ - - //if (new_session_dialog) { - - - /* make it break out of Dialog::run() and - start again. - */ - - //new_session_dialog->response (1); - //} } } @@ -2850,7 +2798,7 @@ ARDOUR_UI::show_about () { if (about == 0) { about = new About; - about->signal_response().connect(sigc::mem_fun (*this, &ARDOUR_UI::about_signal_response) ); + about->signal_response().connect (sigc::mem_fun (*this, &ARDOUR_UI::about_signal_response)); } about->set_transient_for(*editor); @@ -3363,14 +3311,14 @@ ARDOUR_UI::pending_state_dialog () HBox* hbox = new HBox(); Image* image = new Image (Stock::DIALOG_QUESTION, ICON_SIZE_DIALOG); ArdourDialog dialog (_("Crash Recovery"), true); - Label message (_("\ + Label message (string_compose (_("\ This session appears to have been in\n\ middle of recording when ardour or\n\ the computer was shutdown.\n\ \n\ -Ardour can recover any captured audio for\n\ +%1 can recover any captured audio for\n\ you, or it can ignore it. Please decide\n\ -what you would like to do.\n")); +what you would like to do.\n"), PROGRAM_NAME)); image->set_alignment(ALIGN_CENTER, ALIGN_TOP); hbox->pack_start (*image, PACK_EXPAND_WIDGET, 12); hbox->pack_end (message, PACK_EXPAND_PADDING, 12); @@ -3398,9 +3346,9 @@ ARDOUR_UI::sr_mismatch_dialog (framecnt_t desired, framecnt_t actual) Image* image = new Image (Stock::DIALOG_QUESTION, ICON_SIZE_DIALOG); ArdourDialog dialog (_("Sample Rate Mismatch"), true); Label message (string_compose (_("\ -This session was created with a sample rate of %1 Hz\n\ -\n\ -The audioengine is currently running at %2 Hz\n"), desired, actual)); +This session was created with a sample rate of %1 Hz, but\n\ +%2 is currently running at %3 Hz. If you load this session,\n\ +audio may be played at the wrong sample rate.\n"), desired, PROGRAM_NAME, actual)); image->set_alignment(ALIGN_CENTER, ALIGN_TOP); hbox->pack_start (*image, PACK_EXPAND_WIDGET, 12); @@ -3427,7 +3375,7 @@ void ARDOUR_UI::disconnect_from_jack () { if (engine) { - if( engine->disconnect_from_jack ()) { + if (engine->disconnect_from_jack ()) { MessageDialog msg (*editor, _("Could not disconnect from JACK")); msg.run (); } @@ -3628,11 +3576,10 @@ ARDOUR_UI::TransportControllable::get_value (void) const void ARDOUR_UI::setup_profile () { - if (gdk_screen_width() < 1200) { + if (gdk_screen_width() < 1200 || getenv ("ARDOUR_NARROW_SCREEN")) { Profile->set_small_screen (); } - if (getenv ("ARDOUR_SAE")) { Profile->set_sae (); Profile->set_single_package (); @@ -3654,11 +3601,11 @@ ARDOUR_UI::toggle_translations () bool already_enabled = !ARDOUR::translations_are_disabled (); if (ract->get_active ()) { -/* we don't care about errors */ + /* we don't care about errors */ int fd = ::open (i18n_killer.c_str(), O_RDONLY|O_CREAT, 0644); close (fd); } else { -/* we don't care about errors */ + /* we don't care about errors */ unlink (i18n_killer.c_str()); } diff --git a/gtk2_ardour/ardour_ui_dialogs.cc b/gtk2_ardour/ardour_ui_dialogs.cc index bb3887862d..2dd48b71d1 100644 --- a/gtk2_ardour/ardour_ui_dialogs.cc +++ b/gtk2_ardour/ardour_ui_dialogs.cc @@ -161,15 +161,15 @@ ARDOUR_UI::set_session (Session *s) _session->TransportStateChange.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::map_transport_state, this), gui_context()); _session->DirtyChanged.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::update_autosave, this), gui_context()); - _session->Xrun.connect (_session_connections, MISSING_INVALIDATOR, ui_bind (&ARDOUR_UI::xrun_handler, this, _1), gui_context()); - _session->SoloActive.connect (_session_connections, MISSING_INVALIDATOR, ui_bind (&ARDOUR_UI::soloing_changed, this, _1), gui_context()); - _session->AuditionActive.connect (_session_connections, MISSING_INVALIDATOR, ui_bind (&ARDOUR_UI::auditioning_changed, this, _1), gui_context()); - _session->locations()->added.connect (_session_connections, MISSING_INVALIDATOR, ui_bind (&ARDOUR_UI::handle_locations_change, this, _1), gui_context()); - _session->locations()->removed.connect (_session_connections, MISSING_INVALIDATOR, ui_bind (&ARDOUR_UI::handle_locations_change, this, _1), gui_context()); - _session->config.ParameterChanged.connect (_session_connections, MISSING_INVALIDATOR, ui_bind (&ARDOUR_UI::session_parameter_changed, this, _1), gui_context ()); + _session->Xrun.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::xrun_handler, this, _1), gui_context()); + _session->SoloActive.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::soloing_changed, this, _1), gui_context()); + _session->AuditionActive.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::auditioning_changed, this, _1), gui_context()); + _session->locations()->added.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::handle_locations_change, this, _1), gui_context()); + _session->locations()->removed.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::handle_locations_change, this, _1), gui_context()); + _session->config.ParameterChanged.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::session_parameter_changed, this, _1), gui_context ()); #ifdef HAVE_JACK_SESSION - engine->JackSessionEvent.connect (*_session, MISSING_INVALIDATOR, ui_bind (&Session::jack_session_event, _session, _1), gui_context()); + engine->JackSessionEvent.connect (*_session, MISSING_INVALIDATOR, boost::bind (&Session::jack_session_event, _session, _1), gui_context()); #endif /* Clocks are on by default after we are connected to a session, so show that here. diff --git a/gtk2_ardour/ardour_ui_options.cc b/gtk2_ardour/ardour_ui_options.cc index dcb95a0e69..6c8319e808 100644 --- a/gtk2_ardour/ardour_ui_options.cc +++ b/gtk2_ardour/ardour_ui_options.cc @@ -293,7 +293,7 @@ ARDOUR_UI::toggle_editing_space() void ARDOUR_UI::setup_session_options () { - _session->config.ParameterChanged.connect (_session_connections, MISSING_INVALIDATOR, ui_bind (&ARDOUR_UI::parameter_changed, this, _1), gui_context()); + _session->config.ParameterChanged.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::parameter_changed, this, _1), gui_context()); boost::function pc (boost::bind (&ARDOUR_UI::parameter_changed, this, _1)); _session->config.map_parameters (pc); } diff --git a/gtk2_ardour/au_pluginui.mm b/gtk2_ardour/au_pluginui.mm index d1ba3d4d7f..43378aed3e 100644 --- a/gtk2_ardour/au_pluginui.mm +++ b/gtk2_ardour/au_pluginui.mm @@ -392,7 +392,12 @@ AUPluginUI::create_cocoa_view () // watch for size changes of the view [[NSNotificationCenter defaultCenter] addObserver:_notify - selector:@selector(auViewResized:) name:NSWindowDidResizeNotification + selector:@selector(auViewResized:) name:NSViewBoundsDidChangeNotification + object:au_view]; + + + [[NSNotificationCenter defaultCenter] addObserver:_notify + selector:@selector(auViewResized:) name:NSViewFrameDidChangeNotification object:au_view]; // Get the size of the new AU View's frame @@ -410,6 +415,9 @@ void AUPluginUI::cocoa_view_resized () { NSRect packFrame = [au_view frame]; + prefwidth = packFrame.size.width; + prefheight = packFrame.size.height; + low_box.set_size_request (prefwidth, prefheight); } int diff --git a/gtk2_ardour/audio_streamview.cc b/gtk2_ardour/audio_streamview.cc index c7afa6e3bf..e9ee8fe647 100644 --- a/gtk2_ardour/audio_streamview.cc +++ b/gtk2_ardour/audio_streamview.cc @@ -46,7 +46,6 @@ #include "selection.h" #include "public_editor.h" #include "ardour_ui.h" -#include "crossfade_view.h" #include "rgb_macros.h" #include "gui_thread.h" #include "utils.h" @@ -61,30 +60,10 @@ using namespace Editing; AudioStreamView::AudioStreamView (AudioTimeAxisView& tv) : StreamView (tv) { - crossfades_visible = tv.session()->config.get_xfades_visible (); color_handler (); _amplitude_above_axis = 1.0; - Config->ParameterChanged.connect (*this, invalidator (*this), ui_bind (&AudioStreamView::parameter_changed, this, _1), gui_context()); -} - -AudioStreamView::~AudioStreamView () -{ - for (CrossfadeViewList::iterator xi = crossfade_views.begin(); xi != crossfade_views.end(); ++xi) { - delete xi->second; - } -} - -int -AudioStreamView::set_samples_per_unit (gdouble spp) -{ - StreamView::set_samples_per_unit(spp); - - for (CrossfadeViewList::iterator xi = crossfade_views.begin(); xi != crossfade_views.end(); ++xi) { - xi->second->set_samples_per_unit (spp); - } - - return 0; + Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&AudioStreamView::parameter_changed, this, _1), gui_context()); } int @@ -209,163 +188,10 @@ AudioStreamView::add_region_view_internal (boost::shared_ptr r, bool wai return region_view; } -void -AudioStreamView::remove_region_view (boost::weak_ptr weak_r) -{ - ENSURE_GUI_THREAD (*this, &AudioStreamView::remove_region_view, weak_r); - - boost::shared_ptr r (weak_r.lock()); - - if (!r) { - return; - } - - if (!_trackview.session()->deletion_in_progress()) { - - for (CrossfadeViewList::iterator i = crossfade_views.begin(); i != crossfade_views.end();) { - CrossfadeViewList::iterator tmp; - - tmp = i; - ++tmp; - - boost::shared_ptr ar = boost::dynamic_pointer_cast(r); - if (ar && i->second->crossfade->involves (ar)) { - delete i->second; - crossfade_views.erase (i); - } - - i = tmp; - } - } - - StreamView::remove_region_view(r); -} - -void -AudioStreamView::undisplay_track () -{ - StreamView::undisplay_track (); - - for (CrossfadeViewList::iterator i = crossfade_views.begin(); i != crossfade_views.end(); ++i) { - delete i->second; - } - - crossfade_views.clear (); -} - -void -AudioStreamView::playlist_layered (boost::weak_ptr wtr) -{ - boost::shared_ptr tr (wtr.lock()); - - if (!tr) { - return; - } - - StreamView::playlist_layered (wtr); - - /* make sure xfades are on top and all the regionviews are stacked correctly. */ - - for (CrossfadeViewList::iterator i = crossfade_views.begin(); i != crossfade_views.end(); ++i) { - i->second->get_canvas_group()->raise_to_top(); - } -} - -void -AudioStreamView::playlist_switched (boost::weak_ptr wtr) -{ - boost::shared_ptr tr (wtr.lock()); - - if (!tr) { - return; - } - - playlist_connections.drop_connections (); - - StreamView::playlist_switched (tr); - - boost::shared_ptr apl = boost::dynamic_pointer_cast (tr->playlist()); - - if (apl) { - apl->NewCrossfade.connect (playlist_connections, invalidator (*this), ui_bind (&AudioStreamView::add_crossfade, this, _1), gui_context()); - } -} - -void -AudioStreamView::add_crossfade (boost::weak_ptr wc) -{ - boost::shared_ptr crossfade (wc.lock()); - - if (!crossfade) { - return; - } - - AudioRegionView* lview = 0; - AudioRegionView* rview = 0; - - /* first see if we already have a CrossfadeView for this Crossfade */ - - CrossfadeViewList::iterator i = crossfade_views.find (crossfade); - if (i != crossfade_views.end()) { - if (!crossfades_visible) { - i->second->hide(); - } else { - i->second->show (); - } - i->second->set_valid (true); - return; - } - - /* create a new one */ - - for (list::iterator i = region_views.begin(); i != region_views.end(); ++i) { - AudioRegionView* arv = dynamic_cast(*i); - - if (!lview && arv && (arv->region() == crossfade->out())) { - lview = arv; - } - if (!rview && arv && (arv->region() == crossfade->in())) { - rview = arv; - } - } - - CrossfadeView *cv = new CrossfadeView (_trackview.canvas_display (), - _trackview, - crossfade, - _samples_per_unit, - region_color, - *lview, *rview); - cv->set_valid (true); - crossfade->Invalidated.connect (*this, invalidator (*this), ui_bind (&AudioStreamView::remove_crossfade, this, _1), gui_context()); - crossfade_views[cv->crossfade] = cv; - if (!crossfades_visible) { - cv->hide (); - } - - update_content_height (cv); -} - -void -AudioStreamView::remove_crossfade (boost::shared_ptr r) -{ - ENSURE_GUI_THREAD (*this, &AudioStreamView::remove_crossfade, r) - - boost::shared_ptr xfade = boost::dynamic_pointer_cast (r); - - for (CrossfadeViewList::iterator i = crossfade_views.begin(); i != crossfade_views.end(); ++i) { - if (i->second->crossfade == xfade) { - delete i->second; - crossfade_views.erase (i); - break; - } - } -} - void AudioStreamView::redisplay_track () { list::iterator i; - CrossfadeViewList::iterator xi, tmpx; // Flag region views as invalid and disable drawing for (i = region_views.begin(); i != region_views.end(); ++i) { @@ -373,41 +199,11 @@ AudioStreamView::redisplay_track () (*i)->enable_display (false); } - // Flag crossfade views as invalid - for (xi = crossfade_views.begin(); xi != crossfade_views.end(); ++xi) { - xi->second->set_valid (false); - if (xi->second->visible()) { - xi->second->show (); - } - } - - // Add and display region and crossfade views, and flag them as valid - + // Add and display views, and flag them as valid if (_trackview.is_audio_track()) { _trackview.track()->playlist()->foreach_region( sigc::hide_return (sigc::mem_fun (*this, &StreamView::add_region_view)) ); - - boost::shared_ptr apl = boost::dynamic_pointer_cast( - _trackview.track()->playlist() - ); - - if (apl) { - apl->foreach_crossfade (sigc::mem_fun (*this, &AudioStreamView::add_crossfade)); - } - } - - // Remove invalid crossfade views - for (xi = crossfade_views.begin(); xi != crossfade_views.end();) { - tmpx = xi; - tmpx++; - - if (!xi->second->valid()) { - delete xi->second; - crossfade_views.erase (xi); - } - - xi = tmpx; } // Stack regions by layer, and remove invalid regions @@ -473,7 +269,7 @@ AudioStreamView::setup_rec_box () sources.push_back (src); src->PeakRangeReady.connect (rec_data_ready_connections, invalidator (*this), - ui_bind (&AudioStreamView::rec_peak_range_ready, this, _1, _2, boost::weak_ptr(src)), + boost::bind (&AudioStreamView::rec_peak_range_ready, this, _1, _2, boost::weak_ptr(src)), gui_context()); } } @@ -600,14 +396,6 @@ AudioStreamView::setup_rec_box () } } -void -AudioStreamView::foreach_crossfadeview (void (CrossfadeView::*pmf)(void)) -{ - for (CrossfadeViewList::iterator i = crossfade_views.begin(); i != crossfade_views.end(); ++i) { - (i->second->*pmf) (); - } -} - void AudioStreamView::rec_peak_range_ready (framepos_t start, framecnt_t cnt, boost::weak_ptr weak_src) { @@ -744,40 +532,6 @@ AudioStreamView::hide_all_fades () } } -void -AudioStreamView::show_all_xfades () -{ - foreach_crossfadeview (&CrossfadeView::show); - crossfades_visible = true; -} - -void -AudioStreamView::hide_all_xfades () -{ - foreach_crossfadeview (&CrossfadeView::hide); - crossfades_visible = false; -} - -void -AudioStreamView::hide_xfades_involving (AudioRegionView& rv) -{ - for (CrossfadeViewList::iterator i = crossfade_views.begin(); i != crossfade_views.end(); ++i) { - if (i->second->crossfade->involves (rv.audio_region())) { - i->second->fake_hide (); - } - } -} - -void -AudioStreamView::reveal_xfades_involving (AudioRegionView& rv) -{ - for (CrossfadeViewList::iterator i = crossfade_views.begin(); i != crossfade_views.end(); ++i) { - if (i->second->crossfade->involves (rv.audio_region()) && i->second->visible()) { - i->second->show (); - } - } -} - void AudioStreamView::color_handler () { @@ -796,45 +550,6 @@ AudioStreamView::color_handler () } } -void -AudioStreamView::update_contents_height () -{ - StreamView::update_contents_height (); - - for (CrossfadeViewList::iterator i = crossfade_views.begin(); i != crossfade_views.end(); ++i) { - update_content_height (i->second); - } -} - -void -AudioStreamView::update_content_height (CrossfadeView* cv) -{ - switch (_layer_display) { - case Overlaid: - cv->set_y (0); - cv->set_heights (height, height); - break; - - case Stacked: - case Expanded: - layer_t const inl = cv->crossfade->in()->layer (); - layer_t const outl = cv->crossfade->out()->layer (); - - layer_t const high = max (inl, outl); - layer_t const low = min (inl, outl); - - const double h = child_height (); - - if (_layer_display == Stacked) { - cv->set_y ((_layers - high - 1) * h); - cv->set_heights ((high - low + 1) * h, h); - } else { - cv->set_y (((_layers - high) * 2 - 1) * h); - cv->set_heights (((high - low) * 2 + 1) * h, h); - } - } -} - void AudioStreamView::parameter_changed (string const & p) { @@ -847,13 +562,3 @@ AudioStreamView::parameter_changed (string const & p) } } -void -AudioStreamView::horizontal_position_changed () -{ - /* we only `draw' the bit of the curve that is visible, so we need to update here */ - - for (CrossfadeViewList::iterator i = crossfade_views.begin(); i != crossfade_views.end(); ++i) { - i->second->horizontal_position_changed (); - } -} - diff --git a/gtk2_ardour/audio_streamview.h b/gtk2_ardour/audio_streamview.h index 0c50986f93..4812903a35 100644 --- a/gtk2_ardour/audio_streamview.h +++ b/gtk2_ardour/audio_streamview.h @@ -36,7 +36,6 @@ namespace Gdk { namespace ARDOUR { class Route; - class Crossfade; class PeakData; class AudioRegion; class Source; @@ -47,33 +46,21 @@ class Selectable; class AudioTimeAxisView; class AudioRegionView; class RegionSelection; -class CrossfadeView; class Selection; class AudioStreamView : public StreamView { public: AudioStreamView (AudioTimeAxisView&); - ~AudioStreamView (); - - int set_samples_per_unit (gdouble spp); - void horizontal_position_changed (); int set_amplitude_above_axis (gdouble app); gdouble get_amplitude_above_axis () { return _amplitude_above_axis; } void set_show_waveforms (bool yn); - void foreach_crossfadeview (void (CrossfadeView::*pmf)(void)); - void show_all_fades (); void hide_all_fades (); - void show_all_xfades (); - void hide_all_xfades (); - void hide_xfades_involving (AudioRegionView&); - void reveal_xfades_involving (AudioRegionView&); - RegionView* create_region_view (boost::shared_ptr, bool, bool); private: @@ -82,32 +69,18 @@ class AudioStreamView : public StreamView void update_rec_regions (ARDOUR::framepos_t, ARDOUR::framecnt_t); RegionView* add_region_view_internal (boost::shared_ptr, bool wait_for_waves, bool recording = false); - void remove_region_view (boost::weak_ptr ); void remove_audio_region_view (boost::shared_ptr ); - void undisplay_track (); void redisplay_track (); - void playlist_layered (boost::weak_ptr); - void playlist_switched (boost::weak_ptr); - - void add_crossfade (boost::weak_ptr); - void remove_crossfade (boost::shared_ptr); void color_handler (); - void update_contents_height (); - void update_content_height (CrossfadeView *); - void parameter_changed (std::string const &); void set_waveform_shape (ARDOUR::WaveformShape); void set_waveform_scale (ARDOUR::WaveformScale); double _amplitude_above_axis; - typedef std::map, CrossfadeView*> CrossfadeViewList; - CrossfadeViewList crossfade_views; - bool crossfades_visible; - std::map, bool> rec_data_ready_map; bool outline_region; diff --git a/gtk2_ardour/audio_time_axis.cc b/gtk2_ardour/audio_time_axis.cc index b2a1d6bf10..eca908eeb4 100644 --- a/gtk2_ardour/audio_time_axis.cc +++ b/gtk2_ardour/audio_time_axis.cc @@ -55,7 +55,6 @@ #include "audio_time_axis.h" #include "automation_line.h" #include "canvas_impl.h" -#include "crossfade_view.h" #include "enums.h" #include "gui_thread.h" #include "automation_time_axis.h" @@ -176,22 +175,6 @@ AudioTimeAxisView::hide () TimeAxisView::hide (); } - -void -AudioTimeAxisView::append_extra_display_menu_items () -{ - using namespace Menu_Helpers; - - MenuList& items = display_menu->items(); - - // crossfade stuff - if (!Profile->get_sae() && is_track ()) { - items.push_back (MenuElem (_("Hide All Crossfades"), sigc::bind (sigc::mem_fun(*this, &AudioTimeAxisView::hide_all_xfades), true))); - items.push_back (MenuElem (_("Show All Crossfades"), sigc::bind (sigc::mem_fun(*this, &AudioTimeAxisView::show_all_xfades), true))); - items.push_back (SeparatorElem ()); - } -} - void AudioTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show) { @@ -365,54 +348,6 @@ AudioTimeAxisView::hide_all_automation (bool apply_to_selection) } } -void -AudioTimeAxisView::show_all_xfades (bool apply_to_selection) -{ - if (apply_to_selection) { - _editor.get_selection().tracks.foreach_audio_time_axis (boost::bind (&AudioTimeAxisView::show_all_xfades, _1, false)); - } else { - AudioStreamView* asv = audio_view (); - if (asv) { - asv->show_all_xfades (); - } - } -} - -void -AudioTimeAxisView::hide_all_xfades (bool apply_to_selection) -{ - if (apply_to_selection) { - _editor.get_selection().tracks.foreach_audio_time_axis (boost::bind (&AudioTimeAxisView::hide_all_xfades, _1, false)); - } else { - AudioStreamView* asv = audio_view (); - if (asv) { - asv->hide_all_xfades (); - } - } -} - -void -AudioTimeAxisView::hide_dependent_views (TimeAxisViewItem& tavi) -{ - AudioStreamView* asv = audio_view(); - AudioRegionView* rv; - - if (asv && (rv = dynamic_cast(&tavi)) != 0) { - asv->hide_xfades_involving (*rv); - } -} - -void -AudioTimeAxisView::reveal_dependent_views (TimeAxisViewItem& tavi) -{ - AudioStreamView* asv = audio_view(); - AudioRegionView* rv; - - if (asv && (rv = dynamic_cast(&tavi)) != 0) { - asv->reveal_xfades_involving (*rv); - } -} - void AudioTimeAxisView::route_active_changed () { diff --git a/gtk2_ardour/audio_time_axis.h b/gtk2_ardour/audio_time_axis.h index c096c6706a..8a5b6ab0ac 100644 --- a/gtk2_ardour/audio_time_axis.h +++ b/gtk2_ardour/audio_time_axis.h @@ -73,10 +73,6 @@ class AudioTimeAxisView : public RouteTimeAxisView AudioStreamView* audio_view(); void set_show_waveforms_recording (bool yn); - void show_all_xfades (bool apply_to_selection = false); - void hide_all_xfades (bool apply_to_selection = false); - void hide_dependent_views (TimeAxisViewItem&); - void reveal_dependent_views (TimeAxisViewItem&); /* Overridden from parent to store display state */ guint32 show_at (double y, int& nth, Gtk::VBox *parent); @@ -94,7 +90,6 @@ class AudioTimeAxisView : public RouteTimeAxisView void route_active_changed (); - void append_extra_display_menu_items (); Gtk::Menu* build_mode_menu(); void build_automation_action_menu (bool); diff --git a/gtk2_ardour/automation_line.cc b/gtk2_ardour/automation_line.cc index 2ee64e5706..975281440a 100644 --- a/gtk2_ardour/automation_line.cc +++ b/gtk2_ardour/automation_line.cc @@ -254,7 +254,7 @@ AutomationLine::modify_point_y (ControlPoint& cp, double y) } alist->freeze (); - sync_model_with_view_point (cp, false, 0); + sync_model_with_view_point (cp, 0); alist->thaw (); update_pending = false; @@ -277,255 +277,15 @@ AutomationLine::reset_line_coords (ControlPoint& cp) } void -AutomationLine::sync_model_with_view_points (list cp, bool did_push, int64_t distance) +AutomationLine::sync_model_with_view_points (list cp, int64_t distance) { update_pending = true; for (list::iterator i = cp.begin(); i != cp.end(); ++i) { - sync_model_with_view_point (**i, did_push, distance); + sync_model_with_view_point (**i, distance); } } -void -AutomationLine::model_representation (ControlPoint& cp, ModelRepresentation& mr) -{ - /* part one: find out where the visual control point is. - initial results are in canvas units. ask the - line to convert them to something relevant. - */ - - mr.xval = cp.get_x(); - mr.yval = 1.0 - (cp.get_y() / _height); - - /* if xval has not changed, set it directly from the model to avoid rounding errors */ - - if (mr.xval == trackview.editor().frame_to_unit(_time_converter->to((*cp.model())->when)) - _offset) { - mr.xval = (*cp.model())->when - _offset; - } else { - mr.xval = trackview.editor().unit_to_frame (mr.xval); - mr.xval = _time_converter->from (mr.xval + _offset); - } - - /* convert y to model units; the x was already done above - */ - - view_to_model_coord_y (mr.yval); - - /* part 2: find out where the model point is now - */ - - mr.xpos = (*cp.model())->when - _offset; - mr.ypos = (*cp.model())->value; - - /* part 3: get the position of the visual control - points before and after us. - */ - - ControlPoint* before; - ControlPoint* after; - - if (cp.view_index()) { - before = nth (cp.view_index() - 1); - } else { - before = 0; - } - - after = nth (cp.view_index() + 1); - - if (before) { - mr.xmin = (*before->model())->when; - mr.ymin = (*before->model())->value; - mr.start = before->model(); - ++mr.start; - } else { - mr.xmin = mr.xpos; - mr.ymin = mr.ypos; - mr.start = cp.model(); - } - - if (after) { - mr.end = after->model(); - } else { - mr.xmax = mr.xpos; - mr.ymax = mr.ypos; - mr.end = cp.model(); - ++mr.end; - } -} - -/** @param points AutomationLine points to consider. These will correspond 1-to-1 to - * points in the AutomationList, but will have been transformed so that they are in pixels; - * the x coordinate being the pixel distance from the start of the line (0, or the start - * of the AutomationRegionView if we are in one). - * - * @param skipped Number of points in the AutomationList that were skipped before - * `points' starts. - */ - -void -AutomationLine::determine_visible_control_points (ALPoints& points, int skipped) -{ - uint32_t view_index, pi, n; - uint32_t npoints; - uint32_t this_rx = 0; - uint32_t prev_rx = 0; - uint32_t this_ry = 0; - uint32_t prev_ry = 0; - double* slope; - uint32_t box_size; - - /* hide all existing points, and the line */ - - for (vector::iterator i = control_points.begin(); i != control_points.end(); ++i) { - (*i)->hide(); - } - - line->hide (); - - if (points.empty()) { - return; - } - - npoints = points.size(); - - /* compute derivative/slope for the entire line */ - - slope = new double[npoints]; - - for (n = 0; n < npoints - 1; ++n) { - double xdelta = points[n+1].x - points[n].x; - double ydelta = points[n+1].y - points[n].y; - slope[n] = ydelta/xdelta; - } - - box_size = (uint32_t) control_point_box_size (); - - /* read all points and decide which ones to show as control points */ - - view_index = 0; - - /* skip over unused AutomationList points before we start */ - - AutomationList::iterator model = alist->begin (); - for (int i = 0; i < skipped; ++i) { - ++model; - } - - for (pi = 0; pi < npoints; ++model, ++pi) { - - /* If this line is in an AutomationRegionView, this is an offset from the region position, in pixels */ - double tx = points[pi].x; - double ty = points[pi].y; - - if (find (_always_in_view.begin(), _always_in_view.end(), (*model)->when) != _always_in_view.end()) { - add_visible_control_point (view_index, pi, tx, ty, model, npoints); - prev_rx = this_rx; - prev_ry = this_ry; - ++view_index; - continue; - } - - if (isnan (tx) || isnan (ty)) { - warning << string_compose (_("Ignoring illegal points on AutomationLine \"%1\""), - _name) << endmsg; - continue; - } - - /* now ensure that the control_points vector reflects the current curve - state, but don't plot control points too close together. also, don't - plot a series of points all with the same value. - - always plot the first and last points, of course. - */ - - if (invalid_point (points, pi)) { - /* for some reason, we are supposed to ignore this point, - but still keep track of the model index. - */ - continue; - } - - if (pi > 0 && pi < npoints - 1) { - if (slope[pi] == slope[pi-1]) { - - /* no reason to display this point */ - - continue; - } - } - - /* need to round here. the ultimate coordinates are integer - pixels, so tiny deltas in the coords will be eliminated - and we end up with "colinear" line segments. since the - line rendering code in libart doesn't like this very - much, we eliminate them here. don't do this for the first and last - points. - */ - - this_rx = (uint32_t) rint (tx); - this_ry = (uint32_t) rint (ty); - - if (view_index && pi != npoints && /* not the first, not the last */ - (((this_rx == prev_rx) && (this_ry == prev_ry)) || /* same point */ - (((this_rx - prev_rx) < (box_size + 2)) && /* not identical, but still too close horizontally */ - (abs ((int)(this_ry - prev_ry)) < (int) (box_size + 2))))) { /* too close vertically */ - continue; - } - - /* ok, we should display this point */ - - add_visible_control_point (view_index, pi, tx, ty, model, npoints); - - prev_rx = this_rx; - prev_ry = this_ry; - - view_index++; - } - - /* discard extra CP's to avoid confusing ourselves */ - - while (control_points.size() > view_index) { - ControlPoint* cp = control_points.back(); - control_points.pop_back (); - delete cp; - } - - if (!terminal_points_can_slide) { - control_points.back()->set_can_slide(false); - } - - delete [] slope; - - if (view_index > 1) { - - npoints = view_index; - - /* reset the line coordinates */ - - while (line_points.size() < npoints) { - line_points.push_back (Art::Point (0,0)); - } - - while (line_points.size() > npoints) { - line_points.pop_back (); - } - - for (view_index = 0; view_index < npoints; ++view_index) { - line_points[view_index].set_x (control_points[view_index]->get_x()); - line_points[view_index].set_y (control_points[view_index]->get_y()); - } - - line->property_points() = line_points; - - if (_visible && alist->interpolation() != AutomationList::Discrete) { - line->show(); - } - - } - - set_selected_points (trackview.editor().get_selection().points); -} - string AutomationLine::get_verbose_cursor_string (double fraction) const { @@ -589,19 +349,6 @@ AutomationLine::string_to_fraction (string const & s) const return v; } -bool -AutomationLine::invalid_point (ALPoints& p, uint32_t index) -{ - return p[index].x == max_framepos && p[index].y == DBL_MAX; -} - -void -AutomationLine::invalidate_point (ALPoints& p, uint32_t index) -{ - p[index].x = max_framepos; - p[index].y = DBL_MAX; -} - /** Start dragging a single point, possibly adding others if the supplied point is selected and there * are other selected points. * @@ -705,7 +452,7 @@ AutomationLine::start_drag_common (double x, float fraction) } /** Should be called to indicate motion during a drag. - * @param x New x position of the drag in units, or undefined if ignore_x == true. + * @param x New x position of the drag in canvas units, or undefined if ignore_x == true. * @param fraction New y fraction. * @return x position and y fraction that were actually used (once clamped). */ @@ -823,7 +570,7 @@ AutomationLine::end_drag () points.sort (ControlPointSorter ()); } - sync_model_with_view_points (points, did_push, rint (_drag_distance * trackview.editor().get_current_zoom ())); + sync_model_with_view_points (points, trackview.editor().unit_to_frame (_drag_distance)); alist->thaw (); @@ -834,72 +581,38 @@ AutomationLine::end_drag () ); trackview.editor().session()->set_dirty (); + did_push = false; } void -AutomationLine::sync_model_with_view_point (ControlPoint& cp, bool did_push, int64_t distance) +AutomationLine::sync_model_with_view_point (ControlPoint& cp, framecnt_t distance) { - ModelRepresentation mr; - double ydelta; - - model_representation (cp, mr); - - /* how much are we changing the central point by */ - - ydelta = mr.yval - mr.ypos; - - /* - apply the full change to the central point, and interpolate - on both axes to cover all model points represented - by the control point. + /* find out where the visual control point is. + initial results are in canvas units. ask the + line to convert them to something relevant. */ - /* change all points before the primary point */ + double view_x = cp.get_x(); + double view_y = 1.0 - (cp.get_y() / _height); - for (AutomationList::iterator i = mr.start; i != cp.model(); ++i) { + /* if xval has not changed, set it directly from the model to avoid rounding errors */ - double fract = ((*i)->when - mr.xmin) / (mr.xpos - mr.xmin); - double y_delta = ydelta * fract; - double x_delta = distance * fract; - - /* interpolate */ - - if (y_delta || x_delta) { - alist->modify (i, (*i)->when + x_delta, mr.ymin + y_delta); - } + if (view_x == trackview.editor().frame_to_unit (_time_converter->to ((*cp.model())->when)) - _offset) { + view_x = (*cp.model())->when - _offset; + } else { + view_x = trackview.editor().unit_to_frame (view_x); + view_x = _time_converter->from (view_x + _offset); } - /* change the primary point */ - update_pending = true; - alist->modify (cp.model(), mr.xval, mr.yval); - /* change later points */ + view_to_model_coord_y (view_y); - AutomationList::iterator i = cp.model(); - - ++i; - - while (i != mr.end) { - - double delta = ydelta * (mr.xmax - (*i)->when) / (mr.xmax - mr.xpos); - - /* all later points move by the same distance along the x-axis as the main point */ - - if (delta) { - alist->modify (i, (*i)->when + distance, (*i)->value + delta); - } - - ++i; - } + alist->modify (cp.model(), view_x, view_y); if (did_push) { - - /* move all points after the range represented by the view by the same distance - as the main point moved. - */ - - alist->slide (mr.end, distance); + /* move all points after cp by the same distance */ + alist->slide (cp.model()++, _time_converter->from (distance)); } } @@ -934,13 +647,16 @@ AutomationLine::control_points_adjacent (double xval, uint32_t & before, uint32_ bool AutomationLine::is_last_point (ControlPoint& cp) { - ModelRepresentation mr; - - model_representation (cp, mr); - // If the list is not empty, and the point is the last point in the list - if (!alist->empty() && mr.end == alist->end()) { + if (alist->empty()) { + return false; + } + + AutomationList::const_iterator i = alist->end(); + --i; + + if (cp.model() == i) { return true; } @@ -950,13 +666,9 @@ AutomationLine::is_last_point (ControlPoint& cp) bool AutomationLine::is_first_point (ControlPoint& cp) { - ModelRepresentation mr; - - model_representation (cp, mr); - // If the list is not empty, and the point is the first point in the list - if (!alist->empty() && mr.start == alist->begin()) { + if (!alist->empty() && cp.model() == alist->begin()) { return true; } @@ -967,15 +679,11 @@ AutomationLine::is_first_point (ControlPoint& cp) void AutomationLine::remove_point (ControlPoint& cp) { - ModelRepresentation mr; - - model_representation (cp, mr); - trackview.editor().session()->begin_reversible_command (_("remove control point")); XMLNode &before = alist->get_state(); - alist->erase (mr.start, mr.end); - + alist->erase (cp.model()); + trackview.editor().session()->add_command( new MementoCommand (memento_command_binder (), &before, &alist->get_state()) ); @@ -1021,38 +729,6 @@ AutomationLine::get_inverted_selectables (Selection&, list& /*resul // hmmm .... } -/** Take a PointSelection and find ControlPoints that fall within it */ -list -AutomationLine::point_selection_to_control_points (PointSelection const & s) -{ - list cp; - - for (PointSelection::const_iterator i = s.begin(); i != s.end(); ++i) { - - if (i->track != &trackview) { - continue; - } - - double const bot = (1 - i->high_fract) * trackview.current_height (); - double const top = (1 - i->low_fract) * trackview.current_height (); - - for (vector::iterator j = control_points.begin(); j != control_points.end(); ++j) { - - double const rstart = trackview.editor().frame_to_unit (_time_converter->to (i->start) - _offset); - double const rend = trackview.editor().frame_to_unit (_time_converter->to (i->end) - _offset); - - if ((*j)->get_x() >= rstart && (*j)->get_x() <= rend) { - if ((*j)->get_y() >= bot && (*j)->get_y() <= top) { - cp.push_back (*j); - } - } - } - - } - - return cp; -} - void AutomationLine::set_selected_points (PointSelection const & points) { @@ -1060,11 +736,8 @@ AutomationLine::set_selected_points (PointSelection const & points) (*i)->set_selected (false); } - if (!points.empty()) { - list cp = point_selection_to_control_points (points); - for (list::iterator i = cp.begin(); i != cp.end(); ++i) { - (*i)->set_selected (true); - } + for (PointSelection::const_iterator i = points.begin(); i != points.end(); ++i) { + (*i)->set_selected (true); } set_colors (); @@ -1087,10 +760,11 @@ AutomationLine::list_changed () void AutomationLine::reset_callback (const Evoral::ControlList& events) { - ALPoints tmp_points; - uint32_t npoints = events.size(); + uint32_t vp = 0; + uint32_t pi = 0; + uint32_t np; - if (npoints == 0) { + if (events.empty()) { for (vector::iterator i = control_points.begin(); i != control_points.end(); ++i) { delete *i; } @@ -1099,26 +773,89 @@ AutomationLine::reset_callback (const Evoral::ControlList& events) return; } - AutomationList::const_iterator ai; - int skipped = 0; + /* hide all existing points, and the line */ - for (ai = events.begin(); ai != events.end(); ++ai) { + for (vector::iterator i = control_points.begin(); i != control_points.end(); ++i) { + (*i)->hide(); + } - double translated_x = (*ai)->when; - double translated_y = (*ai)->value; - model_to_view_coord (translated_x, translated_y); + line->hide (); + np = events.size(); - if (translated_x >= 0 && translated_x < _maximum_time) { - tmp_points.push_back (ALPoint ( - trackview.editor().frame_to_unit (translated_x), - _height - (translated_y * _height)) - ); - } else if (translated_x < 0) { - ++skipped; + Evoral::ControlList& e = const_cast (events); + + for (AutomationList::iterator ai = e.begin(); ai != e.end(); ++ai, ++pi) { + + double tx = (*ai)->when; + double ty = (*ai)->value; + + /* convert from model coordinates to canonical view coordinates */ + + model_to_view_coord (tx, ty); + + if (std::isnan (tx) || std::isnan (ty)) { + warning << string_compose (_("Ignoring illegal points on AutomationLine \"%1\""), + _name) << endmsg; + continue; + } + + if (tx >= max_framepos || tx < 0 || tx >= _maximum_time) { + continue; + } + + /* convert x-coordinate to a canvas unit coordinate (this takes + * zoom and scroll into account). + */ + + tx = trackview.editor().frame_to_unit (tx); + + /* convert from canonical view height (0..1.0) to actual + * height coordinates (using X11's top-left rooted system) + */ + + ty = _height - (ty * _height); + + add_visible_control_point (vp, pi, tx, ty, ai, np); + vp++; + } + + /* discard extra CP's to avoid confusing ourselves */ + + while (control_points.size() > vp) { + ControlPoint* cp = control_points.back(); + control_points.pop_back (); + delete cp; + } + + if (!terminal_points_can_slide) { + control_points.back()->set_can_slide(false); + } + + if (vp > 1) { + + /* reset the line coordinates given to the CanvasLine */ + + while (line_points.size() < vp) { + line_points.push_back (Art::Point (0,0)); + } + + while (line_points.size() > vp) { + line_points.pop_back (); + } + + for (uint32_t n = 0; n < vp; ++n) { + line_points[n].set_x (control_points[n]->get_x()); + line_points[n].set_y (control_points[n]->get_y()); + } + + line->property_points() = line_points; + + if (_visible && alist->interpolation() != AutomationList::Discrete) { + line->show(); } } - determine_visible_control_points (tmp_points, skipped); + set_selected_points (trackview.editor().get_selection().points); } void @@ -1284,8 +1021,11 @@ AutomationLine::interpolation_changed (AutomationList::InterpolationStyle style) } void -AutomationLine::add_visible_control_point (uint32_t view_index, uint32_t pi, double tx, double ty, AutomationList::iterator model, uint32_t npoints) +AutomationLine::add_visible_control_point (uint32_t view_index, uint32_t pi, double tx, double ty, + AutomationList::iterator model, uint32_t npoints) { + ControlPoint::ShapeType shape; + if (view_index >= control_points.size()) { /* make sure we have enough control points */ @@ -1296,25 +1036,23 @@ AutomationLine::add_visible_control_point (uint32_t view_index, uint32_t pi, dou control_points.push_back (ncp); } - ControlPoint::ShapeType shape; - if (!terminal_points_can_slide) { if (pi == 0) { - control_points[view_index]->set_can_slide(false); + control_points[view_index]->set_can_slide (false); if (tx == 0) { shape = ControlPoint::Start; } else { shape = ControlPoint::Full; } } else if (pi == npoints - 1) { - control_points[view_index]->set_can_slide(false); + control_points[view_index]->set_can_slide (false); shape = ControlPoint::End; } else { - control_points[view_index]->set_can_slide(true); + control_points[view_index]->set_can_slide (true); shape = ControlPoint::Full; } } else { - control_points[view_index]->set_can_slide(true); + control_points[view_index]->set_can_slide (true); shape = ControlPoint::Full; } @@ -1332,20 +1070,6 @@ AutomationLine::add_visible_control_point (uint32_t view_index, uint32_t pi, dou } } -void -AutomationLine::add_always_in_view (double x) -{ - _always_in_view.push_back (x); - alist->apply_to_points (*this, &AutomationLine::reset_callback); -} - -void -AutomationLine::clear_always_in_view () -{ - _always_in_view.clear (); - alist->apply_to_points (*this, &AutomationLine::reset_callback); -} - void AutomationLine::connect_to_list () { @@ -1386,13 +1110,19 @@ AutomationLine::get_point_x_range () const pair r (max_framepos, 0); for (AutomationList::const_iterator i = the_list()->begin(); i != the_list()->end(); ++i) { - r.first = min (r.first, _time_converter->to ((*i)->when) + _offset + _time_converter->origin_b ()); - r.second = max (r.second, _time_converter->to ((*i)->when) + _offset + _time_converter->origin_b ()); + r.first = min (r.first, session_position (i)); + r.second = max (r.second, session_position (i)); } return r; } +framepos_t +AutomationLine::session_position (AutomationList::const_iterator p) const +{ + return _time_converter->to ((*p)->when) + _offset + _time_converter->origin_b (); +} + void AutomationLine::set_offset (framepos_t off) { diff --git a/gtk2_ardour/automation_line.h b/gtk2_ardour/automation_line.h index 5ca2c83cd6..bee78c4ef5 100644 --- a/gtk2_ardour/automation_line.h +++ b/gtk2_ardour/automation_line.h @@ -66,7 +66,6 @@ class AutomationLine : public sigc::trackable, public PBD::StatefulDestructible void reset (); void clear (); - std::list point_selection_to_control_points (PointSelection const &); void set_selected_points (PointSelection const &); void get_selectables (ARDOUR::framepos_t, ARDOUR::framepos_t, double, double, std::list&); void get_inverted_selectables (Selection&, std::list& results); @@ -129,9 +128,6 @@ class AutomationLine : public sigc::trackable, public PBD::StatefulDestructible void modify_point_y (ControlPoint&, double); - void add_always_in_view (double); - void clear_always_in_view (); - virtual MementoCommandBinder* memento_command_binder (); const Evoral::TimeConverter& time_converter () const { @@ -148,6 +144,8 @@ class AutomationLine : public sigc::trackable, public PBD::StatefulDestructible void set_offset (ARDOUR::framecnt_t); void set_width (ARDOUR::framecnt_t); + framepos_t session_position (ARDOUR::AutomationList::const_iterator) const; + protected: std::string _name; @@ -156,7 +154,7 @@ class AutomationLine : public sigc::trackable, public PBD::StatefulDestructible boost::shared_ptr alist; Evoral::TimeConverter* _time_converter; - /** true if _time_converter belongs to us (ie we should delete it) */ + /** true if _time_converter belongs to us (ie we should delete it on destruction) */ bool _our_time_converter; bool _visible : 1; @@ -174,20 +172,8 @@ class AutomationLine : public sigc::trackable, public PBD::StatefulDestructible ArdourCanvas::Points line_points; /* coordinates for canvas line */ std::vector control_points; /* visible control points */ - struct ALPoint { - double x; - double y; - ALPoint (double xx, double yy) : x(xx), y(yy) {} - }; - - typedef std::vector ALPoints; - - static void invalidate_point (ALPoints&, uint32_t index); - static bool invalid_point (ALPoints&, uint32_t index); - - void determine_visible_control_points (ALPoints &, int); - void sync_model_with_view_point (ControlPoint&, bool, int64_t); - void sync_model_with_view_points (std::list, bool, int64_t); + void sync_model_with_view_point (ControlPoint&, ARDOUR::framecnt_t); + void sync_model_with_view_points (std::list, ARDOUR::framecnt_t); void start_drag_common (double, float); virtual void change_model (ARDOUR::AutomationList::iterator, double x, double y); @@ -202,9 +188,8 @@ class AutomationLine : public sigc::trackable, public PBD::StatefulDestructible std::list _push_points; ///< additional points we are dragging if "push" is enabled bool _drag_had_movement; ///< true if the drag has seen movement, otherwise false double _drag_x; ///< last x position of the drag, in units - double _drag_distance; ///< total x movement of the drag, in units + double _drag_distance; ///< total x movement of the drag, in canvas units double _last_drag_fraction; ///< last y position of the drag, as a fraction - std::list _always_in_view; /** offset from the start of the automation list to the start of the line, so that * a +ve offset means that the 0 on the line is at _offset in the list */ @@ -216,21 +201,6 @@ class AutomationLine : public sigc::trackable, public PBD::StatefulDestructible void connect_to_list (); void interpolation_changed (ARDOUR::AutomationList::InterpolationStyle); - struct ModelRepresentation { - ARDOUR::AutomationList::iterator start; - ARDOUR::AutomationList::iterator end; - double xpos; - double ypos; - double xmin; - double ymin; - double xmax; - double ymax; - double xval; - double yval; - }; - - void model_representation (ControlPoint&, ModelRepresentation&); - PBD::ScopedConnectionList _list_connections; /** maximum time that a point on this line can be at, relative to the position of its region or start of its track */ diff --git a/gtk2_ardour/automation_range.h b/gtk2_ardour/automation_range.h deleted file mode 100644 index 2baa515d8b..0000000000 --- a/gtk2_ardour/automation_range.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - Copyright (C) 2000-2007 Paul Davis - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#ifndef __ardour_gtk_automation_range_h__ -#define __ardour_gtk_automation_range_h__ - -class TimeAxisView; - -/** A rectangular range of an automation line, used to express a selected area. - * - * x coordinates start/end are in AutomationList model coordinates. - * y coordinates are a expressed as a fraction of the AutomationTimeAxisView's height, where 0 is the - * bottom of the track, and 1 is the top. - * - * This representation falls between the visible GUI control points and - * the back-end "actual" automation points, some of which may not be - * visible; it is not trivial to convert from one of these to the - * other, so the AutomationSelectable is a kind of "best and worst of - * both worlds". - * - * It offers a zoom-independent representation of a selected area of automation. - */ -struct AutomationRange -{ - double start; - double end; - double low_fract; - double high_fract; - TimeAxisView* track; // ref would be better, but ARDOUR::SessionHandlePtr is non-assignable - - AutomationRange (double s, double e, double l, double h, TimeAxisView* atv) - : start (s), end (e), low_fract (l), high_fract (h), track (atv) {} -}; - -#endif /* __ardour_gtk_automation_range_h__ */ diff --git a/gtk2_ardour/automation_time_axis.cc b/gtk2_ardour/automation_time_axis.cc index 586e2aef18..4b10ea5cb5 100644 --- a/gtk2_ardour/automation_time_axis.cc +++ b/gtk2_ardour/automation_time_axis.cc @@ -44,6 +44,7 @@ #include "rgb_macros.h" #include "point_selection.h" #include "canvas_impl.h" +#include "control_point.h" #include "utils.h" #include "i18n.h" @@ -226,7 +227,7 @@ AutomationTimeAxisView::AutomationTimeAxisView ( ColorsChanged.connect (sigc::mem_fun (*this, &AutomationTimeAxisView::color_handler)); _route->DropReferences.connect ( - _route_connections, invalidator (*this), ui_bind (&AutomationTimeAxisView::route_going_away, this), gui_context () + _route_connections, invalidator (*this), boost::bind (&AutomationTimeAxisView::route_going_away, this), gui_context () ); } @@ -592,172 +593,6 @@ AutomationTimeAxisView::add_automation_event (GdkEvent* event, framepos_t when, _session->set_dirty (); } -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; - } - } -} - -void -AutomationTimeAxisView::reset_objects (PointSelection& selection) -{ - 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) { - reset_objects_one (**i, selection); - } -} - -void -AutomationTimeAxisView::reset_objects_one (AutomationLine& line, PointSelection& selection) -{ - boost::shared_ptr alist(line.the_list()); - - _session->add_command (new MementoCommand(*alist.get(), &alist->get_state(), 0)); - - for (PointSelection::iterator i = selection.begin(); i != selection.end(); ++i) { - - if ((*i).track != this) { - continue; - } - - alist->reset_range ((*i).start, (*i).end); - } -} - -void -AutomationTimeAxisView::cut_copy_clear_objects (PointSelection& 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_objects_one (**i, selection, op); - } -} - -void -AutomationTimeAxisView::cut_copy_clear_objects_one (AutomationLine& line, PointSelection& selection, CutCopyOp op) -{ - boost::shared_ptr what_we_got; - boost::shared_ptr alist(line.the_list()); - - XMLNode &before = alist->get_state(); - - for (PointSelection::iterator i = selection.begin(); i != selection.end(); ++i) { - - if ((*i).track != this) { - continue; - } - - switch (op) { - case Delete: - if (alist->cut ((*i).start, (*i).end) != 0) { - _session->add_command (new MementoCommand(*alist.get(), new XMLNode (before), &alist->get_state())); - } - break; - case Cut: - if ((what_we_got = alist->cut ((*i).start, (*i).end)) != 0) { - _editor.get_cut_buffer().add (what_we_got); - _session->add_command (new MementoCommand(*alist.get(), new XMLNode (before), &alist->get_state())); - } - break; - case Copy: - if ((what_we_got = alist->copy ((*i).start, (*i).end)) != 0) { - _editor.get_cut_buffer().add (what_we_got); - } - break; - - case Clear: - if ((what_we_got = alist->cut ((*i).start, (*i).end)) != 0) { - _session->add_command (new MementoCommand(*alist.get(), new XMLNode (before), &alist->get_state())); - } - break; - } - } - - delete &before; - - 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; - } - } -} - /** Paste a selection. * @param pos Position to paste to (session frames). * @param times Number of times to paste. @@ -794,25 +629,10 @@ AutomationTimeAxisView::paste_one (AutomationLine& line, framepos_t pos, float t return false; } - /* Make a copy of the list because we have to scale the - values from view coordinates to model coordinates, and we're - not supposed to modify the points in the selection. - */ - - AutomationList copy (**p); - - for (AutomationList::iterator x = copy.begin(); x != copy.end(); ++x) { - double when = (*x)->when; - double val = (*x)->value; - line.view_to_model_coord (when, val); - (*x)->when = when; - (*x)->value = val; - } - double const model_pos = line.time_converter().from (pos - line.time_converter().origin_b ()); XMLNode &before = alist->get_state(); - alist->paste (copy, model_pos, times); + alist->paste (**p, model_pos, times); _session->add_command (new MementoCommand(*alist.get(), &before, &alist->get_state())); return true; diff --git a/gtk2_ardour/automation_time_axis.h b/gtk2_ardour/automation_time_axis.h index 7706ed7ee3..913cd1b467 100644 --- a/gtk2_ardour/automation_time_axis.h +++ b/gtk2_ardour/automation_time_axis.h @@ -91,10 +91,7 @@ class AutomationTimeAxisView : public TimeAxisView { /* editing operations */ - void cut_copy_clear (Selection&, Editing::CutCopyOp); - void cut_copy_clear_objects (PointSelection&, Editing::CutCopyOp); bool paste (ARDOUR::framepos_t, float times, Selection&, size_t nth); - void reset_objects (PointSelection&); int set_state (const XMLNode&, int version); @@ -169,10 +166,7 @@ class AutomationTimeAxisView : public TimeAxisView { void build_display_menu (); - void cut_copy_clear_one (AutomationLine&, Selection&, Editing::CutCopyOp); - void cut_copy_clear_objects_one (AutomationLine&, PointSelection&, Editing::CutCopyOp); bool paste_one (AutomationLine&, ARDOUR::framepos_t, float times, Selection&, size_t nth); - void reset_objects_one (AutomationLine&, PointSelection&); void route_going_away (); void set_automation_state (ARDOUR::AutoState); diff --git a/gtk2_ardour/axis_view.cc b/gtk2_ardour/axis_view.cc index 5e0a50fdf7..43763abcc3 100644 --- a/gtk2_ardour/axis_view.cc +++ b/gtk2_ardour/axis_view.cc @@ -26,6 +26,7 @@ #include #include "pbd/error.h" +#include "pbd/convert.h" #include #include @@ -74,14 +75,14 @@ bool AxisView::marked_for_display () const { string const v = gui_property ("visible"); - return (v == "" || string_is_affirmative (v)); + return (v == "" || PBD::string_is_affirmative (v)); } bool AxisView::set_marked_for_display (bool yn) { string const v = gui_property ("visible"); - if (v == "" || yn != string_is_affirmative (v)) { + if (v == "" || yn != PBD::string_is_affirmative (v)) { set_gui_property ("visible", yn); return true; // things changed } diff --git a/gtk2_ardour/bundle_manager.cc b/gtk2_ardour/bundle_manager.cc index 3d0a0e3a37..0cd44af9a2 100644 --- a/gtk2_ardour/bundle_manager.cc +++ b/gtk2_ardour/bundle_manager.cc @@ -377,7 +377,7 @@ BundleManager::add_bundle (boost::shared_ptr b) (*i)[_list_model_columns.name] = u->name (); (*i)[_list_model_columns.bundle] = u; - u->Changed.connect (bundle_connections, invalidator (*this), ui_bind (&BundleManager::bundle_changed, this, _1, u), gui_context()); + u->Changed.connect (bundle_connections, invalidator (*this), boost::bind (&BundleManager::bundle_changed, this, _1, u), gui_context()); } void diff --git a/gtk2_ardour/button_joiner.cc b/gtk2_ardour/button_joiner.cc index fffe874f03..86bb055d01 100644 --- a/gtk2_ardour/button_joiner.cc +++ b/gtk2_ardour/button_joiner.cc @@ -13,21 +13,38 @@ using namespace Gtk; -ButtonJoiner::ButtonJoiner (const std::string& str, Gtk::Widget& lw, Gtk::Widget& rw) +ButtonJoiner::ButtonJoiner (const std::string& str, Gtk::Widget& lw, Gtk::Widget& rw, bool central_joiner) : left (lw) , right (rw) , name (str) , active_fill_pattern (0) , inactive_fill_pattern (0) + , central_link (central_joiner) { packer.set_homogeneous (true); + + if (central_link) { + packer.set_spacing (20); + } + packer.pack_start (left); packer.pack_start (right); packer.show (); + /* this alignment is how we position the box that holds the two widgets + within our allocation, and how we request more space around them. + */ + align.add (packer); - align.set (0.5, 1.0); - align.set_padding (9, 0, 9, 9); + + if (!central_link) { + align.set (0.5, 1.0); + align.set_padding (9, 0, 9, 9); + } else { + align.set (0.5, 0.5); + align.set_padding (1, 1, 1, 1); + } + align.show (); add (align); @@ -73,22 +90,44 @@ ButtonJoiner::render (cairo_t* cr) cairo_set_source (cr, active_fill_pattern); } - /* outer rect */ - - Gtkmm2ext::rounded_top_rectangle (cr, 0, 0, get_width(), h, 12); - cairo_fill_preserve (cr); - - /* outer edge */ - - cairo_set_line_width (cr, 1); - cairo_set_source_rgb (cr, border_r, border_g, border_b); - cairo_stroke (cr); - - /* inner "edge" */ - - Gtkmm2ext::rounded_top_rectangle (cr, 8, 8, get_width() - 16, h - 8, 10); - cairo_stroke (cr); + if (!central_link) { + /* outer rect */ + + Gtkmm2ext::rounded_top_rectangle (cr, 0, 0, get_width(), h, 8); + cairo_fill_preserve (cr); + + /* outer edge */ + + cairo_set_line_width (cr, 1.5); + cairo_set_source_rgb (cr, border_r, border_g, border_b); + cairo_stroke (cr); + + /* inner "edge" */ + + Gtkmm2ext::rounded_top_rectangle (cr, 8, 8, get_width() - 16, h - 8, 6); + cairo_stroke (cr); + } else { + if (get_active()) { + Gtkmm2ext::rounded_top_rectangle (cr, 0, 0, (get_width() - 20.0)/2.0 , h, 8); + cairo_fill_preserve (cr); + + Gtkmm2ext::rounded_top_rectangle (cr, (get_width() - 20.)/2.0 + 20.0, 0.0, + (get_width() - 20.0)/2.0 , h, 8); + cairo_fill_preserve (cr); + cairo_move_to (cr, get_width()/2.0 - 10.0, h/2.0); + cairo_set_line_width (cr, 1.5); + cairo_rel_line_to (cr, 20.0, 0.0); + cairo_set_source (cr, active_fill_pattern); + cairo_stroke (cr); + } else { + cairo_arc (cr, get_width()/2.0, h/2.0, 6.0, 0, M_PI*2.0); + cairo_set_line_width (cr, 1.5); + cairo_fill_preserve (cr); + cairo_set_source_rgb (cr, border_r, border_g, border_b); + cairo_stroke (cr); + } + } } void diff --git a/gtk2_ardour/button_joiner.h b/gtk2_ardour/button_joiner.h index b7e734a11d..e39e6b5134 100644 --- a/gtk2_ardour/button_joiner.h +++ b/gtk2_ardour/button_joiner.h @@ -10,7 +10,7 @@ class ButtonJoiner : public CairoWidget, public Gtkmm2ext::Activatable { public: - ButtonJoiner (const std::string&, Gtk::Widget&, Gtk::Widget&); + ButtonJoiner (const std::string&, Gtk::Widget&, Gtk::Widget&, bool central_link = false); ~ButtonJoiner (); void set_related_action (Glib::RefPtr); @@ -35,6 +35,7 @@ class ButtonJoiner : public CairoWidget, public Gtkmm2ext::Activatable { std::string name; cairo_pattern_t* active_fill_pattern; cairo_pattern_t* inactive_fill_pattern; + bool central_link; double border_r; double border_g; double border_b; diff --git a/gtk2_ardour/control_point.cc b/gtk2_ardour/control_point.cc index bc7301dc1c..d869e094aa 100644 --- a/gtk2_ardour/control_point.cc +++ b/gtk2_ardour/control_point.cc @@ -30,6 +30,8 @@ using namespace ARDOUR; using namespace PBD; using namespace Gnome; // for Canvas +PBD::Signal1 ControlPoint::CatchDeletion; + ControlPoint::ControlPoint (AutomationLine& al) : _line (al) { @@ -82,6 +84,8 @@ ControlPoint::ControlPoint (const ControlPoint& other, bool /*dummy_arg_to_force ControlPoint::~ControlPoint () { + CatchDeletion (this); /* EMIT SIGNAL */ + delete _item; } diff --git a/gtk2_ardour/control_point.h b/gtk2_ardour/control_point.h index 40eb7b9179..0c2bf1e01d 100644 --- a/gtk2_ardour/control_point.h +++ b/gtk2_ardour/control_point.h @@ -83,21 +83,21 @@ class ControlPoint : public Selectable ARDOUR::AutomationList::iterator model() const { return _model; } AutomationLine& line() const { return _line; } + static PBD::Signal1 CatchDeletion; + private: - ArdourCanvas::SimpleRect* _item; - - AutomationLine& _line; - + ArdourCanvas::SimpleRect* _item; + AutomationLine& _line; ARDOUR::AutomationList::iterator _model; - uint32_t _view_index; - bool _can_slide; + uint32_t _view_index; + bool _can_slide; + double _x; + double _y; + double _size; + ShapeType _shape; virtual bool event_handler (GdkEvent*); - double _x; - double _y; - double _size; - ShapeType _shape; }; diff --git a/gtk2_ardour/crossfade_edit.cc b/gtk2_ardour/crossfade_edit.cc index da833b8ea4..65031917c7 100644 --- a/gtk2_ardour/crossfade_edit.cc +++ b/gtk2_ardour/crossfade_edit.cc @@ -296,9 +296,9 @@ CrossfadeEditor::CrossfadeEditor (Session* s, boost::shared_ptr xf, d curve_select_clicked (In); - xfade->PropertyChanged.connect (state_connection, invalidator (*this), ui_bind (&CrossfadeEditor::xfade_changed, this, _1), gui_context()); + xfade->PropertyChanged.connect (state_connection, invalidator (*this), boost::bind (&CrossfadeEditor::xfade_changed, this, _1), gui_context()); - _session->AuditionActive.connect (_session_connections, invalidator (*this), ui_bind (&CrossfadeEditor::audition_state_changed, this, _1), gui_context()); + _session->AuditionActive.connect (_session_connections, invalidator (*this), boost::bind (&CrossfadeEditor::audition_state_changed, this, _1), gui_context()); show_all_children(); } diff --git a/gtk2_ardour/crossfade_view.cc b/gtk2_ardour/crossfade_view.cc deleted file mode 100644 index 10792e0a93..0000000000 --- a/gtk2_ardour/crossfade_view.cc +++ /dev/null @@ -1,302 +0,0 @@ -/* - Copyright (C) 2003 Paul Davis - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#include - -#include "ardour/region.h" -#include - -#include "canvas-simplerect.h" -#include "canvas-curve.h" -#include "crossfade_view.h" -#include "global_signals.h" -#include "gui_thread.h" -#include "rgb_macros.h" -#include "audio_time_axis.h" -#include "public_editor.h" -#include "audio_region_view.h" -#include "utils.h" -#include "canvas_impl.h" -#include "ardour_ui.h" - -using namespace ARDOUR; -using namespace PBD; -using namespace Editing; -using namespace Gnome; -using namespace Canvas; - -PBD::Signal1 CrossfadeView::CatchDeletion; - -CrossfadeView::CrossfadeView (ArdourCanvas::Group *parent, - RouteTimeAxisView &tv, - boost::shared_ptr xf, - double spu, - Gdk::Color& basic_color, - AudioRegionView& lview, - AudioRegionView& rview) - - - : TimeAxisViewItem ("xfade" /*xf.name()*/, *parent, tv, spu, basic_color, xf->position(), - xf->length(), false, false, TimeAxisViewItem::Visibility (TimeAxisViewItem::ShowFrame)), - crossfade (xf), - left_view (lview), - right_view (rview), - _all_in_view (false), - _child_height (0) -{ - _valid = true; - _visible = true; - - fade_in = new Line (*group); - fade_in->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_CrossfadeLine.get(); - fade_in->property_width_pixels() = 1; - - fade_out = new Line (*group); - fade_out->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_CrossfadeLine.get(); - fade_out->property_width_pixels() = 1; - - /* no frame around the xfade or overlap rects */ - - frame->property_outline_what() = 0; - - /* never show the vestigial frame */ - vestigial_frame->hide(); - show_vestigial = false; - - group->signal_event().connect (sigc::bind (sigc::mem_fun (tv.editor(), &PublicEditor::canvas_crossfade_view_event), group, this)); - - PropertyChange all_crossfade_properties; - all_crossfade_properties.add (ARDOUR::Properties::active); - all_crossfade_properties.add (ARDOUR::Properties::follow_overlap); - crossfade_changed (all_crossfade_properties); - - crossfade->PropertyChanged.connect (*this, invalidator (*this), ui_bind (&CrossfadeView::crossfade_changed, this, _1), gui_context()); - crossfade->FadesChanged.connect (*this, invalidator (*this), ui_bind (&CrossfadeView::crossfade_fades_changed, this), gui_context()); - ColorsChanged.connect (sigc::mem_fun (*this, &CrossfadeView::color_handler)); -} - -CrossfadeView::~CrossfadeView () -{ - CatchDeletion (this) ; /* EMIT_SIGNAL */ -} - -void -CrossfadeView::reset_width_dependent_items (double pixel_width) -{ - TimeAxisViewItem::reset_width_dependent_items (pixel_width); - - active_changed (); - - if (pixel_width < 5) { - fade_in->hide(); - fade_out->hide(); - } -} - -void -CrossfadeView::set_heights (double fade_height, double child_height) -{ - if (child_height > TimeAxisViewItem::NAME_HIGHLIGHT_THRESH) { - fade_height -= NAME_HIGHLIGHT_SIZE; - child_height -= NAME_HIGHLIGHT_SIZE; - } - - TimeAxisViewItem::set_height (fade_height); - _child_height = child_height; - - redraw_curves (); -} - -void -CrossfadeView::crossfade_changed (const PropertyChange& what_changed) -{ - bool need_redraw_curves = false; - - if (what_changed.contains (ARDOUR::bounds_change)) { - set_position (crossfade->position(), this); - set_duration (crossfade->length(), this); - - /* set_duration will call reset_width_dependent_items which in turn will call redraw_curves via active_changed, - so no need for us to call it */ - need_redraw_curves = false; - } - - if (what_changed.contains (ARDOUR::Properties::follow_overlap)) { - need_redraw_curves = true; - } - - if (what_changed.contains (ARDOUR::Properties::active)) { - /* calls redraw_curves */ - active_changed (); - } else if (need_redraw_curves) { - redraw_curves (); - } -} - -/** Set up our fade_in and fade_out curves to contain points for the currently visible portion - * of the crossfade. - */ -void -CrossfadeView::redraw_curves () -{ - if (!crossfade->following_overlap()) { - /* curves should not be visible */ - fade_in->hide (); - fade_out->hide (); - return; - } - - if (_height < 0) { - /* no space allocated yet */ - return; - } - - PublicEditor& editor = get_time_axis_view().editor (); - - framepos_t const editor_left = editor.leftmost_position (); - framepos_t const editor_right = editor_left + editor.current_page_frames (); - framepos_t const xfade_left = crossfade->position (); - framepos_t const xfade_right = xfade_left + crossfade->length (); - - /* Work out the range of our frames that are visible */ - framepos_t const min_frames = std::max (editor_left, xfade_left); - framepos_t const max_frames = std::min (editor_right, xfade_right); - - _all_in_view = (editor_left <= xfade_left && editor_right >= xfade_right); - - /* Hence the number of points that we will render */ - int32_t const npoints = editor.frame_to_pixel (max_frames - min_frames); - - if (!_visible || !crossfade->active() || npoints < 3) { - fade_in->hide(); - fade_out->hide(); - return; - } else { - fade_in->show(); - fade_out->show(); - } - - Points* points = get_canvas_points ("xfade edit redraw", npoints); - float* vec = new float[npoints]; - - crossfade->fade_in().curve().get_vector (min_frames - crossfade->position(), max_frames - crossfade->position(), vec, npoints); - - /* Work out the offset from the start of the crossfade to the visible part, in pixels */ - double xoff = 0; - if (crossfade->position() < editor.leftmost_position()) { - xoff = editor.frame_to_pixel (min_frames) - editor.frame_to_pixel (crossfade->position ()); - } - - for (int i = 0, pci = 0; i < npoints; ++i) { - Art::Point &p = (*points)[pci++]; - p.set_x (xoff + i + 1); - - double const ho = crossfade->in()->layer() > crossfade->out()->layer() ? _child_height : _height; - p.set_y (ho - ((_child_height - 2) * vec[i])); - } - - fade_in->property_points() = *points; - - crossfade->fade_out().curve().get_vector (min_frames - crossfade->position(), max_frames - crossfade->position(), vec, npoints); - - for (int i = 0, pci = 0; i < npoints; ++i) { - Art::Point &p = (*points)[pci++]; - p.set_x (xoff + i + 1); - - double const ho = crossfade->in()->layer() < crossfade->out()->layer() ? _child_height : _height; - p.set_y (ho - ((_child_height - 2) * vec[i])); - } - - fade_out->property_points() = *points; - - delete [] vec; - - delete points; - - /* XXX this is ugly, but it will have to wait till Crossfades are reimplented - as regions. This puts crossfade views on top of a track, above all regions. - */ - - group->raise_to_top(); -} - -void -CrossfadeView::active_changed () -{ - if (crossfade->active()) { - frame->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_ActiveCrossfade.get(); - } else { - frame->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_InactiveCrossfade.get(); - } - - redraw_curves (); -} - -void -CrossfadeView::color_handler () -{ - active_changed (); -} - -void -CrossfadeView::set_valid (bool yn) -{ - _valid = yn; -} - -void -CrossfadeView::show () -{ - _visible = true; - group->show(); - redraw_curves (); -} - -void -CrossfadeView::hide () -{ - group->hide(); - _visible = false; -} - -void -CrossfadeView::fake_hide () -{ - group->hide(); -} - -void -CrossfadeView::crossfade_fades_changed () -{ - redraw_curves (); -} - -void -CrossfadeView::horizontal_position_changed () -{ - /* If the crossfade curves are entirely within the editor's visible space, there is - no need to redraw them here as they will be completely drawn (as distinct from - the other case where the horizontal position change will uncover `undrawn' - sections). - */ - - if (!_all_in_view) { - redraw_curves (); - } -} diff --git a/gtk2_ardour/editor.cc b/gtk2_ardour/editor.cc index d2f5fbe5de..b7f768adca 100644 --- a/gtk2_ardour/editor.cc +++ b/gtk2_ardour/editor.cc @@ -90,7 +90,6 @@ #include "canvas-noevent-text.h" #include "canvas_impl.h" #include "crossfade_edit.h" -#include "crossfade_view.h" #include "debug.h" #include "editing.h" #include "editor.h" @@ -303,7 +302,6 @@ Editor::Editor () clicked_regionview = 0; clicked_axisview = 0; clicked_routeview = 0; - clicked_crossfadeview = 0; clicked_control_point = 0; last_update_frame = 0; pre_press_cursor = 0; @@ -340,7 +338,6 @@ Editor::Editor () have_pending_keyboard_selection = false; _follow_playhead = true; _stationary_playhead = false; - _xfade_visibility = true; editor_ruler_menu = 0; no_ruler_shown_update = false; marker_menu = 0; @@ -659,7 +656,7 @@ Editor::Editor () _playlist_selector = new PlaylistSelector(); _playlist_selector->signal_delete_event().connect (sigc::bind (sigc::ptr_fun (just_hide_it), static_cast (_playlist_selector))); - RegionView::RegionViewGoingAway.connect (*this, invalidator (*this), ui_bind (&Editor::catch_vanishing_regionview, this, _1), gui_context()); + RegionView::RegionViewGoingAway.connect (*this, invalidator (*this), boost::bind (&Editor::catch_vanishing_regionview, this, _1), gui_context()); /* nudge stuff */ @@ -709,17 +706,32 @@ Editor::Editor () ControlProtocol::ZoomToSession.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_session, this), gui_context()); ControlProtocol::ZoomIn.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_step, this, false), gui_context()); ControlProtocol::ZoomOut.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_step, this, true), gui_context()); - ControlProtocol::ScrollTimeline.connect (*this, invalidator (*this), ui_bind (&Editor::control_scroll, this, _1), gui_context()); - ControlProtocol::SelectByRID.connect (*this, invalidator (*this), ui_bind (&Editor::control_select, this, _1), gui_context()); - BasicUI::AccessAction.connect (*this, invalidator (*this), ui_bind (&Editor::access_action, this, _1, _2), gui_context()); + ControlProtocol::Undo.connect (*this, invalidator (*this), boost::bind (&Editor::undo, this, true), gui_context()); + ControlProtocol::Redo.connect (*this, invalidator (*this), boost::bind (&Editor::redo, this, true), gui_context()); + ControlProtocol::ScrollTimeline.connect (*this, invalidator (*this), boost::bind (&Editor::control_scroll, this, _1), gui_context()); + ControlProtocol::StepTracksUp.connect (*this, invalidator (*this), boost::bind (&Editor::control_step_tracks_up, this), gui_context()); + ControlProtocol::StepTracksDown.connect (*this, invalidator (*this), boost::bind (&Editor::control_step_tracks_down, this), gui_context()); + ControlProtocol::GotoView.connect (*this, invalidator (*this), boost::bind (&Editor::control_view, this, _1), gui_context()); + ControlProtocol::CloseDialog.connect (*this, invalidator (*this), Keyboard::close_current_dialog, gui_context()); + ControlProtocol::VerticalZoomInAll.connect (*this, invalidator (*this), boost::bind (&Editor::control_vertical_zoom_in_all, this), gui_context()); + ControlProtocol::VerticalZoomOutAll.connect (*this, invalidator (*this), boost::bind (&Editor::control_vertical_zoom_out_all, this), gui_context()); + ControlProtocol::VerticalZoomInSelected.connect (*this, invalidator (*this), boost::bind (&Editor::control_vertical_zoom_in_selected, this), gui_context()); + ControlProtocol::VerticalZoomOutSelected.connect (*this, invalidator (*this), boost::bind (&Editor::control_vertical_zoom_out_selected, this), gui_context()); + + 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::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()); /* problematic: has to return a value and thus cannot be x-thread */ Session::AskAboutPlaylistDeletion.connect_same_thread (*this, boost::bind (&Editor::playlist_deletion_dialog, this, _1)); - Config->ParameterChanged.connect (*this, invalidator (*this), ui_bind (&Editor::parameter_changed, this, _1), gui_context()); + Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&Editor::parameter_changed, this, _1), gui_context()); - TimeAxisView::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&Editor::timeaxisview_deleted, this, _1), gui_context()); + TimeAxisView::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&Editor::timeaxisview_deleted, this, _1), gui_context()); _ignore_region_action = false; _last_region_menu_was_main = false; @@ -920,7 +932,43 @@ Editor::zoom_adjustment_changed () } void -Editor::control_select (uint32_t rid) +Editor::control_vertical_zoom_in_all () +{ + tav_zoom_smooth (false, true); +} + +void +Editor::control_vertical_zoom_out_all () +{ + tav_zoom_smooth (true, true); +} + +void +Editor::control_vertical_zoom_in_selected () +{ + tav_zoom_smooth (false, false); +} + +void +Editor::control_vertical_zoom_out_selected () +{ + tav_zoom_smooth (true, false); +} + +void +Editor::control_view (uint32_t view) +{ + goto_visual_state (view); +} + +void +Editor::control_unselect () +{ + selection->clear_tracks (); +} + +void +Editor::control_select (uint32_t rid, Selection::Operation op) { /* handles the (static) signal from the ControlProtocol class that * requests setting the selected track to a given RID @@ -939,12 +987,36 @@ Editor::control_select (uint32_t rid) TimeAxisView* tav = axis_view_from_route (r); if (tav) { - selection->set (tav); + switch (op) { + case Selection::Add: + selection->add (tav); + break; + case Selection::Toggle: + selection->toggle (tav); + break; + case Selection::Extend: + break; + case Selection::Set: + selection->set (tav); + break; + } } else { selection->clear_tracks (); } } +void +Editor::control_step_tracks_up () +{ + scroll_tracks_up_line (); +} + +void +Editor::control_step_tracks_down () +{ + scroll_tracks_down_line (); +} + void Editor::control_scroll (float fraction) { @@ -1198,19 +1270,19 @@ Editor::set_session (Session *t) but use Gtkmm2ext::UI::instance()->call_slot(); */ - _session->StepEditStatusChange.connect (_session_connections, invalidator (*this), ui_bind(&Editor::step_edit_status_change, this, _1), gui_context()); + _session->StepEditStatusChange.connect (_session_connections, invalidator (*this), boost::bind (&Editor::step_edit_status_change, this, _1), gui_context()); _session->TransportStateChange.connect (_session_connections, invalidator (*this), boost::bind (&Editor::map_transport_state, this), gui_context()); - _session->PositionChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::map_position_change, this, _1), gui_context()); - _session->RouteAdded.connect (_session_connections, invalidator (*this), ui_bind (&Editor::handle_new_route, this, _1), gui_context()); + _session->PositionChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::map_position_change, this, _1), gui_context()); + _session->RouteAdded.connect (_session_connections, invalidator (*this), boost::bind (&Editor::handle_new_route, this, _1), gui_context()); _session->DirtyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::update_title, this), gui_context()); - _session->tempo_map().PropertyChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::tempo_map_changed, this, _1), gui_context()); + _session->tempo_map().PropertyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::tempo_map_changed, this, _1), gui_context()); _session->Located.connect (_session_connections, invalidator (*this), boost::bind (&Editor::located, this), gui_context()); - _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::parameter_changed, this, _1), gui_context()); - _session->StateSaved.connect (_session_connections, invalidator (*this), ui_bind (&Editor::session_state_saved, this, _1), gui_context()); - _session->locations()->added.connect (_session_connections, invalidator (*this), ui_bind (&Editor::add_new_location, this, _1), gui_context()); - _session->locations()->removed.connect (_session_connections, invalidator (*this), ui_bind (&Editor::location_gone, this, _1), gui_context()); + _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::parameter_changed, this, _1), gui_context()); + _session->StateSaved.connect (_session_connections, invalidator (*this), boost::bind (&Editor::session_state_saved, this, _1), gui_context()); + _session->locations()->added.connect (_session_connections, invalidator (*this), boost::bind (&Editor::add_new_location, this, _1), gui_context()); + _session->locations()->removed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::location_gone, this, _1), gui_context()); _session->locations()->changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::refresh_location_display, this), gui_context()); - _session->locations()->StateChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::refresh_location_display, this), gui_context()); + _session->locations()->StateChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::refresh_location_display, this), gui_context()); _session->history().Changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::history_changed, this), gui_context()); playhead_cursor->canvas_item.show (); @@ -1453,10 +1525,6 @@ Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, } break; - case CrossfadeViewItem: - build_menu_function = &Editor::build_track_crossfade_context_menu; - break; - case StreamItem: if (clicked_routeview->track()) { build_menu_function = &Editor::build_track_context_menu; @@ -1502,9 +1570,6 @@ Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, case SelectionItem: break; - case CrossfadeViewItem: - break; - case StreamItem: break; @@ -1589,11 +1654,6 @@ Editor::build_track_region_context_menu () region_edit_menu_split_item = 0; region_edit_menu_split_multichannel_item = 0; - /* we might try to use items that are currently attached to a crossfade menu, - so clear that, too. - */ - track_crossfade_context_menu.items().clear (); - RouteTimeAxisView* rtv = dynamic_cast (clicked_axisview); if (rtv) { @@ -1610,54 +1670,6 @@ Editor::build_track_region_context_menu () return &track_region_context_menu; } -Menu* -Editor::build_track_crossfade_context_menu () -{ - using namespace Menu_Helpers; - MenuList& edit_items = track_crossfade_context_menu.items(); - edit_items.clear (); - - /* we might try to use items that are currently attached to a crossfade menu, - so clear that, too. - */ - track_region_context_menu.items().clear (); - - AudioTimeAxisView* atv = dynamic_cast (clicked_axisview); - - if (atv) { - boost::shared_ptr tr; - boost::shared_ptr pl; - boost::shared_ptr apl; - - if ((tr = atv->track()) && ((pl = tr->playlist()) != 0) && ((apl = boost::dynamic_pointer_cast (pl)) != 0)) { - - AudioPlaylist::Crossfades xfades; - framepos_t where; - bool ignored; - - /* The xfade menu is a bit of a special case, as we always use the mouse position - to decide whether or not to display it (rather than the edit point). No particularly - strong reasons for this, other than it is a bit surprising to right-click on a xfade - and not get a menu. - */ - mouse_frame (where, ignored); - apl->crossfades_at (where, xfades); - - bool const many = xfades.size() > 1; - - for (AudioPlaylist::Crossfades::iterator i = xfades.begin(); i != xfades.end(); ++i) { - add_crossfade_context_items (atv->audio_view(), (*i), edit_items, many); - } - - add_region_context_items (edit_items, tr); - } - } - - add_dstream_context_items (edit_items); - - return &track_crossfade_context_menu; -} - void Editor::analyze_region_selection () { @@ -1708,73 +1720,6 @@ Editor::build_track_selection_context_menu () return &track_selection_context_menu; } -/** Add context menu items relevant to crossfades. - * @param edit_items List to add the items to. - */ -void -Editor::add_crossfade_context_items (AudioStreamView* view, boost::shared_ptr xfade, Menu_Helpers::MenuList& edit_items, bool many) -{ - using namespace Menu_Helpers; - Menu *xfade_menu = manage (new Menu); - MenuList& items = xfade_menu->items(); - xfade_menu->set_name ("ArdourContextMenu"); - string str; - - if (xfade->active()) { - str = _("Mute"); - } else { - str = _("Unmute"); - } - - items.push_back ( - MenuElem (str, sigc::bind (sigc::mem_fun (*this, &Editor::toggle_xfade_active), &view->trackview(), boost::weak_ptr (xfade))) - ); - - items.push_back ( - MenuElem (_("Edit..."), sigc::bind (sigc::mem_fun (*this, &Editor::edit_xfade), boost::weak_ptr (xfade))) - ); - - if (xfade->can_follow_overlap()) { - - if (xfade->following_overlap()) { - str = _("Convert to Short"); - } else { - str = _("Convert to Full"); - } - - items.push_back ( - MenuElem (str, sigc::bind (sigc::mem_fun (*this, &Editor::toggle_xfade_length), &view->trackview(), xfade)) - ); - } - - if (many) { - str = xfade->out()->name(); - str += "->"; - str += xfade->in()->name(); - } else { - str = _("Crossfade"); - } - - edit_items.push_back (MenuElem (str, *xfade_menu)); - edit_items.push_back (SeparatorElem()); -} - -void -Editor::xfade_edit_left_region () -{ - if (clicked_crossfadeview) { - clicked_crossfadeview->left_view.show_region_editor (); - } -} - -void -Editor::xfade_edit_right_region () -{ - if (clicked_crossfadeview) { - clicked_crossfadeview->right_view.show_region_editor (); - } -} - void Editor::add_region_context_items (Menu_Helpers::MenuList& edit_items, boost::shared_ptr track) { @@ -2330,12 +2275,6 @@ Editor::set_state (const XMLNode& node, int /*version*/) _regions->reset_sort_type ((RegionListSortType) string_2_enum (prop->value(), st), true); } - if ((prop = node.property ("xfades-visible"))) { - bool yn = string_is_affirmative (prop->value()); - _xfade_visibility = !yn; - // set_xfade_visibility (yn); - } - if ((prop = node.property ("show-editor-mixer"))) { Glib::RefPtr act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer")); @@ -2462,7 +2401,6 @@ Editor::get_state () node->add_property ("maximised", _maximised ? "yes" : "no"); node->add_property ("follow-playhead", _follow_playhead ? "yes" : "no"); node->add_property ("stationary-playhead", _stationary_playhead ? "yes" : "no"); - node->add_property ("xfades-visible", _xfade_visibility ? "yes" : "no"); node->add_property ("region-list-sort-type", enum_2_string (_regions->sort_type ())); node->add_property ("mouse-mode", enum2str(mouse_mode)); node->add_property ("internal-edit", _internal_editing ? "yes" : "no"); @@ -2817,15 +2755,9 @@ Editor::setup_toolbar () /* make them just a bit bigger */ mouse_move_button.set_size_request (-1, 25); - smart_mode_joiner = manage (new ButtonJoiner ("mouse mode button", mouse_move_button, mouse_select_button)); + smart_mode_joiner = manage (new ButtonJoiner ("mouse mode button", mouse_move_button, mouse_select_button, true)); smart_mode_joiner->set_related_action (smart_mode_action); - mouse_move_button.set_elements (ArdourButton::Element (ArdourButton::Body|ArdourButton::Text)); - mouse_select_button.set_elements (ArdourButton::Element (ArdourButton::Body|ArdourButton::Text)); - - mouse_move_button.set_rounded_corner_mask (0x1); // upper left only - mouse_select_button.set_rounded_corner_mask (0x2); // upper right only - mouse_mode_hbox2->set_spacing (2); mouse_mode_box->set_spacing (2); @@ -3690,80 +3622,6 @@ Editor::set_stationary_playhead (bool yn) } } -void -Editor::toggle_xfade_active (RouteTimeAxisView* tv, boost::weak_ptr wxfade) -{ - boost::shared_ptr xfade (wxfade.lock()); - if (!xfade) { - return; - } - - vector > all = get_equivalent_crossfades (*tv, xfade, ARDOUR::Properties::edit.property_id); - - _session->begin_reversible_command (_("Change crossfade active state")); - - for (vector >::iterator i = all.begin(); i != all.end(); ++i) { - (*i)->clear_changes (); - (*i)->set_active (!(*i)->active()); - _session->add_command (new StatefulDiffCommand (*i)); - } - - _session->commit_reversible_command (); -} - -void -Editor::toggle_xfade_length (RouteTimeAxisView* tv, boost::weak_ptr wxfade) -{ - boost::shared_ptr xfade (wxfade.lock()); - if (!xfade) { - return; - } - - vector > all = get_equivalent_crossfades (*tv, xfade, ARDOUR::Properties::edit.property_id); - - /* This can't be a StatefulDiffCommand as the fade shapes are not - managed by the Stateful properties system. - */ - _session->begin_reversible_command (_("Change crossfade length")); - - for (vector >::iterator i = all.begin(); i != all.end(); ++i) { - XMLNode& before = (*i)->get_state (); - (*i)->set_follow_overlap (!(*i)->following_overlap()); - XMLNode& after = (*i)->get_state (); - - _session->add_command (new MementoCommand (*i->get(), &before, &after)); - } - - _session->commit_reversible_command (); -} - -void -Editor::edit_xfade (boost::weak_ptr wxfade) -{ - boost::shared_ptr xfade (wxfade.lock()); - - if (!xfade) { - return; - } - - CrossfadeEditor cew (_session, xfade, xfade->fade_in().get_min_y(), 1.0); - - ensure_float (cew); - - switch (cew.run ()) { - case RESPONSE_ACCEPT: - break; - default: - return; - } - - cew.apply (); - PropertyChange all_crossfade_properties; - all_crossfade_properties.add (ARDOUR::Properties::active); - all_crossfade_properties.add (ARDOUR::Properties::follow_overlap); - xfade->PropertyChanged (all_crossfade_properties); -} - PlaylistSelector& Editor::playlist_selector () const { @@ -4341,19 +4199,6 @@ Editor::idle_visual_changer () double const last_time_origin = horizontal_position (); - if (p & VisualChange::TimeOrigin) { - /* This is a bit of a hack, but set_frames_per_unit - below will (if called) end up with the - CrossfadeViews looking at Editor::leftmost_frame, - and if we're changing origin and zoom in the same - operation it will be the wrong value unless we - update it here. - */ - - leftmost_frame = pending_visual_change.time_origin; - assert (leftmost_frame >= 0); - } - if (p & VisualChange::ZoomLevel) { set_frames_per_unit (pending_visual_change.frames_per_unit); @@ -5327,7 +5172,6 @@ Editor::session_going_away () clicked_regionview = 0; clicked_axisview = 0; clicked_routeview = 0; - clicked_crossfadeview = 0; entered_regionview = 0; entered_track = 0; last_update_frame = 0; @@ -5451,7 +5295,6 @@ Editor::setup_fade_images () _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' */ Gtk::MenuItem& Editor::action_menu_item (std::string const & name) diff --git a/gtk2_ardour/editor.h b/gtk2_ardour/editor.h index d5b2cb9172..589b0ead6c 100644 --- a/gtk2_ardour/editor.h +++ b/gtk2_ardour/editor.h @@ -83,7 +83,6 @@ namespace ARDOUR { class NamedSelection; class Session; class Filter; - class Crossfade; class ChanCount; class MidiOperator; class Track; @@ -106,7 +105,6 @@ class AutomationTimeAxisView; class BundleManager; class ButtonJoiner; class ControlPoint; -class CrossfadeView; class DragManager; class GroupedButtons; class GUIObjectState; @@ -318,6 +316,7 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD void temporal_zoom_step (bool coarser); void tav_zoom_step (bool coarser); + void tav_zoom_smooth (bool coarser, bool force_all); /* stuff that AudioTimeAxisView and related classes use */ @@ -369,12 +368,10 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD void toggle_measure_visibility (); void toggle_logo_visibility (); - /* fades/xfades */ + /* fades */ void toggle_region_fades (int dir); void update_region_fade_visibility (); - bool xfade_visibility() const { return _xfade_visibility; } - void update_xfade_visibility (); /* redirect shared ops menu. caller must free returned menu */ @@ -631,16 +628,12 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD RegionView* clicked_regionview; RegionSelection latest_regionviews; uint32_t clicked_selection; - CrossfadeView* clicked_crossfadeview; ControlPoint* clicked_control_point; void sort_track_selection (TrackViewList&); void get_equivalent_regions (RegionView* rv, std::vector &, PBD::PropertyID) const; RegionSelection get_equivalent_regions (RegionSelection &, PBD::PropertyID) const; - std::vector > get_equivalent_crossfades ( - RouteTimeAxisView&, boost::shared_ptr, PBD::PropertyID - ) const; void mapover_tracks (sigc::slot sl, TimeAxisView*, PBD::PropertyID) const; void mapover_tracks_with_unique_playlists (sigc::slot sl, TimeAxisView*, PBD::PropertyID) const; @@ -649,9 +642,6 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD void mapped_use_new_playlist (RouteTimeAxisView&, uint32_t, std::vector > const &); void mapped_use_copy_playlist (RouteTimeAxisView&, uint32_t, std::vector > const &); void mapped_clear_playlist (RouteTimeAxisView&, uint32_t); - void mapped_get_equivalent_crossfades ( - RouteTimeAxisView&, uint32_t, boost::shared_ptr, std::vector >* - ) const; void button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type); bool button_release_can_deselect; @@ -662,7 +652,7 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD void select_all_tracks (); void select_all_internal_edit (Selection::Operation); - bool set_selected_control_point_from_click (Selection::Operation op = Selection::Set, bool no_remove=false); + bool set_selected_control_point_from_click (bool press, Selection::Operation op = Selection::Set); void set_selected_track_from_click (bool press, Selection::Operation op = Selection::Set, bool no_remove=false); void set_selected_track_as_side_effect (Selection::Operation op); bool set_selected_regionview_from_click (bool press, Selection::Operation op = Selection::Set); @@ -674,7 +664,6 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD Gtk::Menu track_context_menu; Gtk::Menu track_region_context_menu; Gtk::Menu track_selection_context_menu; - Gtk::Menu track_crossfade_context_menu; Gtk::MenuItem* region_edit_menu_split_item; Gtk::MenuItem* region_edit_menu_split_multichannel_item; @@ -688,12 +677,10 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD Gtk::Menu* build_track_context_menu (); Gtk::Menu* build_track_bus_context_menu (); Gtk::Menu* build_track_region_context_menu (); - Gtk::Menu* build_track_crossfade_context_menu (); Gtk::Menu* build_track_selection_context_menu (); void add_dstream_context_items (Gtk::Menu_Helpers::MenuList&); void add_bus_context_items (Gtk::Menu_Helpers::MenuList&); void add_region_context_items (Gtk::Menu_Helpers::MenuList&, boost::shared_ptr); - void add_crossfade_context_items (AudioStreamView*, boost::shared_ptr, Gtk::Menu_Helpers::MenuList&, bool many); void add_selection_context_items (Gtk::Menu_Helpers::MenuList&); Gtk::MenuItem* _popup_region_menu_item; @@ -987,8 +974,16 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD Gtk::VBox edit_controls_vbox; Gtk::HBox edit_controls_hbox; + void control_vertical_zoom_in_all (); + void control_vertical_zoom_out_all (); + void control_vertical_zoom_in_selected (); + void control_vertical_zoom_out_selected (); + void control_step_tracks_up (); + void control_step_tracks_down (); + void control_view (uint32_t); void control_scroll (float); - void control_select (uint32_t rid); + void control_select (uint32_t rid, Selection::Operation); + void control_unselect (); void access_action (std::string,std::string); bool deferred_control_scroll (framepos_t); sigc::connection control_scroll_connection; @@ -1379,7 +1374,6 @@ 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_crossfade_view_event (GdkEvent* event,ArdourCanvas::Item*, CrossfadeView*); 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*); @@ -1857,10 +1851,6 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD void nudge_track (bool use_edit_point, bool forwards); - /* xfades */ - - bool _xfade_visibility; - #ifdef WITH_CMT void handle_new_imageframe_time_axis_view(const std::string & track_name, void* src) ; void handle_new_imageframe_marker_time_axis_view(const std::string & track_name, TimeAxisView* marked_track) ; @@ -1902,12 +1892,6 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD ImageFrameSocketHandler* image_socket_listener ; #endif - void toggle_xfade_active (RouteTimeAxisView *, boost::weak_ptr); - void toggle_xfade_length (RouteTimeAxisView *, boost::weak_ptr); - void edit_xfade (boost::weak_ptr); - void xfade_edit_left_region (); - void xfade_edit_right_region (); - static const int32_t default_width = 995; static const int32_t default_height = 765; diff --git a/gtk2_ardour/editor_actions.cc b/gtk2_ardour/editor_actions.cc index 2a55c20630..071e76ffff 100644 --- a/gtk2_ardour/editor_actions.cc +++ b/gtk2_ardour/editor_actions.cc @@ -1428,8 +1428,6 @@ Editor::parameter_changed (std::string p) update_punch_range_view (true); } else if (p == "timecode-format") { update_just_timecode (); - } else if (p == "xfades-visible") { - update_xfade_visibility (); } else if (p == "show-region-fades") { update_region_fade_visibility (); } else if (p == "edit-mode") { diff --git a/gtk2_ardour/editor_canvas_events.cc b/gtk2_ardour/editor_canvas_events.cc index 71f5a9fb21..72de353500 100644 --- a/gtk2_ardour/editor_canvas_events.cc +++ b/gtk2_ardour/editor_canvas_events.cc @@ -35,7 +35,6 @@ #include "audio_region_view.h" #include "audio_streamview.h" #include "canvas-noevent-text.h" -#include "crossfade_view.h" #include "audio_time_axis.h" #include "region_gain_line.h" #include "automation_line.h" @@ -517,127 +516,6 @@ struct DescendingRegionLayerSorter { } }; -bool -Editor::canvas_crossfade_view_event (GdkEvent* event, ArdourCanvas::Item* item, CrossfadeView* xfv) -{ - /* we handle only button 3 press/release events */ - - switch (event->type) { - case GDK_BUTTON_PRESS: - clicked_crossfadeview = xfv; - clicked_axisview = &clicked_crossfadeview->get_time_axis_view(); - clicked_routeview = dynamic_cast(clicked_axisview); - if (event->button.button == 3) { - return button_press_handler (item, event, CrossfadeViewItem); - } - break; - - case GDK_BUTTON_RELEASE: - if (event->button.button == 3) { - bool ret = button_release_handler (item, event, CrossfadeViewItem); - return ret; - } - break; - - default: - break; - - } - - /* XXX do not forward double clicks */ - - if (event->type == GDK_2BUTTON_PRESS) { - return false; - } - - /* proxy for an underlying regionview */ - - /* XXX really need to check if we are in the name highlight, - and proxy to that when required. - - XXX or in the trim rectangles - */ - - TimeAxisView& tv (xfv->get_time_axis_view()); - AudioTimeAxisView* atv; - - if ((atv = dynamic_cast(&tv)) != 0) { - - if (atv->is_audio_track()) { - - boost::shared_ptr pl; - if ((pl = boost::dynamic_pointer_cast (atv->track()->playlist())) != 0) { - - boost::shared_ptr rl = pl->regions_at (event_frame (event)); - if (!rl->empty()) { - - if (atv->layer_display() == Overlaid) { - - /* we're in overlaid mode; proxy to the uppermost region view */ - - DescendingRegionLayerSorter cmp; - rl->sort (cmp); - - RegionView* rv = atv->view()->find_view (rl->front()); - - /* proxy */ - return canvas_region_view_event (event, rv->get_canvas_group(), rv); - - } else { - - /* we're in stacked mode; proxy to the region view under the mouse */ - - double cx = 0; - double cy = 0; - switch (event->type) { - case GDK_BUTTON_PRESS: - case GDK_BUTTON_RELEASE: - cx = event->button.x; - cy = event->button.y; - break; - case GDK_MOTION_NOTIFY: - cx = event->motion.x; - cy = event->motion.y; - break; - case GDK_ENTER_NOTIFY: - case GDK_LEAVE_NOTIFY: - cx = event->crossing.x; - cy = event->crossing.y; - break; - default: - /* XXX: this may be wrong for some events */ - cx = event->button.x; - cy = event->button.y; - } - - /* position of the event within the track */ - atv->view()->canvas_item()->w2i (cx, cy); - - /* hence layer that we're over */ - double const c = atv->view()->child_height (); - layer_t const l = pl->top_layer () + 1 - (cy / c); - - /* hence region */ - RegionList::iterator i = rl->begin(); - while (i != rl->end() && (*i)->layer() != l) { - ++i; - } - - if (i != rl->end()) { - RegionView* rv = atv->view()->find_view (*i); - - /* proxy */ - return canvas_region_view_event (event, rv->get_canvas_group(), rv); - } - } - } - } - } - } - - return TRUE; -} - bool Editor::canvas_control_point_event (GdkEvent *event, ArdourCanvas::Item* item, ControlPoint* cp) { diff --git a/gtk2_ardour/editor_drag.cc b/gtk2_ardour/editor_drag.cc index 21b47c388f..c418e85018 100644 --- a/gtk2_ardour/editor_drag.cc +++ b/gtk2_ardour/editor_drag.cc @@ -453,7 +453,7 @@ RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, listheight(), cy); double const fraction = 1.0 - (cy / _line->height()); - - bool push; - - if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) { - push = false; - } else { - push = true; - } + bool const push = !Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier); /* we are ignoring x position for this drag, so we can just pass in anything */ _line->drag_motion (0, fraction, true, push); @@ -4117,7 +4110,7 @@ AutomationRangeDrag::setup (list > const & lin /* check this range against all the AudioRanges that we are using */ list::const_iterator k = _ranges.begin (); while (k != _ranges.end()) { - if (k->coverage (r.first, r.second) != OverlapNone) { + if (k->coverage (r.first, r.second) != Evoral::OverlapNone) { break; } ++k; @@ -4186,9 +4179,7 @@ AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ()); the_list->add (p, the_list->eval (p)); - j->line->add_always_in_view (p); the_list->add (q, the_list->eval (q)); - j->line->add_always_in_view (q); } /* same thing for the end */ @@ -4214,9 +4205,7 @@ AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ()); the_list->add (p, the_list->eval (p)); - j->line->add_always_in_view (p); the_list->add (q, the_list->eval (q)); - j->line->add_always_in_view (q); } } @@ -4284,7 +4273,6 @@ AutomationRangeDrag::finished (GdkEvent* event, bool) motion (event, false); for (list::iterator i = _lines.begin(); i != _lines.end(); ++i) { i->line->end_drag (); - i->line->clear_always_in_view (); } _editor->session()->commit_reversible_command (); @@ -4294,7 +4282,6 @@ void AutomationRangeDrag::aborted (bool) { for (list::iterator i = _lines.begin(); i != _lines.end(); ++i) { - i->line->clear_always_in_view (); i->line->reset (); } } diff --git a/gtk2_ardour/editor_drag.h b/gtk2_ardour/editor_drag.h index 2c806126bd..0d457a39d9 100644 --- a/gtk2_ardour/editor_drag.h +++ b/gtk2_ardour/editor_drag.h @@ -310,7 +310,7 @@ public: protected: - double compute_x_delta (GdkEvent const *, ARDOUR::framecnt_t *); + double compute_x_delta (GdkEvent const *, ARDOUR::framepos_t *); bool y_movement_allowed (int, double) const; bool _brushing; diff --git a/gtk2_ardour/editor_markers.cc b/gtk2_ardour/editor_markers.cc index 7090fc23cd..46f4a0ddd5 100644 --- a/gtk2_ardour/editor_markers.cc +++ b/gtk2_ardour/editor_markers.cc @@ -154,11 +154,11 @@ Editor::add_new_location_internal (Location* location) lam->show (); } - location->start_changed.connect (*this, invalidator (*this), ui_bind (&Editor::location_changed, this, _1), gui_context()); - location->end_changed.connect (*this, invalidator (*this), ui_bind (&Editor::location_changed, this, _1), gui_context()); - location->changed.connect (*this, invalidator (*this), ui_bind (&Editor::location_changed, this, _1), gui_context()); - location->name_changed.connect (*this, invalidator (*this), ui_bind (&Editor::location_changed, this, _1), gui_context()); - location->FlagsChanged.connect (*this, invalidator (*this), ui_bind (&Editor::location_flags_changed, this, _1, _2), gui_context()); + location->start_changed.connect (*this, invalidator (*this), boost::bind (&Editor::location_changed, this, _1), gui_context()); + location->end_changed.connect (*this, invalidator (*this), boost::bind (&Editor::location_changed, this, _1), gui_context()); + location->changed.connect (*this, invalidator (*this), boost::bind (&Editor::location_changed, this, _1), gui_context()); + location->name_changed.connect (*this, invalidator (*this), boost::bind (&Editor::location_changed, this, _1), gui_context()); + location->FlagsChanged.connect (*this, invalidator (*this), boost::bind (&Editor::location_flags_changed, this, _1, _2), gui_context()); pair newpair; diff --git a/gtk2_ardour/editor_mouse.cc b/gtk2_ardour/editor_mouse.cc index 5ee05336a4..bd5cdd3f8f 100644 --- a/gtk2_ardour/editor_mouse.cc +++ b/gtk2_ardour/editor_mouse.cc @@ -655,7 +655,7 @@ Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemTyp case ControlPointItem: set_selected_track_as_side_effect (op); if (doing_object_stuff() || (mouse_mode != MouseRange && mouse_mode != MouseObject)) { - set_selected_control_point_from_click (op, false); + set_selected_control_point_from_click (press, op); } break; @@ -889,6 +889,7 @@ Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemT default: break; } + break; case MouseObject: switch (item_type) { diff --git a/gtk2_ardour/editor_ops.cc b/gtk2_ardour/editor_ops.cc index 1dbad3ac2e..bae511e031 100644 --- a/gtk2_ardour/editor_ops.cc +++ b/gtk2_ardour/editor_ops.cc @@ -63,6 +63,7 @@ #include "route_time_axis.h" #include "audio_time_axis.h" #include "automation_time_axis.h" +#include "control_point.h" #include "streamview.h" #include "audio_streamview.h" #include "audio_region_view.h" @@ -1278,11 +1279,17 @@ Editor::scroll_tracks_up_line () void Editor::tav_zoom_step (bool coarser) { - ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_step, coarser) - _routes->suspend_redisplay (); - for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { + TrackViewList* ts; + + if (selection->tracks.empty()) { + ts = &track_views; + } else { + ts = &selection->tracks; + } + + for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) { TimeAxisView *tv = (static_cast(*i)); tv->step_height (coarser); } @@ -1290,6 +1297,38 @@ Editor::tav_zoom_step (bool coarser) _routes->resume_redisplay (); } +void +Editor::tav_zoom_smooth (bool coarser, bool force_all) +{ + _routes->suspend_redisplay (); + + TrackViewList* ts; + + if (selection->tracks.empty() || force_all) { + ts = &track_views; + } else { + ts = &selection->tracks; + } + + for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) { + TimeAxisView *tv = (static_cast(*i)); + uint32_t h = tv->current_height (); + + if (coarser) { + if (h > 5) { + h -= 5; // pixels + if (h >= TimeAxisView::preset_height (HeightSmall)) { + tv->set_height (h); + } + } + } else { + tv->set_height (h + 5); + } + } + + _routes->resume_redisplay (); +} + void Editor::temporal_zoom_step (bool coarser) { @@ -2472,7 +2511,7 @@ static void add_if_covered (RegionView* rv, const AudioRange* ar, RegionSelection* rs) { switch (rv->region()->coverage (ar->start, ar->end - 1)) { - case OverlapNone: + case Evoral::OverlapNone: break; default: rs->push_back (rv); @@ -3225,7 +3264,7 @@ Editor::trim_region_to_location (const Location& loc, const char* str) /* require region to span proposed trim */ switch (rv->region()->coverage (loc.start(), loc.end())) { - case OverlapInternal: + case Evoral::OverlapInternal: break; default: continue; @@ -3355,9 +3394,10 @@ Editor::freeze_thread () SessionEvent::create_per_thread_pool ("freeze events", 64); /* create per-thread buffers for process() tree to use */ current_interthread_info->process_thread.init (); - + current_interthread_info->process_thread.get_buffers (); clicked_routeview->audio_track()->freeze_me (*current_interthread_info); current_interthread_info->done = true; + current_interthread_info->process_thread.drop_buffers(); return 0; } @@ -3682,19 +3722,87 @@ Editor::cut_copy (CutCopyOp op) } } +struct AutomationRecord { + AutomationRecord () : state (0) {} + AutomationRecord (XMLNode* s) : state (s) {} + + XMLNode* state; ///< state before any operation + boost::shared_ptr copy; ///< copied events for the cut buffer +}; + /** Cut, copy or clear selected automation points. - * @param op Operation (Cut, Copy or Clear) + * @param op Operation (Cut, Copy or Clear) */ void Editor::cut_copy_points (CutCopyOp op) { + if (selection->points.empty ()) { + return; + } + + /* XXX: not ideal, as there may be more than one track involved in the point selection */ + _last_cut_copy_source_track = &selection->points.front()->line().trackview; + + /* Keep a record of the AutomationLists that we end up using in this operation */ + typedef std::map, AutomationRecord> Lists; + Lists lists; + + /* Go through all selected points, making an AutomationRecord for each distinct AutomationList */ for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) { + boost::shared_ptr al = (*i)->line().the_list(); + if (lists.find (al) == lists.end ()) { + /* We haven't seen this list yet, so make a record for it. This includes + taking a copy of its current state, in case this is needed for undo later. + */ + lists[al] = AutomationRecord (&al->get_state ()); + } + } - AutomationTimeAxisView* atv = dynamic_cast((*i).track); - _last_cut_copy_source_track = atv; + if (op == Cut || op == Copy) { + /* This operation will involve putting things in the cut buffer, so create an empty + ControlList for each of our source lists to put the cut buffer data in. + */ + for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) { + i->second.copy = i->first->create (i->first->parameter ()); + } - if (atv) { - atv->cut_copy_clear_objects (selection->points, op); + /* Add all selected points to the relevant copy ControlLists */ + for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) { + boost::shared_ptr al = (*i)->line().the_list(); + AutomationList::const_iterator j = (*i)->model (); + lists[al].copy->add ((*j)->when, (*j)->value); + } + + for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) { + /* Correct this copy list so that it starts at time 0 */ + double const start = i->second.copy->front()->when; + for (AutomationList::iterator j = i->second.copy->begin(); j != i->second.copy->end(); ++j) { + (*j)->when -= start; + } + + /* And add it to the cut buffer */ + cut_buffer->add (i->second.copy); + } + } + + if (op == Delete || op == Cut) { + /* This operation needs to remove things from the main AutomationList, so do that now */ + + for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) { + i->first->freeze (); + } + + /* Remove each selected point from its AutomationList */ + for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) { + boost::shared_ptr al = (*i)->line().the_list(); + al->erase ((*i)->model ()); + } + + /* Thaw the lists and add undo records for them */ + for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) { + boost::shared_ptr al = i->first; + al->thaw (); + _session->add_command (new MementoCommand (*al.get(), i->second.state, &(al->get_state ()))); } } } @@ -4190,18 +4298,13 @@ Editor::duplicate_selection (float times) commit_reversible_command (); } +/** Reset all selected points to the relevant default value */ void Editor::reset_point_selection () { - /* reset all selected points to the relevant default value */ - for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) { - - AutomationTimeAxisView* atv = dynamic_cast((*i).track); - - if (atv) { - atv->reset_objects (selection->points); - } + ARDOUR::AutomationList::iterator j = (*i)->model (); + (*j)->value = (*i)->line().the_list()->default_value (); } } @@ -5207,24 +5310,6 @@ Editor::update_region_fade_visibility () } } -/** Update crossfade visibility after its configuration has been changed */ -void -Editor::update_xfade_visibility () -{ - _xfade_visibility = _session->config.get_xfades_visible (); - - for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { - AudioTimeAxisView* v = dynamic_cast(*i); - if (v) { - if (_xfade_visibility) { - v->show_all_xfades (); - } else { - v->hide_all_xfades (); - } - } - } -} - void Editor::set_edit_point () { diff --git a/gtk2_ardour/editor_regions.cc b/gtk2_ardour/editor_regions.cc index 672a0c5b8e..5a06c69648 100644 --- a/gtk2_ardour/editor_regions.cc +++ b/gtk2_ardour/editor_regions.cc @@ -212,11 +212,11 @@ EditorRegions::EditorRegions (Editor* e) //ARDOUR_UI::instance()->secondary_clock.mode_changed.connect (sigc::mem_fun(*this, &Editor::redisplay_regions)); ARDOUR_UI::instance()->secondary_clock->mode_changed.connect (sigc::mem_fun(*this, &EditorRegions::update_all_rows)); - ARDOUR::Region::RegionPropertyChanged.connect (region_property_connection, MISSING_INVALIDATOR, ui_bind (&EditorRegions::region_changed, this, _1, _2), gui_context()); - ARDOUR::RegionFactory::CheckNewRegion.connect (check_new_region_connection, MISSING_INVALIDATOR, ui_bind (&EditorRegions::add_region, this, _1), gui_context()); + ARDOUR::Region::RegionPropertyChanged.connect (region_property_connection, MISSING_INVALIDATOR, boost::bind (&EditorRegions::region_changed, this, _1, _2), gui_context()); + ARDOUR::RegionFactory::CheckNewRegion.connect (check_new_region_connection, MISSING_INVALIDATOR, boost::bind (&EditorRegions::add_region, this, _1), gui_context()); - e->EditorFreeze.connect (editor_freeze_connection, MISSING_INVALIDATOR, ui_bind (&EditorRegions::freeze_tree_model, this), gui_context()); - e->EditorThaw.connect (editor_thaw_connection, MISSING_INVALIDATOR, ui_bind (&EditorRegions::thaw_tree_model, this), gui_context()); + e->EditorFreeze.connect (editor_freeze_connection, MISSING_INVALIDATOR, boost::bind (&EditorRegions::freeze_tree_model, this), gui_context()); + e->EditorThaw.connect (editor_thaw_connection, MISSING_INVALIDATOR, boost::bind (&EditorRegions::thaw_tree_model, this), gui_context()); } bool diff --git a/gtk2_ardour/editor_route_groups.cc b/gtk2_ardour/editor_route_groups.cc index da6f57bf19..1b72425032 100644 --- a/gtk2_ardour/editor_route_groups.cc +++ b/gtk2_ardour/editor_route_groups.cc @@ -455,7 +455,7 @@ EditorRouteGroups::add (RouteGroup* group) focus = true; } - group->PropertyChanged.connect (_property_changed_connections, MISSING_INVALIDATOR, ui_bind (&EditorRouteGroups::property_changed, this, group, _1), gui_context()); + group->PropertyChanged.connect (_property_changed_connections, MISSING_INVALIDATOR, boost::bind (&EditorRouteGroups::property_changed, this, group, _1), gui_context()); if (focus) { TreeViewColumn* col = _display.get_column (0); @@ -567,9 +567,9 @@ EditorRouteGroups::set_session (Session* s) RouteGroup& arg (_session->all_route_group()); - arg.PropertyChanged.connect (all_route_groups_changed_connection, MISSING_INVALIDATOR, ui_bind (&EditorRouteGroups::all_group_changed, this, _1), gui_context()); + arg.PropertyChanged.connect (all_route_groups_changed_connection, MISSING_INVALIDATOR, boost::bind (&EditorRouteGroups::all_group_changed, this, _1), gui_context()); - _session->route_group_added.connect (_session_connections, MISSING_INVALIDATOR, ui_bind (&EditorRouteGroups::add, this, _1), gui_context()); + _session->route_group_added.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&EditorRouteGroups::add, this, _1), gui_context()); _session->route_group_removed.connect ( _session_connections, MISSING_INVALIDATOR, boost::bind (&EditorRouteGroups::groups_changed, this), gui_context() ); diff --git a/gtk2_ardour/editor_routes.cc b/gtk2_ardour/editor_routes.cc index 985ae37b60..c4443cbed0 100644 --- a/gtk2_ardour/editor_routes.cc +++ b/gtk2_ardour/editor_routes.cc @@ -282,7 +282,7 @@ EditorRoutes::EditorRoutes (Editor* e) _display.set_enable_search (false); - Route::SyncOrderKeys.connect (*this, MISSING_INVALIDATOR, ui_bind (&EditorRoutes::sync_order_keys, this, _1), gui_context()); + Route::SyncOrderKeys.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::sync_order_keys, this, _1), gui_context()); } bool @@ -660,8 +660,8 @@ EditorRoutes::routes_added (list routes) boost::weak_ptr wr ((*x)->route()); - (*x)->route()->gui_changed.connect (*this, MISSING_INVALIDATOR, ui_bind (&EditorRoutes::handle_gui_changes, this, _1, _2), gui_context()); - (*x)->route()->PropertyChanged.connect (*this, MISSING_INVALIDATOR, ui_bind (&EditorRoutes::route_property_changed, this, _1, wr), gui_context()); + (*x)->route()->gui_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::handle_gui_changes, this, _1, _2), gui_context()); + (*x)->route()->PropertyChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::route_property_changed, this, _1, wr), gui_context()); if ((*x)->is_track()) { boost::shared_ptr t = boost::dynamic_pointer_cast ((*x)->route()); @@ -675,8 +675,8 @@ EditorRoutes::routes_added (list routes) } (*x)->route()->mute_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_mute_display, this), gui_context()); - (*x)->route()->solo_changed.connect (*this, MISSING_INVALIDATOR, ui_bind (&EditorRoutes::update_solo_display, this, _1), gui_context()); - (*x)->route()->listen_changed.connect (*this, MISSING_INVALIDATOR, ui_bind (&EditorRoutes::update_solo_display, this, _1), gui_context()); + (*x)->route()->solo_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_display, this, _1), gui_context()); + (*x)->route()->listen_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_display, this, _1), gui_context()); (*x)->route()->solo_isolated_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_isolate_display, this), gui_context()); (*x)->route()->solo_safe_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_safe_display, this), gui_context()); (*x)->route()->active_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_active_display, this), gui_context ()); diff --git a/gtk2_ardour/editor_selection.cc b/gtk2_ardour/editor_selection.cc index 727b0d272b..fe93516063 100644 --- a/gtk2_ardour/editor_selection.cc +++ b/gtk2_ardour/editor_selection.cc @@ -29,6 +29,8 @@ #include "ardour/midi_region.h" #include "ardour/audioplaylist.h" +#include "control_protocol/control_protocol.h" + #include "editor.h" #include "actions.h" #include "audio_time_axis.h" @@ -36,7 +38,6 @@ #include "audio_streamview.h" #include "automation_line.h" #include "control_point.h" -#include "crossfade_view.h" #include "editor_regions.h" #include "editor_cursors.h" #include "midi_region_view.h" @@ -313,12 +314,16 @@ Editor::set_selected_track_from_click (bool press, Selection::Operation op, bool } bool -Editor::set_selected_control_point_from_click (Selection::Operation op, bool /*no_remove*/) +Editor::set_selected_control_point_from_click (bool press, Selection::Operation op) { if (!clicked_control_point) { return false; } + if (!press) { + return true; + } + switch (op) { case Selection::Set: selection->set (clicked_control_point); @@ -476,32 +481,6 @@ Editor::mapped_get_equivalent_regions (RouteTimeAxisView& tv, uint32_t, RegionVi } } -void -Editor::mapped_get_equivalent_crossfades ( - RouteTimeAxisView& tv, uint32_t, boost::shared_ptr basis, vector >* equivs - ) const -{ - boost::shared_ptr pl; - vector > results; - boost::shared_ptr tr; - - if ((tr = tv.track()) == 0) { - /* bus */ - return; - } - - if ((pl = tr->playlist()) != 0) { - boost::shared_ptr apl = boost::dynamic_pointer_cast (pl); - if (apl) { - apl->get_equivalent_crossfades (basis, *equivs); - } - } - - /* We might have just checked basis for equivalency with itself, so we need to remove dupes */ - sort (equivs->begin (), equivs->end ()); - unique (equivs->begin (), equivs->end ()); -} - void Editor::get_equivalent_regions (RegionView* basis, vector& equivalent_regions, PBD::PropertyID property) const { @@ -535,19 +514,6 @@ Editor::get_equivalent_regions (RegionSelection & basis, PBD::PropertyID prop) c return equivalent; } -vector > -Editor::get_equivalent_crossfades (RouteTimeAxisView& v, boost::shared_ptr c, PBD::PropertyID prop) const -{ - vector > e; - mapover_tracks_with_unique_playlists ( - sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_crossfades), c, &e), - &v, - prop - ); - - return e; -} - int Editor::get_regionview_count_from_region_list (boost::shared_ptr region) { @@ -698,7 +664,7 @@ Editor::set_selected_regionview_from_click (bool press, Selection::Operation op) /* 2. figure out the boundaries for our search for new objects */ switch (clicked_regionview->region()->coverage (first_frame, last_frame)) { - case OverlapNone: + case Evoral::OverlapNone: if (last_frame < clicked_regionview->region()->first_frame()) { first_frame = last_frame; last_frame = clicked_regionview->region()->last_frame(); @@ -708,7 +674,7 @@ Editor::set_selected_regionview_from_click (bool press, Selection::Operation op) } break; - case OverlapExternal: + case Evoral::OverlapExternal: if (last_frame < clicked_regionview->region()->first_frame()) { first_frame = last_frame; last_frame = clicked_regionview->region()->last_frame(); @@ -718,7 +684,7 @@ Editor::set_selected_regionview_from_click (bool press, Selection::Operation op) } break; - case OverlapInternal: + case Evoral::OverlapInternal: if (last_frame < clicked_regionview->region()->first_frame()) { first_frame = last_frame; last_frame = clicked_regionview->region()->last_frame(); @@ -728,8 +694,8 @@ Editor::set_selected_regionview_from_click (bool press, Selection::Operation op) } break; - case OverlapStart: - case OverlapEnd: + case Evoral::OverlapStart: + case Evoral::OverlapEnd: /* nothing to do except add clicked region to selection, since it overlaps with the existing selection in this track. */ @@ -965,6 +931,8 @@ Editor::track_selection_changed () break; } + RouteNotificationListPtr routes (new RouteNotificationList); + for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { bool yn = (find (selection->tracks.begin(), selection->tracks.end(), *i) != selection->tracks.end()); @@ -983,9 +951,21 @@ Editor::track_selection_changed () } else { (*i)->hide_selection (); } + + + if (yn) { + RouteTimeAxisView* rtav = dynamic_cast (*i); + if (rtav) { + routes->push_back (rtav->route()); + } + } } ActionManager::set_sensitive (ActionManager::track_selection_sensitive_actions, !selection->tracks.empty()); + + /* notify control protocols */ + + ControlProtocol::TrackSelectionChanged (routes); } void diff --git a/gtk2_ardour/editor_summary.cc b/gtk2_ardour/editor_summary.cc index deba448071..c8e794b2ef 100644 --- a/gtk2_ardour/editor_summary.cc +++ b/gtk2_ardour/editor_summary.cc @@ -54,7 +54,7 @@ EditorSummary::EditorSummary (Editor* e) _old_follow_playhead (false) { Region::RegionPropertyChanged.connect (region_property_connection, invalidator (*this), boost::bind (&CairoWidget::set_dirty, this), gui_context()); - _editor->playhead_cursor->PositionChanged.connect (position_connection, invalidator (*this), ui_bind (&EditorSummary::playhead_position_changed, this, _1), gui_context()); + _editor->playhead_cursor->PositionChanged.connect (position_connection, invalidator (*this), boost::bind (&EditorSummary::playhead_position_changed, this, _1), gui_context()); add_events (Gdk::POINTER_MOTION_MASK); } @@ -930,10 +930,10 @@ EditorSummary::routes_added (list const & r) { for (list::const_iterator i = r.begin(); i != r.end(); ++i) { /* Connect to gui_changed() on the route so that we know when their colour has changed */ - (*i)->route()->gui_changed.connect (*this, invalidator (*this), ui_bind (&EditorSummary::route_gui_changed, this, _1), gui_context ()); + (*i)->route()->gui_changed.connect (*this, invalidator (*this), boost::bind (&EditorSummary::route_gui_changed, this, _1), gui_context ()); boost::shared_ptr tr = boost::dynamic_pointer_cast ((*i)->route ()); if (tr) { - tr->PlaylistChanged.connect (*this, invalidator (*this), ui_bind (&CairoWidget::set_dirty, this), gui_context ()); + tr->PlaylistChanged.connect (*this, invalidator (*this), boost::bind (&CairoWidget::set_dirty, this), gui_context ()); } } diff --git a/gtk2_ardour/editor_xpms b/gtk2_ardour/editor_xpms index cb4a044cfd..c8b4e83a0c 100644 --- a/gtk2_ardour/editor_xpms +++ b/gtk2_ardour/editor_xpms @@ -3,7 +3,7 @@ #define fader_cursor_height 25 #define fader_cursor_x_hot 3 #define fader_cursor_y_hot 21 -static const gchar fader_cursor_bits[] = { +static const unsigned char fader_cursor_bits[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0xfe, 0x01, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x02, 0x01, @@ -15,7 +15,7 @@ static const gchar fader_cursor_bits[] = { 0x00, 0x00, 0x00, 0x00 }; /* Created with The GIMP */ -static const gchar fader_cursor_mask_bits[] = { +static const unsigned char fader_cursor_mask_bits[] = { 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0xfe, 0x01, 0x00, 0x00, 0xfe, 0x01, 0x00, 0x00, 0xfe, 0x01, 0x00, 0x00, 0xfe, 0x01, 0x00, 0x00, 0xfe, 0x01, @@ -31,7 +31,7 @@ static const gchar fader_cursor_mask_bits[] = { #define speaker_cursor_height 26 #define speaker_cursor_x_hot 0 #define speaker_cursor_y_hot 0 -static const gchar speaker_cursor_bits[] = { +static const unsigned char speaker_cursor_bits[] = { 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xcc, 0x00, 0xcc, 0xff, 0xc3, 0xff, 0xc3, 0x03, 0xc0, 0x03, 0xc0, 0xc3, 0xc0, 0xc3, 0xc0, 0xc3, 0xc0, 0xc3, 0xc0, 0xc3, 0xc0, 0xc3, 0xc0, 0x03, 0xc0, 0x03, 0xc0, @@ -42,7 +42,7 @@ static const gchar speaker_cursor_bits[] = { #define speaker_cursor_mask_height 26 #define speaker_cursor_mask_x_hot 0 #define speaker_cursor_mask_y_hot 0 -static const gchar speaker_cursor_mask_bits[] = { +static const unsigned char speaker_cursor_mask_bits[] = { 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xfc, 0x00, 0xfc, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0x3f, 0xff, @@ -85,7 +85,7 @@ static const short cursor_timestretch_mask_bits[] = { #define cursor_zoom_height 16 #define cursor_zoom_x_hot 6 #define cursor_zoom_y_hot 6 -static const short cursor_zoom_bits[] = { +static const unsigned short cursor_zoom_bits[] = { 0x00e0, 0x0000, 0x03b8, 0x0000, 0x0604, 0x0000, 0x0806, 0x0000, 0x0842, 0x0000, 0x1843, 0x0000, 0x11f1, 0x0000, 0x1843, 0x0000, 0x0842, 0x0000, 0x1806, 0x0000, 0x2604, 0x0000, 0x4758, 0x0000, 0x88e0, 0x0000, 0x1000, @@ -95,7 +95,7 @@ static const short cursor_zoom_bits[] = { #define cursor_zoom_mask_height 16 #define cursor_zoom_mask_x_hot 6 #define cursor_zoom_mask_y_hot 6 -static const short cursor_zoom_mask_bits[] = { +static const unsigned short cursor_zoom_mask_bits[] = { 0x00e0, 0x0000, 0x03f8, 0x0000, 0x07fc, 0x0000, 0x0ffe, 0x0000, 0x0ffe, 0x0000, 0x1fff, 0x0000, 0x1fff, 0x0000, 0x1fff, 0x0000, 0x0ffe, 0x0000, 0x1ffe, 0x0000, 0x3ffc, 0x0000, 0x7ff8, 0x0000, 0xf8e0, 0x0000, 0xf000, diff --git a/gtk2_ardour/engine_dialog.cc b/gtk2_ardour/engine_dialog.cc index 9a572216cf..650f82f8b9 100644 --- a/gtk2_ardour/engine_dialog.cc +++ b/gtk2_ardour/engine_dialog.cc @@ -1107,6 +1107,9 @@ EngineControl::find_jack_servers (vector& strings) #endif jack_servers = scanner (path, jack_server_filter, 0, false, true); + if (!jack_servers) { + return; + } vector::iterator iter; diff --git a/gtk2_ardour/export_format_dialog.cc b/gtk2_ardour/export_format_dialog.cc index 68c3f3c41f..814e0388be 100644 --- a/gtk2_ardour/export_format_dialog.cc +++ b/gtk2_ardour/export_format_dialog.cc @@ -131,7 +131,7 @@ ExportFormatDialog::ExportFormatDialog (FormatPtr format, bool new_dialog) : close_button = add_button (Gtk::Stock::SAVE, Gtk::RESPONSE_APPLY); close_button->set_sensitive (false); close_button->signal_clicked().connect (sigc::mem_fun (*this, &ExportFormatDialog::end_dialog)); - manager.CompleteChanged.connect (*this, invalidator (*this), ui_bind (&Gtk::Button::set_sensitive, close_button, _1), gui_context()); + manager.CompleteChanged.connect (*this, invalidator (*this), boost::bind (&Gtk::Button::set_sensitive, close_button, _1), gui_context()); with_cue.signal_toggled().connect (sigc::mem_fun (*this, &ExportFormatDialog::update_with_cue)); with_toc.signal_toggled().connect (sigc::mem_fun (*this, &ExportFormatDialog::update_with_toc)); @@ -336,7 +336,7 @@ ExportFormatDialog::init_format_table () row[compatibility_cols.label] = (*it)->name(); WeakCompatPtr ptr (*it); - (*it)->SelectChanged.connect (*this, invalidator (*this), ui_bind (&ExportFormatDialog::change_compatibility_selection, this, _1, ptr), gui_context()); + (*it)->SelectChanged.connect (*this, invalidator (*this), boost::bind (&ExportFormatDialog::change_compatibility_selection, this, _1, ptr), gui_context()); } compatibility_view.append_column_editable ("", compatibility_cols.selected); @@ -364,8 +364,8 @@ ExportFormatDialog::init_format_table () row[quality_cols.label] = (*it)->name(); WeakQualityPtr ptr (*it); - (*it)->SelectChanged.connect (*this, invalidator (*this), ui_bind (&ExportFormatDialog::change_quality_selection, this, _1, ptr), gui_context()); - (*it)->CompatibleChanged.connect (*this, invalidator (*this), ui_bind (&ExportFormatDialog::change_quality_compatibility, this, _1, ptr), gui_context()); + (*it)->SelectChanged.connect (*this, invalidator (*this), boost::bind (&ExportFormatDialog::change_quality_selection, this, _1, ptr), gui_context()); + (*it)->CompatibleChanged.connect (*this, invalidator (*this), boost::bind (&ExportFormatDialog::change_quality_compatibility, this, _1, ptr), gui_context()); } quality_view.append_column ("", quality_cols.label); @@ -386,19 +386,19 @@ ExportFormatDialog::init_format_table () row[format_cols.label] = (*it)->name(); WeakFormatPtr ptr (*it); - (*it)->SelectChanged.connect (*this, invalidator (*this), ui_bind (&ExportFormatDialog::change_format_selection, this, _1, ptr), gui_context()); - (*it)->CompatibleChanged.connect (*this, invalidator (*this), ui_bind (&ExportFormatDialog::change_format_compatibility, this, _1, ptr), gui_context()); + (*it)->SelectChanged.connect (*this, invalidator (*this), boost::bind (&ExportFormatDialog::change_format_selection, this, _1, ptr), gui_context()); + (*it)->CompatibleChanged.connect (*this, invalidator (*this), boost::bind (&ExportFormatDialog::change_format_compatibility, this, _1, ptr), gui_context()); /* Encoding options */ boost::shared_ptr hsf; if (hsf = boost::dynamic_pointer_cast (*it)) { - hsf->SampleFormatSelectChanged.connect (*this, invalidator (*this), ui_bind (&ExportFormatDialog::change_sample_format_selection, this, _1, _2), gui_context()); - hsf->SampleFormatCompatibleChanged.connect (*this, invalidator (*this), ui_bind (&ExportFormatDialog::change_sample_format_compatibility, this, _1, _2), gui_context()); + hsf->SampleFormatSelectChanged.connect (*this, invalidator (*this), boost::bind (&ExportFormatDialog::change_sample_format_selection, this, _1, _2), gui_context()); + hsf->SampleFormatCompatibleChanged.connect (*this, invalidator (*this), boost::bind (&ExportFormatDialog::change_sample_format_compatibility, this, _1, _2), gui_context()); - hsf->DitherTypeSelectChanged.connect (*this, invalidator (*this), ui_bind (&ExportFormatDialog::change_dither_type_selection, this, _1, _2), gui_context()); - hsf->DitherTypeCompatibleChanged.connect (*this, invalidator (*this), ui_bind (&ExportFormatDialog::change_dither_type_compatibility, this, _1, _2), gui_context()); + hsf->DitherTypeSelectChanged.connect (*this, invalidator (*this), boost::bind (&ExportFormatDialog::change_dither_type_selection, this, _1, _2), gui_context()); + hsf->DitherTypeCompatibleChanged.connect (*this, invalidator (*this), boost::bind (&ExportFormatDialog::change_dither_type_compatibility, this, _1, _2), gui_context()); } } @@ -420,8 +420,8 @@ ExportFormatDialog::init_format_table () row[sample_rate_cols.label] = (*it)->name(); WeakSampleRatePtr ptr (*it); - (*it)->SelectChanged.connect (*this, invalidator (*this), ui_bind (&ExportFormatDialog::change_sample_rate_selection, this, _1, ptr), gui_context()); - (*it)->CompatibleChanged.connect (*this, invalidator (*this), ui_bind (&ExportFormatDialog::change_sample_rate_compatibility, this, _1, ptr), gui_context()); + (*it)->SelectChanged.connect (*this, invalidator (*this), boost::bind (&ExportFormatDialog::change_sample_rate_selection, this, _1, ptr), gui_context()); + (*it)->CompatibleChanged.connect (*this, invalidator (*this), boost::bind (&ExportFormatDialog::change_sample_rate_compatibility, this, _1, ptr), gui_context()); } sample_rate_view.append_column ("", sample_rate_cols.label); diff --git a/gtk2_ardour/gain_meter.cc b/gtk2_ardour/gain_meter.cc index d651604df9..b571ed52c7 100644 --- a/gtk2_ardour/gain_meter.cc +++ b/gtk2_ardour/gain_meter.cc @@ -192,7 +192,7 @@ GainMeterBase::set_controls (boost::shared_ptr r, if (amp) { amp->ConfigurationChanged.connect ( - model_connections, invalidator (*this), ui_bind (&GainMeterBase::setup_gain_adjustment, this), gui_context () + model_connections, invalidator (*this), boost::bind (&GainMeterBase::setup_gain_adjustment, this), gui_context () ); } @@ -905,7 +905,7 @@ GainMeter::set_controls (boost::shared_ptr r, if (_meter) { _meter->ConfigurationChanged.connect ( - model_connections, invalidator (*this), ui_bind (&GainMeter::meter_configuration_changed, this, _1), gui_context() + model_connections, invalidator (*this), boost::bind (&GainMeter::meter_configuration_changed, this, _1), gui_context() ); meter_configuration_changed (_meter->input_streams ()); diff --git a/gtk2_ardour/generic_pluginui.cc b/gtk2_ardour/generic_pluginui.cc index 3f8d0f63c9..17825c1373 100644 --- a/gtk2_ardour/generic_pluginui.cc +++ b/gtk2_ardour/generic_pluginui.cc @@ -101,6 +101,9 @@ GenericPluginUI::GenericPluginUI (boost::shared_ptr pi, bool scrol VBox* v1_box = manage (new VBox); VBox* v2_box = manage (new VBox); pack_end (plugin_analysis_expander, false, false); + if (!plugin->get_docs().empty()) { + pack_end (description_expander, false, false); + } v1_box->pack_start (*smaller_hbox, false, true); v2_box->pack_start (focus_button, false, true); @@ -264,6 +267,11 @@ GenericPluginUI::build () continue; } + const std::string param_docs = plugin->get_parameter_docs(i); + if (!param_docs.empty()) { + ARDOUR_UI::instance()->set_tip(cui, param_docs.c_str()); + } + if (cui->controller || cui->clickbox || cui->combo) { // Get all of the controls into a list, so that // we can lay them out a bit more nicely later. diff --git a/gtk2_ardour/imageframe_time_axis.cc b/gtk2_ardour/imageframe_time_axis.cc index 3e0f38d4be..3b3d9bffde 100644 --- a/gtk2_ardour/imageframe_time_axis.cc +++ b/gtk2_ardour/imageframe_time_axis.cc @@ -81,7 +81,7 @@ ImageFrameTimeAxis::ImageFrameTimeAxis(const string & track_id, PublicEditor& ed // set the initial height of this time axis set_height(hNormal) ; - TimeAxisView::CatchDeletion.connect (*this, ui_bind (&ImageFrameTimeAxis::remove_time_axis_view, this, _1), gui_context()); + TimeAxisView::CatchDeletion.connect (*this, boost::bind (&ImageFrameTimeAxis::remove_time_axis_view, this, _1), gui_context()); } /** diff --git a/gtk2_ardour/imageframe_time_axis_group.cc b/gtk2_ardour/imageframe_time_axis_group.cc index 301d0d2fdd..c42452bafe 100644 --- a/gtk2_ardour/imageframe_time_axis_group.cc +++ b/gtk2_ardour/imageframe_time_axis_group.cc @@ -52,7 +52,7 @@ ImageFrameTimeAxisGroup::ImageFrameTimeAxisGroup(ImageFrameTimeAxisView& iftav, selected_imageframe_item = 0; is_selected = false; - ImageFrameView::CatchDeletion.connect (*this, ui_bind (&ImageFrameTimeAxisGroup::remove_imageframe_item, this, _1), gui_context()); + ImageFrameView::CatchDeletion.connect (*this, boost::bind (&ImageFrameTimeAxisGroup::remove_imageframe_item, this, _1), gui_context()); } /** diff --git a/gtk2_ardour/imageframe_time_axis_view.cc b/gtk2_ardour/imageframe_time_axis_view.cc index bbec156efd..8df7a91612 100644 --- a/gtk2_ardour/imageframe_time_axis_view.cc +++ b/gtk2_ardour/imageframe_time_axis_view.cc @@ -67,7 +67,7 @@ ImageFrameTimeAxisView::ImageFrameTimeAxisView (ImageFrameTimeAxis& tv) selected_imageframe_group = 0 ; selected_imageframe_view = 0 ; - ImageFrameTimeAxisGroup::CatchDeletion.connect (*this, ui_bind (&ImageFrameTimeAxisView::remove_imageframe_group, this, _1), gui_context()); + ImageFrameTimeAxisGroup::CatchDeletion.connect (*this, boost::bind (&ImageFrameTimeAxisView::remove_imageframe_group, this, _1), gui_context()); } /** diff --git a/gtk2_ardour/imageframe_view.cc b/gtk2_ardour/imageframe_view.cc index 817088124c..9b9a8c2bdb 100644 --- a/gtk2_ardour/imageframe_view.cc +++ b/gtk2_ardour/imageframe_view.cc @@ -104,7 +104,7 @@ ImageFrameView::ImageFrameView(const string & item_id, set_position(start, this); set_duration(duration, this); - MarkerView::CatchDeletion.connect (*this, ui_bind (&ImageFrameView::remove_marker_view_item, this, _1), gui_context()); + MarkerView::CatchDeletion.connect (*this, boost::bind (&ImageFrameView::remove_marker_view_item, this, _1), gui_context()); } /** diff --git a/gtk2_ardour/keyeditor.cc b/gtk2_ardour/keyeditor.cc index efcff5b901..0d2cd98942 100644 --- a/gtk2_ardour/keyeditor.cc +++ b/gtk2_ardour/keyeditor.cc @@ -193,9 +193,7 @@ KeyEditor::on_key_release_event (GdkEventKey* ev) goto out; } - cerr << "real lkeyval: " << ev->keyval << endl; Gtkmm2ext::possibly_translate_keyval_to_make_legal_accelerator (ev->keyval); - cerr << "using keyval = " << ev->keyval << endl; bool result = AccelMap::change_entry (path, @@ -203,8 +201,6 @@ KeyEditor::on_key_release_event (GdkEventKey* ev) ModifierType (Keyboard::RelevantModifierKeyMask & ev->state), true); - cerr << "New binding to " << ev->keyval << " worked: " << result << endl; - if (result) { AccelKey key; (*i)[columns.binding] = ActionManager::get_key_representation (path, key); diff --git a/gtk2_ardour/level_meter.cc b/gtk2_ardour/level_meter.cc index b073d54a7a..7268e8a2f4 100644 --- a/gtk2_ardour/level_meter.cc +++ b/gtk2_ardour/level_meter.cc @@ -59,7 +59,7 @@ LevelMeter::LevelMeter (Session* s) { set_session (s); set_spacing (1); - Config->ParameterChanged.connect (_parameter_connection, invalidator (*this), ui_bind (&LevelMeter::parameter_changed, this, _1), gui_context()); + Config->ParameterChanged.connect (_parameter_connection, invalidator (*this), boost::bind (&LevelMeter::parameter_changed, this, _1), gui_context()); UI::instance()->theme_changed.connect (sigc::mem_fun(*this, &LevelMeter::on_theme_changed)); ColorsChanged.connect (sigc::mem_fun (*this, &LevelMeter::color_handler)); max_peak = minus_infinity(); @@ -85,7 +85,7 @@ LevelMeter::set_meter (PeakMeter* meter) _meter = meter; if (_meter) { - _meter->ConfigurationChanged.connect (_configuration_connection, invalidator (*this), ui_bind (&LevelMeter::configuration_changed, this, _1, _2), gui_context()); + _meter->ConfigurationChanged.connect (_configuration_connection, invalidator (*this), boost::bind (&LevelMeter::configuration_changed, this, _1, _2), gui_context()); } } diff --git a/gtk2_ardour/location_ui.cc b/gtk2_ardour/location_ui.cc index c27dbafcc7..60e0be5676 100644 --- a/gtk2_ardour/location_ui.cc +++ b/gtk2_ardour/location_ui.cc @@ -328,13 +328,13 @@ LocationEditRow::set_location (Location *loc) --i_am_the_modifier; - location->start_changed.connect (connections, invalidator (*this), ui_bind (&LocationEditRow::start_changed, this, _1), gui_context()); - location->end_changed.connect (connections, invalidator (*this), ui_bind (&LocationEditRow::end_changed, this, _1), gui_context()); - location->name_changed.connect (connections, invalidator (*this), ui_bind (&LocationEditRow::name_changed, this, _1), gui_context()); - location->changed.connect (connections, invalidator (*this), ui_bind (&LocationEditRow::location_changed, this, _1), gui_context()); - location->FlagsChanged.connect (connections, invalidator (*this), ui_bind (&LocationEditRow::flags_changed, this, _1, _2), gui_context()); - location->LockChanged.connect (connections, invalidator (*this), ui_bind (&LocationEditRow::lock_changed, this, _1), gui_context()); - location->PositionLockStyleChanged.connect (connections, invalidator (*this), ui_bind (&LocationEditRow::position_lock_style_changed, this, _1), gui_context()); + location->start_changed.connect (connections, invalidator (*this), boost::bind (&LocationEditRow::start_changed, this, _1), gui_context()); + location->end_changed.connect (connections, invalidator (*this), boost::bind (&LocationEditRow::end_changed, this, _1), gui_context()); + location->name_changed.connect (connections, invalidator (*this), boost::bind (&LocationEditRow::name_changed, this, _1), gui_context()); + location->changed.connect (connections, invalidator (*this), boost::bind (&LocationEditRow::location_changed, this, _1), gui_context()); + location->FlagsChanged.connect (connections, invalidator (*this), boost::bind (&LocationEditRow::flags_changed, this, _1, _2), gui_context()); + location->LockChanged.connect (connections, invalidator (*this), boost::bind (&LocationEditRow::lock_changed, this, _1), gui_context()); + location->PositionLockStyleChanged.connect (connections, invalidator (*this), boost::bind (&LocationEditRow::position_lock_style_changed, this, _1), gui_context()); } void @@ -1056,8 +1056,8 @@ LocationUI::set_session(ARDOUR::Session* s) if (_session) { _session->locations()->changed.connect (_session_connections, invalidator (*this), boost::bind (&LocationUI::locations_changed, this, _1), gui_context()); _session->locations()->StateChanged.connect (_session_connections, invalidator (*this), boost::bind (&LocationUI::refresh_location_list, this), gui_context()); - _session->locations()->added.connect (_session_connections, invalidator (*this), ui_bind (&LocationUI::location_added, this, _1), gui_context()); - _session->locations()->removed.connect (_session_connections, invalidator (*this), ui_bind (&LocationUI::location_removed, this, _1), gui_context()); + _session->locations()->added.connect (_session_connections, invalidator (*this), boost::bind (&LocationUI::location_added, this, _1), gui_context()); + _session->locations()->removed.connect (_session_connections, invalidator (*this), boost::bind (&LocationUI::location_removed, this, _1), gui_context()); _clock_group->set_clock_mode (clock_mode_from_session_instant_xml ()); } diff --git a/gtk2_ardour/lv2_plugin_ui.cc b/gtk2_ardour/lv2_plugin_ui.cc index dec02a7ebc..ec1f1323d5 100644 --- a/gtk2_ardour/lv2_plugin_ui.cc +++ b/gtk2_ardour/lv2_plugin_ui.cc @@ -20,12 +20,13 @@ #include "ardour/lv2_plugin.h" #include "ardour/plugin_manager.h" #include "ardour/processor.h" +#include "ardour/session.h" #include "ardour_ui.h" #include "gui_thread.h" #include "lv2_plugin_ui.h" -#include "lv2_ui.h" +#include "lv2/lv2plug.in/ns/extensions/ui/ui.h" #include #include @@ -68,13 +69,36 @@ LV2PluginUI::write_to_ui(void* controller, const void* buffer) { LV2PluginUI* me = (LV2PluginUI*)controller; - if (me->_inst) { suil_instance_port_event((SuilInstance*)me->_inst, port_index, buffer_size, format, buffer); } } +uint32_t +LV2PluginUI::port_index(void* controller, const char* symbol) +{ + return ((LV2PluginUI*)controller)->_lv2->port_index(symbol); +} + +void +LV2PluginUI::touch(void* controller, + uint32_t port_index, + bool grabbed) +{ + LV2PluginUI* me = (LV2PluginUI*)controller; + if (port_index >= me->_controllables.size()) { + return; + } + + ControllableRef control = me->_controllables[port_index]; + if (grabbed) { + control->start_touch(control->session().transport_frame()); + } else { + control->stop_touch(false, control->session().transport_frame()); + } +} + void LV2PluginUI::update_timeout() { @@ -196,7 +220,10 @@ LV2PluginUI::lv2ui_instantiate(const std::string& title) } if (!ui_host) { - ui_host = suil_host_new(LV2PluginUI::write_from_ui, NULL, NULL, NULL); + ui_host = suil_host_new(LV2PluginUI::write_from_ui, + LV2PluginUI::port_index, + NULL, NULL); + suil_host_set_touch_func(ui_host, LV2PluginUI::touch); } const char* container_type = (is_external_ui) ? NS_UI "external" @@ -245,7 +272,7 @@ LV2PluginUI::lv2ui_instantiate(const std::string& title) pack_start(*_ardour_buttons_box, false, false); GtkWidget* c_widget = (GtkWidget*)GET_WIDGET(_inst); - _gui_widget = Glib::wrap(c_widget); + _gui_widget = Gtk::manage(Glib::wrap(c_widget)); _gui_widget->show_all(); pack_start(*_gui_widget, true, true); } else { @@ -283,18 +310,13 @@ LV2PluginUI::lv2ui_free() if (_gui_widget) { remove (*_gui_widget); + _gui_widget = NULL; } - if (_ardour_buttons_box) { - remove (*_ardour_buttons_box); - delete _ardour_buttons_box; - _ardour_buttons_box = 0; + if (_inst) { + suil_instance_free((SuilInstance*)_inst); + _inst = NULL; } - - suil_instance_free((SuilInstance*)_inst); - - _inst = NULL; - _gui_widget = NULL; } LV2PluginUI::~LV2PluginUI () diff --git a/gtk2_ardour/lv2_plugin_ui.h b/gtk2_ardour/lv2_plugin_ui.h index e90b27cf8f..a989213146 100644 --- a/gtk2_ardour/lv2_plugin_ui.h +++ b/gtk2_ardour/lv2_plugin_ui.h @@ -94,6 +94,12 @@ class LV2PluginUI : public PlugUIBase, public Gtk::VBox uint32_t format, const void* buffer); + static uint32_t port_index(void* controller, const char* symbol); + + static void touch(void* controller, + uint32_t port_index, + bool grabbed); + void update_timeout(); void lv2ui_instantiate(const std::string& title); diff --git a/gtk2_ardour/lv2_ui.h b/gtk2_ardour/lv2_ui.h deleted file mode 100644 index fa0a711cf4..0000000000 --- a/gtk2_ardour/lv2_ui.h +++ /dev/null @@ -1,241 +0,0 @@ -/* LV2 UI Extension - * Copyright (C) 2006-2008 Lars Luthman - * Copyright (C) 2009-2010 David Robillard - * - * Based on lv2.h, which was - * Copyright (C) 2000-2002 Richard W.E. Furse, Paul Barton-Davis, - * Stefan Westerfeld - * Copyright (C) 2006 Steve Harris, David Robillard. - * - * This header is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation; either version 2.1 of the License, - * or (at your option) any later version. - * - * This header is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 - * USA. - * - */ - -/** @file - * C header for the LV2 UI extension . - */ - -#ifndef LV2_UI_H -#define LV2_UI_H - -#include "lv2.h" - -#define LV2_UI_URI "http://lv2plug.in/ns/extensions/ui" - - -#ifdef __cplusplus -extern "C" { -#endif - - -/** A pointer to some widget or other type of UI handle. - The actual type is defined by the type URI of the UI. - All the functionality provided by this extension is toolkit - independent, the host only needs to pass the necessary callbacks and - display the widget, if possible. Plugins may have several UIs, in various - toolkits. */ -typedef void* LV2UI_Widget; - - -/** A pointer to a particular instance of a UI. - It is valid to compare this to NULL (0 for C++) but otherwise the - host MUST not attempt to interpret it. The UI plugin may use it to - reference internal instance data. */ -typedef void* LV2UI_Handle; - - -/** A pointer to a particular plugin controller, provided by the host. - It is valid to compare this to NULL (0 for C++) but otherwise the - UI plugin MUST not attempt to interpret it. The host may use it to - reference internal instance data. */ -typedef void* LV2UI_Controller; - - -/** The type of the host-provided function that the UI can use to - send data to a plugin's input ports. The @c buffer parameter must point - to a block of data, @c buffer_size bytes large. The contents of this buffer - and what the host should do with it depends on the value of the @c format - parameter. - - The @c format parameter should either be 0 or a numeric ID for a "Transfer - mechanism". Transfer mechanisms are Features and may be defined in - meta-extensions. They specify how to translate the data buffers passed - to this function to input data for the plugin ports. If a UI wishes to - write data to an input port, it must list a transfer mechanism Feature - for that port's class as an optional or required feature (depending on - whether the UI will work without being able to write to that port or not). - The only exception is when the UI wants to write single float values to - input ports of the class lv2:ControlPort, in which case @c buffer_size - should always be 4, the buffer should always contain a single IEEE-754 - float, and @c format should be 0. - - The numeric IDs for the transfer mechanisms are provided by a - URI-to-integer mapping function provided by the host, using the URI Map - feature with the map URI - "http://lv2plug.in/ns/extensions/ui". Thus a UI that requires transfer - mechanism features also requires the URI Map feature, but this is - implicit - the UI does not have to list the URI map feature as a required - or optional feature in it's RDF data. - - An UI MUST NOT pass a @c format parameter value (except 0) that has not - been returned by the host-provided URI mapping function for a - host-supported transfer mechanism feature URI. - - The UI MUST NOT try to write to a port for which there is no specified - transfer mechanism, or to an output port. The UI is responsible for - allocating the buffer and deallocating it after the call. -*/ -typedef void (*LV2UI_Write_Function)(LV2UI_Controller controller, - uint32_t port_index, - uint32_t buffer_size, - uint32_t format, - const void* buffer); - - -/** This struct contains the implementation of a UI. A pointer to an - object of this type is returned by the lv2ui_descriptor() function. -*/ -typedef struct _LV2UI_Descriptor { - - /** The URI for this UI (not for the plugin it controls). */ - const char* URI; - - /** Create a new UI object and return a handle to it. This function works - similarly to the instantiate() member in LV2_Descriptor. - - @param descriptor The descriptor for the UI that you want to instantiate. - @param plugin_uri The URI of the plugin that this UI will control. - @param bundle_path The path to the bundle containing the RDF data file - that references this shared object file, including the - trailing '/'. - @param write_function A function provided by the host that the UI can - use to send data to the plugin's input ports. - @param controller A handle for the plugin instance that should be passed - as the first parameter of @c write_function. - @param widget A pointer to an LV2UI_Widget. The UI will write a - widget pointer to this location (what type of widget - depends on the RDF class of the UI) that will be the - main UI widget. - @param features An array of LV2_Feature pointers. The host must pass - all feature URIs that it and the UI supports and any - additional data, just like in the LV2 plugin - instantiate() function. Note that UI features and plugin - features are NOT necessarily the same, they just share - the same data structure - this will probably not be the - same array as the one the plugin host passes to a - plugin. - */ - LV2UI_Handle (*instantiate)(const struct _LV2UI_Descriptor* descriptor, - const char* plugin_uri, - const char* bundle_path, - LV2UI_Write_Function write_function, - LV2UI_Controller controller, - LV2UI_Widget* widget, - const LV2_Feature* const* features); - - - /** Destroy the UI object and the associated widget. The host must not try - to access the widget after calling this function. - */ - void (*cleanup)(LV2UI_Handle ui); - - /** Tell the UI that something interesting has happened at a plugin port. - What is interesting and how it is written to the buffer passed to this - function is defined by the @c format parameter, which has the same - meaning as in LV2UI_Write_Function. The only exception is ports of the - class lv2:ControlPort, for which this function should be called - when the port value changes (it does not have to be called for every - single change if the host's UI thread has problems keeping up with the - thread the plugin is running in), @c buffer_size should be 4, the buffer - should contain a single IEEE-754 float, and @c format should be 0. - - By default, the host should only call this function for input ports of - the lv2:ControlPort class. However, the default setting can be modified - by using the following URIs in the UI's RDF data: -
-      uiext:portNotification
-      uiext:noPortNotification
-      uiext:plugin
-      uiext:portIndex
-      
- For example, if you want the UI with uri - for the plugin with URI - to get notified when the value of the - output control port with index 4 changes, you would use the following - in the RDF for your UI: -
-       uiext:portNotification [ uiext:plugin  ;
-                                                    uiext:portIndex 4 ] .
-      
- and similarly with uiext:noPortNotification if you wanted - to prevent notifications for a port for which it would be on by default - otherwise. The UI is not allowed to request notifications for ports of - types for which no transfer mechanism is specified, if it does it should - be considered broken and the host should not load it. - - The @c buffer is only valid during the time of this function call, so if - the UI wants to keep it for later use it has to copy the contents to an - internal buffer. - - This member may be set to NULL if the UI is not interested in any - port events. - */ - void (*port_event)(LV2UI_Handle ui, - uint32_t port_index, - uint32_t buffer_size, - uint32_t format, - const void* buffer); - - /** Returns a data structure associated with an extension URI, for example - a struct containing additional function pointers. Avoid returning - function pointers directly since standard C/C++ has no valid way of - casting a void* to a function pointer. This member may be set to NULL - if the UI is not interested in supporting any extensions. This is similar - to the extension_data() member in LV2_Descriptor. - */ - const void* (*extension_data)(const char* uri); - -} LV2UI_Descriptor; - - - -/** A plugin UI programmer must include a function called "lv2ui_descriptor" - with the following function prototype within the shared object - file. This function will have C-style linkage (if you are using - C++ this is taken care of by the 'extern "C"' clause at the top of - the file). This function will be accessed by the UI host using the - @c dlsym() function and called to get a LV2UI_UIDescriptor for the - wanted plugin. - - Just like lv2_descriptor(), this function takes an index parameter. The - index should only be used for enumeration and not as any sort of ID number - - the host should just iterate from 0 and upwards until the function returns - NULL or a descriptor with an URI matching the one the host is looking for. -*/ -const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index); - - -/** This is the type of the lv2ui_descriptor() function. */ -typedef const LV2UI_Descriptor* (*LV2UI_DescriptorFunction)(uint32_t index); - - - -#ifdef __cplusplus -} -#endif - - -#endif diff --git a/gtk2_ardour/marker_time_axis_view.cc b/gtk2_ardour/marker_time_axis_view.cc index 124aac0852..b1eb7066cd 100644 --- a/gtk2_ardour/marker_time_axis_view.cc +++ b/gtk2_ardour/marker_time_axis_view.cc @@ -67,7 +67,7 @@ MarkerTimeAxisView::MarkerTimeAxisView(MarkerTimeAxis& tv) _samples_per_unit = _trackview.editor.get_current_zoom() ; _trackview.editor.ZoomChanged.connect (sigc::mem_fun(*this, &MarkerTimeAxisView::reset_samples_per_unit)); - MarkerView::CatchDeletion.connect (*this, ui_bind (&MarkerTimeAxisView::remove_marker_view, this, _1), gui_context()); + MarkerView::CatchDeletion.connect (*this, boost::bind (&MarkerTimeAxisView::remove_marker_view, this, _1), gui_context()); } /** diff --git a/gtk2_ardour/midi_list_editor.cc b/gtk2_ardour/midi_list_editor.cc index 53af17488c..66a8e8b6df 100644 --- a/gtk2_ardour/midi_list_editor.cc +++ b/gtk2_ardour/midi_list_editor.cc @@ -180,8 +180,8 @@ MidiListEditor::scroll_event (GdkEventScroll* ev) TreeViewColumn* col; int cellx; int celly; - int idelta; - double fdelta; + int idelta = 0; + double fdelta = 0; MidiModel::NoteDiffCommand::Property prop (MidiModel::NoteDiffCommand::NoteNumber); bool apply = false; bool was_selected = false; @@ -586,8 +586,8 @@ MidiListEditor::edited (const std::string& path, const std::string& text) double fval; int ival; bool apply = false; - int idelta; - double fdelta; + int idelta = 0; + double fdelta = 0; char* opname; switch (edit_column) { case 0: // start diff --git a/gtk2_ardour/midi_region_view.cc b/gtk2_ardour/midi_region_view.cc index c535d4b02b..154cd158dc 100644 --- a/gtk2_ardour/midi_region_view.cc +++ b/gtk2_ardour/midi_region_view.cc @@ -116,10 +116,10 @@ MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView & _note_group->raise_to_top(); PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys)); - Config->ParameterChanged.connect (*this, invalidator (*this), ui_bind (&MidiRegionView::parameter_changed, this, _1), gui_context()); + Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context()); connect_to_diskstream (); - SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), ui_bind (&MidiRegionView::selection_cleared, this, _1), gui_context ()); + SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ()); } MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv, @@ -155,7 +155,7 @@ MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView & connect_to_diskstream (); - SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), ui_bind (&MidiRegionView::selection_cleared, this, _1), gui_context ()); + SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ()); } void @@ -245,7 +245,7 @@ MidiRegionView::init (Gdk::Color const & basic_color, bool wfd) PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys)); CanvasNoteEvent::CanvasNoteEventDeleted.connect (note_delete_connection, MISSING_INVALIDATOR, - ui_bind (&MidiRegionView::maybe_remove_deleted_note_from_selection, this, _1), + boost::bind (&MidiRegionView::maybe_remove_deleted_note_from_selection, this, _1), gui_context()); if (wfd) { @@ -288,13 +288,13 @@ MidiRegionView::init (Gdk::Color const & basic_color, bool wfd) sigc::mem_fun(this, &MidiRegionView::midi_patch_settings_changed)); trackview.editor().SnapChanged.connect(snap_changed_connection, invalidator(*this), - ui_bind(&MidiRegionView::snap_changed, this), + boost::bind (&MidiRegionView::snap_changed, this), gui_context()); - Config->ParameterChanged.connect (*this, invalidator (*this), ui_bind (&MidiRegionView::parameter_changed, this, _1), gui_context()); + Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context()); connect_to_diskstream (); - SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), ui_bind (&MidiRegionView::selection_cleared, this, _1), gui_context ()); + SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ()); } const boost::shared_ptr @@ -308,7 +308,7 @@ MidiRegionView::connect_to_diskstream () { midi_view()->midi_track()->DataRecorded.connect( *this, invalidator(*this), - ui_bind(&MidiRegionView::data_recorded, this, _1), + boost::bind (&MidiRegionView::data_recorded, this, _1), gui_context()); } @@ -380,7 +380,7 @@ bool MidiRegionView::enter_notify (GdkEventCrossing* ev) { trackview.editor().MouseModeChanged.connect ( - _mouse_mode_connection, invalidator (*this), ui_bind (&MidiRegionView::mouse_mode_changed, this), gui_context () + _mouse_mode_connection, invalidator (*this), boost::bind (&MidiRegionView::mouse_mode_changed, this), gui_context () ); if (trackview.editor().current_mouse_mode() == MouseDraw && _mouse_state != AddDragging) { @@ -1396,7 +1396,7 @@ MidiRegionView::add_ghost (TimeAxisView& tv) ghost->set_duration (_region->length() / samples_per_unit); ghosts.push_back (ghost); - GhostRegion::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&RegionView::remove_ghost, this, _1), gui_context()); + GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&RegionView::remove_ghost, this, _1), gui_context()); return ghost; } @@ -2953,7 +2953,7 @@ MidiRegionView::nudge_notes (bool forward) framepos_t ref_point = source_beats_to_absolute_frames ((*(_selection.begin()))->note()->time()); framepos_t unused; - framepos_t distance; + framecnt_t distance; if (trackview.editor().snap_mode() == Editing::SnapOff) { diff --git a/gtk2_ardour/midi_selection.cc b/gtk2_ardour/midi_selection.cc index 93f62f11a3..bf9b88899b 100644 --- a/gtk2_ardour/midi_selection.cc +++ b/gtk2_ardour/midi_selection.cc @@ -24,7 +24,7 @@ MidiRegionSelection::MidiRegionSelection () { - RegionView::RegionViewGoingAway.connect (_death_connection, MISSING_INVALIDATOR, ui_bind (&MidiRegionSelection::remove_it, this, _1), gui_context()); + RegionView::RegionViewGoingAway.connect (_death_connection, MISSING_INVALIDATOR, boost::bind (&MidiRegionSelection::remove_it, this, _1), gui_context()); } /** Copy constructor. @@ -33,7 +33,7 @@ MidiRegionSelection::MidiRegionSelection () MidiRegionSelection::MidiRegionSelection (MidiRegionSelection const & other) : std::list (other) { - RegionView::RegionViewGoingAway.connect (_death_connection, MISSING_INVALIDATOR, ui_bind (&MidiRegionSelection::remove_it, this, _1), gui_context()); + RegionView::RegionViewGoingAway.connect (_death_connection, MISSING_INVALIDATOR, boost::bind (&MidiRegionSelection::remove_it, this, _1), gui_context()); } diff --git a/gtk2_ardour/midi_time_axis.cc b/gtk2_ardour/midi_time_axis.cc index 259f7c14d2..3bd67af9a0 100644 --- a/gtk2_ardour/midi_time_axis.cc +++ b/gtk2_ardour/midi_time_axis.cc @@ -63,7 +63,6 @@ #include "automation_time_axis.h" #include "canvas-note-event.h" #include "canvas_impl.h" -#include "crossfade_view.h" #include "editor.h" #include "enums.h" #include "ghostregion.h" @@ -160,7 +159,7 @@ MidiTimeAxisView::set_route (boost::shared_ptr rt) processors_changed (RouteProcessorChange ()); - _route->processors_changed.connect (*this, invalidator (*this), ui_bind (&MidiTimeAxisView::processors_changed, this, _1), gui_context()); + _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&MidiTimeAxisView::processors_changed, this, _1), gui_context()); if (is_track()) { _piano_roll_header->SetNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection)); @@ -314,7 +313,6 @@ MidiTimeAxisView::model_changed() for (std::list::const_iterator i = device_modes.begin(); i != device_modes.end(); ++i) { - cerr << "found custom device mode " << *i << " thread_id: " << pthread_self() << endl; _custom_device_mode_selector.append_text(*i); } diff --git a/gtk2_ardour/mixer_strip.cc b/gtk2_ardour/mixer_strip.cc index 72d69d5e43..16ac8780b8 100644 --- a/gtk2_ardour/mixer_strip.cc +++ b/gtk2_ardour/mixer_strip.cc @@ -112,7 +112,7 @@ MixerStrip::MixerStrip (Mixer_UI& mx, Session* sess, boost::shared_ptr rt , RouteUI (sess) , _mixer(mx) , _mixer_owned (in_mixer) - , processor_box (sess, sigc::mem_fun(*this, &MixerStrip::plugin_selector), mx.selection(), this, in_mixer) + , processor_box (sess, boost::bind (&MixerStrip::plugin_selector, this), mx.selection(), this, in_mixer) , gpm (sess, 250) , panners (sess) , button_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_HORIZONTAL)) @@ -355,7 +355,7 @@ MixerStrip::init () parameter_changed (X_("mixer-strip-visibility")); - Config->ParameterChanged.connect (_config_connection, MISSING_INVALIDATOR, ui_bind (&MixerStrip::parameter_changed, this, _1), gui_context()); + Config->ParameterChanged.connect (_config_connection, MISSING_INVALIDATOR, boost::bind (&MixerStrip::parameter_changed, this, _1), gui_context()); gpm.LevelMeterButtonPress.connect_same_thread (_level_meter_connection, boost::bind (&MixerStrip::level_meter_button_press, this, _1)); } @@ -509,8 +509,8 @@ MixerStrip::set_route (boost::shared_ptr rt) audio_track()->DiskstreamChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::diskstream_changed, this), gui_context()); } - _route->comment_changed.connect (route_connections, invalidator (*this), ui_bind (&MixerStrip::comment_changed, this, _1), gui_context()); - _route->PropertyChanged.connect (route_connections, invalidator (*this), ui_bind (&MixerStrip::property_changed, this, _1), gui_context()); + _route->comment_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::comment_changed, this, _1), gui_context()); + _route->PropertyChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::property_changed, this, _1), gui_context()); set_stuff_from_route (); diff --git a/gtk2_ardour/mixer_ui.cc b/gtk2_ardour/mixer_ui.cc index 053f8eb595..3779f0c042 100644 --- a/gtk2_ardour/mixer_ui.cc +++ b/gtk2_ardour/mixer_ui.cc @@ -95,7 +95,7 @@ Mixer_UI::Mixer_UI () strip_redisplay_does_not_sync_order_keys = false; ignore_sync = false; - Route::SyncOrderKeys.connect (*this, invalidator (*this), ui_bind (&Mixer_UI::sync_order_keys, this, _1), gui_context()); + Route::SyncOrderKeys.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::sync_order_keys, this, _1), gui_context()); scroller_base.set_flags (Gtk::CAN_FOCUS); scroller_base.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK); @@ -239,7 +239,7 @@ Mixer_UI::Mixer_UI () _in_group_rebuild_or_clear = false; - MixerStrip::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&Mixer_UI::remove_strip, this, _1), gui_context()); + MixerStrip::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::remove_strip, this, _1), gui_context()); MonitorSection::setup_knob_images (); @@ -340,7 +340,7 @@ Mixer_UI::add_strip (RouteList& routes) _monitor_section->set_session (_session); _monitor_section->tearoff().show_all (); - route->DropReferences.connect (*this, invalidator(*this), ui_bind (&Mixer_UI::monitor_section_going_away, this), gui_context()); + route->DropReferences.connect (*this, invalidator(*this), boost::bind (&Mixer_UI::monitor_section_going_away, this), gui_context()); /* no regular strip shown for control out */ @@ -368,7 +368,7 @@ Mixer_UI::add_strip (RouteList& routes) route->set_order_key (N_("signal"), track_model->children().size()-1); } - route->PropertyChanged.connect (*this, invalidator (*this), ui_bind (&Mixer_UI::strip_property_changed, this, _1, strip), gui_context()); + route->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::strip_property_changed, this, _1, strip), gui_context()); strip->WidthChanged.connect (sigc::mem_fun(*this, &Mixer_UI::strip_width_changed)); strip->signal_button_release_event().connect (sigc::bind (sigc::mem_fun(*this, &Mixer_UI::strip_button_release_event), strip)); @@ -583,15 +583,15 @@ Mixer_UI::set_session (Session* sess) initial_track_display (); - _session->RouteAdded.connect (_session_connections, invalidator (*this), ui_bind (&Mixer_UI::add_strip, this, _1), gui_context()); - _session->route_group_added.connect (_session_connections, invalidator (*this), ui_bind (&Mixer_UI::add_route_group, this, _1), gui_context()); + _session->RouteAdded.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::add_strip, this, _1), gui_context()); + _session->route_group_added.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::add_route_group, this, _1), gui_context()); _session->route_group_removed.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::route_groups_changed, this), gui_context()); _session->route_groups_reordered.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::route_groups_changed, this), gui_context()); - _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), ui_bind (&Mixer_UI::parameter_changed, this, _1), gui_context()); + _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::parameter_changed, this, _1), gui_context()); _session->DirtyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::update_title, this), gui_context()); - _session->StateSaved.connect (_session_connections, invalidator (*this), ui_bind (&Mixer_UI::update_title, this), gui_context()); + _session->StateSaved.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::update_title, this), gui_context()); - Config->ParameterChanged.connect (*this, invalidator (*this), ui_bind (&Mixer_UI::parameter_changed, this, _1), gui_context ()); + Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::parameter_changed, this, _1), gui_context ()); route_groups_changed (); @@ -1395,7 +1395,7 @@ Mixer_UI::add_route_group (RouteGroup* group) focus = true; } - group->PropertyChanged.connect (*this, invalidator (*this), ui_bind (&Mixer_UI::route_group_property_changed, this, group, _1), gui_context()); + group->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::route_group_property_changed, this, group, _1), gui_context()); if (focus) { TreeViewColumn* col = group_display.get_column (0); diff --git a/gtk2_ardour/mnemonic-us.bindings.in b/gtk2_ardour/mnemonic-us.bindings.in index 489bcf1665..aff1435781 100644 --- a/gtk2_ardour/mnemonic-us.bindings.in +++ b/gtk2_ardour/mnemonic-us.bindings.in @@ -164,6 +164,7 @@ This mode provides many different operations on both regions and control points, @rop|Region/naturalize-region|<@SECONDARY@>o|move to original position @trans|Editor/set-playhead|p|set playhead position @select|Editor/select-all-before-playhead|<@PRIMARY@>p|all before playhead +@wvis|Common/ToggleRCOptionsEditor|<@PRIMARY@><@TERTIARY@>p|toggle preferences dialog @wvis|Common/toggle-audio-connection-manager|<@WINDOW@>p|toggle global audio patchbay @wvis|Common/toggle-midi-connection-manager|<@WINDOW@><@TERTIARY@>p|toggle global midi patchbay @midi|MIDI/panic|<@PRIMARY@><@SECONDARY@>p|MIDI panic (stop all notes etc) diff --git a/gtk2_ardour/monitor_section.cc b/gtk2_ardour/monitor_section.cc index 3b78e34612..82519cc85e 100644 --- a/gtk2_ardour/monitor_section.cc +++ b/gtk2_ardour/monitor_section.cc @@ -36,6 +36,8 @@ MonitorSection::MonitorSection (Session* s) : AxisView (s) , RouteUI (s) , _tearoff (0) + , channel_table_viewport (*channel_table_scroller.get_hadjustment(), + *channel_table_scroller.get_vadjustment ()) , gain_control (0) , dim_control (0) , solo_boost_control (0) @@ -257,7 +259,8 @@ MonitorSection::MonitorSection (Session* s) channel_table_scroller.set_size_request (-1, 150); channel_table_scroller.set_shadow_type (Gtk::SHADOW_NONE); channel_table_scroller.show (); - + channel_table_scroller.add (channel_table_viewport); + channel_size_group = SizeGroup::create (SIZE_GROUP_HORIZONTAL); channel_size_group->add_widget (channel_table_header); channel_size_group->add_widget (channel_table); @@ -318,7 +321,7 @@ MonitorSection::MonitorSection (Session* s) /* catch changes that affect us */ - Config->ParameterChanged.connect (config_connection, invalidator (*this), ui_bind (&MonitorSection::parameter_changed, this, _1), gui_context()); + Config->ParameterChanged.connect (config_connection, invalidator (*this), boost::bind (&MonitorSection::parameter_changed, this, _1), gui_context()); } MonitorSection::~MonitorSection () @@ -357,23 +360,24 @@ MonitorSection::set_session (Session* s) if (channel_table_scroller.get_parent()) { /* scroller is packed, so remove it */ channel_table_packer.remove (channel_table_scroller); - /* remove the table_hpacker from the scroller */ - channel_table_scroller.remove (); } - if (table_hpacker.get_parent ()) { + if (table_hpacker.get_parent () == &channel_table_packer) { /* this occurs when the table hpacker is directly packed, so remove it. */ channel_table_packer.remove (table_hpacker); + } else if (table_hpacker.get_parent()) { + channel_table_viewport.remove (); } if (_monitor->output_streams().n_audio() > 7) { /* put the table into a scrolled window, and then put * that into the channel vpacker, after the table header */ - channel_table_scroller.add (table_hpacker); + channel_table_viewport.add (table_hpacker); channel_table_packer.pack_start (channel_table_scroller, true, true); + channel_table_viewport.show (); channel_table_scroller.show (); } else { diff --git a/gtk2_ardour/monitor_section.h b/gtk2_ardour/monitor_section.h index 185720856b..f7848a3f01 100644 --- a/gtk2_ardour/monitor_section.h +++ b/gtk2_ardour/monitor_section.h @@ -59,6 +59,7 @@ class MonitorSection : public RouteUI Gtk::Table channel_table; Gtk::Table channel_table_header; Gtk::ScrolledWindow channel_table_scroller; + Gtk::Viewport channel_table_viewport; Glib::RefPtr channel_size_group; struct ChannelButtonSet { diff --git a/gtk2_ardour/mouse_cursors.cc b/gtk2_ardour/mouse_cursors.cc index 3cbe8342a7..fa3ce331d6 100644 --- a/gtk2_ardour/mouse_cursors.cc +++ b/gtk2_ardour/mouse_cursors.cc @@ -41,14 +41,14 @@ MouseCursors::MouseCursors () Color ffg ("#000000"); { - RefPtr source = Bitmap::create (fader_cursor_bits, fader_cursor_width, fader_cursor_height); - RefPtr mask = Bitmap::create (fader_cursor_mask_bits, fader_cursor_width, fader_cursor_height); + RefPtr source = Bitmap::create ((char const *) fader_cursor_bits, fader_cursor_width, fader_cursor_height); + RefPtr mask = Bitmap::create ((char const *) fader_cursor_mask_bits, fader_cursor_width, fader_cursor_height); fader = new Cursor (source, mask, ffg, fbg, fader_cursor_x_hot, fader_cursor_y_hot); } { - RefPtr source = Bitmap::create (speaker_cursor_bits, speaker_cursor_width, speaker_cursor_height); - RefPtr mask = Bitmap::create (speaker_cursor_mask_bits, speaker_cursor_width, speaker_cursor_height); + RefPtr source = Bitmap::create ((char const *) speaker_cursor_bits, speaker_cursor_width, speaker_cursor_height); + RefPtr mask = Bitmap::create ((char const *) speaker_cursor_mask_bits, speaker_cursor_width, speaker_cursor_height); speaker = new Cursor (source, mask, ffg, fbg, speaker_cursor_x_hot, speaker_cursor_y_hot); } diff --git a/gtk2_ardour/option_editor.cc b/gtk2_ardour/option_editor.cc index 2142d23faa..3e1fa73ca3 100644 --- a/gtk2_ardour/option_editor.cc +++ b/gtk2_ardour/option_editor.cc @@ -291,7 +291,7 @@ OptionEditor::OptionEditor (Configuration* c, std::string const & t) show_all_children(); /* Watch out for changes to parameters */ - _config->ParameterChanged.connect (config_connection, invalidator (*this), ui_bind (&OptionEditor::parameter_changed, this, _1), gui_context()); + _config->ParameterChanged.connect (config_connection, invalidator (*this), boost::bind (&OptionEditor::parameter_changed, this, _1), gui_context()); } OptionEditor::~OptionEditor () diff --git a/gtk2_ardour/playlist_selector.cc b/gtk2_ardour/playlist_selector.cc index f81e199d62..31710deb71 100644 --- a/gtk2_ardour/playlist_selector.cc +++ b/gtk2_ardour/playlist_selector.cc @@ -145,7 +145,8 @@ PlaylistSelector::show_for (RouteUI* ruix) } TreeModel::Row row; - TreeModel::Row* selected_row = 0; + TreeModel::Row selected_row; + bool have_selected = false; TreePath this_path; if (tr == this_track) { @@ -173,12 +174,13 @@ PlaylistSelector::show_for (RouteUI* ruix) child_row[columns.playlist] = *p; if (*p == this_track->playlist()) { - selected_row = &child_row; + selected_row = child_row; + have_selected = true; } } - if (selected_row != 0) { - tree.get_selection()->select (*selected_row); + if (have_selected) { + tree.get_selection()->select (selected_row); } } @@ -187,7 +189,8 @@ PlaylistSelector::show_for (RouteUI* ruix) _session->playlists->unassigned (unassigned); TreeModel::Row row; - TreeModel::Row* selected_row = 0; + TreeModel::Row selected_row; + bool have_selected = false; TreePath this_path; row = *(model->append (others.children())); @@ -203,11 +206,12 @@ PlaylistSelector::show_for (RouteUI* ruix) child_row[columns.playlist] = *p; if (*p == this_track->playlist()) { - selected_row = &child_row; + selected_row = child_row; + have_selected = true; } - if (selected_row != 0) { - tree.get_selection()->select (*selected_row); + if (have_selected) { + tree.get_selection()->select (selected_row); } } diff --git a/gtk2_ardour/plugin_eq_gui.cc b/gtk2_ardour/plugin_eq_gui.cc index bab2119f69..631fb7be71 100644 --- a/gtk2_ardour/plugin_eq_gui.cc +++ b/gtk2_ardour/plugin_eq_gui.cc @@ -141,7 +141,7 @@ PluginEqGui::start_listening () _plugin->activate(); set_buffer_size(4096, 16384); // Connect the realtime signal collection callback - _plugin_insert->AnalysisDataGathered.connect (analysis_connection, invalidator (*this), ui_bind (&PluginEqGui::signal_collect_callback, this, _1, _2), gui_context()); + _plugin_insert->AnalysisDataGathered.connect (analysis_connection, invalidator (*this), boost::bind (&PluginEqGui::signal_collect_callback, this, _1, _2), gui_context()); } void diff --git a/gtk2_ardour/plugin_selector.cc b/gtk2_ardour/plugin_selector.cc index b1698b59ae..75040eca06 100644 --- a/gtk2_ardour/plugin_selector.cc +++ b/gtk2_ardour/plugin_selector.cc @@ -181,7 +181,7 @@ PluginSelector::PluginSelector (PluginManager& mgr) //plugin_display.set_name("PluginSelectorList"); added_list.set_name("PluginSelectorList"); - plugin_display.signal_button_press_event().connect_notify (sigc::mem_fun(*this, &PluginSelector::row_clicked)); + plugin_display.signal_row_activated().connect_notify (sigc::mem_fun(*this, &PluginSelector::row_activated)); plugin_display.get_selection()->signal_changed().connect (sigc::mem_fun(*this, &PluginSelector::display_selection_changed)); plugin_display.grab_focus(); @@ -199,11 +199,9 @@ PluginSelector::~PluginSelector () } void -PluginSelector::row_clicked(GdkEventButton* event) +PluginSelector::row_activated(Gtk::TreeModel::Path path, Gtk::TreeViewColumn* col) { - if (event->type == GDK_2BUTTON_PRESS) { - btn_add_clicked(); - } + btn_add_clicked(); } bool diff --git a/gtk2_ardour/plugin_selector.h b/gtk2_ardour/plugin_selector.h index 7eef190b5f..034622b386 100644 --- a/gtk2_ardour/plugin_selector.h +++ b/gtk2_ardour/plugin_selector.h @@ -118,7 +118,7 @@ class PluginSelector : public ArdourDialog Gtk::Menu* _plugin_menu; ARDOUR::PluginManager& manager; - void row_clicked(GdkEventButton *); + void row_activated(Gtk::TreeModel::Path path, Gtk::TreeViewColumn* col); void btn_add_clicked(); void btn_remove_clicked(); void btn_update_clicked(); diff --git a/gtk2_ardour/plugin_ui.cc b/gtk2_ardour/plugin_ui.cc index b4a412aa3f..9357fc7136 100644 --- a/gtk2_ardour/plugin_ui.cc +++ b/gtk2_ardour/plugin_ui.cc @@ -452,6 +452,7 @@ PlugUIBase::PlugUIBase (boost::shared_ptr pi) , save_button (_("Save")) , delete_button (_("Delete")) , bypass_button (ArdourButton::led_default_elements) + , description_expander (_("Description")) , plugin_analysis_expander (_("Plugin analysis")) , latency_gui (0) , latency_dialog (0) @@ -499,6 +500,9 @@ PlugUIBase::PlugUIBase (boost::shared_ptr pi) ARDOUR_UI::instance()->set_tip (focus_button, string_compose (_("Click to allow the plugin to receive keyboard events that %1 would normally use as a shortcut"), PROGRAM_NAME)); ARDOUR_UI::instance()->set_tip (bypass_button, _("Click to enable/disable this plugin")); + description_expander.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_description)); + description_expander.set_expanded(false); + plugin_analysis_expander.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_plugin_analysis)); plugin_analysis_expander.set_expanded(false); @@ -662,11 +666,34 @@ PlugUIBase::focus_toggled (GdkEventButton*) return true; } +void +PlugUIBase::toggle_description() +{ + if (description_expander.get_expanded() && + !description_expander.get_child()) { + const std::string text = plugin->get_docs(); + if (text.empty()) { + return; + } + + Gtk::Label* label = manage(new Gtk::Label(text)); + label->set_line_wrap(true); + label->set_line_wrap_mode(Pango::WRAP_WORD); + description_expander.add(*label); + description_expander.show_all(); + } + + if (!description_expander.get_expanded()) { + description_expander.remove(); + } +} + + void PlugUIBase::toggle_plugin_analysis() { if (plugin_analysis_expander.get_expanded() && - !plugin_analysis_expander.get_child()) { + !plugin_analysis_expander.get_child()) { // Create the GUI if (eqgui == 0) { eqgui = new PluginEqGui (insert); @@ -684,7 +711,6 @@ PlugUIBase::toggle_plugin_analysis() } if (!plugin_analysis_expander.get_expanded()) { - // Hide & remove from expander eqgui->hide (); diff --git a/gtk2_ardour/plugin_ui.h b/gtk2_ardour/plugin_ui.h index 2f98a28f6d..a65fd1a223 100644 --- a/gtk2_ardour/plugin_ui.h +++ b/gtk2_ardour/plugin_ui.h @@ -125,6 +125,8 @@ class PlugUIBase : public virtual sigc::trackable, public PBD::ScopedConnectionL ArdourButton bypass_button; /** a button to acquire keyboard focus */ Gtk::EventBox focus_button; + /** an expander containing the plugin description */ + Gtk::Expander description_expander; /** an expander containing the plugin analysis graph */ Gtk::Expander plugin_analysis_expander; /** a label indicating the plugin latency */ @@ -150,6 +152,7 @@ class PlugUIBase : public virtual sigc::trackable, public PBD::ScopedConnectionL void delete_plugin_setting (); bool focus_toggled(GdkEventButton*); bool bypass_button_release(GdkEventButton*); + void toggle_description (); void toggle_plugin_analysis (); void processor_active_changed (boost::weak_ptr p); void plugin_going_away (); diff --git a/gtk2_ardour/point_selection.h b/gtk2_ardour/point_selection.h index 85acc90ec8..5ed5ae1573 100644 --- a/gtk2_ardour/point_selection.h +++ b/gtk2_ardour/point_selection.h @@ -21,11 +21,10 @@ #define __ardour_gtk_point_selection_h__ #include -#include -#include "automation_range.h" +class ControlPoint; -struct PointSelection : public std::list +struct PointSelection : public std::list { }; diff --git a/gtk2_ardour/port_group.cc b/gtk2_ardour/port_group.cc index a3a7ca8e77..5b5cdc0f52 100644 --- a/gtk2_ardour/port_group.cc +++ b/gtk2_ardour/port_group.cc @@ -123,7 +123,7 @@ PortGroup::add_bundle_internal (boost::shared_ptr b, boost::shared_ptrChanged.connect (br->changed_connection, invalidator (*this), ui_bind (&PortGroup::bundle_changed, this, _1), gui_context()); + b->Changed.connect (br->changed_connection, invalidator (*this), boost::bind (&PortGroup::bundle_changed, this, _1), gui_context()); _bundles.push_back (br); Changed (); @@ -695,7 +695,7 @@ PortGroupList::add_group (boost::shared_ptr g) _groups.push_back (g); g->Changed.connect (_changed_connections, invalidator (*this), boost::bind (&PortGroupList::emit_changed, this), gui_context()); - g->BundleChanged.connect (_bundle_changed_connections, invalidator (*this), ui_bind (&PortGroupList::emit_bundle_changed, this, _1), gui_context()); + g->BundleChanged.connect (_bundle_changed_connections, invalidator (*this), boost::bind (&PortGroupList::emit_bundle_changed, this, _1), gui_context()); emit_changed (); } diff --git a/gtk2_ardour/port_matrix.cc b/gtk2_ardour/port_matrix.cc index 8bdc1c4cc0..d942c99c89 100644 --- a/gtk2_ardour/port_matrix.cc +++ b/gtk2_ardour/port_matrix.cc @@ -179,7 +179,7 @@ PortMatrix::reconnect_to_routes () boost::shared_ptr routes = _session->get_routes (); for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) { - (*i)->processors_changed.connect (_route_connections, invalidator (*this), ui_bind (&PortMatrix::route_processors_changed, this, _1), gui_context()); + (*i)->processors_changed.connect (_route_connections, invalidator (*this), boost::bind (&PortMatrix::route_processors_changed, this, _1), gui_context()); } } diff --git a/gtk2_ardour/processor_box.cc b/gtk2_ardour/processor_box.cc index 483bed6197..e2d33b9891 100644 --- a/gtk2_ardour/processor_box.cc +++ b/gtk2_ardour/processor_box.cc @@ -119,7 +119,7 @@ ProcessorEntry::ProcessorEntry (ProcessorBox* parent, boost::shared_ptrActiveChanged.connect (active_connection, invalidator (*this), boost::bind (&ProcessorEntry::processor_active_changed, this), gui_context()); - _processor->PropertyChanged.connect (name_connection, invalidator (*this), ui_bind (&ProcessorEntry::processor_property_changed, this, _1), gui_context()); + _processor->PropertyChanged.connect (name_connection, invalidator (*this), boost::bind (&ProcessorEntry::processor_property_changed, this, _1), gui_context()); set p = _processor->what_can_be_automated (); for (set::iterator i = p.begin(); i != p.end(); ++i) { @@ -605,7 +605,7 @@ PluginInsertProcessorEntry::PluginInsertProcessorEntry (ProcessorBox* b, boost:: , _plugin_insert (p) { p->SplittingChanged.connect ( - _splitting_connection, invalidator (*this), ui_bind (&PluginInsertProcessorEntry::plugin_insert_splitting_changed, this), gui_context() + _splitting_connection, invalidator (*this), boost::bind (&PluginInsertProcessorEntry::plugin_insert_splitting_changed, this), gui_context() ); _splitting_icon.set_size_request (-1, 12); @@ -723,7 +723,7 @@ ProcessorBox::ProcessorBox (ARDOUR::Session* sess, boost::functionDeliveryChanged.connect ( - _mixer_strip_connections, invalidator (*this), ui_bind (&ProcessorBox::mixer_strip_delivery_changed, this, _1), gui_context () + _mixer_strip_connections, invalidator (*this), boost::bind (&ProcessorBox::mixer_strip_delivery_changed, this, _1), gui_context () ); } @@ -757,7 +757,7 @@ ProcessorBox::set_route (boost::shared_ptr r) _route = r; _route->processors_changed.connect ( - _route_connections, invalidator (*this), ui_bind (&ProcessorBox::route_processors_changed, this, _1), gui_context() + _route_connections, invalidator (*this), boost::bind (&ProcessorBox::route_processors_changed, this, _1), gui_context() ); _route->DropReferences.connect ( @@ -765,7 +765,7 @@ ProcessorBox::set_route (boost::shared_ptr r) ); _route->PropertyChanged.connect ( - _route_connections, invalidator (*this), ui_bind (&ProcessorBox::route_property_changed, this, _1), gui_context() + _route_connections, invalidator (*this), boost::bind (&ProcessorBox::route_property_changed, this, _1), gui_context() ); redisplay_processors (); diff --git a/gtk2_ardour/public_editor.h b/gtk2_ardour/public_editor.h index 5a0a6138e1..ee4c7162cb 100644 --- a/gtk2_ardour/public_editor.h +++ b/gtk2_ardour/public_editor.h @@ -73,7 +73,6 @@ class Selection; class AutomationLine; class ControlPoint; class SelectionRect; -class CrossfadeView; class RouteTimeAxisView; class RegionView; class AudioRegionView; @@ -315,7 +314,6 @@ 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_crossfade_view_event (GdkEvent* event, ArdourCanvas::Item*, CrossfadeView*) = 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/rc_option_editor.cc b/gtk2_ardour/rc_option_editor.cc index da551a59e7..8bdc9f2610 100644 --- a/gtk2_ardour/rc_option_editor.cc +++ b/gtk2_ardour/rc_option_editor.cc @@ -903,7 +903,7 @@ RCOptionEditor::RCOptionEditor () sigc::mem_fun (*_rc_config, &RCConfiguration::set_periodic_safety_backups) )); - add_option (_("Misc"), new OptionEditorHeading (_("Misc"))); + add_option (_("Misc"), new OptionEditorHeading (_("Session Management"))); add_option (_("Misc"), new BoolOption ( @@ -941,6 +941,17 @@ RCOptionEditor::RCOptionEditor () sigc::mem_fun (*_rc_config, &RCConfiguration::set_click_gain) )); + add_option (_("Misc"), new OptionEditorHeading (_("Automation"))); + + add_option (_("Misc"), + new SpinOption ( + "automation-thinning-factor", + _("Thinning factor (larger value => less data)"), + sigc::mem_fun (*_rc_config, &RCConfiguration::get_automation_thinning_factor), + sigc::mem_fun (*_rc_config, &RCConfiguration::set_automation_thinning_factor), + 0, 1000, 1, 20 + )); + /* TRANSPORT */ add_option (_("Transport"), diff --git a/gtk2_ardour/rc_option_editor.h b/gtk2_ardour/rc_option_editor.h index 7a40449fb3..a5711919a9 100644 --- a/gtk2_ardour/rc_option_editor.h +++ b/gtk2_ardour/rc_option_editor.h @@ -36,7 +36,6 @@ public: private: void parameter_changed (std::string const &); - ARDOUR::RCConfiguration* _rc_config; BoolOption* _solo_control_is_listen_control; ComboOption* _listen_position; diff --git a/gtk2_ardour/region_editor.cc b/gtk2_ardour/region_editor.cc index 0662f6f1a8..622223047a 100755 --- a/gtk2_ardour/region_editor.cc +++ b/gtk2_ardour/region_editor.cc @@ -189,7 +189,7 @@ RegionEditor::RegionEditor (Session* s, boost::shared_ptr r) bounds_changed (change); - _region->PropertyChanged.connect (state_connection, invalidator (*this), ui_bind (&RegionEditor::region_changed, this, _1), gui_context()); + _region->PropertyChanged.connect (state_connection, invalidator (*this), boost::bind (&RegionEditor::region_changed, this, _1), gui_context()); spin_arrow_grab = false; @@ -266,7 +266,7 @@ RegionEditor::connect_editor_events () audition_button.signal_toggled().connect (sigc::mem_fun(*this, &RegionEditor::audition_button_toggled)); - _session->AuditionActive.connect (audition_connection, invalidator (*this), ui_bind (&RegionEditor::audition_state_changed, this, _1), gui_context()); + _session->AuditionActive.connect (audition_connection, invalidator (*this), boost::bind (&RegionEditor::audition_state_changed, this, _1), gui_context()); } void diff --git a/gtk2_ardour/region_gain_line.cc b/gtk2_ardour/region_gain_line.cc index bc360f25d6..39e1b7fda3 100644 --- a/gtk2_ardour/region_gain_line.cc +++ b/gtk2_ardour/region_gain_line.cc @@ -24,6 +24,7 @@ #include "ardour/audioregion.h" #include "ardour/session.h" +#include "control_point.h" #include "region_gain_line.h" #include "audio_region_view.h" #include "utils.h" @@ -69,10 +70,6 @@ AudioRegionGainLine::start_drag_single (ControlPoint* cp, double x, float fracti void AudioRegionGainLine::remove_point (ControlPoint& cp) { - ModelRepresentation mr; - - model_representation (cp, mr); - trackview.editor().session()->begin_reversible_command (_("remove control point")); XMLNode &before = alist->get_state(); @@ -82,7 +79,7 @@ AudioRegionGainLine::remove_point (ControlPoint& cp) trackview.session()->add_command(new StatefulDiffCommand (rv.audio_region())); } - alist->erase (mr.start, mr.end); + alist->erase (cp.model()); trackview.editor().session()->add_command (new MementoCommand(*alist.get(), &before, &alist->get_state())); trackview.editor().session()->commit_reversible_command (); diff --git a/gtk2_ardour/region_selection.cc b/gtk2_ardour/region_selection.cc index 127a0ffe8e..57ab59fd68 100644 --- a/gtk2_ardour/region_selection.cc +++ b/gtk2_ardour/region_selection.cc @@ -33,7 +33,7 @@ using namespace PBD; */ RegionSelection::RegionSelection () { - RegionView::RegionViewGoingAway.connect (death_connection, MISSING_INVALIDATOR, ui_bind (&RegionSelection::remove_it, this, _1), gui_context()); + RegionView::RegionViewGoingAway.connect (death_connection, MISSING_INVALIDATOR, boost::bind (&RegionSelection::remove_it, this, _1), gui_context()); } /** Copy constructor. @@ -42,7 +42,7 @@ RegionSelection::RegionSelection () RegionSelection::RegionSelection (const RegionSelection& other) : std::list() { - RegionView::RegionViewGoingAway.connect (death_connection, MISSING_INVALIDATOR, ui_bind (&RegionSelection::remove_it, this, _1), gui_context()); + RegionView::RegionViewGoingAway.connect (death_connection, MISSING_INVALIDATOR, boost::bind (&RegionSelection::remove_it, this, _1), gui_context()); for (RegionSelection::const_iterator i = other.begin(); i != other.end(); ++i) { add (*i); diff --git a/gtk2_ardour/region_view.cc b/gtk2_ardour/region_view.cc index a0fd276d12..1c9b8fe67b 100644 --- a/gtk2_ardour/region_view.cc +++ b/gtk2_ardour/region_view.cc @@ -86,7 +86,7 @@ RegionView::RegionView (ArdourCanvas::Group* parent, , _region_relative_time_converter(r->session().tempo_map(), r->position()) , _source_relative_time_converter(r->session().tempo_map(), r->position() - r->start()) { - GhostRegion::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&RegionView::remove_ghost, this, _1), gui_context()); + GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&RegionView::remove_ghost, this, _1), gui_context()); } RegionView::RegionView (const RegionView& other) @@ -103,7 +103,7 @@ RegionView::RegionView (const RegionView& other) valid = false; _pixel_width = other._pixel_width; - GhostRegion::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&RegionView::remove_ghost, this, _1), gui_context()); + GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&RegionView::remove_ghost, this, _1), gui_context()); } RegionView::RegionView (const RegionView& other, boost::shared_ptr other_region) @@ -124,7 +124,7 @@ RegionView::RegionView (const RegionView& other, boost::shared_ptr other valid = false; _pixel_width = other._pixel_width; - GhostRegion::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&RegionView::remove_ghost, this, _1), gui_context()); + GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&RegionView::remove_ghost, this, _1), gui_context()); } RegionView::RegionView (ArdourCanvas::Group* parent, @@ -195,7 +195,7 @@ RegionView::init (Gdk::Color const & basic_color, bool wfd) set_height (trackview.current_height()); - _region->PropertyChanged.connect (*this, invalidator (*this), ui_bind (&RegionView::region_changed, this, _1), gui_context()); + _region->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&RegionView::region_changed, this, _1), gui_context()); group->signal_event().connect (sigc::bind (sigc::mem_fun (PublicEditor::instance(), &PublicEditor::canvas_region_view_event), group, this)); diff --git a/gtk2_ardour/return_ui.cc b/gtk2_ardour/return_ui.cc index 6edd57a286..eabb164785 100644 --- a/gtk2_ardour/return_ui.cc +++ b/gtk2_ardour/return_ui.cc @@ -59,7 +59,7 @@ ReturnUI::ReturnUI (Gtk::Window* parent, boost::shared_ptr r, Session* s show_all (); _return->set_metering (true); - _return->input()->changed.connect (input_change_connection, invalidator (*this), ui_bind (&ReturnUI::ins_changed, this, _1, _2), gui_context()); + _return->input()->changed.connect (input_change_connection, invalidator (*this), boost::bind (&ReturnUI::ins_changed, this, _1, _2), gui_context()); _gpm.setup_meters (); _gpm.set_fader_name ("ReturnUIFrame"); diff --git a/gtk2_ardour/rhythm_ferret.cc b/gtk2_ardour/rhythm_ferret.cc index 7d7250d055..bd9dd42f87 100644 --- a/gtk2_ardour/rhythm_ferret.cc +++ b/gtk2_ardour/rhythm_ferret.cc @@ -216,7 +216,7 @@ RhythmFerret::run_analysis () } int -RhythmFerret::run_percussion_onset_analysis (boost::shared_ptr readable, framepos_t /*offset*/, AnalysisFeatureList& results) +RhythmFerret::run_percussion_onset_analysis (boost::shared_ptr readable, frameoffset_t /*offset*/, AnalysisFeatureList& results) { TransientDetector t (_session->frame_rate()); @@ -263,7 +263,7 @@ RhythmFerret::get_note_onset_function () } int -RhythmFerret::run_note_onset_analysis (boost::shared_ptr readable, framepos_t /*offset*/, AnalysisFeatureList& results) +RhythmFerret::run_note_onset_analysis (boost::shared_ptr readable, frameoffset_t /*offset*/, AnalysisFeatureList& results) { try { OnsetDetector t (_session->frame_rate()); diff --git a/gtk2_ardour/route_params_ui.cc b/gtk2_ardour/route_params_ui.cc index 8f31e7e95e..e10fcb4139 100644 --- a/gtk2_ardour/route_params_ui.cc +++ b/gtk2_ardour/route_params_ui.cc @@ -180,7 +180,7 @@ RouteParams_UI::add_routes (RouteList& routes) //route_select_list.rows().back().select (); - route->PropertyChanged.connect (*this, invalidator (*this), ui_bind (&RouteParams_UI::route_property_changed, this, _1, boost::weak_ptr(route)), gui_context()); + route->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&RouteParams_UI::route_property_changed, this, _1, boost::weak_ptr(route)), gui_context()); route->DropReferences.connect (*this, invalidator (*this), boost::bind (&RouteParams_UI::route_removed, this, boost::weak_ptr(route)), gui_context()); } } @@ -402,7 +402,7 @@ RouteParams_UI::set_session (Session *sess) if (_session) { boost::shared_ptr r = _session->get_routes(); add_routes (*r); - _session->RouteAdded.connect (_session_connections, invalidator (*this), ui_bind (&RouteParams_UI::add_routes, this, _1), gui_context()); + _session->RouteAdded.connect (_session_connections, invalidator (*this), boost::bind (&RouteParams_UI::add_routes, this, _1), gui_context()); start_updating (); } else { stop_updating (); @@ -461,7 +461,7 @@ RouteParams_UI::route_selected() setup_processor_boxes(); setup_latency_frame (); - route->processors_changed.connect (_route_processors_connection, invalidator (*this), ui_bind (&RouteParams_UI::processors_changed, this, _1), gui_context()); + route->processors_changed.connect (_route_processors_connection, invalidator (*this), boost::bind (&RouteParams_UI::processors_changed, this, _1), gui_context()); track_input_label.set_text (_route->name()); diff --git a/gtk2_ardour/route_params_ui.h b/gtk2_ardour/route_params_ui.h index 085949539b..4c8adfd9c6 100644 --- a/gtk2_ardour/route_params_ui.h +++ b/gtk2_ardour/route_params_ui.h @@ -63,7 +63,7 @@ class RouteParams_UI : public ArdourWindow, public PBD::ScopedConnectionList void set_session (ARDOUR::Session*); void session_going_away (); - PluginSelector* plugin_selector() { return _plugin_selector; } + PluginSelector* plugin_selector() { return _plugin_selector; } private: Gtk::HBox global_hpacker; diff --git a/gtk2_ardour/route_processor_selection.cc b/gtk2_ardour/route_processor_selection.cc index 1b4c0c538b..1810ba674b 100644 --- a/gtk2_ardour/route_processor_selection.cc +++ b/gtk2_ardour/route_processor_selection.cc @@ -111,7 +111,7 @@ RouteProcessorSelection::add (RouteUI* r) MixerStrip* ms = dynamic_cast (r); if (ms) { - ms->CatchDeletion.connect (*this, invalidator (*this), ui_bind (&RouteProcessorSelection::remove, this, _1), gui_context()); + ms->CatchDeletion.connect (*this, invalidator (*this), boost::bind (&RouteProcessorSelection::remove, this, _1), gui_context()); } if (!_no_route_change_signal) { diff --git a/gtk2_ardour/route_time_axis.cc b/gtk2_ardour/route_time_axis.cc index 13f169b96e..ef2cba7f9b 100644 --- a/gtk2_ardour/route_time_axis.cc +++ b/gtk2_ardour/route_time_axis.cc @@ -68,7 +68,6 @@ #include "route_time_axis.h" #include "automation_time_axis.h" #include "canvas_impl.h" -#include "crossfade_view.h" #include "enums.h" #include "gui_thread.h" #include "keyboard.h" @@ -194,8 +193,8 @@ RouteTimeAxisView::set_route (boost::shared_ptr rt) controls_hbox.pack_start(gm.get_level_meter(), false, false); _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context()); - _route->input()->changed.connect (*this, invalidator (*this), ui_bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context()); - _route->output()->changed.connect (*this, invalidator (*this), ui_bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context()); + _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context()); + _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context()); controls_table.attach (*mute_button, 6, 7, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0); @@ -227,8 +226,8 @@ RouteTimeAxisView::set_route (boost::shared_ptr rt) _y_position = -1; - _route->processors_changed.connect (*this, invalidator (*this), ui_bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context()); - _route->PropertyChanged.connect (*this, invalidator (*this), ui_bind (&RouteTimeAxisView::route_property_changed, this, _1), gui_context()); + _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context()); + _route->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::route_property_changed, this, _1), gui_context()); if (is_track()) { diff --git a/gtk2_ardour/route_ui.cc b/gtk2_ardour/route_ui.cc index 17e08f20ef..8d8c4224b0 100644 --- a/gtk2_ardour/route_ui.cc +++ b/gtk2_ardour/route_ui.cc @@ -145,8 +145,8 @@ RouteUI::init () _session->TransportStateChange.connect (_session_connections, invalidator (*this), boost::bind (&RouteUI::check_rec_enable_sensitivity, this), gui_context()); _session->RecordStateChanged.connect (_session_connections, invalidator (*this), boost::bind (&RouteUI::session_rec_enable_changed, this), gui_context()); - _session->config.ParameterChanged.connect (*this, invalidator (*this), ui_bind (&RouteUI::parameter_changed, this, _1), gui_context()); - Config->ParameterChanged.connect (*this, invalidator (*this), ui_bind (&RouteUI::parameter_changed, this, _1), gui_context()); + _session->config.ParameterChanged.connect (*this, invalidator (*this), boost::bind (&RouteUI::parameter_changed, this, _1), gui_context()); + Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&RouteUI::parameter_changed, this, _1), gui_context()); rec_enable_button->signal_button_press_event().connect (sigc::mem_fun(*this, &RouteUI::rec_enable_press), false); rec_enable_button->signal_button_release_event().connect (sigc::mem_fun(*this, &RouteUI::rec_enable_release), false); @@ -210,7 +210,7 @@ RouteUI::set_route (boost::shared_ptr rp) solo_button->set_controllable (_route->solo_control()); _route->active_changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::route_active_changed, this), gui_context()); - _route->mute_changed.connect (route_connections, invalidator (*this), ui_bind (&RouteUI::mute_changed, this, _1), gui_context()); + _route->mute_changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::mute_changed, this, _1), gui_context()); _route->solo_changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::update_solo_display, this), gui_context()); _route->solo_safe_changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::update_solo_display, this), gui_context()); @@ -218,10 +218,10 @@ RouteUI::set_route (boost::shared_ptr rp) _route->solo_isolated_changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::update_solo_display, this), gui_context()); _route->phase_invert_changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::polarity_changed, this), gui_context()); - _route->PropertyChanged.connect (route_connections, invalidator (*this), ui_bind (&RouteUI::property_changed, this, _1), gui_context()); + _route->PropertyChanged.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::property_changed, this, _1), gui_context()); - _route->io_changed.connect (route_connections, invalidator (*this), ui_bind (&RouteUI::setup_invert_buttons, this), gui_context ()); - _route->gui_changed.connect (route_connections, invalidator (*this), ui_bind (&RouteUI::route_gui_changed, this, _1), gui_context ()); + _route->io_changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::setup_invert_buttons, this), gui_context ()); + _route->gui_changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::route_gui_changed, this, _1), gui_context ()); if (_session->writable() && is_track()) { boost::shared_ptr t = boost::dynamic_pointer_cast(_route); @@ -235,7 +235,7 @@ RouteUI::set_route (boost::shared_ptr rp) if (is_midi_track()) { midi_track()->StepEditStatusChange.connect (route_connections, invalidator (*this), - ui_bind (&RouteUI::step_edit_changed, this, _1), gui_context()); + boost::bind (&RouteUI::step_edit_changed, this, _1), gui_context()); } } diff --git a/gtk2_ardour/selection.cc b/gtk2_ardour/selection.cc index 4d9262bb3a..8a6f04a6f7 100644 --- a/gtk2_ardour/selection.cc +++ b/gtk2_ardour/selection.cc @@ -58,10 +58,13 @@ Selection::Selection (const PublicEditor* e) /* we have disambiguate which remove() for the compiler */ void (Selection::*track_remove)(TimeAxisView*) = &Selection::remove; - TimeAxisView::CatchDeletion.connect (*this, MISSING_INVALIDATOR, ui_bind (track_remove, this, _1), gui_context()); + TimeAxisView::CatchDeletion.connect (*this, MISSING_INVALIDATOR, boost::bind (track_remove, this, _1), gui_context()); void (Selection::*marker_remove)(Marker*) = &Selection::remove; - Marker::CatchDeletion.connect (*this, MISSING_INVALIDATOR, ui_bind (marker_remove, this, _1), gui_context()); + Marker::CatchDeletion.connect (*this, MISSING_INVALIDATOR, boost::bind (marker_remove, this, _1), gui_context()); + + void (Selection::*point_remove)(ControlPoint*) = &Selection::remove; + ControlPoint::CatchDeletion.connect (*this, MISSING_INVALIDATOR, boost::bind (point_remove, this, _1), gui_context()); } #if 0 @@ -516,7 +519,6 @@ Selection::add (boost::shared_ptr cl) if (!al) { warning << "Programming error: Selected list is not an ARDOUR::AutomationList" << endmsg; return; - return; } if (find (lines.begin(), lines.end(), al) == lines.end()) { lines.push_back (al); @@ -536,6 +538,15 @@ Selection::remove (TimeAxisView* track) } } +void +Selection::remove (ControlPoint* p) +{ + PointSelection::iterator i = find (points.begin(), points.end(), p); + if (i != points.end ()) { + points.erase (i); + } +} + void Selection::remove (const TrackViewList& track_list) { @@ -879,17 +890,22 @@ void Selection::toggle (ControlPoint* cp) { cp->set_selected (!cp->get_selected ()); - set_point_selection_from_line (cp->line ()); + PointSelection::iterator i = find (points.begin(), points.end(), cp); + if (i == points.end()) { + points.push_back (cp); + } else { + points.erase (i); + } + + PointsChanged (); /* EMIT SIGNAL */ } void Selection::toggle (vector const & cps) { for (vector::const_iterator i = cps.begin(); i != cps.end(); ++i) { - (*i)->set_selected (!(*i)->get_selected ()); + toggle (*i); } - - set_point_selection_from_line (cps.front()->line ()); } void @@ -935,6 +951,13 @@ Selection::set (list const & selectables) add (selectables); } +void +Selection::add (PointSelection const & s) +{ + for (PointSelection::const_iterator i = s.begin(); i != s.end(); ++i) { + points.push_back (*i); + } +} void Selection::add (list const & selectables) @@ -979,17 +1002,16 @@ void Selection::add (ControlPoint* cp) { cp->set_selected (true); - set_point_selection_from_line (cp->line ()); + points.push_back (cp); + PointsChanged (); /* EMIT SIGNAL */ } void Selection::add (vector const & cps) { for (vector::const_iterator i = cps.begin(); i != cps.end(); ++i) { - (*i)->set_selected (true); + add (*i); } - - set_point_selection_from_line (cps.front()->line ()); } void @@ -999,18 +1021,11 @@ Selection::set (ControlPoint* cp) return; } - /* We're going to set up the PointSelection from the selected ControlPoints - on this point's line, so we need to deselect all ControlPoints before - we re-add this one. - */ - for (uint32_t i = 0; i < cp->line().npoints(); ++i) { cp->line().nth (i)->set_selected (false); } - vector cps; - cps.push_back (cp); - add (cps); + add (cp); } void @@ -1083,73 +1098,6 @@ MarkerSelection::range (framepos_t& s, framepos_t& e) e = std::max (s, e); } -/** Automation control point selection is mostly manipulated using the selected state - * of the ControlPoints themselves. For example, to add a point to a selection, its - * ControlPoint is marked as selected and then this method is called. It sets up - * our PointSelection from the selected ControlPoints of a given AutomationLine. - * - * We can't use ControlPoints directly in the selection, as we need to express a - * selection of not just a visible ControlPoint but also (possibly) some invisible - * points nearby. Hence the selection stores AutomationRanges, and these are synced - * with ControlPoint selection state using AutomationLine::set_selected_points. - */ - -void -Selection::set_point_selection_from_line (AutomationLine const & line) -{ - points.clear (); - - AutomationRange current (DBL_MAX, 0, 1, 0, &line.trackview); - - for (uint32_t i = 0; i < line.npoints(); ++i) { - ControlPoint const * cp = line.nth (i); - - if (cp->get_selected()) { - /* x and y position of this control point in coordinates suitable for - an AutomationRange (ie model time and fraction of track height) - */ - double const x = (*(cp->model()))->when; - double const y = 1 - (cp->get_y() / line.trackview.current_height ()); - - /* work out the position of a rectangle the size of a control point centred - on this point - */ - - double const size = cp->size (); - double const x_size = line.time_converter().from (line.trackview.editor().pixel_to_frame (size)); - double const y_size = size / line.trackview.current_height (); - - double const x1 = max (0.0, x - x_size / 2); - double const x2 = x + x_size / 2; - double const y1 = max (0.0, y - y_size / 2); - double const y2 = y + y_size / 2; - - /* extend the current AutomationRange to put this point in */ - current.start = min (current.start, x1); - current.end = max (current.end, x2); - current.low_fract = min (current.low_fract, y1); - current.high_fract = max (current.high_fract, y2); - - } else { - /* this point isn't selected; if the current AutomationRange has some - stuff in it, push it onto the list and make a new one - */ - if (current.start < DBL_MAX) { - points.push_back (current); - current = AutomationRange (DBL_MAX, 0, 1, 0, &line.trackview); - } - } - } - - /* Maybe push the current AutomationRange, as above */ - if (current.start < DBL_MAX) { - points.push_back (current); - current = AutomationRange (DBL_MAX, 0, 1, 0, &line.trackview); - } - - PointsChanged (); /* EMIT SIGNAL */ -} - XMLNode& Selection::get_state () const { diff --git a/gtk2_ardour/selection.h b/gtk2_ardour/selection.h index c474faa5b2..df7212593f 100644 --- a/gtk2_ardour/selection.h +++ b/gtk2_ardour/selection.h @@ -165,6 +165,7 @@ class Selection : public sigc::trackable, public PBD::ScopedConnectionList void add (Marker*); void add (const std::list&); void add (const RegionSelection&); + void add (const PointSelection&); void remove (TimeAxisView*); void remove (const TrackViewList&); void remove (const MidiNoteSelection&); @@ -178,6 +179,7 @@ class Selection : public sigc::trackable, public PBD::ScopedConnectionList void remove (const std::list >&); void remove (const std::list&); void remove (Marker*); + void remove (ControlPoint *); void remove_regions (TimeAxisView *); @@ -202,8 +204,6 @@ class Selection : public sigc::trackable, public PBD::ScopedConnectionList int set_state (XMLNode const &, int); private: - void set_point_selection_from_line (AutomationLine const &); - PublicEditor const * editor; uint32_t next_time_id; bool _no_tracks_changed; diff --git a/gtk2_ardour/send_ui.cc b/gtk2_ardour/send_ui.cc index 627fe531f6..e1e3a8b7da 100644 --- a/gtk2_ardour/send_ui.cc +++ b/gtk2_ardour/send_ui.cc @@ -72,7 +72,7 @@ SendUI::SendUI (Gtk::Window* parent, boost::shared_ptr s, Session* session _send->set_metering (true); - _send->output()->changed.connect (connections, invalidator (*this), ui_bind (&SendUI::outs_changed, this, _1, _2), gui_context()); + _send->output()->changed.connect (connections, invalidator (*this), boost::bind (&SendUI::outs_changed, this, _1, _2), gui_context()); _panners.set_width (Wide); _panners.setup_pan (); diff --git a/gtk2_ardour/session_metadata_dialog.cc b/gtk2_ardour/session_metadata_dialog.cc index 996262f0fa..5bac706106 100644 --- a/gtk2_ardour/session_metadata_dialog.cc +++ b/gtk2_ardour/session_metadata_dialog.cc @@ -27,6 +27,7 @@ #include "ardour/session.h" #include "ardour/session_directory.h" #include "ardour/session_utils.h" +#include "ardour/configuration.h" #include "i18n.h" @@ -81,8 +82,8 @@ TextMetadataField::load_data (ARDOUR::SessionMetadata const & data) Gtk::Widget & TextMetadataField::name_widget () { - label = Gtk::manage (new Gtk::Label(_name + ':', Gtk::ALIGN_LEFT)); - label->set_alignment (0, 0.5); + label = Gtk::manage (new Gtk::Label(_name + ':')); + label->set_alignment (1, 0.5); return *label; } @@ -160,8 +161,8 @@ NumberMetadataField::update_value () Gtk::Widget & NumberMetadataField::name_widget () { - label = Gtk::manage (new Gtk::Label(_name + ':', Gtk::ALIGN_LEFT)); - label->set_alignment (0, 0.5); + label = Gtk::manage (new Gtk::Label(_name + ':')); + label->set_alignment (1, 0.5); return *label; } @@ -255,7 +256,7 @@ SessionMetadataSetEditable::set_session (ARDOUR::Session * s) return; } - ARDOUR::SessionMetadata const & data = _session->metadata(); + ARDOUR::SessionMetadata const & data = *(ARDOUR::SessionMetadata::Metadata()); table.resize (list.size(), 2); uint32_t row = 0; @@ -272,7 +273,7 @@ SessionMetadataSetEditable::set_session (ARDOUR::Session * s) void SessionMetadataSetEditable::save_data () { - ARDOUR::SessionMetadata & data = _session->metadata(); + ARDOUR::SessionMetadata & data = *(ARDOUR::SessionMetadata::Metadata()); for (DataList::const_iterator it = list.begin(); it != list.end(); ++it) { (*it)->save_data(data); } @@ -330,7 +331,7 @@ SessionMetadataSetImportable::load_extra_data (ARDOUR::SessionMetadata const & d return; } - ARDOUR::SessionMetadata & session_data = _session->metadata(); + ARDOUR::SessionMetadata const & session_data = *(ARDOUR::SessionMetadata::Metadata()); MetadataPtr session_field; MetadataPtr import_field; @@ -378,7 +379,7 @@ SessionMetadataSetImportable::save_data () return; } - ARDOUR::SessionMetadata & session_data = _session->metadata(); + ARDOUR::SessionMetadata & session_data = *(ARDOUR::SessionMetadata::Metadata()); Gtk::TreeModel::Children fields = tree->children(); Gtk::TreeModel::Children::iterator it; @@ -421,22 +422,25 @@ SessionMetadataDialog::SessionMetadataDialog (string const & name) : { cancel_button = add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); cancel_button->signal_clicked().connect (sigc::mem_fun(*this, &SessionMetadataDialog::end_dialog)); - save_button = add_button (Gtk::Stock::SAVE, Gtk::RESPONSE_ACCEPT); + save_button = add_button (Gtk::Stock::OK, Gtk::RESPONSE_ACCEPT); save_button->signal_clicked().connect (sigc::mem_fun(*this, &SessionMetadataDialog::save_and_close)); } template void -SessionMetadataDialog::init_data () +SessionMetadataDialog::init_data ( bool skip_user ) { if (!_session) { std::cerr << "Programming error: no session set for SessionMetaDataDialog (in init_data)!" << std::endl; return; } + if (!skip_user) + init_user_data (); init_track_data (); init_album_data (); init_people_data (); + init_school_data (); for (DataSetList::iterator it = data_list.begin(); it != data_list.end(); ++it) { (*it)->set_session (_session); @@ -468,6 +472,7 @@ void SessionMetadataDialog::save_and_close () { save_data (); + _session->set_dirty(); end_dialog (); } @@ -507,6 +512,32 @@ SessionMetadataDialog::add_widget (Gtk::Widget & widget) get_vbox()->pack_start (widget, true, true, 0); } +template +void +SessionMetadataDialog::init_user_data () +{ + DataSetPtr data_set (new DataSet (_("User"))); + data_list.push_back (data_set); + + MetadataPtr ptr; + + ptr = MetadataPtr (new TextMetadataField (&ARDOUR::SessionMetadata::user_name, &ARDOUR::SessionMetadata::set_user_name, _("Name"))); + data_set->add_data_field (ptr); + + ptr = MetadataPtr (new TextMetadataField (&ARDOUR::SessionMetadata::user_email, &ARDOUR::SessionMetadata::set_user_email, _("Email"))); + data_set->add_data_field (ptr); + + ptr = MetadataPtr (new TextMetadataField (&ARDOUR::SessionMetadata::user_web, &ARDOUR::SessionMetadata::set_user_web, _("Web"))); + data_set->add_data_field (ptr); + + ptr = MetadataPtr (new TextMetadataField (&ARDOUR::SessionMetadata::organization, &ARDOUR::SessionMetadata::set_organization, _("Organization"))); + data_set->add_data_field (ptr); + + ptr = MetadataPtr (new TextMetadataField (&ARDOUR::SessionMetadata::country, &ARDOUR::SessionMetadata::set_country, _("Country"))); + data_set->add_data_field (ptr); + +} + template void SessionMetadataDialog::init_track_data () @@ -615,6 +646,23 @@ SessionMetadataDialog::init_people_data () data_set->add_data_field (ptr); } +template +void +SessionMetadataDialog::init_school_data () +{ + DataSetPtr data_set (new DataSet (_("School"))); + data_list.push_back (data_set); + + MetadataPtr ptr; + + ptr = MetadataPtr (new TextMetadataField (&ARDOUR::SessionMetadata::instructor, &ARDOUR::SessionMetadata::set_instructor, _("Instructor"))); + data_set->add_data_field (ptr); + + ptr = MetadataPtr (new TextMetadataField (&ARDOUR::SessionMetadata::course, &ARDOUR::SessionMetadata::set_course, _("Course"))); + data_set->add_data_field (ptr); + +} + /* SessionMetadataEditor */ SessionMetadataEditor::SessionMetadataEditor () : @@ -722,10 +770,10 @@ SessionMetadataImporter::run () return; } + //create a temporary ARDOUR::SessionMetadata data; data.set_state (*node, version); - - init_data (); + init_data ( true ); //skip user data here load_extra_data (data); init_gui(); diff --git a/gtk2_ardour/session_metadata_dialog.h b/gtk2_ardour/session_metadata_dialog.h index fbe430e7ad..9aa62ed4ca 100644 --- a/gtk2_ardour/session_metadata_dialog.h +++ b/gtk2_ardour/session_metadata_dialog.h @@ -209,7 +209,7 @@ class SessionMetadataDialog : public ArdourDialog SessionMetadataDialog (std::string const & name); protected: - void init_data (); + void init_data ( bool skip_user = false ); void load_extra_data (ARDOUR::SessionMetadata const & data); void save_data (); @@ -232,9 +232,11 @@ class SessionMetadataDialog : public ArdourDialog Gtk::Notebook notebook; private: + void init_user_data (); void init_track_data (); void init_album_data (); void init_people_data (); + void init_school_data (); typedef boost::shared_ptr DataSetPtr; typedef std::list DataSetList; diff --git a/gtk2_ardour/session_option_editor.cc b/gtk2_ardour/session_option_editor.cc index b524644542..62d555c13d 100644 --- a/gtk2_ardour/session_option_editor.cc +++ b/gtk2_ardour/session_option_editor.cc @@ -294,22 +294,6 @@ SessionOptionEditor::SessionOptionEditor (Session* s) add_option (_("Misc"), li); - add_option (_("Misc"), new OptionEditorHeading (_("Broadcast WAVE metadata"))); - - add_option (_("Misc"), new EntryOption ( - "bwf-country-code", - _("Country code"), - sigc::mem_fun (*_session_config, &SessionConfiguration::get_bwf_country_code), - sigc::mem_fun (*_session_config, &SessionConfiguration::set_bwf_country_code) - )); - - add_option (_("Misc"), new EntryOption ( - "bwf-organization-code", - _("Organization code"), - sigc::mem_fun (*_session_config, &SessionConfiguration::get_bwf_organization_code), - sigc::mem_fun (*_session_config, &SessionConfiguration::set_bwf_organization_code) - )); - add_option (_("Misc"), new OptionEditorHeading (_("Glue to bars and beats"))); add_option (_("Misc"), new BoolOption ( diff --git a/gtk2_ardour/sfdb_freesound_mootcher.cc b/gtk2_ardour/sfdb_freesound_mootcher.cc index 1444660a88..7e13c4f68b 100644 --- a/gtk2_ardour/sfdb_freesound_mootcher.cc +++ b/gtk2_ardour/sfdb_freesound_mootcher.cc @@ -50,6 +50,8 @@ #include #include +#include "i18n.h" + #include "ardour/audio_library.h" static const std::string base_url = "http://www.freesound.org/api"; @@ -67,6 +69,7 @@ Mootcher::Mootcher() //------------------------------------------------------------------------ Mootcher:: ~Mootcher() { + curl_easy_cleanup(curl); } //------------------------------------------------------------------------ @@ -184,16 +187,16 @@ std::string Mootcher::doRequest(std::string uri, std::string params) return ""; } - result = xml_page.memory; // free the memory - if(xml_page.memory){ - free( xml_page.memory ); - xml_page.memory = NULL; - xml_page.size = 0; + if (xml_page.memory) { + result = xml_page.memory; } + + free (xml_page.memory); + xml_page.memory = NULL; + xml_page.size = 0; return result; - } @@ -215,6 +218,8 @@ std::string Mootcher::searchText(std::string query, int page, std::string filter if (sort) params += "&s=" + sortMethodString(sort); + params += "&fields=id,original_filename,duration,serve"; + return doRequest("/sounds/search", params); } @@ -225,7 +230,6 @@ std::string Mootcher::getSoundResourceFile(std::string ID) std::string originalSoundURI; std::string audioFileName; - std::string xmlFileName; std::string xml; @@ -250,28 +254,12 @@ std::string Mootcher::getSoundResourceFile(std::string ID) } XMLNode *name = freesound->child("original_filename"); - XMLNode *filesize = freesound->child("filesize"); - // get the file name and size from xml file - if (name && filesize) { + if (name) { audioFileName = basePath + "snd/" + ID + "-" + name->child("text")->content(); - // create new filename with the ID number - xmlFileName = basePath; - xmlFileName += "snd/"; - xmlFileName += freesound->child("id")->child("text")->content(); - xmlFileName += "-"; - xmlFileName += name->child("text")->content(); - xmlFileName += ".xml"; - - // std::cerr << "getSoundResourceFile: saving XML: " << xmlFileName << std::endl; - - // save the xml file to disk - ensureWorkingDir(); - doc.write(xmlFileName.c_str()); - //store all the tags in the database XMLNode *tags = freesound->child("tags"); if (tags) { @@ -326,6 +314,11 @@ std::string Mootcher::getAudioFile(std::string originalFileName, std::string ID, return ""; } + //if already canceling a previous download, bail out here ( this can happen b/c getAudioFile gets called by various UI update funcs ) + if ( caller->freesound_download_cancel ) { + return ""; + } + //now download the actual file FILE* theFile; theFile = g_fopen( audioFileName.c_str(), "wb" ); @@ -344,8 +337,12 @@ std::string Mootcher::getAudioFile(std::string originalFileName, std::string ID, std::cerr << "downloading " << audioFileName << " from " << audioURL << "..." << std::endl; /* hack to get rid of the barber-pole stripes */ - caller->progress_bar.hide(); - caller->progress_bar.show(); + caller->freesound_progress_bar.hide(); + caller->freesound_progress_bar.show(); + + std::string prog; + prog = string_compose (_("%1: [Stop]->"), originalFileName); + caller->freesound_progress_bar.set_text(prog); curl_easy_setopt (curl, CURLOPT_NOPROGRESS, 0); // turn on the progress bar curl_easy_setopt (curl, CURLOPT_PROGRESSFUNCTION, progress_callback); @@ -355,7 +352,8 @@ std::string Mootcher::getAudioFile(std::string originalFileName, std::string ID, fclose(theFile); curl_easy_setopt (curl, CURLOPT_NOPROGRESS, 1); // turn off the progress bar - caller->progress_bar.set_fraction(0.0); + caller->freesound_progress_bar.set_fraction(0.0); + caller->freesound_progress_bar.set_text(""); if( res != 0 ) { std::cerr << "curl error " << res << " (" << curl_easy_strerror(res) << ")" << std::endl; @@ -377,12 +375,12 @@ int Mootcher::progress_callback(void *caller, double dltotal, double dlnow, doub SoundFileBrowser *sfb = (SoundFileBrowser *) caller; //XXX I hope it's OK to do GTK things in this callback. Otherwise // I'll have to do stuff like in interthread_progress_window. - if (sfb->freesound_stop) { + if (sfb->freesound_download_cancel) { return -1; } - sfb->progress_bar.set_fraction(dlnow/dltotal); + sfb->freesound_progress_bar.set_fraction(dlnow/dltotal); /* Make sure the progress widget gets updated */ while (Glib::MainContext::get_default()->iteration (false)) { /* do nothing */ diff --git a/gtk2_ardour/sfdb_ui.cc b/gtk2_ardour/sfdb_ui.cc index 347e2d7966..dc9edf56c6 100644 --- a/gtk2_ardour/sfdb_ui.cc +++ b/gtk2_ardour/sfdb_ui.cc @@ -188,10 +188,10 @@ SoundFileBox::SoundFileBox (bool persistent) main_box.pack_start (bottom_box, false, false); play_btn.set_image (*(manage (new Image (Stock::MEDIA_PLAY, ICON_SIZE_BUTTON)))); - play_btn.set_label (_("Play")); +// play_btn.set_label (_("Play")); stop_btn.set_image (*(manage (new Image (Stock::MEDIA_STOP, ICON_SIZE_BUTTON)))); - stop_btn.set_label (_("Stop")); +// stop_btn.set_label (_("Stop")); bottom_box.set_homogeneous (false); bottom_box.set_spacing (6); @@ -439,6 +439,10 @@ SoundFileBrowser::SoundFileBrowser (Gtk::Window& parent, string title, ARDOUR::S chooser.add_shortcut_folder_uri("file:///Volumes"); #endif +#ifdef FREESOUND + mootcher = new Mootcher(); +#endif + //add the file chooser { chooser.set_border_width (12); @@ -502,6 +506,8 @@ SoundFileBrowser::SoundFileBrowser (Gtk::Window& parent, string title, ARDOUR::S found_search_btn.signal_clicked().connect(sigc::mem_fun(*this, &SoundFileBrowser::found_search_clicked)); found_entry.signal_activate().connect(sigc::mem_fun(*this, &SoundFileBrowser::found_search_clicked)); + freesound_stop_btn.signal_clicked().connect(sigc::mem_fun(*this, &SoundFileBrowser::freesound_stop_clicked)); + notebook.append_page (*vbox, _("Search Tags")); } @@ -540,15 +546,8 @@ SoundFileBrowser::SoundFileBrowser (Gtk::Window& parent, string title, ARDOUR::S freesound_sort.append_text(_("Lowest rated")); freesound_sort.set_active(0); - label = manage (new Label); - label->set_text (_("Page:")); - passbox->pack_start (*label, false, false); - passbox->pack_start (freesound_page, false, false); - freesound_page.set_range(1, 1000); - freesound_page.set_increments(1, 10); - passbox->pack_start (freesound_search_btn, false, false); - passbox->pack_start (progress_bar); + passbox->pack_start (freesound_progress_bar); passbox->pack_end (freesound_stop_btn, false, false); freesound_stop_btn.set_label(_("Stop")); @@ -575,6 +574,7 @@ SoundFileBrowser::SoundFileBrowser (Gtk::Window& parent, string title, ARDOUR::S freesound_stop_btn.signal_clicked().connect(sigc::mem_fun(*this, &SoundFileBrowser::freesound_stop_clicked)); notebook.append_page (*vbox, _("Search Freesound")); } + #endif @@ -736,13 +736,13 @@ SoundFileBrowser::found_list_view_selected () void SoundFileBrowser::freesound_list_view_selected () { + freesound_download_cancel = false; + #ifdef FREESOUND if (!reset_options ()) { set_response_sensitive (RESPONSE_OK, false); } else { - Mootcher theMootcher; // XXX should be a member of SoundFileBrowser - string file; TreeView::Selection::ListHandle_Path rows = freesound_list_view.get_selection()->get_selected_rows (); @@ -760,8 +760,7 @@ SoundFileBrowser::freesound_list_view_selected () gdk_window_set_cursor (get_window()->gobj(), gdk_cursor_new(GDK_WATCH)); gdk_flush(); - freesound_stop = false; - file = theMootcher.getAudioFile(ofn, id, uri, this); + file = mootcher->getAudioFile(ofn, id, uri, this); gdk_window_set_cursor (get_window()->gobj(), prev_cursor); @@ -805,13 +804,15 @@ SoundFileBrowser::found_search_clicked () void SoundFileBrowser::freesound_search_clicked () { + freesound_search_cancel = false; freesound_search(); } void SoundFileBrowser::freesound_stop_clicked () { - freesound_stop = true; + freesound_download_cancel = true; + freesound_search_cancel = true; } @@ -821,97 +822,111 @@ SoundFileBrowser::freesound_search() #ifdef FREESOUND freesound_list->clear(); - Mootcher theMootcher; - string search_string = freesound_entry.get_text (); enum sortMethod sort_method = (enum sortMethod) freesound_sort.get_active_row_number(); - int page = freesound_page.get_value_as_int(); GdkCursor *prev_cursor; prev_cursor = gdk_window_get_cursor (get_window()->gobj()); gdk_window_set_cursor (get_window()->gobj(), gdk_cursor_new(GDK_WATCH)); gdk_flush(); + for (int page = 1; page <= 99; page++ ) { + + std::string prog; + prog = string_compose (_("Page %1, [Stop]->"), page); + freesound_progress_bar.set_text(prog); + while (Glib::MainContext::get_default()->iteration (false)) { + /* do nothing */ + } - string theString = theMootcher.searchText( - search_string, - page, - "", // filter, could do, e.g. "type:wav" - sort_method - ); + std::string theString = mootcher->searchText( + search_string, + page, + "", // filter, could do, e.g. "type:wav" + sort_method + ); + + XMLTree doc; + doc.read_buffer( theString ); + XMLNode *root = doc.root(); + + if (!root) { + cerr << "no root XML node!" << endl; + break; + } + + if ( strcmp(root->name().c_str(), "response") != 0) { + cerr << "root node name == " << root->name() << ", != \"response\"!" << endl; + break; + } + + XMLNode *sounds_root = root->child("sounds"); + + if (!sounds_root) { + cerr << "no child node \"sounds\" found!" << endl; + break; + } + + XMLNodeList sounds = sounds_root->children(); + XMLNodeConstIterator niter; + XMLNode *node; + for (niter = sounds.begin(); niter != sounds.end(); ++niter) { + node = *niter; + if( strcmp( node->name().c_str(), "resource") != 0 ){ + cerr << "node->name()=" << node->name() << ",!= \"resource\"!" << endl; + freesound_search_cancel = true; + break; + } + + // node->dump(cerr, "node:"); + + XMLNode *id_node = node->child ("id"); + XMLNode *uri_node = node->child ("serve"); + XMLNode *ofn_node = node->child ("original_filename"); + XMLNode *dur_node = node->child ("duration"); + + if (id_node && uri_node && ofn_node && dur_node) { + + std::string id = id_node->child("text")->content(); + std::string uri = uri_node->child("text")->content(); + std::string ofn = ofn_node->child("text")->content(); + std::string dur = dur_node->child("text")->content(); + + std::string r; + // cerr << "id=" << id << ",uri=" << uri << ",ofn=" << ofn << ",dur=" << dur << endl; + + double duration_seconds = atof(dur.c_str()); + double h, m, s; + char duration_hhmmss[16]; + if (duration_seconds >= 99 * 60 * 60) { + strcpy(duration_hhmmss, ">99h"); + } else { + s = modf(duration_seconds/60, &m) * 60; + m = modf(m/60, &h) * 60; + sprintf(duration_hhmmss, "%02.fh:%02.fm:%04.1fs", + h, m, s + ); + } + + TreeModel::iterator new_row = freesound_list->append(); + TreeModel::Row row = *new_row; + + row[freesound_list_columns.id ] = id; + row[freesound_list_columns.uri ] = uri; + row[freesound_list_columns.filename] = ofn; + row[freesound_list_columns.duration] = duration_hhmmss; + + } + } + + if (freesound_search_cancel) + break; + + } //page "for" loop gdk_window_set_cursor (get_window()->gobj(), prev_cursor); - XMLTree doc; - doc.read_buffer( theString ); - XMLNode *root = doc.root(); + freesound_progress_bar.set_text(""); - if (!root) { - cerr << "no root XML node!" << endl; - return; - } - - if ( strcmp(root->name().c_str(), "response") != 0) { - cerr << "root node name == " << root->name() << ", != \"response\"!" << endl; - return; - } - - XMLNode *sounds_root = root->child("sounds"); - - if (!sounds_root) { - cerr << "no child node \"sounds\" found!" << endl; - return; - } - - XMLNodeList sounds = sounds_root->children(); - XMLNodeConstIterator niter; - XMLNode *node; - for (niter = sounds.begin(); niter != sounds.end(); ++niter) { - node = *niter; - if( strcmp( node->name().c_str(), "resource") != 0 ){ - cerr << "node->name()=" << node->name() << ",!= \"resource\"!" << endl; - continue; // return; - } - - // node->dump(cerr, "node:"); - - XMLNode *id_node = node->child ("id"); - XMLNode *uri_node = node->child ("serve"); - XMLNode *ofn_node = node->child ("original_filename"); - XMLNode *dur_node = node->child ("duration"); - - if (id_node && uri_node && ofn_node) { - - std::string id = id_node->child("text")->content(); - std::string uri = uri_node->child("text")->content(); - std::string ofn = ofn_node->child("text")->content(); - std::string dur = dur_node->child("text")->content(); - - std::string r; - // cerr << "id=" << id << ",uri=" << uri << ",ofn=" << ofn << ",dur=" << dur << endl; - - double duration_seconds = atof(dur.c_str()); - double h, m, s; - char duration_hhmmss[16]; - if (duration_seconds >= 99 * 60 * 60) { - strcpy(duration_hhmmss, ">99h"); - } else { - s = modf(duration_seconds/60, &m) * 60; - m = modf(m/60, &h) * 60; - sprintf(duration_hhmmss, "%02.fh:%02.fm:%04.1fs", - h, m, s - ); - } - - TreeModel::iterator new_row = freesound_list->append(); - TreeModel::Row row = *new_row; - - row[freesound_list_columns.id ] = id; - row[freesound_list_columns.uri ] = uri; - row[freesound_list_columns.filename] = ofn; - row[freesound_list_columns.duration] = duration_hhmmss; - - } - } #endif } @@ -948,9 +963,6 @@ SoundFileBrowser::get_paths () #ifdef FREESOUND typedef TreeView::Selection::ListHandle_Path ListPath; - Mootcher theMootcher; // XXX should be a member of SoundFileBrowser - - ListPath rows = freesound_list_view.get_selection()->get_selected_rows (); for (ListPath::iterator i = rows.begin() ; i != rows.end(); ++i) { TreeIter iter = freesound_list->get_iter(*i); @@ -963,8 +975,7 @@ SoundFileBrowser::get_paths () gdk_window_set_cursor (get_window()->gobj(), gdk_cursor_new(GDK_WATCH)); gdk_flush(); - freesound_stop = false; - string str = theMootcher.getAudioFile(ofn, id, uri, this); + string str = mootcher->getAudioFile(ofn, id, uri, this); if (str != "") { results.push_back (str); } diff --git a/gtk2_ardour/sfdb_ui.h b/gtk2_ardour/sfdb_ui.h index 7ec544baf1..f497477866 100644 --- a/gtk2_ardour/sfdb_ui.h +++ b/gtk2_ardour/sfdb_ui.h @@ -55,6 +55,7 @@ namespace ARDOUR { }; class GainMeter; +class Mootcher; class SoundFileBox : public Gtk::VBox, public ARDOUR::SessionHandlePtr { @@ -167,11 +168,16 @@ class SoundFileBrowser : public ArdourDialog Gtk::Button freesound_search_btn; Gtk::TreeView freesound_list_view; - Gtk::ProgressBar progress_bar; + Gtk::ProgressBar freesound_progress_bar; - bool freesound_stop; + bool freesound_search_cancel; + bool freesound_download_cancel; void freesound_search(); + +#ifdef FREESOUND + Mootcher *mootcher; +#endif protected: bool resetting_ourselves; diff --git a/gtk2_ardour/shuttle_control.cc b/gtk2_ardour/shuttle_control.cc index 2fb2a3357e..3b5f8dbb78 100644 --- a/gtk2_ardour/shuttle_control.cc +++ b/gtk2_ardour/shuttle_control.cc @@ -67,7 +67,7 @@ ShuttleControl::ShuttleControl () set_size_request (100, 15); set_name (X_("ShuttleControl")); - Config->ParameterChanged.connect (parameter_connection, MISSING_INVALIDATOR, ui_bind (&ShuttleControl::parameter_changed, this, _1), gui_context()); + Config->ParameterChanged.connect (parameter_connection, MISSING_INVALIDATOR, boost::bind (&ShuttleControl::parameter_changed, this, _1), gui_context()); /* gtkmm 2.4: the C++ wrapper doesn't work */ g_signal_connect ((GObject*) gobj(), "query-tooltip", G_CALLBACK (qt), NULL); diff --git a/gtk2_ardour/splash.cc b/gtk2_ardour/splash.cc index d459a308b1..8d6e84bae2 100644 --- a/gtk2_ardour/splash.cc +++ b/gtk2_ardour/splash.cc @@ -56,7 +56,7 @@ Splash::Splash () set_default_size (pixbuf->get_width(), pixbuf->get_height()); the_splash = this; - ARDOUR::BootMessage.connect (msg_connection, invalidator (*this), ui_bind (&Splash::boot_message, this, _1), gui_context()); + ARDOUR::BootMessage.connect (msg_connection, invalidator (*this), boost::bind (&Splash::boot_message, this, _1), gui_context()); } void diff --git a/gtk2_ardour/startup.cc b/gtk2_ardour/startup.cc index 3b9d7e5e12..0a07e4ec98 100644 --- a/gtk2_ardour/startup.cc +++ b/gtk2_ardour/startup.cc @@ -66,6 +66,7 @@ static string poor_mans_glob (string path) ArdourStartup::ArdourStartup () : _response (RESPONSE_OK) , ic_new_session_button (_("Create a new session")) + , ic_existing_session_button (_("Open an existing session")) , monitor_via_hardware_button (_("Use an external mixer or the hardware mixer of your audio interface.\n\ Ardour will play NO role in monitoring")) , monitor_via_ardour_button (string_compose (_("Ask %1 to play back material as it is being recorded"), PROGRAM_NAME)) @@ -81,7 +82,7 @@ Ardour will play NO role in monitoring")) new_user_page_index = -1; default_folder_page_index = -1; monitoring_page_index = -1; - new_session_page_index = -1; + session_page_index = -1; final_page_index = -1; session_options_page_index = -1; new_only = false; @@ -159,7 +160,7 @@ Ardour will play NO role in monitoring")) setup_initial_choice_page (); } - setup_new_session_page (); + setup_session_page (); setup_more_options_page (); if (new_user) { @@ -249,6 +250,10 @@ ArdourStartup::session_template_name () return the_path; } + if (ic_existing_session_button.get_active()) { + return string(); + } + if (use_template_button.get_active()) { TreeModel::iterator iter = template_chooser.get_active (); TreeModel::Row row = (*iter); @@ -365,7 +370,7 @@ ArdourStartup::default_dir_changed () { Config->set_default_session_parent_dir (default_dir_chooser->get_filename()); // make new session folder chooser point to the new default - new_folder_chooser.set_current_folder (Config->get_default_session_parent_dir()); + new_folder_chooser.set_current_folder (Config->get_default_session_parent_dir()); config_changed (); } @@ -530,84 +535,36 @@ greater control in monitoring without affecting the mix.")); void ArdourStartup::setup_initial_choice_page () { - initial_choice_index = append_page (ic_vbox); - set_page_title (ic_vbox, _("What would you like to do ?")); - set_page_header_image (ic_vbox, icon_pixbuf); - ic_vbox.set_spacing (6); ic_vbox.set_border_width (24); + RadioButton::Group g (ic_new_session_button.get_group()); + ic_existing_session_button.set_group (g); + HBox* centering_hbox = manage (new HBox); VBox* centering_vbox = manage (new VBox); centering_vbox->set_spacing (6); - ic_new_session_button.set_active (true); centering_vbox->pack_start (ic_new_session_button, false, true); - - Gtk::Label* l = manage (new Label); - l->set_markup (_("Open a recent session")); - l->set_alignment (0.0, 0.5); - centering_vbox->pack_start (*l, true, true); - - recent_session_model = TreeStore::create (recent_session_columns); - redisplay_recent_sessions (); - - if (!new_session_hbox.get_children().empty()) { - new_session_hbox.remove (**new_session_hbox.get_children().begin()); - } - - if (session_existing_vbox.get_children().empty()) { - - recent_session_display.set_model (recent_session_model); - recent_session_display.append_column (_("Recent Sessions"), recent_session_columns.visible_name); - recent_session_display.set_headers_visible (false); - recent_session_display.get_selection()->set_mode (SELECTION_BROWSE); - - recent_session_display.get_selection()->signal_changed().connect (sigc::mem_fun (*this, &ArdourStartup::recent_session_row_selected)); - - recent_scroller.add (recent_session_display); - recent_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC); - recent_scroller.set_shadow_type (Gtk::SHADOW_IN); - - recent_session_display.show(); - - recent_scroller.show(); - int cnt = redisplay_recent_sessions (); - recent_session_display.signal_row_activated().connect (sigc::mem_fun (*this, &ArdourStartup::recent_row_activated)); - - if (cnt > 4) { - recent_scroller.set_size_request (-1, 300); - } - - centering_vbox->pack_start (recent_scroller, true, true); - - l = manage (new Label); - l->set_markup (_("Browse for existing sessions")); - l->set_alignment (0.0, 0.5); - centering_vbox->pack_start (*l, true, true); - - existing_session_chooser.set_title (_("Select session file")); - existing_session_chooser.signal_file_set().connect (sigc::mem_fun (*this, &ArdourStartup::existing_session_selected)); - existing_session_chooser.set_current_folder(poor_mans_glob (Config->get_default_session_parent_dir())); - -#ifdef GTKOSX - existing_session_chooser.add_shortcut_folder ("/Volumes"); -#endif - - centering_vbox->pack_start (existing_session_chooser, true, true); - } + centering_vbox->pack_start (ic_existing_session_button, false, true); ic_new_session_button.signal_button_press_event().connect(sigc::mem_fun(*this, &ArdourStartup::initial_button_press), false); - ic_new_session_button.signal_clicked().connect(sigc::mem_fun(*this, &ArdourStartup::initial_button_clicked)); ic_new_session_button.signal_activate().connect(sigc::mem_fun(*this, &ArdourStartup::initial_button_activated), false); + ic_existing_session_button.signal_button_press_event().connect(sigc::mem_fun(*this, &ArdourStartup::initial_button_press), false); + ic_existing_session_button.signal_activate().connect(sigc::mem_fun(*this, &ArdourStartup::initial_button_activated), false); + centering_hbox->pack_start (*centering_vbox, true, true); ic_vbox.pack_start (*centering_hbox, true, true); ic_vbox.show_all (); + initial_choice_index = append_page (ic_vbox); + set_page_title (ic_vbox, _("What would you like to do ?")); + set_page_header_image (ic_vbox, icon_pixbuf); + /* user could just click on "Forward" if default * choice is correct. */ @@ -615,21 +572,11 @@ ArdourStartup::setup_initial_choice_page () set_page_complete (ic_vbox, true); } -void -ArdourStartup::initial_button_clicked () -{ - if (ic_new_session_button.get_active ()) { - recent_session_display.get_selection()->unselect_all (); - } -} - bool ArdourStartup::initial_button_press (GdkEventButton *event) { - if (event && event->type == GDK_2BUTTON_PRESS && new_session_page_index != -1) { - ic_new_session_button.set_active (true); - recent_session_display.get_selection()->unselect_all (); - set_current_page (new_session_page_index); + if (event && event->type == GDK_2BUTTON_PRESS && session_page_index != -1) { + set_current_page(session_page_index); return true; } else { return false; @@ -639,8 +586,20 @@ ArdourStartup::initial_button_press (GdkEventButton *event) void ArdourStartup::initial_button_activated () { - recent_session_display.get_selection()->unselect_all (); - set_current_page (new_session_page_index); + set_current_page(session_page_index); +} + +void +ArdourStartup::setup_session_page () +{ + session_vbox.set_border_width (24); + + session_vbox.pack_start (session_hbox, true, true); + session_vbox.show_all (); + + session_page_index = append_page (session_vbox); + /* initial setting */ + set_page_type (session_vbox, ASSISTANT_PAGE_CONFIRM); } void @@ -703,19 +662,22 @@ ArdourStartup::on_apply () void ArdourStartup::on_prepare (Gtk::Widget* page) { - if (page == &new_session_vbox) { + if (page == &session_vbox) { - if (recent_session_display.get_selection()->count_selected_rows() > 0) { - /* row selected, use it */ - set_page_type (new_session_vbox, ASSISTANT_PAGE_CONFIRM); - on_apply (); - } + if (ic_new_session_button.get_active()) { + /* new session requested */ + setup_new_session_page (); + } else { + /* existing session requested */ + setup_existing_session_page (); + + } /* HACK HACK HACK ... change the "Apply" button label to say "Open" */ - Gtk::Widget* tl = new_session_vbox.get_toplevel(); + Gtk::Widget* tl = session_vbox.get_toplevel(); Gtk::Window* win; if ((win = dynamic_cast(tl)) != 0) { /* ::get_default_widget() is not wrapped in gtkmm */ @@ -761,17 +723,11 @@ lost_name_entry_focus (GdkEventFocus*) void ArdourStartup::setup_new_session_page () { - if (!new_session_hbox.get_children().empty()) { - new_session_hbox.remove (**new_session_hbox.get_children().begin()); + if (!session_hbox.get_children().empty()) { + session_hbox.remove (**session_hbox.get_children().begin()); } session_new_vbox.set_spacing (18); - new_session_hbox.pack_start (session_new_vbox, true, true); - new_session_vbox.pack_start (new_session_hbox, true, true); - new_session_page_index = append_page (new_session_vbox); - /* initial setting */ - set_page_type (new_session_vbox, ASSISTANT_PAGE_CONFIRM); - set_page_title (new_session_vbox, _("New Session")); if (session_new_vbox.get_children().empty()) { VBox *vbox1 = manage (new VBox); @@ -786,10 +742,11 @@ ArdourStartup::setup_new_session_page () label1->set_text (_("Session name:")); + if (!ARDOUR_COMMAND_LINE::session_name.empty()) { new_name_entry.set_text (Glib::path_get_basename (ARDOUR_COMMAND_LINE::session_name)); /* name provided - they can move right along */ - set_page_complete (new_session_vbox, true); + set_page_complete (session_vbox, true); } new_name_entry.signal_changed().connect (sigc::mem_fun (*this, &ArdourStartup::new_name_changed)); @@ -920,12 +877,12 @@ ArdourStartup::setup_new_session_page () } session_new_vbox.show_all (); - - new_session_vbox.set_border_width (24); - new_session_vbox.show_all (); + session_hbox.pack_start (session_new_vbox, true, true); + set_page_title (session_vbox, _("New Session")); + set_page_type (session_vbox, ASSISTANT_PAGE_CONFIRM); if (more_new_session_options_button.get_active()) { - set_page_type (new_session_vbox, ASSISTANT_PAGE_CONTENT); + set_page_type (session_vbox, ASSISTANT_PAGE_CONTENT); } new_name_entry.signal_map().connect (sigc::mem_fun (*this, &ArdourStartup::new_name_mapped)); @@ -943,9 +900,9 @@ void ArdourStartup::new_name_changed () { if (!new_name_entry.get_text().empty()) { - set_page_complete (new_session_vbox, true); + set_page_complete (session_vbox, true); } else { - set_page_complete (new_session_vbox, false); + set_page_complete (session_vbox, false); } } @@ -1027,7 +984,7 @@ ArdourStartup::redisplay_recent_sessions () } } - recent_session_display.set_tooltip_column(1); // recent_session_columns.fullpath + recent_session_display.set_tooltip_column(1); // recent_session_columns.fullpath recent_session_display.set_model (recent_session_model); return rs.size(); } @@ -1036,21 +993,81 @@ void ArdourStartup::recent_session_row_selected () { if (recent_session_display.get_selection()->count_selected_rows() > 0) { - ic_new_session_button.set_active (false); + set_page_complete (session_vbox, true); } else { - ic_new_session_button.set_active (true); + set_page_complete (session_vbox, false); } } +void +ArdourStartup::setup_existing_session_page () +{ + recent_session_model = TreeStore::create (recent_session_columns); + redisplay_recent_sessions (); + + if (!session_hbox.get_children().empty()) { + session_hbox.remove (**session_hbox.get_children().begin()); + } + + if (session_existing_vbox.get_children().empty()) { + + recent_session_display.set_model (recent_session_model); + recent_session_display.append_column (_("Recent Sessions"), recent_session_columns.visible_name); + recent_session_display.set_headers_visible (false); + recent_session_display.get_selection()->set_mode (SELECTION_BROWSE); + + recent_session_display.get_selection()->signal_changed().connect (sigc::mem_fun (*this, &ArdourStartup::recent_session_row_selected)); + + recent_scroller.add (recent_session_display); + recent_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC); + recent_scroller.set_shadow_type (Gtk::SHADOW_IN); + + recent_session_display.show(); + + recent_scroller.show(); + int cnt = redisplay_recent_sessions (); + recent_session_display.signal_row_activated().connect (sigc::mem_fun (*this, &ArdourStartup::recent_row_activated)); + + if (cnt > 4) { + recent_scroller.set_size_request (-1, 300); + } + + session_existing_vbox.set_spacing (8); + session_existing_vbox.pack_start (recent_scroller, true, true); + + existing_session_chooser.set_title (_("Select session file")); + existing_session_chooser.signal_file_set().connect (sigc::mem_fun (*this, &ArdourStartup::existing_session_selected)); + cerr << "Set existing chooser to " << Config->get_default_session_parent_dir() << endl; + existing_session_chooser.set_current_folder(poor_mans_glob (Config->get_default_session_parent_dir())); + +#ifdef GTKOSX + existing_session_chooser.add_shortcut_folder ("/Volumes"); +#endif + + HBox* hbox = manage (new HBox); + hbox->set_spacing (4); + hbox->pack_start (*manage (new Label (_("Browse:"))), PACK_SHRINK); + hbox->pack_start (existing_session_chooser); + session_existing_vbox.pack_start (*hbox, false, false); + hbox->show_all (); + } + + session_existing_vbox.show_all (); + session_hbox.pack_start (session_existing_vbox, true, true); + + set_page_title (session_vbox, _("Select a session")); + set_page_type (session_vbox, ASSISTANT_PAGE_CONFIRM); +} + void ArdourStartup::more_new_session_options_button_clicked () { if (more_new_session_options_button.get_active()) { more_options_vbox.show_all (); set_page_type (more_options_vbox, ASSISTANT_PAGE_CONFIRM); - set_page_type (new_session_vbox, ASSISTANT_PAGE_CONTENT); + set_page_type (session_vbox, ASSISTANT_PAGE_CONTENT); } else { - set_page_type (new_session_vbox, ASSISTANT_PAGE_CONFIRM); + set_page_type (session_vbox, ASSISTANT_PAGE_CONFIRM); more_options_vbox.hide (); } } @@ -1354,7 +1371,7 @@ ArdourStartup::move_along_now () { gint cur = get_current_page (); - if (cur == new_session_page_index) { + if (cur == session_page_index) { if (more_new_session_options_button.get_active()) { set_current_page (session_options_page_index); } else { @@ -1366,18 +1383,17 @@ ArdourStartup::move_along_now () void ArdourStartup::recent_row_activated (const Gtk::TreePath&, Gtk::TreeViewColumn*) { - ic_new_session_button.set_active (false); - set_page_type (ic_vbox, ASSISTANT_PAGE_CONFIRM); - on_apply (); + set_page_complete (session_vbox, true); + move_along_now (); } void ArdourStartup::existing_session_selected () { - ic_new_session_button.set_active (false); _existing_session_chooser_used = true; - set_page_type (ic_vbox, ASSISTANT_PAGE_CONFIRM); - on_apply (); + + set_page_complete (session_vbox, true); + move_along_now (); } sys::path diff --git a/gtk2_ardour/startup.h b/gtk2_ardour/startup.h index 19cb1b609d..052a168a37 100644 --- a/gtk2_ardour/startup.h +++ b/gtk2_ardour/startup.h @@ -115,9 +115,9 @@ class ArdourStartup : public Gtk::Assistant { void setup_initial_choice_page (); Gtk::VBox ic_vbox; - Gtk::CheckButton ic_new_session_button; + Gtk::RadioButton ic_new_session_button; + Gtk::RadioButton ic_existing_session_button; bool initial_button_press(GdkEventButton *); - void initial_button_clicked (); void initial_button_activated(); /* monitoring choices */ @@ -136,11 +136,15 @@ class ArdourStartup : public Gtk::Assistant { Gtk::RadioButton no_monitor_section_button; void setup_monitor_section_choice_page (); - /* new session page */ + /* session page (could be new or existing) */ - void setup_new_session_page (); - Gtk::VBox new_session_vbox; - Gtk::HBox new_session_hbox; + void setup_session_page (); + Gtk::VBox session_vbox; + Gtk::HBox session_hbox; + + /* recent sessions */ + + void setup_existing_session_page (); struct RecentSessionsSorter { bool operator() (std::pair a, std::pair b) const { @@ -175,6 +179,7 @@ class ArdourStartup : public Gtk::Assistant { /* new sessions */ + void setup_new_session_page (); Gtk::Entry new_name_entry; Gtk::FileChooserButton new_folder_chooser; Gtk::FileChooserButton session_template_chooser; @@ -279,7 +284,7 @@ class ArdourStartup : public Gtk::Assistant { gint default_folder_page_index; gint monitoring_page_index; gint monitor_section_page_index; - gint new_session_page_index; + gint session_page_index; gint initial_choice_index; gint final_page_index; gint session_options_page_index; diff --git a/gtk2_ardour/step_editor.cc b/gtk2_ardour/step_editor.cc index 45e286dc70..2b5ab0f690 100644 --- a/gtk2_ardour/step_editor.cc +++ b/gtk2_ardour/step_editor.cc @@ -388,7 +388,7 @@ StepEditor::playlist_changed () { step_edit_region_connection.disconnect (); _track->playlist()->RegionRemoved.connect (step_edit_region_connection, invalidator (*this), - ui_bind (&StepEditor::region_removed, this, _1), + boost::bind (&StepEditor::region_removed, this, _1), gui_context()); } diff --git a/gtk2_ardour/stereo_panner.cc b/gtk2_ardour/stereo_panner.cc index 83e51a5540..bceb009b75 100644 --- a/gtk2_ardour/stereo_panner.cc +++ b/gtk2_ardour/stereo_panner.cc @@ -549,6 +549,7 @@ StereoPanner::on_key_press_event (GdkEventKey* ev) } else { width_control->set_value (wv - step); } + break; case GDK_Left: pv -= step; @@ -558,8 +559,6 @@ StereoPanner::on_key_press_event (GdkEventKey* ev) pv += step; position_control->set_value (pv); break; - - break; case GDK_0: case GDK_KP_0: width_control->set_value (0.0); diff --git a/gtk2_ardour/streamview.cc b/gtk2_ardour/streamview.cc index 040635d628..5dd58deb26 100644 --- a/gtk2_ardour/streamview.cc +++ b/gtk2_ardour/streamview.cc @@ -336,9 +336,9 @@ StreamView::playlist_switched (boost::weak_ptr wtr) /* catch changes */ tr->playlist()->LayeringChanged.connect (playlist_connections, invalidator (*this), boost::bind (&StreamView::playlist_layered, this, boost::weak_ptr (tr)), gui_context()); - tr->playlist()->RegionAdded.connect (playlist_connections, invalidator (*this), ui_bind (&StreamView::add_region_view, this, _1), gui_context()); - tr->playlist()->RegionRemoved.connect (playlist_connections, invalidator (*this), ui_bind (&StreamView::remove_region_view, this, _1), gui_context()); - tr->playlist()->ContentsChanged.connect (playlist_connections, invalidator (*this), ui_bind (&StreamView::update_coverage_frames, this), gui_context()); + tr->playlist()->RegionAdded.connect (playlist_connections, invalidator (*this), boost::bind (&StreamView::add_region_view, this, _1), gui_context()); + tr->playlist()->RegionRemoved.connect (playlist_connections, invalidator (*this), boost::bind (&StreamView::remove_region_view, this, _1), gui_context()); + tr->playlist()->ContentsChanged.connect (playlist_connections, invalidator (*this), boost::bind (&StreamView::update_coverage_frames, this), gui_context()); } @@ -547,7 +547,7 @@ StreamView::get_selectables (framepos_t start, framepos_t end, double top, doubl layer_ok = (min_layer <= l && l <= max_layer); } - if ((*i)->region()->coverage (start, end) != OverlapNone && layer_ok) { + if ((*i)->region()->coverage (start, end) != Evoral::OverlapNone && layer_ok) { results.push_back (*i); } } diff --git a/gtk2_ardour/streamview.h b/gtk2_ardour/streamview.h index 349b380d9f..f49bc87259 100644 --- a/gtk2_ardour/streamview.h +++ b/gtk2_ardour/streamview.h @@ -142,7 +142,7 @@ protected: void diskstream_changed (); void layer_regions (); - virtual void playlist_switched (boost::weak_ptr); + void playlist_switched (boost::weak_ptr); virtual void color_handler () = 0; diff --git a/gtk2_ardour/strip_silence_dialog.cc b/gtk2_ardour/strip_silence_dialog.cc index 6a6c6fc0d7..0049371a17 100644 --- a/gtk2_ardour/strip_silence_dialog.cc +++ b/gtk2_ardour/strip_silence_dialog.cc @@ -113,7 +113,7 @@ StripSilenceDialog::StripSilenceDialog (Session* s, list const & v) update_threshold_line (); /* Create a thread which runs while the dialogue is open to compute the silence regions */ - Completed.connect (_completed_connection, MISSING_INVALIDATOR, ui_bind (&StripSilenceDialog::update, this), gui_context ()); + Completed.connect (_completed_connection, MISSING_INVALIDATOR, boost::bind (&StripSilenceDialog::update, this), gui_context ()); _thread_should_finish = false; pthread_create (&_thread, 0, StripSilenceDialog::_detection_thread_work, this); } diff --git a/gtk2_ardour/tempo_lines.cc b/gtk2_ardour/tempo_lines.cc index 2c7895f7a8..ace2426bac 100644 --- a/gtk2_ardour/tempo_lines.cc +++ b/gtk2_ardour/tempo_lines.cc @@ -48,8 +48,9 @@ TempoLines::tempo_map_changed() ++next; i->second->property_x1() = - d; i->second->property_x2() = - d; + ArdourCanvas::SimpleLine* f = i->second; _lines.erase(i); - _lines.insert(make_pair(- d, i->second)); + _lines.insert(make_pair(- d, f)); i = next; } } @@ -173,6 +174,7 @@ TempoLines::draw (const ARDOUR::TempoMap::BBTPointList::const_iterator& begin, // Steal from the right if (left->first > needed_left && li != steal && steal->first > needed_right) { //cout << "*** STEALING FROM RIGHT" << endl; + double const x = steal->first; line = steal->second; _lines.erase(steal); line->property_x1() = xpos; @@ -184,7 +186,7 @@ TempoLines::draw (const ARDOUR::TempoMap::BBTPointList::const_iterator& begin, // Shift clean range left _clean_left = min(_clean_left, xpos); - _clean_right = min(_clean_right, steal->first); + _clean_right = min(_clean_right, x); // Move this line to where we need it } else { @@ -229,6 +231,7 @@ TempoLines::draw (const ARDOUR::TempoMap::BBTPointList::const_iterator& begin, //cout << "*** STEALING FROM LEFT" << endl; assert(_lines.find(xpos) == _lines.end()); Lines::iterator steal = _lines.begin(); + double const x = steal->first; line = steal->second; _lines.erase(steal); line->property_color_rgba() = color; @@ -239,7 +242,7 @@ TempoLines::draw (const ARDOUR::TempoMap::BBTPointList::const_iterator& begin, invalidated = true; // Shift clean range right - _clean_left = max(_clean_left, steal->first); + _clean_left = max(_clean_left, x); _clean_right = max(_clean_right, xpos); } } diff --git a/gtk2_ardour/theme_manager.cc b/gtk2_ardour/theme_manager.cc index 47ae4b7390..887ca5453c 100644 --- a/gtk2_ardour/theme_manager.cc +++ b/gtk2_ardour/theme_manager.cc @@ -334,7 +334,7 @@ ThemeManager::setup_theme () if (rcfile == "ardour3_ui_dark.rc" || rcfile == "ardour3_ui_dark_sae.rc") { dark_button.set_active(); - } else if (rcfile == "ardour3_ui_light.rc" || "ardour3_ui_light_sae.rc") { + } else if (rcfile == "ardour3_ui_light.rc" || rcfile == "ardour3_ui_light_sae.rc") { light_button.set_active(); } diff --git a/gtk2_ardour/time_axis_view.cc b/gtk2_ardour/time_axis_view.cc index 104209d884..8c30772f0c 100644 --- a/gtk2_ardour/time_axis_view.cc +++ b/gtk2_ardour/time_axis_view.cc @@ -177,7 +177,7 @@ TimeAxisView::TimeAxisView (ARDOUR::Session* sess, PublicEditor& ed, TimeAxisVie ColorsChanged.connect (sigc::mem_fun (*this, &TimeAxisView::color_handler)); - GhostRegion::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&TimeAxisView::erase_ghost, this, _1), gui_context()); + GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&TimeAxisView::erase_ghost, this, _1), gui_context()); } TimeAxisView::~TimeAxisView() @@ -495,16 +495,6 @@ TimeAxisView::step_height (bool coarser) } } -void -TimeAxisView::set_heights (uint32_t h) -{ - TrackSelection& ts (_editor.get_selection().tracks); - - for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) { - (*i)->set_height (h); - } -} - void TimeAxisView::set_height_enum (Height h, bool apply_to_selection) { diff --git a/gtk2_ardour/time_axis_view.h b/gtk2_ardour/time_axis_view.h index 52385b17aa..03c50e7ccb 100644 --- a/gtk2_ardour/time_axis_view.h +++ b/gtk2_ardour/time_axis_view.h @@ -286,7 +286,6 @@ class TimeAxisView : public virtual AxisView bool in_destructor; NamePackingBits name_packing; - void set_heights (uint32_t h); void color_handler (); void conditionally_add_to_selection (); diff --git a/gtk2_ardour/time_axis_view_item.cc b/gtk2_ardour/time_axis_view_item.cc index 9df586bf7f..c0a90f7111 100644 --- a/gtk2_ardour/time_axis_view_item.cc +++ b/gtk2_ardour/time_axis_view_item.cc @@ -228,7 +228,7 @@ TimeAxisViewItem::init ( set_duration (item_duration, this); set_position (start, this); - Config->ParameterChanged.connect (*this, invalidator (*this), ui_bind (&TimeAxisViewItem::parameter_changed, this, _1), gui_context ()); + Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&TimeAxisViewItem::parameter_changed, this, _1), gui_context ()); } TimeAxisViewItem::~TimeAxisViewItem() @@ -321,7 +321,7 @@ TimeAxisViewItem::get_position() const */ bool -TimeAxisViewItem::set_duration (framepos_t dur, void* src) +TimeAxisViewItem::set_duration (framecnt_t dur, void* src) { if ((dur > max_item_duration) || (dur < min_item_duration)) { warning << string_compose (_("new duration %1 frames is out of bounds for %2"), get_item_name(), dur) diff --git a/gtk2_ardour/time_axis_view_item.h b/gtk2_ardour/time_axis_view_item.h index 5bcaa48545..6b4bc09ec1 100644 --- a/gtk2_ardour/time_axis_view_item.h +++ b/gtk2_ardour/time_axis_view_item.h @@ -144,7 +144,7 @@ class TimeAxisViewItem : public Selectable, public PBD::ScopedConnectionList protected: TimeAxisViewItem(const std::string &, ArdourCanvas::Group&, TimeAxisView&, double, Gdk::Color const &, - framepos_t, framepos_t, bool recording = false, bool automation = false, Visibility v = Visibility (0)); + framepos_t, framecnt_t, bool recording = false, bool automation = false, Visibility v = Visibility (0)); TimeAxisViewItem (const TimeAxisViewItem&); diff --git a/gtk2_ardour/time_info_box.cc b/gtk2_ardour/time_info_box.cc index 00b760952c..2cb7e652b5 100644 --- a/gtk2_ardour/time_info_box.cc +++ b/gtk2_ardour/time_info_box.cc @@ -32,6 +32,8 @@ #include "time_info_box.h" #include "audio_clock.h" #include "editor.h" +#include "control_point.h" +#include "automation_line.h" #include "i18n.h" @@ -142,7 +144,7 @@ TimeInfoBox::TimeInfoBox () Editor::instance().get_selection().TimeChanged.connect (sigc::mem_fun (*this, &TimeInfoBox::selection_changed)); Editor::instance().get_selection().RegionsChanged.connect (sigc::mem_fun (*this, &TimeInfoBox::selection_changed)); - Editor::instance().MouseModeChanged.connect (editor_connections, invalidator(*this), ui_bind (&TimeInfoBox::track_mouse_mode, this), gui_context()); + Editor::instance().MouseModeChanged.connect (editor_connections, invalidator(*this), boost::bind (&TimeInfoBox::track_mouse_mode, this), gui_context()); } TimeInfoBox::~TimeInfoBox () @@ -267,8 +269,9 @@ TimeInfoBox::selection_changed () s = max_framepos; e = 0; for (PointSelection::iterator i = selection.points.begin(); i != selection.points.end(); ++i) { - s = min (s, (framepos_t) i->start); - e = max (e, (framepos_t) i->end); + framepos_t const p = (*i)->line().session_position ((*i)->model ()); + s = min (s, p); + e = max (e, p); } selection_start->set_off (false); selection_end->set_off (false); diff --git a/gtk2_ardour/time_selection.cc b/gtk2_ardour/time_selection.cc index 064e654cff..4bc0e2a96a 100644 --- a/gtk2_ardour/time_selection.cc +++ b/gtk2_ardour/time_selection.cc @@ -55,9 +55,9 @@ TimeSelection::consolidate () continue; } - if ((*a).coverage ((*b).start, (*b).end) != OverlapNone) { - (*a).start = std::min ((*a).start, (*b).start); - (*a).end = std::max ((*a).end, (*b).end); + if (a->coverage (b->start, b->end) != Evoral::OverlapNone) { + a->start = std::min (a->start, b->start); + a->end = std::max (a->end, b->end); erase (b); changed = true; goto restart; diff --git a/gtk2_ardour/window_proxy.cc b/gtk2_ardour/window_proxy.cc index ff525eb392..99da166af0 100755 --- a/gtk2_ardour/window_proxy.cc +++ b/gtk2_ardour/window_proxy.cc @@ -19,6 +19,9 @@ #include #include "window_proxy.h" + +#include "pbd/convert.h" + #include "i18n.h" using namespace std; @@ -57,7 +60,7 @@ WindowProxyBase::WindowProxyBase (string const & name, XMLNode const * node) XMLProperty* prop; if ((prop = (*i)->property (X_("visible"))) != 0) { - _visible = string_is_affirmative (prop->value ()); + _visible = PBD::string_is_affirmative (prop->value ()); } if ((prop = (*i)->property (X_("x-off"))) != 0) { diff --git a/gtk2_ardour/wscript b/gtk2_ardour/wscript index 575a7a50d9..338a585dca 100644 --- a/gtk2_ardour/wscript +++ b/gtk2_ardour/wscript @@ -67,8 +67,6 @@ gtk2_ardour_sources = [ 'configinfo.cc', 'control_point.cc', 'control_point_dialog.cc', - 'crossfade_edit.cc', - 'crossfade_view.cc', 'curvetest.cc', 'debug.cc', 'diamond.cc', diff --git a/libs/ardour/amp.cc b/libs/ardour/amp.cc index b74a57dbce..24b2066ed5 100644 --- a/libs/ardour/amp.cc +++ b/libs/ardour/amp.cc @@ -47,6 +47,8 @@ Amp::Amp (Session& s) p.set_range (0, 1.99526231f, 1, false); boost::shared_ptr gl (new AutomationList (p)); _gain_control = boost::shared_ptr (new GainControl (X_("gaincontrol"), s, this, p, gl)); + _gain_control->set_flags (Controllable::GainLike); + add_control(_gain_control); } diff --git a/libs/ardour/ardour/audioplaylist.h b/libs/ardour/ardour/audioplaylist.h index fea70aade7..39efd2505d 100644 --- a/libs/ardour/ardour/audioplaylist.h +++ b/libs/ardour/ardour/audioplaylist.h @@ -33,96 +33,32 @@ class Region; class AudioRegion; class Source; -namespace Properties { - /* fake the type, since crossfades are handled by SequenceProperty which doesn't - care about such things. - */ - extern PBD::PropertyDescriptor crossfades; -} - class AudioPlaylist; -class CrossfadeListProperty : public PBD::SequenceProperty > > -{ -public: - CrossfadeListProperty (AudioPlaylist &); - - void get_content_as_xml (boost::shared_ptr, XMLNode &) const; - boost::shared_ptr get_content_from_xml (XMLNode const &) const; - -private: - CrossfadeListProperty* clone () const; - CrossfadeListProperty* create () const; - - /* copy construction only by ourselves */ - CrossfadeListProperty (CrossfadeListProperty const & p); - - friend class AudioPlaylist; - /* we live and die with our playlist, no lifetime management needed */ - AudioPlaylist& _playlist; -}; - - class AudioPlaylist : public ARDOUR::Playlist { public: - typedef std::list > Crossfades; - static void make_property_quarks (); - AudioPlaylist (Session&, const XMLNode&, bool hidden = false); AudioPlaylist (Session&, std::string name, bool hidden = false); AudioPlaylist (boost::shared_ptr, std::string name, bool hidden = false); AudioPlaylist (boost::shared_ptr, framepos_t start, framecnt_t cnt, std::string name, bool hidden = false); - ~AudioPlaylist (); - - void clear (bool with_signals=true); - framecnt_t read (Sample *dst, Sample *mixdown, float *gain_buffer, framepos_t start, framecnt_t cnt, uint32_t chan_n=0); - int set_state (const XMLNode&, int version); - - PBD::Signal1 > NewCrossfade; - - void foreach_crossfade (boost::function)>); - void crossfades_at (framepos_t frame, Crossfades&); - bool destroy_region (boost::shared_ptr); - void update (const CrossfadeListProperty::ChangeRecord &); - - boost::shared_ptr find_crossfade (const PBD::ID &) const; - void get_equivalent_crossfades (boost::shared_ptr, std::vector > &); - protected: - /* playlist "callbacks" */ - void notify_crossfade_added (boost::shared_ptr); - void flush_notifications (bool); - - void finalize_split_region (boost::shared_ptr orig, boost::shared_ptr left, boost::shared_ptr right); - - void refresh_dependents (boost::shared_ptr region); - void check_dependents (boost::shared_ptr region, bool norefresh); - void remove_dependents (boost::shared_ptr region); - void copy_dependents (const std::vector&, Playlist*) const; + void check_crossfades (Evoral::Range); void pre_combine (std::vector >&); void post_combine (std::vector >&, boost::shared_ptr); void pre_uncombine (std::vector >&, boost::shared_ptr); private: - CrossfadeListProperty _crossfades; - Crossfades _pending_xfade_adds; - - void crossfade_invalidated (boost::shared_ptr); - XMLNode& state (bool full_state); + int set_state (const XMLNode&, int version); void dump () const; - bool region_changed (const PBD::PropertyChange&, boost::shared_ptr); - void crossfade_changed (const PBD::PropertyChange&); - void add_crossfade (boost::shared_ptr); - void source_offset_changed (boost::shared_ptr region); }; diff --git a/libs/ardour/ardour/audioregion.h b/libs/ardour/ardour/audioregion.h index c1476f69a4..7236cd69a9 100644 --- a/libs/ardour/ardour/audioregion.h +++ b/libs/ardour/ardour/audioregion.h @@ -34,7 +34,8 @@ #include "ardour/region.h" class XMLNode; - +class AudioRegionTest; +class PlaylistReadTest; namespace ARDOUR { @@ -92,6 +93,8 @@ class AudioRegion : public Region boost::shared_ptr fade_out() { return _fade_out; } boost::shared_ptr envelope() { return _envelope; } + Evoral::Range body_range () const; + virtual framecnt_t read_peaks (PeakData *buf, framecnt_t npeaks, framecnt_t offset, framecnt_t cnt, uint32_t chan_n=0, double samples_per_unit= 1.0) const; @@ -137,6 +140,9 @@ class AudioRegion : public Region void set_fade_out (FadeShape, framecnt_t); void set_fade_out (boost::shared_ptr); + void set_default_fade_in (); + void set_default_fade_out (); + void set_envelope_active (bool yn); void set_default_envelope (); @@ -166,7 +172,7 @@ class AudioRegion : public Region int set_transients (AnalysisFeatureList&); int get_transients (AnalysisFeatureList&, bool force_new = false); int update_transient (framepos_t old_position, framepos_t new_position); - int adjust_transients (framepos_t delta); + int adjust_transients (frameoffset_t delta); AudioIntervalResult find_silence (Sample, framecnt_t, InterThreadInfo&) const; @@ -182,6 +188,9 @@ class AudioRegion : public Region AudioRegion (SourceList &); private: + friend class ::AudioRegionTest; + friend class ::PlaylistReadTest; + PBD::Property _envelope_active; PBD::Property _default_fade_in; PBD::Property _default_fade_out; @@ -195,8 +204,6 @@ class AudioRegion : public Region void init (); void set_default_fades (); - void set_default_fade_in (); - void set_default_fade_out (); void recompute_gain_at_end (); void recompute_gain_at_start (); diff --git a/libs/ardour/ardour/audiosource.h b/libs/ardour/ardour/audiosource.h index 1d811bbfa3..c6ec2a56ad 100644 --- a/libs/ardour/ardour/audiosource.h +++ b/libs/ardour/ardour/audiosource.h @@ -51,7 +51,7 @@ class AudioSource : virtual public Source, virtual bool empty() const; framecnt_t length (framepos_t pos) const; - void update_length (framepos_t pos, framecnt_t cnt); + void update_length (framecnt_t cnt); virtual framecnt_t available_peaks (double zoom) const; diff --git a/libs/ardour/ardour/auditioner.h b/libs/ardour/ardour/auditioner.h index 8c5a21bea6..3650f0c92c 100644 --- a/libs/ardour/ardour/auditioner.h +++ b/libs/ardour/ardour/auditioner.h @@ -44,7 +44,6 @@ class Auditioner : public AudioTrack void audition_region (boost::shared_ptr); ARDOUR::AudioPlaylist& prepare_playlist (); - void audition_current_playlist (); int play_audition (framecnt_t nframes); diff --git a/libs/ardour/ardour/automation_control.h b/libs/ardour/ardour/automation_control.h index 9ad46c9090..2c15a1b1b0 100644 --- a/libs/ardour/ardour/automation_control.h +++ b/libs/ardour/ardour/automation_control.h @@ -38,9 +38,9 @@ class AutomationControl : public PBD::Controllable, public Evoral::Control { public: AutomationControl(ARDOUR::Session&, - const Evoral::Parameter& parameter, - boost::shared_ptr l=boost::shared_ptr(), - const std::string& name=""); + const Evoral::Parameter& parameter, + boost::shared_ptr l=boost::shared_ptr(), + const std::string& name=""); boost::shared_ptr alist() const { return boost::dynamic_pointer_cast(_list); diff --git a/libs/ardour/ardour/configuration_variable.h b/libs/ardour/ardour/configuration_variable.h index da8fc1b9a1..a7fe8def48 100644 --- a/libs/ardour/ardour/configuration_variable.h +++ b/libs/ardour/ardour/configuration_variable.h @@ -24,6 +24,7 @@ #include #include "pbd/xml++.h" +#include "pbd/convert.h" #include "ardour/types.h" #include "ardour/utils.h" @@ -153,7 +154,7 @@ class ConfigVariable : public ConfigVariableBase } void set_from_string (std::string const & s) { - value = string_is_affirmative (s); + value = PBD::string_is_affirmative (s); } protected: diff --git a/libs/ardour/ardour/control_protocol_manager.h b/libs/ardour/ardour/control_protocol_manager.h index ad5bb3166e..19abbaf6be 100644 --- a/libs/ardour/ardour/control_protocol_manager.h +++ b/libs/ardour/ardour/control_protocol_manager.h @@ -62,10 +62,11 @@ class ControlProtocolManager : public PBD::Stateful, public ARDOUR::SessionHandl void discover_control_protocols (); void foreach_known_protocol (boost::function); void load_mandatory_protocols (); + void midi_connectivity_established (); ControlProtocol* instantiate (ControlProtocolInfo&); int teardown (ControlProtocolInfo&); - + std::list control_protocol_info; static const std::string state_node_name; diff --git a/libs/ardour/ardour/crossfade.h b/libs/ardour/ardour/crossfade.h deleted file mode 100644 index 1c7075eb7d..0000000000 --- a/libs/ardour/ardour/crossfade.h +++ /dev/null @@ -1,180 +0,0 @@ -/* - Copyright (C) 2000 Paul Davis - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#ifndef __ardour_overlap_h__ -#define __ardour_overlap_h__ - -#include -#include -#include - - -#include "pbd/undo.h" -#include "pbd/statefuldestructible.h" - -#include "ardour/ardour.h" -#include "ardour/audioregion.h" -#include "evoral/Curve.hpp" - -namespace ARDOUR { - namespace Properties { - /* "active" is defined elsewhere but we use it with crossfade also */ - extern PBD::PropertyDescriptor active; - extern PBD::PropertyDescriptor follow_overlap; - } - -enum AnchorPoint { - StartOfIn, - EndOfIn, - EndOfOut -}; - -class Playlist; - -class Crossfade : public ARDOUR::AudioRegion -{ - public: - - class NoCrossfadeHere: std::exception { - public: - virtual const char *what() const throw() { return "no crossfade should be constructed here"; } - }; - - /* constructor for "fixed" xfades at each end of an internal overlap */ - - Crossfade (boost::shared_ptr in, boost::shared_ptr out, - framecnt_t initial_length, - AnchorPoint); - - /* constructor for xfade between two regions that are overlapped in any way - except the "internal" case. - */ - - Crossfade (boost::shared_ptr in, boost::shared_ptr out, CrossfadeModel, bool active); - - - /* copy constructor to copy a crossfade with new regions. used (for example) - when a playlist copy is made - */ - Crossfade (boost::shared_ptr, boost::shared_ptr, boost::shared_ptr); - - /* the usual XML constructor */ - - Crossfade (const Playlist&, XMLNode const &); - virtual ~Crossfade(); - - static void make_property_quarks (); - - XMLNode& get_state (void); - int set_state (const XMLNode&, int version); - - boost::shared_ptr in() const { return _in; } - boost::shared_ptr out() const { return _out; } - - framecnt_t read_at (Sample *buf, Sample *mixdown_buffer, - float *gain_buffer, framepos_t position, framecnt_t cnt, - uint32_t chan_n) const; - - bool refresh (); - - uint32_t upper_layer () const { - return std::max (_in->layer(), _out->layer()); - } - - uint32_t lower_layer () const { - return std::min (_in->layer(), _out->layer()); - } - - bool involves (boost::shared_ptr region) const { - return _in == region || _out == region; - } - - bool involves (boost::shared_ptr a, boost::shared_ptr b) const { - return (_in == a && _out == b) || (_in == b && _out == a); - } - - framecnt_t overlap_length() const; - - PBD::Signal1 > Invalidated; - - OverlapType coverage (framepos_t start, framepos_t end) const; - - static void set_buffer_size (framecnt_t); - - bool active () const { return _active; } - void set_active (bool yn); - - bool following_overlap() const { return _follow_overlap; } - bool can_follow_overlap() const; - void set_follow_overlap (bool yn); - - AutomationList& fade_in() { return _fade_in; } - AutomationList& fade_out() { return _fade_out; } - - framecnt_t set_xfade_length (framecnt_t); - - bool is_dependent() const { return true; } - bool depends_on (boost::shared_ptr other) const { - return other == _in || other == _out; - } - - static framecnt_t short_xfade_length() { return _short_xfade_length; } - static void set_short_xfade_length (framecnt_t n); - - /** emitted when the actual fade curves change, as opposed to one of the Stateful properties */ - PBD::Signal0 FadesChanged; - - private: - friend struct CrossfadeComparePtr; - friend class AudioPlaylist; - - static framecnt_t _short_xfade_length; - - boost::shared_ptr _in; - boost::shared_ptr _out; - PBD::Property _active; - PBD::Property _follow_overlap; - bool _in_update; - OverlapType overlap_type; - AnchorPoint _anchor_point; - bool _fixed; - int32_t layer_relation; - - - mutable AutomationList _fade_in; - mutable AutomationList _fade_out; - - static Sample* crossfade_buffer_out; - static Sample* crossfade_buffer_in; - - void initialize (); - void register_properties (); - int compute (boost::shared_ptr, boost::shared_ptr, CrossfadeModel); - bool update (); - - bool operator== (const ARDOUR::Crossfade&); - - protected: - framecnt_t read_raw_internal (Sample*, framepos_t, framecnt_t, int) const; -}; - - -} // namespace ARDOUR - -#endif /* __ardour_overlap_h__ */ diff --git a/libs/ardour/ardour/crossfade_binder.h b/libs/ardour/ardour/crossfade_binder.h deleted file mode 100644 index 1adfdde545..0000000000 --- a/libs/ardour/ardour/crossfade_binder.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - Copyright (C) 2011 Paul Davis - Author: Carl Hetherington - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#include "pbd/id.h" -#include "pbd/memento_command.h" - -class XMLNode; - -namespace ARDOUR { - -class Crossfade; -class Playlist; -class SessionPlaylists; - -/** A MementoCommandBinder for Crossfades; required because the undo record - * may contain details of crossfades that have subsequently been deleted. - * This class allows recovery of a crossfade from an ID once it has been - * recreated by a previous undo step. - */ -class CrossfadeBinder : public MementoCommandBinder -{ -public: - CrossfadeBinder (boost::shared_ptr, PBD::ID); - CrossfadeBinder (XMLNode *, boost::shared_ptr); - - ARDOUR::Crossfade* get () const; - std::string type_name () const; - void add_state (XMLNode *); - -private: - boost::shared_ptr _playlists; - PBD::ID _id; -}; - -} diff --git a/libs/ardour/ardour/delivery.h b/libs/ardour/ardour/delivery.h index cb2f37d1f0..4ee171c033 100644 --- a/libs/ardour/ardour/delivery.h +++ b/libs/ardour/ardour/delivery.h @@ -74,7 +74,7 @@ public: /* supplemental method used with MIDI */ - void flush_buffers (framecnt_t nframes, framepos_t time); + void flush_buffers (framecnt_t nframes); void no_outs_cuz_we_no_monitor(bool); void transport_stopped (framepos_t frame); void realtime_locate (); diff --git a/libs/ardour/ardour/diskstream.h b/libs/ardour/ardour/diskstream.h index fb2327324d..bbc3a85dbe 100644 --- a/libs/ardour/ardour/diskstream.h +++ b/libs/ardour/ardour/diskstream.h @@ -243,8 +243,10 @@ class Diskstream : public SessionObject, public PublicDiskstream virtual void use_destructive_playlist () {} virtual void prepare_to_stop (framepos_t pos); - void calculate_record_range(OverlapType ot, framepos_t transport_frame, framecnt_t nframes, - framecnt_t& rec_nframes, framecnt_t& rec_offset); + void calculate_record_range ( + Evoral::OverlapType ot, framepos_t transport_frame, framecnt_t nframes, + framecnt_t& rec_nframes, framecnt_t& rec_offset + ); static framecnt_t disk_io_chunk_frames; std::vector capture_info; diff --git a/libs/ardour/ardour/importable_source.h b/libs/ardour/ardour/importable_source.h index 804b062969..fc695d88c1 100644 --- a/libs/ardour/ardour/importable_source.h +++ b/libs/ardour/ardour/importable_source.h @@ -35,7 +35,7 @@ public: virtual uint32_t channels() const = 0; virtual framecnt_t length() const = 0; virtual framecnt_t samplerate() const = 0; - virtual void seek (framecnt_t pos) = 0; + virtual void seek (framepos_t pos) = 0; virtual framepos_t natural_position() const = 0; virtual bool clamped_at_unity () const = 0; diff --git a/libs/ardour/ardour/location.h b/libs/ardour/ardour/location.h index 71f4c1aa6a..069230438e 100644 --- a/libs/ardour/ardour/location.h +++ b/libs/ardour/ardour/location.h @@ -63,7 +63,7 @@ class Location : public SessionHandleRef, public PBD::StatefulDestructible framepos_t start() const { return _start; } framepos_t end() const { return _end; } - framepos_t length() const { return _end - _start; } + framecnt_t length() const { return _end - _start; } int set_start (framepos_t s, bool force = false, bool allow_bbt_recompute = true); int set_end (framepos_t e, bool force = false, bool allow_bbt_recompute = true); diff --git a/libs/ardour/ardour/lv2_plugin.h b/libs/ardour/ardour/lv2_plugin.h index 49d349f844..a4de7c4ae1 100644 --- a/libs/ardour/ardour/lv2_plugin.h +++ b/libs/ardour/ardour/lv2_plugin.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2008-2011 Paul Davis + Copyright (C) 2008-2012 Paul Davis Author: David Robillard This program is free software; you can redistribute it and/or modify @@ -26,6 +26,7 @@ #include "ardour/plugin.h" #include "ardour/uri_map.h" +#include "ardour/worker.h" #include "pbd/ringbuffer.h" namespace ARDOUR { @@ -33,7 +34,7 @@ namespace ARDOUR { class AudioEngine; class Session; -class LV2Plugin : public ARDOUR::Plugin +class LV2Plugin : public ARDOUR::Plugin, public ARDOUR::Workee { public: LV2Plugin (ARDOUR::AudioEngine& engine, @@ -49,14 +50,16 @@ class LV2Plugin : public ARDOUR::Plugin const char* name () const; const char* maker () const; - uint32_t num_ports () const; - uint32_t parameter_count () const; - float default_value (uint32_t port); - framecnt_t signal_latency () const; - void set_parameter (uint32_t port, float val); - float get_parameter (uint32_t port) const; - int get_parameter_descriptor (uint32_t which, ParameterDescriptor&) const; - uint32_t nth_parameter (uint32_t port, bool& ok) const; + uint32_t num_ports () const; + uint32_t parameter_count () const; + float default_value (uint32_t port); + framecnt_t signal_latency () const; + void set_parameter (uint32_t port, float val); + float get_parameter (uint32_t port) const; + std::string get_docs() const; + std::string get_parameter_docs(uint32_t which) const; + int get_parameter_descriptor (uint32_t which, ParameterDescriptor&) const; + uint32_t nth_parameter (uint32_t port, bool& ok) const; const void* extension_data (const char* uri) const; @@ -68,6 +71,7 @@ class LV2Plugin : public ARDOUR::Plugin bool ui_is_resizable () const; const char* port_symbol (uint32_t port) const; + uint32_t port_index (const char* symbol) const; const LV2_Feature* const* features () { return _features; } @@ -129,6 +133,11 @@ class LV2Plugin : public ARDOUR::Plugin void enable_ui_emmission(); void emit_to_ui(void* controller, UIMessageSink sink); + Worker* worker() { return _worker; } + + int work(uint32_t size, const void* data); + int work_response(uint32_t size, const void* data); + static URIMap _uri_map; static uint32_t _midi_event_type_ev; @@ -143,12 +152,15 @@ class LV2Plugin : public ARDOUR::Plugin Impl* _impl; void* _module; LV2_Feature** _features; + Worker* _worker; framecnt_t _sample_rate; float* _control_data; float* _shadow_data; float* _defaults; LV2_Evbuf** _ev_buffers; - float* _latency_control_port; + float* _bpm_control_port; ///< Special input set by ardour + float* _freewheel_control_port; ///< Special input set by ardour + float* _latency_control_port; ///< Special output set by ardour PBD::ID _insert_id; typedef enum { @@ -195,6 +207,7 @@ class LV2Plugin : public ARDOUR::Plugin LV2_Feature _data_access_feature; LV2_Feature _instance_access_feature; LV2_Feature _make_path_feature; + LV2_Feature _work_schedule_feature; mutable unsigned _state_version; diff --git a/libs/ardour/ardour/midi_playlist.h b/libs/ardour/ardour/midi_playlist.h index 339e07faa1..543e1b353f 100644 --- a/libs/ardour/ardour/midi_playlist.h +++ b/libs/ardour/ardour/midi_playlist.h @@ -63,12 +63,6 @@ public: protected: - /* playlist "callbacks" */ - - void finalize_split_region (boost::shared_ptr original, boost::shared_ptr left, boost::shared_ptr right); - - void check_dependents (boost::shared_ptr region, bool norefresh); - void refresh_dependents (boost::shared_ptr region); void remove_dependents (boost::shared_ptr region); private: diff --git a/libs/ardour/ardour/midi_playlist_source.h b/libs/ardour/ardour/midi_playlist_source.h index c64b6e53f9..b75e8c0bd2 100644 --- a/libs/ardour/ardour/midi_playlist_source.h +++ b/libs/ardour/ardour/midi_playlist_source.h @@ -60,12 +60,12 @@ protected: void flush_midi(); - framepos_t read_unlocked (Evoral::EventSink& dst, + framecnt_t read_unlocked (Evoral::EventSink& dst, framepos_t position, framepos_t start, framecnt_t cnt, MidiStateTracker* tracker) const; - framepos_t write_unlocked (MidiRingBuffer& dst, + framecnt_t write_unlocked (MidiRingBuffer& dst, framepos_t position, framecnt_t cnt); diff --git a/libs/ardour/ardour/midi_port.h b/libs/ardour/ardour/midi_port.h index a4d2f53806..84575ca351 100644 --- a/libs/ardour/ardour/midi_port.h +++ b/libs/ardour/ardour/midi_port.h @@ -41,7 +41,7 @@ class MidiPort : public Port { void cycle_end (pframes_t nframes); void cycle_split (); - void flush_buffers (pframes_t nframes, framepos_t time); + void flush_buffers (pframes_t nframes); void transport_stopped (); void realtime_locate (); void reset (); diff --git a/libs/ardour/ardour/midi_region.h b/libs/ardour/ardour/midi_region.h index 9868d1408f..2fe56f015f 100644 --- a/libs/ardour/ardour/midi_region.h +++ b/libs/ardour/ardour/midi_region.h @@ -77,7 +77,7 @@ class MidiRegion : public Region NoteMode mode = Sustained, MidiStateTracker* tracker = 0) const; - framepos_t master_read_at (MidiRingBuffer& dst, + framecnt_t master_read_at (MidiRingBuffer& dst, framepos_t position, framecnt_t dur, uint32_t chan_n = 0, @@ -132,6 +132,7 @@ class MidiRegion : public Region void set_position_internal (framepos_t pos, bool allow_bbt_recompute); void set_length_internal (framecnt_t len); + void set_start_internal (framecnt_t); void update_length_beats (); void model_changed (); diff --git a/libs/ardour/ardour/midi_source.h b/libs/ardour/ardour/midi_source.h index 3564fff4fc..0c2a1b29cb 100644 --- a/libs/ardour/ardour/midi_source.h +++ b/libs/ardour/ardour/midi_source.h @@ -76,7 +76,7 @@ class MidiSource : virtual public Source, public boost::enable_shared_from_this< virtual bool empty () const; virtual framecnt_t length (framepos_t pos) const; - virtual void update_length (framepos_t pos, framecnt_t cnt); + virtual void update_length (framecnt_t); virtual void mark_streaming_midi_write_started (NoteMode mode); virtual void mark_streaming_write_started (); @@ -142,12 +142,12 @@ class MidiSource : virtual public Source, public boost::enable_shared_from_this< protected: virtual void flush_midi() = 0; - virtual framepos_t read_unlocked (Evoral::EventSink& dst, + virtual framecnt_t read_unlocked (Evoral::EventSink& dst, framepos_t position, framepos_t start, framecnt_t cnt, MidiStateTracker* tracker) const = 0; - virtual framepos_t write_unlocked (MidiRingBuffer& dst, + virtual framecnt_t write_unlocked (MidiRingBuffer& dst, framepos_t position, framecnt_t cnt) = 0; diff --git a/libs/ardour/ardour/midi_track.h b/libs/ardour/ardour/midi_track.h index 45055a0c3d..bb810ff74a 100644 --- a/libs/ardour/ardour/midi_track.h +++ b/libs/ardour/ardour/midi_track.h @@ -62,7 +62,7 @@ public: boost::shared_ptr bounce (InterThreadInfo&); boost::shared_ptr bounce_range (framepos_t start, framepos_t end, InterThreadInfo&, boost::shared_ptr endpoint, bool include_endpoint); - int export_stuff (BufferSet& bufs, framecnt_t nframes, framepos_t end_frame, + int export_stuff (BufferSet& bufs, framepos_t start_frame, framecnt_t end_frame, boost::shared_ptr endpoint, bool include_endpoint, bool for_export); int set_state (const XMLNode&, int version); diff --git a/libs/ardour/ardour/midi_ui.h b/libs/ardour/ardour/midi_ui.h index 51e3ca2544..c66df96a6d 100644 --- a/libs/ardour/ardour/midi_ui.h +++ b/libs/ardour/ardour/midi_ui.h @@ -5,6 +5,7 @@ #include "pbd/abstract_ui.h" #include "pbd/signals.h" +#include "pbd/stacktrace.h" namespace MIDI { class Port; @@ -21,8 +22,8 @@ class Session; struct MidiUIRequest : public BaseUI::BaseRequestObject { public: - MidiUIRequest () {} - ~MidiUIRequest() {} + MidiUIRequest () { } + ~MidiUIRequest() { } }; class MidiControlUI : public AbstractUI diff --git a/libs/ardour/ardour/panner.h b/libs/ardour/ardour/panner.h index 6f54ea7d79..51d75ebf03 100644 --- a/libs/ardour/ardour/panner.h +++ b/libs/ardour/ardour/panner.h @@ -142,9 +142,6 @@ public: boost::shared_ptr pannable() const { return _pannable; } - //virtual std::string describe_parameter (Evoral::Parameter); - //virtual std::string value_as_string (Evoral::Parameter, double val); - static bool equivalent (pan_t a, pan_t b) { return fabsf (a - b) < 0.002; // about 1 degree of arc for a stereo panner } diff --git a/libs/ardour/ardour/playlist.h b/libs/ardour/ardour/playlist.h index 1f66310877..be848d381b 100644 --- a/libs/ardour/ardour/playlist.h +++ b/libs/ardour/ardour/playlist.h @@ -31,7 +31,9 @@ #include #include - +#ifdef HAVE_GLIB_THREADS_RECMUTEX +#include +#endif #include "pbd/undo.h" #include "pbd/stateful.h" @@ -154,7 +156,8 @@ public: uint32_t count_regions_at (framepos_t) const; uint32_t count_joined_regions () const; boost::shared_ptr regions_touched (framepos_t start, framepos_t end); - boost::shared_ptr regions_to_read (framepos_t start, framepos_t end); + boost::shared_ptr regions_with_start_within (Evoral::Range); + boost::shared_ptr regions_with_end_within (Evoral::Range); uint32_t region_use_count (boost::shared_ptr) const; boost::shared_ptr find_region (const PBD::ID&) const; boost::shared_ptr top_region_at (framepos_t frame); @@ -171,7 +174,7 @@ public: void foreach_region (boost::function)>); XMLNode& get_state (); - int set_state (const XMLNode&, int version); + virtual int set_state (const XMLNode&, int version); XMLNode& get_template (); PBD::Signal1 InUse; @@ -255,7 +258,11 @@ 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; @@ -328,10 +335,7 @@ public: void splice_locked (framepos_t at, framecnt_t distance, boost::shared_ptr exclude); void splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr exclude); - virtual void finalize_split_region (boost::shared_ptr /*original*/, boost::shared_ptr /*left*/, boost::shared_ptr /*right*/) {} - - virtual void check_dependents (boost::shared_ptr /*region*/, bool /*norefresh*/) {} - virtual void refresh_dependents (boost::shared_ptr /*region*/) {} + virtual void check_crossfades (Evoral::Range) {} virtual void remove_dependents (boost::shared_ptr /*region*/) {} virtual XMLNode& state (bool); @@ -359,14 +363,6 @@ public: void _split_region (boost::shared_ptr, framepos_t position); typedef std::pair, boost::shared_ptr > TwoRegions; - virtual void copy_dependents (const std::vector&, Playlist*) const { } - - struct RegionInfo { - boost::shared_ptr region; - framepos_t position; - framecnt_t length; - framepos_t start; - }; /* this is called before we create a new compound region */ virtual void pre_combine (std::vector >&) {} @@ -380,6 +376,7 @@ public: private: void setup_layering_indices (RegionList const &) 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 d73fef2f53..069a579ff7 100644 --- a/libs/ardour/ardour/plugin.h +++ b/libs/ardour/ardour/plugin.h @@ -118,6 +118,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 int get_parameter_descriptor (uint32_t which, ParameterDescriptor&) const = 0; virtual uint32_t nth_parameter (uint32_t which, bool& ok) const = 0; diff --git a/libs/ardour/ardour/port.h b/libs/ardour/ardour/port.h index 7aa4e8a0d5..37101950d5 100644 --- a/libs/ardour/ardour/port.h +++ b/libs/ardour/ardour/port.h @@ -116,7 +116,7 @@ public: virtual void cycle_end (pframes_t) = 0; virtual void cycle_split () = 0; virtual Buffer& get_buffer (pframes_t nframes) = 0; - virtual void flush_buffers (pframes_t /*nframes*/, framepos_t /*time*/) {} + virtual void flush_buffers (pframes_t /*nframes*/) {} virtual void transport_stopped () {} virtual void realtime_locate () {} diff --git a/libs/ardour/ardour/rc_configuration_vars.h b/libs/ardour/ardour/rc_configuration_vars.h index 9c631b11ee..1f9e3c1037 100644 --- a/libs/ardour/ardour/rc_configuration_vars.h +++ b/libs/ardour/ardour/rc_configuration_vars.h @@ -50,8 +50,6 @@ CONFIG_VARIABLE (bool, first_midi_bank_is_zero, "diplay-first-midi-bank-as-zero" CONFIG_VARIABLE (uint32_t, feedback_interval_ms, "feedback-interval-ms", 100) CONFIG_VARIABLE (bool, use_tranzport, "use-tranzport", false) -CONFIG_VARIABLE (std::string, mackie_emulation, "mackie-emulation", "mcu") -CONFIG_VARIABLE (uint32_t, mackie_extenders, "mackie-extenders", 0) CONFIG_VARIABLE (RemoteModel, remote_model, "remote-model", MixerOrdered) /* disk operations */ @@ -178,6 +176,7 @@ CONFIG_VARIABLE (bool, never_display_periodic_midi, "never-display-periodic-midi CONFIG_VARIABLE (bool, sound_midi_notes, "sound-midi-notes", false) CONFIG_VARIABLE (bool, use_plugin_own_gui, "use-plugin-own-gui", true) CONFIG_VARIABLE (uint32_t, max_recent_sessions, "max-recent-sessions", 10) +CONFIG_VARIABLE (double, automation_thinning_factor, "automation-thinning-factor", 20.0) /* denormal management */ diff --git a/libs/ardour/ardour/region.h b/libs/ardour/ardour/region.h index cf180556b4..788a8d90c9 100644 --- a/libs/ardour/ardour/region.h +++ b/libs/ardour/ardour/region.h @@ -128,7 +128,7 @@ class Region float stretch () const { return _stretch; } float shift () const { return _shift; } - void set_ancestral_data (framepos_t start, framepos_t length, float stretch, float shift); + void set_ancestral_data (framepos_t start, framecnt_t length, float stretch, float shift); frameoffset_t sync_offset (int& dir) const; framepos_t sync_position () const; @@ -141,6 +141,14 @@ class Region framepos_t first_frame () const { return _position; } framepos_t last_frame () const { return _position + _length - 1; } + Evoral::Range last_range () const { + return Evoral::Range (_last_position, _last_position + _last_length - 1); + } + + Evoral::Range range () const { + return Evoral::Range (first_frame(), last_frame()); + } + bool hidden () const { return _hidden; } bool muted () const { return _muted; } bool opaque () const { return _opaque; } @@ -168,8 +176,14 @@ class Region return first_frame() <= frame && frame <= last_frame(); } - OverlapType coverage (framepos_t start, framepos_t end) const { - return ARDOUR::coverage (first_frame(), last_frame(), start, end); + /** @return coverage of this region with the given range; + * OverlapInternal: the range is internal to this region. + * OverlapStart: the range overlaps the start of this region. + * OverlapEnd: the range overlaps the end of this region. + * OverlapExternal: the range overlaps all of this region. + */ + Evoral::OverlapType coverage (framepos_t start, framepos_t end) const { + return Evoral::coverage (first_frame(), last_frame(), start, end); } bool equivalent (boost::shared_ptr) const; @@ -282,7 +296,7 @@ class Region return 0; } - virtual int adjust_transients (framepos_t /*delta*/) { + virtual int adjust_transients (frameoffset_t /*delta*/) { // no transients, but its OK return 0; } @@ -324,7 +338,8 @@ class Region virtual int _set_state (const XMLNode&, int version, PBD::PropertyChange& what_changed, bool send_signal); void post_set (const PBD::PropertyChange&); virtual void set_position_internal (framepos_t pos, bool allow_bbt_recompute); - virtual void set_length_internal (framepos_t pos); + virtual void set_length_internal (framecnt_t); + virtual void set_start_internal (framecnt_t); DataType _type; diff --git a/libs/ardour/ardour/resampled_source.h b/libs/ardour/ardour/resampled_source.h index 74f1a76e5b..a36ad4331f 100644 --- a/libs/ardour/ardour/resampled_source.h +++ b/libs/ardour/ardour/resampled_source.h @@ -39,7 +39,7 @@ class ResampledImportableSource : public ImportableSource uint32_t channels() const { return source->channels(); } framecnt_t length() const { return source->length(); } framecnt_t samplerate() const { return source->samplerate(); } - void seek (framecnt_t); + void seek (framepos_t); framepos_t natural_position() const; bool clamped_at_unity () const { diff --git a/libs/ardour/ardour/route.h b/libs/ardour/ardour/route.h index 42b74b50bc..bdda941a97 100644 --- a/libs/ardour/ardour/route.h +++ b/libs/ardour/ardour/route.h @@ -132,7 +132,7 @@ class Route : public SessionObject, public Automatable, public RouteGroupMember, /* end of vfunc-based API */ - void shift (framepos_t, framepos_t); + void shift (framepos_t, framecnt_t); void set_gain (gain_t val, void *src); void inc_gain (gain_t delta, void *src); @@ -397,6 +397,15 @@ class Route : public SessionObject, public Automatable, public RouteGroupMember, boost::shared_ptr gain_control() const; boost::shared_ptr pannable() const; + /** + Return the first processor that accepts has at least one MIDI input + and at least one audio output. In the vast majority of cases, this + will be "the instrument". This does not preclude other MIDI->audio + processors later in the processing chain, but that would be a + special case not covered by this utility function. + */ + boost::shared_ptr the_instrument() const; + void automation_snapshot (framepos_t now, bool force=false); void protect_automation (); @@ -435,7 +444,7 @@ class Route : public SessionObject, public Automatable, public RouteGroupMember, virtual void set_block_size (pframes_t nframes); protected: - virtual framecnt_t check_initial_delay (framecnt_t nframes, framecnt_t&) { return nframes; } + virtual framecnt_t check_initial_delay (framecnt_t nframes, framepos_t&) { return nframes; } void passthru (framepos_t start_frame, framepos_t end_frame, pframes_t nframes, int declick); diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h index adb000c615..6c7a9af844 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -777,8 +777,6 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi boost::shared_ptr solo_cut_control() const; - SessionMetadata & metadata () { return *_metadata; } - SessionConfiguration config; bool exporting () const { @@ -1126,9 +1124,9 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi /* MIDI Machine Control */ - void spp_start (MIDI::Parser&, framepos_t timestamp); - void spp_continue (MIDI::Parser&, framepos_t timestamp); - void spp_stop (MIDI::Parser&, framepos_t timestamp); + void spp_start (); + void spp_continue (); + void spp_stop (); void mmc_deferred_play (MIDI::MachineControl &); void mmc_stop (MIDI::MachineControl &); @@ -1466,8 +1464,6 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi static bool _disable_all_loaded_plugins; - SessionMetadata * _metadata; - mutable bool have_looped; ///< Used in ::audible_frame(*) void update_have_rec_enabled_track (); diff --git a/libs/ardour/ardour/session_configuration_vars.h b/libs/ardour/ardour/session_configuration_vars.h index 87b317d3e8..a6147f9140 100644 --- a/libs/ardour/ardour/session_configuration_vars.h +++ b/libs/ardour/ardour/session_configuration_vars.h @@ -45,8 +45,6 @@ CONFIG_VARIABLE (TimecodeFormat, timecode_format, "timecode-format", timecode_30 CONFIG_VARIABLE_SPECIAL(std::string, raid_path, "raid-path", "", path_expand) CONFIG_VARIABLE_SPECIAL(std::string, audio_search_path, "audio-search-path", "", search_path_expand) CONFIG_VARIABLE_SPECIAL(std::string, midi_search_path, "midi-search-path", "", search_path_expand) -CONFIG_VARIABLE (std::string, bwf_country_code, "bwf-country-code", "US") -CONFIG_VARIABLE (std::string, bwf_organization_code, "bwf-organization-code", "US") CONFIG_VARIABLE (std::string, auditioner_output_left, "auditioner-output-left", "default") CONFIG_VARIABLE (std::string, auditioner_output_right, "auditioner-output-right", "default") CONFIG_VARIABLE (bool, timecode_source_is_synced, "timecode-source-is-synced", true) diff --git a/libs/ardour/ardour/session_metadata.h b/libs/ardour/ardour/session_metadata.h index 296bf6243f..c4341dbc61 100644 --- a/libs/ardour/ardour/session_metadata.h +++ b/libs/ardour/ardour/session_metadata.h @@ -37,6 +37,9 @@ namespace ARDOUR { class SessionMetadata : public PBD::StatefulDestructible { public: + //singleton instance: + static SessionMetadata *Metadata() { if (_metadata == NULL) _metadata = new SessionMetadata(); return _metadata; } + SessionMetadata (); ~SessionMetadata (); @@ -72,6 +75,15 @@ class SessionMetadata : public PBD::StatefulDestructible std::string genre () const; + std::string instructor () const; + std::string course () const; + + std::string user_name () const; + std::string user_email () const; + std::string user_web () const; + std::string organization () const; + std::string country () const; + /*** Editing ***/ void set_comment (const std::string &); void set_copyright (const std::string &); @@ -104,15 +116,28 @@ class SessionMetadata : public PBD::StatefulDestructible void set_genre (const std::string &); + void set_instructor (const std::string &); + void set_course (const std::string &); + + void set_user_name (const std::string &); + void set_user_email (const std::string &); + void set_user_web (const std::string &); + void set_organization (const std::string &); + void set_country (const std::string &); + /*** Serialization ***/ - XMLNode & get_state (); - int set_state (const XMLNode &, int version); + XMLNode & get_state (); //serializes stuff in the map, to be stored in session file + XMLNode & get_user_state (); //serializes stuff in the user_map, to be stored in user's config file + int set_state (const XMLNode &, int version_num); private: + static SessionMetadata *_metadata; //singleton instance + typedef std::pair Property; typedef std::map PropertyMap; PropertyMap map; + PropertyMap user_map; XMLNode * get_xml (const std::string & name); diff --git a/libs/ardour/ardour/slave.h b/libs/ardour/ardour/slave.h index 9bd3124e23..a487824e51 100644 --- a/libs/ardour/ardour/slave.h +++ b/libs/ardour/ardour/slave.h @@ -155,7 +155,7 @@ class Slave { * only if requires_seekahead() returns true. */ - virtual framepos_t seekahead_distance() const { return 0; } + virtual framecnt_t seekahead_distance() const { return 0; } /** * @return - when returning true, ARDOUR will use transport speed 1.0 no matter what @@ -235,7 +235,7 @@ class MTC_Slave : public Slave { framecnt_t resolution () const; bool requires_seekahead () const { return true; } - framepos_t seekahead_distance() const; + framecnt_t seekahead_distance() const; bool give_slave_full_control_over_transport_speed() const; private: diff --git a/libs/ardour/ardour/sndfileimportable.h b/libs/ardour/ardour/sndfileimportable.h index f085334a21..b146d3231c 100644 --- a/libs/ardour/ardour/sndfileimportable.h +++ b/libs/ardour/ardour/sndfileimportable.h @@ -37,7 +37,7 @@ class SndFileImportableSource : public ImportableSource { uint32_t channels() const; framecnt_t length() const; framecnt_t samplerate() const; - void seek (framecnt_t pos); + void seek (framepos_t pos); framepos_t natural_position() const; bool clamped_at_unity () const; diff --git a/libs/ardour/ardour/source.h b/libs/ardour/ardour/source.h index 1dde32d7ef..e65b4cb093 100644 --- a/libs/ardour/ardour/source.h +++ b/libs/ardour/ardour/source.h @@ -62,7 +62,7 @@ class Source : public SessionObject virtual bool empty () const = 0; virtual framecnt_t length (framepos_t pos) const = 0; - virtual void update_length (framepos_t pos, framecnt_t cnt) = 0; + virtual void update_length (framecnt_t cnt) = 0; virtual framepos_t natural_position() const { return 0; } diff --git a/libs/ardour/ardour/tempo.h b/libs/ardour/ardour/tempo.h index fd89ace9d9..15032b8741 100644 --- a/libs/ardour/ardour/tempo.h +++ b/libs/ardour/ardour/tempo.h @@ -241,7 +241,7 @@ class TempoMap : public PBD::StatefulDestructible to provide an answer. */ void bbt_time_rt (framepos_t when, Timecode::BBT_Time&); - framecnt_t frame_time (const Timecode::BBT_Time&); + framepos_t frame_time (const Timecode::BBT_Time&); framecnt_t bbt_duration_at (framepos_t, const Timecode::BBT_Time&, int dir); /* TEMPO-SENSITIVE FUNCTIONS diff --git a/libs/ardour/ardour/track.h b/libs/ardour/ardour/track.h index 587a8dcd1b..6d2d73519c 100644 --- a/libs/ardour/ardour/track.h +++ b/libs/ardour/ardour/track.h @@ -102,7 +102,7 @@ class Track : public Route, public PublicDiskstream virtual int set_state (const XMLNode&, int version); static void zero_diskstream_id_in_xml (XMLNode&); - boost::shared_ptr rec_enable_control() { return _rec_enable_control; } + boost::shared_ptr rec_enable_control() { return _rec_enable_control; } bool record_enabled() const; void set_record_enabled (bool yn, void *src); @@ -127,8 +127,8 @@ class Track : public Route, public PublicDiskstream void set_pending_overwrite (bool); int seek (framepos_t, bool complete_refill = false); bool hidden () const; - int can_internal_playback_seek (framepos_t); - int internal_playback_seek (framepos_t); + int can_internal_playback_seek (framecnt_t); + int internal_playback_seek (framecnt_t); void non_realtime_input_change (); void non_realtime_locate (framepos_t); void non_realtime_set_speed (); @@ -201,13 +201,13 @@ class Track : public Route, public PublicDiskstream FreezeState state; }; - struct RecEnableControllable : public PBD::Controllable { - RecEnableControllable (Track&); + struct RecEnableControl : public AutomationControl { + RecEnableControl (boost::shared_ptr t); void set_value (double); double get_value (void) const; - Track& track; + boost::weak_ptr track; }; virtual void set_state_part_two () = 0; @@ -218,9 +218,9 @@ class Track : public Route, public PublicDiskstream void maybe_declick (BufferSet&, framecnt_t, int); - boost::shared_ptr _rec_enable_control; + boost::shared_ptr _rec_enable_control; - framecnt_t check_initial_delay (framecnt_t nframes, framecnt_t&); + framecnt_t check_initial_delay (framecnt_t nframes, framepos_t&); private: diff --git a/libs/ardour/ardour/types.h b/libs/ardour/ardour/types.h index b6ab07c743..b3c1666dbb 100644 --- a/libs/ardour/ardour/types.h +++ b/libs/ardour/ardour/types.h @@ -36,6 +36,8 @@ #include "pbd/id.h" +#include "evoral/Range.hpp" + #include "ardour/chan_count.h" #include @@ -101,17 +103,6 @@ namespace ARDOUR { ARDOUR::ChanCount after; }; - enum OverlapType { - OverlapNone, // no overlap - OverlapInternal, // the overlap is 100% with the object - OverlapStart, // overlap covers start, but ends within - OverlapEnd, // overlap begins within and covers end - OverlapExternal // overlap extends to (at least) begin+end - }; - - ARDOUR::OverlapType coverage (framepos_t sa, framepos_t ea, - framepos_t sb, framepos_t eb); - /* policies for inserting/pasting material where overlaps might be an issue. */ @@ -145,7 +136,8 @@ namespace ARDOUR { MidiSystemExclusiveAutomation, FadeInAutomation, FadeOutAutomation, - EnvelopeAutomation + EnvelopeAutomation, + RecEnableAutomation }; enum AutoState { @@ -277,6 +269,9 @@ namespace ARDOUR { } }; + /* XXX: slightly unfortunate that there is this and Evoral::Range<>, + but this has a uint32_t id which Evoral::Range<> does not. + */ struct AudioRange { framepos_t start; framepos_t end; @@ -294,8 +289,8 @@ namespace ARDOUR { return start == other.start && end == other.end; } - OverlapType coverage (framepos_t s, framepos_t e) const { - return ARDOUR::coverage (start, end, s, e); + Evoral::OverlapType coverage (framepos_t s, framepos_t e) const { + return Evoral::coverage (start, end, s, e); } }; diff --git a/libs/ardour/ardour/utils.h b/libs/ardour/ardour/utils.h index 52b7c3053e..7eba3fa18c 100644 --- a/libs/ardour/ardour/utils.h +++ b/libs/ardour/ardour/utils.h @@ -32,8 +32,6 @@ #include #endif /* __APPLE__ */ -bool string_is_affirmative (const std::string&); - #include "ardour/ardour.h" #include "ardour/data_type.h" #include "ardour/dB.h" diff --git a/libs/ardour/ardour/worker.h b/libs/ardour/ardour/worker.h new file mode 100644 index 0000000000..984e375c80 --- /dev/null +++ b/libs/ardour/ardour/worker.h @@ -0,0 +1,90 @@ +/* + Copyright (C) 2012 Paul Davis + Author: David Robillard + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __ardour_worker_h__ +#define __ardour_worker_h__ + +#include + +#include + +#include "pbd/ringbuffer.h" +#include "pbd/semaphore.h" + +namespace ARDOUR { + +/** + An object that needs to schedule non-RT work in the audio thread. +*/ +class Workee { +public: + virtual ~Workee() {} + + /** + Do some work in the worker thread. + */ + virtual int work(uint32_t size, const void* data) = 0; + + /** + Handle a response from the worker thread in the audio thread. + */ + virtual int work_response(uint32_t size, const void* data) = 0; +}; + +/** + A worker thread for non-realtime tasks scheduled in the audio thread. +*/ +class Worker +{ +public: + Worker(Workee* workee, uint32_t ring_size); + ~Worker(); + + /** + Schedule work (audio thread). + @return false on error. + */ + bool schedule(uint32_t size, const void* data); + + /** + Respond from work (worker thread). + @return false on error. + */ + bool respond(uint32_t size, const void* data); + + /** + Emit any pending responses (audio thread). + */ + void emit_responses(); + +private: + void run(); + + Workee* _workee; + Glib::Thread* _thread; + RingBuffer* _requests; + RingBuffer* _responses; + uint8_t* _response; + PBD::Semaphore _sem; + bool _exit; +}; + +} // namespace ARDOUR + +#endif /* __ardour_worker_h__ */ diff --git a/libs/ardour/audio_diskstream.cc b/libs/ardour/audio_diskstream.cc index 037ff34eb5..c38901f430 100644 --- a/libs/ardour/audio_diskstream.cc +++ b/libs/ardour/audio_diskstream.cc @@ -457,7 +457,7 @@ AudioDiskstream::process (framepos_t transport_frame, pframes_t nframes, framecn if (record_enabled()) { - OverlapType ot = coverage (first_recordable_frame, last_recordable_frame, transport_frame, transport_frame + nframes); + Evoral::OverlapType ot = Evoral::coverage (first_recordable_frame, last_recordable_frame, transport_frame, transport_frame + nframes); calculate_record_range (ot, transport_frame, nframes, rec_nframes, rec_offset); if (rec_nframes && !was_recording) { diff --git a/libs/ardour/audio_playlist.cc b/libs/ardour/audio_playlist.cc index 76d0228547..ebebc62c33 100644 --- a/libs/ardour/audio_playlist.cc +++ b/libs/ardour/audio_playlist.cc @@ -26,7 +26,6 @@ #include "ardour/configuration.h" #include "ardour/audioplaylist.h" #include "ardour/audioregion.h" -#include "ardour/crossfade.h" #include "ardour/region_sorters.h" #include "ardour/session.h" #include "pbd/enumwriter.h" @@ -37,77 +36,14 @@ using namespace ARDOUR; using namespace std; using namespace PBD; -namespace ARDOUR { - namespace Properties { - PBD::PropertyDescriptor crossfades; - } -} - -void -AudioPlaylist::make_property_quarks () -{ - Properties::crossfades.property_id = g_quark_from_static_string (X_("crossfades")); - DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for crossfades = %1\n", Properties::crossfades.property_id)); -} - -CrossfadeListProperty::CrossfadeListProperty (AudioPlaylist& pl) - : SequenceProperty > > (Properties::crossfades.property_id, boost::bind (&AudioPlaylist::update, &pl, _1)) - , _playlist (pl) -{ - -} - -CrossfadeListProperty::CrossfadeListProperty (CrossfadeListProperty const & p) - : PBD::SequenceProperty > > (p) - , _playlist (p._playlist) -{ - -} - - -CrossfadeListProperty * -CrossfadeListProperty::create () const -{ - return new CrossfadeListProperty (_playlist); -} - -CrossfadeListProperty * -CrossfadeListProperty::clone () const -{ - return new CrossfadeListProperty (*this); -} - -void -CrossfadeListProperty::get_content_as_xml (boost::shared_ptr xfade, XMLNode & node) const -{ - /* Crossfades are not written to any state when they are no - longer in use, so we must write their state here. - */ - - XMLNode& c = xfade->get_state (); - node.add_child_nocopy (c); -} - -boost::shared_ptr -CrossfadeListProperty::get_content_from_xml (XMLNode const & node) const -{ - XMLNodeList const c = node.children (); - assert (c.size() == 1); - return boost::shared_ptr (new Crossfade (_playlist, *c.front())); -} - - AudioPlaylist::AudioPlaylist (Session& session, const XMLNode& node, bool hidden) : Playlist (session, node, DataType::AUDIO, hidden) - , _crossfades (*this) { #ifndef NDEBUG const XMLProperty* prop = node.property("type"); assert(!prop || DataType(prop->value()) == DataType::AUDIO); #endif - add_property (_crossfades); - in_set_state++; if (set_state (node, Stateful::loading_state_version)) { throw failed_constructor(); @@ -119,64 +55,20 @@ AudioPlaylist::AudioPlaylist (Session& session, const XMLNode& node, bool hidden AudioPlaylist::AudioPlaylist (Session& session, string name, bool hidden) : Playlist (session, name, DataType::AUDIO, hidden) - , _crossfades (*this) { - add_property (_crossfades); } AudioPlaylist::AudioPlaylist (boost::shared_ptr other, string name, bool hidden) : Playlist (other, name, hidden) - , _crossfades (*this) { - add_property (_crossfades); - - RegionList::const_iterator in_o = other->regions.begin(); - RegionList::iterator in_n = regions.begin(); - - while (in_o != other->regions.end()) { - boost::shared_ptr ar = boost::dynamic_pointer_cast(*in_o); - - // We look only for crossfades which begin with the current region, so we don't get doubles - for (Crossfades::const_iterator xfades = other->_crossfades.begin(); xfades != other->_crossfades.end(); ++xfades) { - if ((*xfades)->in() == ar) { - // We found one! Now copy it! - - RegionList::const_iterator out_o = other->regions.begin(); - RegionList::const_iterator out_n = regions.begin(); - - while (out_o != other->regions.end()) { - - boost::shared_ptrar2 = boost::dynamic_pointer_cast(*out_o); - - if ((*xfades)->out() == ar2) { - boost::shared_ptrin = boost::dynamic_pointer_cast(*in_n); - boost::shared_ptrout = boost::dynamic_pointer_cast(*out_n); - boost::shared_ptr new_fade = boost::shared_ptr (new Crossfade (*xfades, in, out)); - add_crossfade(new_fade); - break; - } - - out_o++; - out_n++; - } -// cerr << "HUH!? second region in the crossfade not found!" << endl; - } - } - - in_o++; - in_n++; - } } AudioPlaylist::AudioPlaylist (boost::shared_ptr other, framepos_t start, framecnt_t cnt, string name, bool hidden) : Playlist (other, start, cnt, name, hidden) - , _crossfades (*this) { RegionLock rlock2 (const_cast (other.get())); in_set_state++; - add_property (_crossfades); - framepos_t const end = start + cnt - 1; /* Audio regions that have been created by the Playlist constructor @@ -194,10 +86,10 @@ AudioPlaylist::AudioPlaylist (boost::shared_ptr other, fram framecnt_t fade_out = 64; switch (region->coverage (start, end)) { - case OverlapNone: + case Evoral::OverlapNone: continue; - case OverlapInternal: + case Evoral::OverlapInternal: { framecnt_t const offset = start - region->position (); framecnt_t const trim = region->last_frame() - end; @@ -210,7 +102,7 @@ AudioPlaylist::AudioPlaylist (boost::shared_ptr other, fram break; } - case OverlapStart: { + case Evoral::OverlapStart: { if (end > region->position() + region->fade_in()->back()->when) fade_in = region->fade_in()->back()->when; //end is after fade-in, preserve the fade-in if (end > region->last_frame() - region->fade_out()->back()->when) @@ -218,7 +110,7 @@ AudioPlaylist::AudioPlaylist (boost::shared_ptr other, fram break; } - case OverlapEnd: { + case Evoral::OverlapEnd: { if (start < region->last_frame() - region->fade_out()->back()->when) //start is before fade-out, preserve the fadeout fade_out = region->fade_out()->back()->when; @@ -227,7 +119,7 @@ AudioPlaylist::AudioPlaylist (boost::shared_ptr other, fram break; } - case OverlapExternal: + case Evoral::OverlapExternal: fade_in = region->fade_in()->back()->when; fade_out = region->fade_out()->back()->when; break; @@ -246,25 +138,34 @@ AudioPlaylist::AudioPlaylist (boost::shared_ptr other, fram /* this constructor does NOT notify others (session) */ } -AudioPlaylist::~AudioPlaylist () -{ - _crossfades.clear (); -} - -struct RegionSortByLayer { +/** Sort by descending layer and then by ascending position */ +struct ReadSorter { bool operator() (boost::shared_ptr a, boost::shared_ptr b) { - return a->layer() < b->layer(); + if (a->layer() != b->layer()) { + return a->layer() > b->layer(); + } + + return a->position() < b->position(); } }; +/** A segment of region that needs to be read */ +struct Segment { + Segment (boost::shared_ptr r, Evoral::Range a) : region (r), range (a) {} + + boost::shared_ptr region; ///< the region + Evoral::Range range; ///< range of the region to read, in session frames +}; + +/** @param start Start position in session frames. + * @param cnt Number of frames to read. + */ ARDOUR::framecnt_t AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, framepos_t start, framecnt_t cnt, unsigned chan_n) { - framecnt_t ret = cnt; - - DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Playlist %1 read @ %2 for %3, channel %4, regions %5 xfades %6\n", - name(), start, cnt, chan_n, regions.size(), _crossfades.size())); + DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Playlist %1 read @ %2 for %3, channel %4, regions %5\n", + name(), start, cnt, chan_n, regions.size())); /* optimizing this memset() away involves a lot of conditionals that may well cause more of a hit due to cache misses @@ -285,500 +186,207 @@ 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 - framepos_t const end = start + cnt - 1; - - boost::shared_ptr rlist = regions_to_read (start, start+cnt); - - if (rlist->empty()) { - return cnt; - } - - map > > relevant_regions; - map > > relevant_xfades; - vector relevant_layers; - - for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ++i) { - if ((*i)->coverage (start, end) != OverlapNone) { - relevant_regions[(*i)->layer()].push_back (*i); - relevant_layers.push_back ((*i)->layer()); - } - } - - DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Checking %1 xfades\n", _crossfades.size())); - - for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) { - DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("%1 check xfade between %2 and %3 ... [ %4 ... %5 | %6 ... %7]\n", - name(), (*i)->out()->name(), (*i)->in()->name(), - (*i)->first_frame(), (*i)->last_frame(), - start, end)); - if ((*i)->coverage (start, end) != OverlapNone) { - relevant_xfades[(*i)->upper_layer()].push_back (*i); - DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\t\txfade is relevant (coverage = %2), place on layer %1\n", - (*i)->upper_layer(), enum_2_string ((*i)->coverage (start, end)))); - } - } - -// RegionSortByLayer layer_cmp; -// relevant_regions.sort (layer_cmp); - - /* XXX this whole per-layer approach is a hack that - should be removed once Crossfades become - CrossfadeRegions and we just grab a list of relevant - regions and call read_at() on all of them. + /* 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); + all->sort (ReadSorter ()); - sort (relevant_layers.begin(), relevant_layers.end()); + /* This will be a list of the bits of our read range that we have + handled completely (ie for which no more regions need to be read). + It is a list of ranges in session frames. + */ + Evoral::RangeList done; - for (vector::iterator l = relevant_layers.begin(); l != relevant_layers.end(); ++l) { + /* This will be a list of the bits of regions that we need to read */ + list to_do; + + /* Now go through the `all' list filling in `to_do' and `done' */ + for (RegionList::iterator i = all->begin(); i != all->end(); ++i) { + boost::shared_ptr ar = boost::dynamic_pointer_cast (*i); - DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("read for layer %1\n", *l)); + /* Work out which bits of this region need to be read; + first, trim to the range we are reading... + */ + Evoral::Range region_range = ar->range (); + region_range.from = max (region_range.from, start); + region_range.to = min (region_range.to, start + cnt - 1); - vector > r (relevant_regions[*l]); - vector >& x (relevant_xfades[*l]); + /* ... and then remove the bits that are already done */ + Evoral::RangeList region_to_do = Evoral::subtract (region_range, done); + /* Read those bits, adding their bodies (the parts between end-of-fade-in + and start-of-fade-out) to the `done' list. + */ - for (vector >::iterator i = r.begin(); i != r.end(); ++i) { - boost::shared_ptr ar = boost::dynamic_pointer_cast(*i); - DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("read from region %1\n", ar->name())); - assert(ar); - ar->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n); - } + Evoral::RangeList::List t = region_to_do.get (); - for (vector >::iterator i = x.begin(); i != x.end(); ++i) { - DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("read from xfade between %1 & %2\n", (*i)->out()->name(), (*i)->in()->name())); - (*i)->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n); - } - } + for (Evoral::RangeList::List::iterator j = t.begin(); j != t.end(); ++j) { + Evoral::Range d = *j; + to_do.push_back (Segment (ar, d)); - return ret; -} - - -void -AudioPlaylist::remove_dependents (boost::shared_ptr region) -{ - boost::shared_ptr r = boost::dynamic_pointer_cast (region); - - if (in_set_state) { - return; - } - - if (r == 0) { - fatal << _("programming error: non-audio Region passed to remove_overlap in audio playlist") - << endmsg; - return; - } - - for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ) { - - if ((*i)->involves (r)) { - i = _crossfades.erase (i); - } else { - ++i; - } - } -} - - -void -AudioPlaylist::flush_notifications (bool from_undo) -{ - Playlist::flush_notifications (from_undo); - - if (in_flush) { - return; - } - - in_flush = true; - - Crossfades::iterator a; - for (a = _pending_xfade_adds.begin(); a != _pending_xfade_adds.end(); ++a) { - NewCrossfade (*a); /* EMIT SIGNAL */ - } - - _pending_xfade_adds.clear (); - - in_flush = false; -} - -void -AudioPlaylist::refresh_dependents (boost::shared_ptr r) -{ - boost::shared_ptr ar = boost::dynamic_pointer_cast(r); - set > updated; - - if (ar == 0) { - return; - } - - for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) { - - Crossfades::iterator tmp; - - tmp = x; - ++tmp; - - /* only update them once */ - - if ((*x)->involves (ar)) { - - pair >::iterator, bool> const u = updated.insert (*x); - - if (u.second) { - /* x was successfully inserted into the set, so it has not already been updated */ - try { - (*x)->refresh (); - } - - catch (Crossfade::NoCrossfadeHere& err) { - // relax, Invalidated during refresh + if (ar->opaque ()) { + /* Cut this range down to just the body and mark it done */ + Evoral::Range body = ar->body_range (); + if (body.from < d.to && body.to > d.from) { + d.from = max (d.from, body.from); + d.to = min (d.to, body.to); + done.add (d); } } } - - x = tmp; } + + /* Now go backwards through the to_do list doing the actual reads */ + for (list::reverse_iterator i = to_do.rbegin(); i != to_do.rend(); ++i) { + i->region->read_at (buf + i->range.from - start, mixdown_buffer, gain_buffer, i->range.from, i->range.to - i->range.from + 1, chan_n); + } + + return cnt; } void -AudioPlaylist::finalize_split_region (boost::shared_ptr o, boost::shared_ptr l, boost::shared_ptr r) +AudioPlaylist::check_crossfades (Evoral::Range range) { - boost::shared_ptr orig = boost::dynamic_pointer_cast(o); - boost::shared_ptr left = boost::dynamic_pointer_cast(l); - boost::shared_ptr right = boost::dynamic_pointer_cast(r); + if (in_set_state || in_partition || !_session.config.get_auto_xfade ()) { + return; + } + + boost::shared_ptr starts = regions_with_start_within (range); + boost::shared_ptr ends = regions_with_end_within (range); - for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) { - Crossfades::iterator tmp; - tmp = x; - ++tmp; + RegionList all = *starts; + std::copy (ends->begin(), ends->end(), back_inserter (all)); - boost::shared_ptr fade; + all.sort (RegionSortByLayer ()); - if ((*x)->_in == orig) { - if (! (*x)->covers(right->position())) { - fade = boost::shared_ptr (new Crossfade (*x, left, (*x)->_out)); + set > done_start; + set > done_end; + + for (RegionList::reverse_iterator i = all.rbegin(); i != all.rend(); ++i) { + for (RegionList::reverse_iterator j = all.rbegin(); j != all.rend(); ++j) { + + if (i == j) { + continue; + } + + if ((*i)->muted() || (*j)->muted()) { + continue; + } + + if ((*i)->position() == (*j)->position() && ((*i)->length() == (*j)->length())) { + /* precise overlay: no xfade */ + continue; + } + + if ((*i)->position() == (*j)->position() || ((*i)->last_frame() == (*j)->last_frame())) { + /* starts or ends match: no xfade */ + continue; + } + + + boost::shared_ptr top; + boost::shared_ptr bottom; + + if ((*i)->layer() < (*j)->layer()) { + top = boost::dynamic_pointer_cast (*j); + bottom = boost::dynamic_pointer_cast (*i); } else { - // Overlap, the crossfade is copied on the left side of the right region instead - fade = boost::shared_ptr (new Crossfade (*x, right, (*x)->_out)); + top = boost::dynamic_pointer_cast (*i); + bottom = boost::dynamic_pointer_cast (*j); } - } - - if ((*x)->_out == orig) { - if (! (*x)->covers(right->position())) { - fade = boost::shared_ptr (new Crossfade (*x, (*x)->_in, right)); - } else { - // Overlap, the crossfade is copied on the right side of the left region instead - fade = boost::shared_ptr (new Crossfade (*x, (*x)->_in, left)); + + if (!top->opaque ()) { + continue; } - } - if (fade) { - _crossfades.remove (*x); - add_crossfade (fade); - } - x = tmp; - } -} - -void -AudioPlaylist::check_dependents (boost::shared_ptr r, bool norefresh) -{ - boost::shared_ptr other; - boost::shared_ptr region; - boost::shared_ptr top; - boost::shared_ptr bottom; - boost::shared_ptr xfade; - boost::shared_ptr touched_regions; - - if (in_set_state || in_partition) { - return; - } - - if ((region = boost::dynamic_pointer_cast (r)) == 0) { - fatal << _("programming error: non-audio Region tested for overlap in audio playlist") - << endmsg; - return; - } - - if (!norefresh) { - refresh_dependents (r); - } - - - if (!_session.config.get_auto_xfade()) { - return; - } - - for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) { - other = boost::dynamic_pointer_cast (*i); - - if (other == region) { - continue; - } - - if (other->muted() || region->muted()) { - continue; - } - - if (other->position() == r->position() && other->length() == r->length()) { - /* precise overlay of two regions - no xfade */ - continue; - } - - if (other->layer() < region->layer()) { - top = region; - bottom = other; - } else { - top = other; - bottom = region; - } - - if (!top->opaque()) { - continue; - } - - OverlapType c = top->coverage (bottom->position(), bottom->last_frame()); - - touched_regions.reset (); - - try { - framecnt_t xfade_length; - switch (c) { - case OverlapNone: - break; - - case OverlapInternal: - /* {=============== top =============} - * [ ----- bottom ------- ] - */ - break; - - case OverlapExternal: - - /* [ -------- top ------- ] - * {=========== bottom =============} + Evoral::OverlapType const c = top->coverage (bottom->position(), bottom->last_frame()); + + if (c == Evoral::OverlapStart) { + + /* top starts within bottom but covers bottom's end */ + + /* { ==== top ============ } + * [---- bottom -------------------] */ - /* to avoid discontinuities at the region boundaries of an internal - overlap (this region is completely within another), we create - two hidden crossfades at each boundary. this is not dependent - on the auto-xfade option, because we require it as basic - audio engineering. - */ + 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 */ - xfade_length = min ((framecnt_t) 720, top->length()); - - if (top_region_at (top->first_frame()) == top) { - - xfade = boost::shared_ptr (new Crossfade (top, bottom, xfade_length, StartOfIn)); - xfade->set_position (top->first_frame()); - add_crossfade (xfade); + framecnt_t len = 0; + switch (_session.config.get_xfade_model()) { + case FullCrossfade: + len = bottom->last_frame () - top->first_frame (); + break; + case ShortCrossfade: + len = _session.config.get_short_xfade_seconds() * _session.frame_rate(); + break; + } + + top->set_fade_in_length (len); + top->set_fade_in_active (true); + done_start.insert (top); + done_end.insert (bottom); } - if (top_region_at (top->last_frame() - 1) == top) { - - /* - only add a fade out if there is no region on top of the end of 'top' (which - would cover it). - */ - - xfade = boost::shared_ptr (new Crossfade (bottom, top, xfade_length, EndOfOut)); - xfade->set_position (top->last_frame() - xfade_length); - add_crossfade (xfade); - } - break; - case OverlapStart: - - /* { ==== top ============ } - * [---- bottom -------------------] + } else if (c == Evoral::OverlapEnd) { + + /* top covers start of bottom but ends within it */ + + /* [---- top ------------------------] + * { ==== bottom ============ } */ - if (_session.config.get_xfade_model() == FullCrossfade) { - touched_regions = regions_touched (top->first_frame(), bottom->last_frame()); - if (touched_regions->size() <= 2) { - xfade = boost::shared_ptr (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active())); - add_crossfade (xfade); + if (done_end.find (top) == done_end.end() && done_start.find (bottom) == done_start.end ()) { + /* Top's fade-out will cause an implicit fade-in of bottom */ + + framecnt_t len = 0; + switch (_session.config.get_xfade_model()) { + case FullCrossfade: + len = bottom->last_frame () - top->first_frame (); + break; + case ShortCrossfade: + len = _session.config.get_short_xfade_seconds() * _session.frame_rate(); + break; } - } else { - touched_regions = regions_touched (top->first_frame(), - top->first_frame() + min ((framecnt_t) _session.config.get_short_xfade_seconds() * _session.frame_rate(), - top->length())); - if (touched_regions->size() <= 2) { - xfade = boost::shared_ptr (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active())); - add_crossfade (xfade); - } + top->set_fade_out_length (len); + top->set_fade_out_active (true); + done_end.insert (top); + done_start.insert (bottom); } - break; - case OverlapEnd: - - - /* [---- top ------------------------] - * { ==== bottom ============ } - */ - - if (_session.config.get_xfade_model() == FullCrossfade) { - - touched_regions = regions_touched (bottom->first_frame(), top->last_frame()); - if (touched_regions->size() <= 2) { - xfade = boost::shared_ptr (new Crossfade (region, other, - _session.config.get_xfade_model(), _session.config.get_xfades_active())); - add_crossfade (xfade); - } - - } else { - touched_regions = regions_touched (bottom->first_frame(), - bottom->first_frame() + min ((framecnt_t)_session.config.get_short_xfade_seconds() * _session.frame_rate(), - bottom->length())); - if (touched_regions->size() <= 2) { - xfade = boost::shared_ptr (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active())); - add_crossfade (xfade); - } - } - break; - default: - xfade = boost::shared_ptr (new Crossfade (region, other, - _session.config.get_xfade_model(), _session.config.get_xfades_active())); - add_crossfade (xfade); } } - - catch (failed_constructor& err) { - continue; - } - - catch (Crossfade::NoCrossfadeHere& err) { - continue; - } - } -} -void -AudioPlaylist::add_crossfade (boost::shared_ptr xfade) -{ - Crossfades::iterator ci; - - for (ci = _crossfades.begin(); ci != _crossfades.end(); ++ci) { - if (*(*ci) == *xfade) { // Crossfade::operator==() - break; + for (RegionList::iterator i = starts->begin(); i != starts->end(); ++i) { + if (done_start.find (*i) == done_start.end()) { + boost::shared_ptr r = boost::dynamic_pointer_cast (*i); + r->set_default_fade_in (); } } - if (ci != _crossfades.end()) { - // it will just go away - } else { - _crossfades.push_back (xfade); - - xfade->Invalidated.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_invalidated, this, _1)); - xfade->PropertyChanged.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_changed, this, _1)); - - notify_crossfade_added (xfade); - } -} - -void AudioPlaylist::notify_crossfade_added (boost::shared_ptr x) -{ - if (g_atomic_int_get(&block_notifications)) { - _pending_xfade_adds.insert (_pending_xfade_adds.end(), x); - } else { - NewCrossfade (x); /* EMIT SIGNAL */ - } -} - -void -AudioPlaylist::crossfade_invalidated (boost::shared_ptr r) -{ - Crossfades::iterator i; - boost::shared_ptr xfade = boost::dynamic_pointer_cast (r); - - xfade->in()->resume_fade_in (); - xfade->out()->resume_fade_out (); - - if ((i = find (_crossfades.begin(), _crossfades.end(), xfade)) != _crossfades.end()) { - _crossfades.erase (i); - } -} - -int -AudioPlaylist::set_state (const XMLNode& node, int version) -{ - XMLNode *child; - XMLNodeList nlist; - XMLNodeConstIterator niter; - - in_set_state++; - - if (Playlist::set_state (node, version)) { - return -1; - } - - freeze (); - - nlist = node.children(); - - for (niter = nlist.begin(); niter != nlist.end(); ++niter) { - - child = *niter; - - if (child->name() != "Crossfade") { - continue; - } - - try { - boost::shared_ptr xfade = boost::shared_ptr (new Crossfade (*((const Playlist *)this), *child)); - _crossfades.push_back (xfade); - xfade->Invalidated.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_invalidated, this, _1)); - xfade->PropertyChanged.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_changed, this, _1)); - NewCrossfade(xfade); - } - - catch (failed_constructor& err) { - // cout << string_compose (_("could not create crossfade object in playlist %1"), - // _name) - // << endl; - continue; + for (RegionList::iterator i = ends->begin(); i != ends->end(); ++i) { + if (done_end.find (*i) == done_end.end()) { + boost::shared_ptr r = boost::dynamic_pointer_cast (*i); + r->set_default_fade_out (); } } - - thaw (); - in_set_state--; - - return 0; -} - -void -AudioPlaylist::clear (bool with_signals) -{ - _crossfades.clear (); - Playlist::clear (with_signals); -} - -XMLNode& -AudioPlaylist::state (bool full_state) -{ - XMLNode& node = Playlist::state (full_state); - - if (full_state) { - for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) { - node.add_child_nocopy ((*i)->get_state()); - } - } - - return node; } void AudioPlaylist::dump () const { boost::shared_ptrr; - boost::shared_ptr x; cerr << "Playlist \"" << _name << "\" " << endl << regions.size() << " regions " - << _crossfades.size() << " crossfades" << endl; for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) { @@ -791,21 +399,6 @@ AudioPlaylist::dump () const << r->layer () << endl; } - - for (Crossfades::const_iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) { - x = *i; - cerr << " xfade [" - << x->out()->name() - << ',' - << x->in()->name() - << " @ " - << x->position() - << " length = " - << x->length () - << " active ? " - << (x->active() ? "yes" : "no") - << endl; - } } bool @@ -818,8 +411,6 @@ AudioPlaylist::destroy_region (boost::shared_ptr region) } bool changed = false; - Crossfades::iterator c, ctmp; - set > unique_xfades; { RegionLock rlock (this); @@ -853,18 +444,6 @@ AudioPlaylist::destroy_region (boost::shared_ptr region) region->set_playlist (boost::shared_ptr()); } - for (c = _crossfades.begin(); c != _crossfades.end(); ) { - ctmp = c; - ++ctmp; - - if ((*c)->involves (r)) { - unique_xfades.insert (*c); - _crossfades.erase (c); - } - - c = ctmp; - } - if (changed) { /* overload this, it normally means "removed", not destroyed */ notify_region_removed (region); @@ -873,22 +452,6 @@ AudioPlaylist::destroy_region (boost::shared_ptr region) return changed; } -void -AudioPlaylist::crossfade_changed (const PropertyChange&) -{ - if (in_flush || in_set_state) { - return; - } - - /* XXX is there a loop here? can an xfade change not happen - due to a playlist change? well, sure activation would - be an example. maybe we should check the type of change - that occured. - */ - - notify_contents_changed (); -} - bool AudioPlaylist::region_changed (const PropertyChange& what_changed, boost::shared_ptr region) { @@ -917,168 +480,6 @@ AudioPlaylist::region_changed (const PropertyChange& what_changed, boost::shared return true; } -void -AudioPlaylist::crossfades_at (framepos_t frame, Crossfades& clist) -{ - RegionLock rlock (this); - - for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) { - framepos_t const start = (*i)->position (); - framepos_t const end = start + (*i)->overlap_length(); // not length(), important difference - - if (frame >= start && frame <= end) { - clist.push_back (*i); - } - } -} - -void -AudioPlaylist::foreach_crossfade (boost::function)> s) -{ - RegionLock rl (this, false); - for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) { - s (*i); - } -} - -void -AudioPlaylist::update (const CrossfadeListProperty::ChangeRecord& change) -{ - for (CrossfadeListProperty::ChangeContainer::const_iterator i = change.added.begin(); i != change.added.end(); ++i) { - add_crossfade (*i); - } - - /* don't remove crossfades here; they will be dealt with by the dependency code */ -} - -boost::shared_ptr -AudioPlaylist::find_crossfade (const PBD::ID& id) const -{ - Crossfades::const_iterator i = _crossfades.begin (); - while (i != _crossfades.end() && (*i)->id() != id) { - ++i; - } - - if (i == _crossfades.end()) { - return boost::shared_ptr (); - } - - return *i; -} - -struct crossfade_triple { - boost::shared_ptr old_in; - boost::shared_ptr new_in; - boost::shared_ptr new_out; -}; - -void -AudioPlaylist::copy_dependents (const vector& old_and_new, Playlist* other) const -{ - AudioPlaylist* other_audio = dynamic_cast(other); - - if (!other_audio) { - return; - } - - /* our argument is a vector of old and new regions. Each old region - might be participant in a crossfade that is already present. Each new - region is a copy of the old region, present in the other playlist. - - our task is to find all the relevant xfades in our playlist (involving - the "old" regions) and place copies of them in the other playlist. - */ - - typedef map,crossfade_triple> CrossfadeInfo; - CrossfadeInfo crossfade_info; - - /* build up a record that links crossfades, old regions and new regions - */ - - for (vector::const_iterator on = old_and_new.begin(); on != old_and_new.end(); ++on) { - - for (Crossfades::const_iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) { - - if ((*i)->in() == on->first) { - - CrossfadeInfo::iterator cf; - - if ((cf = crossfade_info.find (*i)) != crossfade_info.end()) { - - /* already have a record for the old fade-in region, - so note the new fade-in region - */ - - cf->second.new_in = on->second; - - } else { - - /* add a record of this crossfade, keeping an association - with the new fade-in region - */ - - crossfade_triple ct; - - ct.old_in = on->first; - ct.new_in = on->second; - - crossfade_info[*i] = ct; - } - - } else if ((*i)->out() == on->first) { - - /* this old region is the fade-out region of this crossfade */ - - CrossfadeInfo::iterator cf; - - if ((cf = crossfade_info.find (*i)) != crossfade_info.end()) { - - /* already have a record for this crossfade, so just keep - an association for the new fade out region - */ - - cf->second.new_out = on->second; - - } else { - - /* add a record of this crossfade, keeping an association - with the new fade-in region - */ - - crossfade_triple ct; - - ct.old_in = on->first; - ct.new_out = on->second; - - crossfade_info[*i] = ct; - } - } - } - } - - for (CrossfadeInfo::iterator ci = crossfade_info.begin(); ci != crossfade_info.end(); ++ci) { - - /* for each crossfade that involves at least two of the old regions, - create a new identical crossfade with the new regions - */ - - if (!ci->second.new_in || !ci->second.new_out) { - continue; - } - - boost::shared_ptr new_xfade (new Crossfade (ci->first, - boost::dynamic_pointer_cast(ci->second.new_in), - boost::dynamic_pointer_cast(ci->second.new_out))); - - /* add it at the right position - which must be at the start - * of the fade-in region - */ - - new_xfade->set_position (ci->second.new_in->position()); - other_audio->add_crossfade (new_xfade); - } -} - void AudioPlaylist::pre_combine (vector >& copies) { @@ -1195,12 +596,52 @@ AudioPlaylist::pre_uncombine (vector >& originals, boo } } -void -AudioPlaylist::get_equivalent_crossfades (boost::shared_ptr c, vector > & results) +int +AudioPlaylist::set_state (const XMLNode& node, int version) { - for (list >::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) { - if ((*i)->equivalent (c)) { - results.push_back (*i); + int const r = Playlist::set_state (node, version); + if (r) { + return r; + } + + /* Read legacy Crossfade nodes and set up region fades accordingly */ + + XMLNodeList children = node.children (); + for (XMLNodeConstIterator i = children.begin(); i != children.end(); ++i) { + if ((*i)->name() == X_("Crossfade")) { + + XMLProperty* p = (*i)->property (X_("active")); + assert (p); + if (!string_is_affirmative (p->value())) { + continue; + } + + p = (*i)->property (X_("in")); + assert (p); + boost::shared_ptr in = region_by_id (PBD::ID (p->value ())); + assert (in); + boost::shared_ptr in_a = boost::dynamic_pointer_cast (in); + assert (in_a); + + p = (*i)->property (X_("out")); + assert (p); + boost::shared_ptr out = region_by_id (PBD::ID (p->value ())); + assert (out); + boost::shared_ptr out_a = boost::dynamic_pointer_cast (out); + assert (out_a); + + XMLNodeList c = (*i)->children (); + for (XMLNodeConstIterator j = c.begin(); j != c.end(); ++j) { + if ((*j)->name() == X_("FadeIn")) { + in_a->fade_in()->set_state (**j, version); + in_a->set_fade_in_active (true); + } else if ((*j)->name() == X_("FadeOut")) { + out_a->fade_out()->set_state (**j, version); + out_a->set_fade_out_active (true); + } + } } } + + return 0; } diff --git a/libs/ardour/audio_playlist_importer.cc b/libs/ardour/audio_playlist_importer.cc index 681f563e68..1c119e0ed7 100644 --- a/libs/ardour/audio_playlist_importer.cc +++ b/libs/ardour/audio_playlist_importer.cc @@ -178,7 +178,13 @@ AudioPlaylistImporter::_prepare_move () } name = rename_pair.second; } - xml_playlist.property ("name")->set_value (name); + + XMLProperty* p = xml_playlist.property ("name"); + if (!p) { + error << _("badly-formed XML in imported playlist") << endmsg; + } + + p->set_value (name); handler.add_name (name); return true; diff --git a/libs/ardour/audio_track_importer.cc b/libs/ardour/audio_track_importer.cc index e3fe0825db..b3ac93e010 100644 --- a/libs/ardour/audio_track_importer.cc +++ b/libs/ardour/audio_track_importer.cc @@ -223,6 +223,7 @@ AudioTrackImporter::get_info () const return name; } +/** @return true if everything is ok */ bool AudioTrackImporter::_prepare_move () { @@ -247,7 +248,21 @@ AudioTrackImporter::_prepare_move () } name = rename_pair.second; } - xml_track.child ("IO")->property ("name")->set_value (name); + + XMLNode* c = xml_track.child ("IO"); + if (!c) { + error << _("badly-formed XML in imported track") << endmsg; + return false; + } + + XMLProperty* p = c->property ("name"); + if (!p) { + error << _("badly-formed XML in imported track") << endmsg; + return false; + } + + p->set_value (name); + track_handler.add_name (name); return true; @@ -275,7 +290,9 @@ AudioTrackImporter::_move () } boost::shared_ptr ds_node = ds_node_list->front(); - ds_node->property ("id")->set_value (new_ds_id.to_s()); + XMLProperty* p = ds_node->property (X_("id")); + assert (p); + p->set_value (new_ds_id.to_s()); boost::shared_ptr new_ds (new AudioDiskstream (session, *ds_node)); new_ds->set_name (name); diff --git a/libs/ardour/audioengine.cc b/libs/ardour/audioengine.cc index 3aa68478aa..372581f4e6 100644 --- a/libs/ardour/audioengine.cc +++ b/libs/ardour/audioengine.cc @@ -34,6 +34,7 @@ #include #include "midi++/port.h" +#include "midi++/jack_midi_port.h" #include "midi++/mmc.h" #include "midi++/manager.h" @@ -133,7 +134,7 @@ _thread_init_callback (void * /*arg*/) SessionEvent::create_per_thread_pool (X_("Audioengine"), 512); - MIDI::Port::set_process_thread (pthread_self()); + MIDI::JackMIDIPort::set_process_thread (pthread_self()); } static void @@ -233,7 +234,7 @@ AudioEngine::stop (bool forever) } else { jack_deactivate (_priv_jack); Stopped(); /* EMIT SIGNAL */ - MIDI::Port::JackHalted (); /* EMIT SIGNAL */ + MIDI::JackMIDIPort::JackHalted (); /* EMIT SIGNAL */ } } @@ -476,12 +477,21 @@ AudioEngine::process_callback (pframes_t nframes) next_processed_frames = _processed_frames + nframes; } - if (!tm.locked() || _session == 0) { + if (!tm.locked()) { /* return having done nothing */ _processed_frames = next_processed_frames; return 0; } + if (_session == 0) { + if (!_freewheeling) { + MIDI::Manager::instance()->cycle_start(nframes); + MIDI::Manager::instance()->cycle_end(); + } + _processed_frames = next_processed_frames; + return 0; + } + if (session_remove_pending) { /* perform the actual session removal */ _session = 0; @@ -518,9 +528,13 @@ AudioEngine::process_callback (pframes_t nframes) } } else { + MIDI::Manager::instance()->cycle_start(nframes); + if (_session) { _session->process (nframes); } + + MIDI::Manager::instance()->cycle_end(); } if (_freewheeling) { @@ -1093,7 +1107,7 @@ AudioEngine::halted (void *arg) if (was_running) { ae->Halted(""); /* EMIT SIGNAL */ - MIDI::Port::JackHalted (); /* EMIT SIGNAL */ + MIDI::JackMIDIPort::JackHalted (); /* EMIT SIGNAL */ } } @@ -1343,7 +1357,7 @@ AudioEngine::disconnect_from_jack () if (_running) { _running = false; Stopped(); /* EMIT SIGNAL */ - MIDI::Port::JackHalted (); /* EMIT SIGNAL */ + MIDI::JackMIDIPort::JackHalted (); /* EMIT SIGNAL */ } return 0; diff --git a/libs/ardour/audioregion.cc b/libs/ardour/audioregion.cc index 7e212630d5..e4a7504ec1 100644 --- a/libs/ardour/audioregion.cc +++ b/libs/ardour/audioregion.cc @@ -345,16 +345,17 @@ framecnt_t AudioRegion::read (Sample* buf, framepos_t timeline_position, 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)); } framecnt_t AudioRegion::read_at (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, - framepos_t file_position, framecnt_t cnt, uint32_t chan_n) const + 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, - file_position, cnt, chan_n, ReadOps (~0)); + position, cnt, chan_n, ReadOps (~0)); } framecnt_t @@ -369,7 +370,9 @@ AudioRegion::master_read_at (Sample *buf, Sample *mixdown_buffer, float *gain_bu buf, mixdown_buffer, gain_buffer, position, cnt, chan_n, ReadOps (0)); } -/** @param position Position within the session */ +/** @param position Position within the session to read from. + * @param cnt Number of frames to read. + */ framecnt_t AudioRegion::_read_at (const SourceList& srcs, framecnt_t limit, Sample *buf, Sample *mixdown_buffer, float *gain_buffer, @@ -378,44 +381,31 @@ AudioRegion::_read_at (const SourceList& srcs, framecnt_t limit, uint32_t chan_n, ReadOps rops) const { + /* We are reading data from this region into buf (possibly via mixdown_buffer). + The caller has verified that we cover the desired section. + */ + + /* See doc/region_read.svg for a drawing which might help to explain + what is going on. + */ + assert (cnt >= 0); - frameoffset_t internal_offset; - frameoffset_t buf_offset; - framecnt_t to_read; - bool raw = (rops == ReadOpsNone); - if (n_channels() == 0) { return 0; } - if (muted() && !raw) { + if (muted() && rops != ReadOpsNone) { return 0; /* read nothing */ } - /* precondition: caller has verified that we cover the desired section */ + + /* WORK OUT WHERE TO GET DATA FROM */ - if (position < _position) { - internal_offset = 0; - buf_offset = _position - position; - /* if this fails then the requested section is entirely - before the position of this region. An error in xfade - construction that was fixed in oct 2011 (rev 10259) - led to this being the case. We don't want to crash - when this error is encountered, so just settle - on displaying an error. - */ - if (cnt < buf_offset) { - error << "trying to read region " << name() << " @ " << position << " which is outside region bounds " - << _position << " .. " << last_frame() << " (len = " << length() << ')' - << endmsg; - return 0; // read nothing - } - cnt -= buf_offset; - } else { - internal_offset = position - _position; - buf_offset = 0; - } + framecnt_t to_read; + + assert (position >= _position); + frameoffset_t const internal_offset = position - _position; if (internal_offset >= limit) { return 0; /* read nothing */ @@ -425,46 +415,26 @@ AudioRegion::_read_at (const SourceList& srcs, framecnt_t limit, return 0; /* read nothing */ } - if (opaque() || raw) { - /* overwrite whatever is there */ - mixdown_buffer = buf + buf_offset; - } else { - mixdown_buffer += buf_offset; - } - if (chan_n < n_channels()) { + /* COMPUTE DETAILS OF ANY FADES INVOLVED IN THIS READ */ - boost::shared_ptr src = boost::dynamic_pointer_cast (srcs[chan_n]); - if (src->read (mixdown_buffer, _start + internal_offset, to_read) != to_read) { - return 0; /* "read nothing" */ - } + /* Amount of fade in that we are dealing with in this read */ + framecnt_t fade_in_limit = 0; - } else { + /* Offset from buf / mixdown_buffer of the start + of any fade out that we are dealing with + */ + frameoffset_t fade_out_offset = 0; + + /* Amount of fade in that we are dealing with in this read */ + framecnt_t fade_out_limit = 0; - /* track is N-channel, this region has less channels; silence the ones - we don't have. - */ - - if (Config->get_replicate_missing_region_channels()) { - /* track is N-channel, this region has less channels, so use a relevant channel - */ - - 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) { - return 0; /* "read nothing" */ - } - - } else { - memset (mixdown_buffer, 0, sizeof (Sample) * cnt); - } - } + framecnt_t fade_interval_start = 0; if (rops & ReadOpsFades) { - /* fade in */ - + /* Fade in */ + if (_fade_in_active && _session.config.get_use_region_fades()) { framecnt_t fade_in_length = (framecnt_t) _fade_in->back()->when; @@ -472,20 +442,11 @@ AudioRegion::_read_at (const SourceList& srcs, framecnt_t limit, /* see if this read is within the fade in */ if (internal_offset < fade_in_length) { - - framecnt_t fi_limit; - - fi_limit = min (to_read, fade_in_length - internal_offset); - - _fade_in->curve().get_vector (internal_offset, internal_offset+fi_limit, gain_buffer, fi_limit); - - for (framecnt_t n = 0; n < fi_limit; ++n) { - mixdown_buffer[n] *= gain_buffer[n]; - } + fade_in_limit = min (to_read, fade_in_length - internal_offset); } } - /* fade out */ + /* Fade out */ if (_fade_out_active && _session.config.get_use_region_fades()) { @@ -508,28 +469,50 @@ AudioRegion::_read_at (const SourceList& srcs, framecnt_t limit, */ - framecnt_t fade_out_length = (framecnt_t) _fade_out->back()->when; - framecnt_t fade_interval_start = max(internal_offset, limit-fade_out_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 */ - - framecnt_t fo_limit = fade_interval_end - fade_interval_start; - framecnt_t curve_offset = fade_interval_start - (limit-fade_out_length); - framecnt_t fade_offset = fade_interval_start - internal_offset; - - _fade_out->curve().get_vector (curve_offset, curve_offset+fo_limit, gain_buffer, fo_limit); - - for (framecnt_t n = 0, m = fade_offset; n < fo_limit; ++n, ++m) { - mixdown_buffer[m] *= gain_buffer[n]; - } + /* (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; } - } } - /* Regular gain curves and scaling */ + /* READ DATA FROM THE SOURCE INTO mixdown_buffer. + We can never read directly into buf, since it may contain data + from a transparent region `below' this one in the stack; we + must always mix. + */ + + 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) { + return 0; /* "read nothing" */ + } + + } else { + + /* track is N-channel, this region has fewer channels; silence the ones + we don't have. + */ + + if (Config->get_replicate_missing_region_channels()) { + /* track is N-channel, this region has less channels, so use a relevant channel + */ + + 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) { + 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); @@ -544,27 +527,46 @@ AudioRegion::_read_at (const SourceList& srcs, framecnt_t limit, } } } else if ((rops & ReadOpsOwnScaling) && _scale_amplitude != 1.0f) { + apply_gain_to_buffer (mixdown_buffer, to_read, _scale_amplitude); + } - // XXX this should be using what in 2.0 would have been: - // Session::apply_gain_to_buffer (mixdown_buffer, to_read, _scale_amplitude); - for (framecnt_t n = 0; n < to_read; ++n) { - mixdown_buffer[n] *= _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 (!opaque() && (buf != mixdown_buffer)) { + 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); - /* gack. the things we do for users. - */ + /* 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]; + } - buf += buf_offset; - - for (framecnt_t n = 0; n < to_read; ++n) { - buf[n] += mixdown_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; } @@ -668,7 +670,6 @@ AudioRegion::_set_state (const XMLNode& node, int version, PropertyChange& what_ set_default_envelope (); } - _envelope->set_max_xval (_length); _envelope->truncate_end (_length); @@ -987,7 +988,6 @@ AudioRegion::recompute_at_end () _envelope->freeze (); _envelope->truncate_end (_length); - _envelope->set_max_xval (_length); _envelope->thaw (); suspend_property_changes(); @@ -1521,7 +1521,11 @@ AudioRegion::find_silence (Sample threshold, framecnt_t min_length, InterThreadI return silent_periods; } - +Evoral::Range +AudioRegion::body_range () const +{ + return Evoral::Range (first_frame() + _fade_in->back()->when, last_frame() - _fade_out->back()->when); +} extern "C" { diff --git a/libs/ardour/audiosource.cc b/libs/ardour/audiosource.cc index 3a84af58b5..8fe3b8d8c3 100644 --- a/libs/ardour/audiosource.cc +++ b/libs/ardour/audiosource.cc @@ -139,10 +139,10 @@ AudioSource::length (framepos_t /*pos*/) const } void -AudioSource::update_length (framepos_t pos, framecnt_t cnt) +AudioSource::update_length (framecnt_t len) { - if (pos + cnt > _length) { - _length = pos + cnt; + if (len > _length) { + _length = len; } } @@ -398,7 +398,6 @@ AudioSource::read_peaks_with_fpp (PeakData *peaks, framecnt_t npeaks, framepos_t #endif nread = ::pread (peakfile_fd, peaks, sizeof (PeakData)* npeaks, first_peak_byte); - delete peakfile_descriptor; if (nread != sizeof (PeakData) * npeaks) { cerr << "AudioSource[" diff --git a/libs/ardour/auditioner.cc b/libs/ardour/auditioner.cc index cdbe063acd..71730ad0bb 100644 --- a/libs/ardour/auditioner.cc +++ b/libs/ardour/auditioner.cc @@ -123,29 +123,6 @@ Auditioner::prepare_playlist () return *apl; } -void -Auditioner::audition_current_playlist () -{ - if (g_atomic_int_get (&_auditioning)) { - /* don't go via session for this, because we are going - to remain active. - */ - cancel_audition (); - } - - Glib::Mutex::Lock lm (lock); - _diskstream->seek (0); - length = _diskstream->playlist()->get_extent().second; - current_frame = 0; - - /* force a panner reset now that we have all channels */ - - _main_outs->panner_shell()->configure_io (ChanCount (DataType::AUDIO, _diskstream->n_channels().n_audio()), - ChanCount (DataType::AUDIO, n_outputs().n_audio())); - - g_atomic_int_set (&_auditioning, 1); -} - void Auditioner::audition_region (boost::shared_ptr region) { diff --git a/libs/ardour/automation_list.cc b/libs/ardour/automation_list.cc index 0810d2f2d0..3c35dbb8fd 100644 --- a/libs/ardour/automation_list.cc +++ b/libs/ardour/automation_list.cc @@ -156,7 +156,6 @@ AutomationList::operator= (const AutomationList& other) _min_yval = other._min_yval; _max_yval = other._max_yval; - _max_xval = other._max_xval; _default_value = other._default_value; mark_dirty (); @@ -278,8 +277,6 @@ AutomationList::state (bool full) root->add_property ("min-yval", buf); snprintf (buf, sizeof (buf), "%.12g", _max_yval); root->add_property ("max-yval", buf); - snprintf (buf, sizeof (buf), "%.12g", _max_xval); - root->add_property ("max-xval", buf); root->add_property ("interpolation-style", enum_2_string (_interpolation)); @@ -491,12 +488,6 @@ AutomationList::set_state (const XMLNode& node, int version) _max_yval = FLT_MAX; } - if ((prop = node.property (X_("max-xval"))) != 0) { - _max_xval = atof (prop->value ().c_str()); - } else { - _max_xval = 0; // means "no limit ; - } - bool have_events = false; for (niter = nlist.begin(); niter != nlist.end(); ++niter) { diff --git a/libs/ardour/beats_frames_converter.cc b/libs/ardour/beats_frames_converter.cc index 62a0ebde69..94042b1b03 100644 --- a/libs/ardour/beats_frames_converter.cc +++ b/libs/ardour/beats_frames_converter.cc @@ -30,7 +30,7 @@ namespace ARDOUR { * supplied to the constructor. Returns the equivalent number of frames, * taking tempo changes into account. */ -framecnt_t +framepos_t BeatsFramesConverter::to (double beats) const { if (beats < 0) { @@ -47,7 +47,7 @@ BeatsFramesConverter::to (double beats) const * taking tempo changes into account. */ double -BeatsFramesConverter::from (framecnt_t frames) const +BeatsFramesConverter::from (framepos_t frames) const { double b = _tempo_map.framewalk_to_beats (_origin_b, frames); return b; diff --git a/libs/ardour/broadcast_info.cc b/libs/ardour/broadcast_info.cc index cdc63cae11..af760629ff 100644 --- a/libs/ardour/broadcast_info.cc +++ b/libs/ardour/broadcast_info.cc @@ -28,6 +28,7 @@ #include "ardour/svn_revision.h" #include "ardour/ardour.h" #include "ardour/session.h" +#include "ardour/session_metadata.h" #include "pbd/convert.h" @@ -94,8 +95,8 @@ BroadcastInfo::set_originator_ref_from_session (Session const & session) serial_number << "ARDOUR" << "r" << std::setfill('0') << std::right << std::setw(5) << svn_revision; snprintf_bounded_null_filled (info->originator_reference, sizeof (info->originator_reference), "%2s%3s%12s%02d%02d%02d%9d", - session.config.get_bwf_country_code().c_str(), - session.config.get_bwf_organization_code().c_str(), + SessionMetadata::Metadata()->country().c_str(), + SessionMetadata::Metadata()->organization().c_str(), serial_number.str().c_str(), _time.tm_hour, _time.tm_min, diff --git a/libs/ardour/butler.cc b/libs/ardour/butler.cc index 72b9e5f586..3e9c7b0d96 100644 --- a/libs/ardour/butler.cc +++ b/libs/ardour/butler.cc @@ -24,7 +24,6 @@ #include "pbd/error.h" #include "pbd/pthread_utils.h" #include "ardour/butler.h" -#include "ardour/crossfade.h" #include "ardour/io.h" #include "ardour/midi_diskstream.h" #include "ardour/session.h" @@ -86,8 +85,6 @@ Butler::start_thread() MidiDiskstream::set_readahead_frames ((framecnt_t) (Config->get_midi_readahead() * rate)); - Crossfade::set_buffer_size (audio_dstream_playback_buffer_size); - should_run = false; if (pipe (request_pipe)) { diff --git a/libs/ardour/control_protocol_manager.cc b/libs/ardour/control_protocol_manager.cc index 6a8dd77d55..6aa0c51da2 100644 --- a/libs/ardour/control_protocol_manager.cc +++ b/libs/ardour/control_protocol_manager.cc @@ -41,7 +41,6 @@ const string ControlProtocolManager::state_node_name = X_("ControlProtocols"); ControlProtocolManager::ControlProtocolManager () { - } ControlProtocolManager::~ControlProtocolManager() @@ -75,8 +74,16 @@ ControlProtocolManager::set_session (Session* s) instantiate (**i); (*i)->requested = false; - if ((*i)->protocol && (*i)->state) { - (*i)->protocol->set_state (*(*i)->state, Stateful::loading_state_version); + if ((*i)->protocol) { + if ((*i)->state) { + (*i)->protocol->set_state (*(*i)->state, Stateful::loading_state_version); + } else { + /* guarantee a call to + set_state() whether we have + existing state or not + */ + (*i)->protocol->set_state (XMLNode(""), Stateful::loading_state_version); + } } } } @@ -312,13 +319,15 @@ ControlProtocolManager::set_state (const XMLNode& node, int /*version*/) if (prop && string_is_affirmative (prop->value())) { if ((prop = (*citer)->property (X_("name"))) != 0) { ControlProtocolInfo* cpi = cpi_by_name (prop->value()); + if (cpi) { - if (!(*citer)->children().empty()) { - cpi->state = (*citer)->children().front (); - } else { - cpi->state = 0; + + if (cpi->state) { + delete cpi->state; } + cpi->state = new XMLNode (**citer); + if (_session) { instantiate (*cpi); } else { @@ -401,3 +410,13 @@ ControlProtocolManager::instance () return *_instance; } + +void +ControlProtocolManager::midi_connectivity_established () +{ + Glib::Mutex::Lock lm (protocols_lock); + + for (list::iterator p = control_protocols.begin(); p != control_protocols.end(); ++p) { + (*p)->midi_connectivity_established (); + } +} diff --git a/libs/ardour/crossfade.cc b/libs/ardour/crossfade.cc deleted file mode 100644 index c687c60cd1..0000000000 --- a/libs/ardour/crossfade.cc +++ /dev/null @@ -1,1008 +0,0 @@ -/* - Copyright (C) 2003-2006 Paul Davis - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#include "ardour/debug.h" -#include "ardour/types.h" -#include "ardour/crossfade.h" -#include "ardour/audioregion.h" -#include "ardour/playlist.h" -#include "ardour/utils.h" -#include "ardour/session.h" -#include "ardour/source.h" -#include "ardour/region_factory.h" - -#include "i18n.h" -#include - -using namespace std; -using namespace ARDOUR; -using namespace PBD; - -framecnt_t Crossfade::_short_xfade_length = 0; - -/* XXX if and when we ever implement parallel processing of the process() - callback, these will need to be handled on a per-thread basis. -*/ - -Sample* Crossfade::crossfade_buffer_out = 0; -Sample* Crossfade::crossfade_buffer_in = 0; - - -#define CROSSFADE_DEFAULT_PROPERTIES \ - _active (Properties::active, _session.config.get_xfades_active ()) \ - , _follow_overlap (Properties::follow_overlap, false) - - -namespace ARDOUR { - namespace Properties { - PropertyDescriptor follow_overlap; - } -} - -void -Crossfade::make_property_quarks () -{ - Properties::follow_overlap.property_id = g_quark_from_static_string (X_("follow-overlap")); - DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for follow-overlap = %1\n", Properties::follow_overlap.property_id)); -} - -void -Crossfade::set_buffer_size (framecnt_t sz) -{ - delete [] crossfade_buffer_out; - crossfade_buffer_out = 0; - - delete [] crossfade_buffer_in; - crossfade_buffer_in = 0; - - if (sz) { - crossfade_buffer_out = new Sample[sz]; - crossfade_buffer_in = new Sample[sz]; - } -} - -bool -Crossfade::operator== (const Crossfade& other) -{ - return (_in == other._in) && (_out == other._out); -} - -Crossfade::Crossfade (boost::shared_ptr in, boost::shared_ptr out, - framecnt_t length, - AnchorPoint ap) - : AudioRegion (in->session(), 0, length, in->name() + string ("<>") + out->name()) - , CROSSFADE_DEFAULT_PROPERTIES - , _fade_in (Evoral::Parameter(FadeInAutomation)) // linear (gain coefficient) => -inf..+6dB - , _fade_out (Evoral::Parameter(FadeOutAutomation)) // linear (gain coefficient) => -inf..+6dB - -{ - register_properties (); - - _in = in; - _out = out; - _anchor_point = ap; - _fixed = true; - _follow_overlap = false; - - initialize (); -} - -Crossfade::Crossfade (boost::shared_ptr a, boost::shared_ptr b, CrossfadeModel model, bool act) - : AudioRegion (a->session(), 0, 0, a->name() + string ("<>") + b->name()) - , CROSSFADE_DEFAULT_PROPERTIES - , _fade_in (Evoral::Parameter(FadeInAutomation)) // linear (gain coefficient) => -inf..+6dB - , _fade_out (Evoral::Parameter(FadeOutAutomation)) // linear (gain coefficient) => -inf..+6dB -{ - register_properties (); - - _in_update = false; - _fixed = false; - _follow_overlap = false; - - if (compute (a, b, model)) { - throw failed_constructor(); - } - - _active = act; - - initialize (); -} - -Crossfade::Crossfade (const Playlist& playlist, XMLNode const & node) - : AudioRegion (playlist.session(), 0, 0, "unnamed crossfade") - , CROSSFADE_DEFAULT_PROPERTIES - , _fade_in (Evoral::Parameter(FadeInAutomation)) // linear (gain coefficient) => -inf..+6dB - , _fade_out (Evoral::Parameter(FadeOutAutomation)) // linear (gain coefficient) => -inf..+6dB - -{ - register_properties (); - - boost::shared_ptr r; - XMLProperty const * prop; - LocaleGuard lg (X_("POSIX")); - - /* we have to find the in/out regions before we can do anything else */ - - if ((prop = node.property ("in")) == 0) { - error << _("Crossfade: no \"in\" region in state") << endmsg; - throw failed_constructor(); - } - - PBD::ID id (prop->value()); - - r = playlist.find_region (id); - - if (!r) { - /* the `in' region is not in a playlist, which probably means that this crossfade - is in the undo record, so we have to find the region in the global region map. - */ - r = RegionFactory::region_by_id (id); - } - - if (!r) { - error << string_compose (_("Crossfade: no \"in\" region %1 found in playlist %2 nor in region map"), id, playlist.name()) - << endmsg; - throw failed_constructor(); - } - - if ((_in = boost::dynamic_pointer_cast (r)) == 0) { - throw failed_constructor(); - } - - if ((prop = node.property ("out")) == 0) { - error << _("Crossfade: no \"out\" region in state") << endmsg; - throw failed_constructor(); - } - - PBD::ID id2 (prop->value()); - - r = playlist.find_region (id2); - - if (!r) { - r = RegionFactory::region_by_id (id2); - } - - if (!r) { - error << string_compose (_("Crossfade: no \"out\" region %1 found in playlist %2 nor in region map"), id2, playlist.name()) - << endmsg; - throw failed_constructor(); - } - - if ((_out = boost::dynamic_pointer_cast (r)) == 0) { - throw failed_constructor(); - } - - _length = 0; - initialize(); - _active = true; - - if (set_state (node, Stateful::loading_state_version)) { - throw failed_constructor(); - } -} - -Crossfade::Crossfade (boost::shared_ptr orig, boost::shared_ptr newin, boost::shared_ptr newout) - : AudioRegion (boost::dynamic_pointer_cast (orig), 0) - , CROSSFADE_DEFAULT_PROPERTIES - , _fade_in (orig->_fade_in) - , _fade_out (orig->_fade_out) -{ - register_properties (); - - _active = orig->_active; - _in_update = orig->_in_update; - _anchor_point = orig->_anchor_point; - _follow_overlap = orig->_follow_overlap; - _fixed = orig->_fixed; - _position = orig->_position; - - _in = newin; - _out = newout; - - // copied from Crossfade::initialize() - _in_update = false; - - _out->suspend_fade_out (); - _in->suspend_fade_in (); - - overlap_type = _in->coverage (_out->position(), _out->last_frame()); - layer_relation = (int32_t) (_in->layer() - _out->layer()); - - // Let's make sure the fade isn't too long - set_xfade_length(_length); -} - - -Crossfade::~Crossfade () -{ -} - -void -Crossfade::register_properties () -{ - add_property (_active); - add_property (_follow_overlap); -} - -void -Crossfade::initialize () -{ - /* merge source lists from regions */ - - _sources = _in->sources(); - _sources.insert (_sources.end(), _out->sources().begin(), _out->sources().end()); - - for (SourceList::iterator i = _sources.begin(); i != _sources.end(); ++i) { - (*i)->inc_use_count (); - } - - _master_sources = _in->master_sources(); - _master_sources.insert(_master_sources.end(), _out->master_sources().begin(), _out->master_sources().end()); - - for (SourceList::iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) { - (*i)->inc_use_count (); - } - - _in_update = false; - - _out->suspend_fade_out (); - _in->suspend_fade_in (); - - _fade_out.freeze (); - _fade_out.clear (); - -#define EQUAL_POWER_MINUS_3DB -#ifdef EQUAL_POWER_MINUS_3DB - - _fade_out.add ((_length * 0.000000), 1.000000); - _fade_out.add ((_length * 0.166667), 0.948859); - _fade_out.add ((_length * 0.333333), 0.851507); - _fade_out.add ((_length * 0.500000), 0.707946); - _fade_out.add ((_length * 0.666667), 0.518174); - _fade_out.add ((_length * 0.833333), 0.282192); - _fade_out.add ((_length * 1.000000), 0.000000); - -#else // EQUAL_POWER_MINUS_6DB - - _fade_out.add ((_length * 0.000000), 1.000000); - _fade_out.add ((_length * 0.166667), 0.833033); - _fade_out.add ((_length * 0.333333), 0.666186); - _fade_out.add ((_length * 0.500000), 0.499459); - _fade_out.add ((_length * 0.666667), 0.332853); - _fade_out.add ((_length * 0.833333), 0.166366); - _fade_out.add ((_length * 1.000000), 0.000000); -#endif - - _fade_out.thaw (); - - _fade_in.freeze (); - _fade_in.clear (); - -#define EQUAL_POWER_MINUS_3DB -#ifdef EQUAL_POWER_MINUS_3DB - - _fade_in.add ((_length * 0.000000), 0.000000); - _fade_in.add ((_length * 0.166667), 0.282192); - _fade_in.add ((_length * 0.333333), 0.518174); - _fade_in.add ((_length * 0.500000), 0.707946); - _fade_in.add ((_length * 0.666667), 0.851507); - _fade_in.add ((_length * 0.833333), 0.948859); - _fade_in.add ((_length * 1.000000), 1.000000); - -#else // EQUAL_POWER_MINUS_SIX_DB - - _fade_in.add ((_length * 0.000000), 0.000000); - _fade_in.add ((_length * 0.166667), 0.166366); - _fade_in.add ((_length * 0.333333), 0.332853); - _fade_in.add ((_length * 0.500000), 0.499459); - _fade_in.add ((_length * 0.666667), 0.666186); - _fade_in.add ((_length * 0.833333), 0.833033); - _fade_in.add ((_length * 1.000000), 1.000000); - -#endif - - _fade_in.thaw (); - - overlap_type = _in->coverage (_out->position(), _out->last_frame()); - layer_relation = (int32_t) (_in->layer() - _out->layer()); -} - -framecnt_t -Crossfade::read_raw_internal (Sample* buf, framecnt_t start, framecnt_t cnt, int channel) const -{ - Sample* mixdown = new Sample[cnt]; - float* gain = new float[cnt]; - framecnt_t ret; - - ret = read_at (buf, mixdown, gain, start, cnt, channel); - - delete [] mixdown; - delete [] gain; - - return ret; -} - -framecnt_t -Crossfade::read_at (Sample *buf, Sample *mixdown_buffer, - float *gain_buffer, framepos_t start, framecnt_t cnt, uint32_t chan_n) const -{ - frameoffset_t offset; - framecnt_t to_write; - - if (!_active) { - return 0; - } - - if (start < _position) { - - /* handle an initial section of the read area that we do not - cover. - */ - - offset = _position - start; - - if (offset < cnt) { - cnt -= offset; - } else { - return 0; - } - - start = _position; - buf += offset; - to_write = min (_length.val(), cnt); - - } else { - - to_write = min ((_length - (start - _position)), cnt); - - } - - offset = start - _position; - - /* Prevent data from piling up inthe crossfade buffers when reading a transparent region */ - if (!(_out->opaque())) { - memset (crossfade_buffer_out, 0, sizeof (Sample) * to_write); - } else if (!(_in->opaque())) { - memset (crossfade_buffer_in, 0, sizeof (Sample) * to_write); - } - - _out->read_at (crossfade_buffer_out, mixdown_buffer, gain_buffer, start, to_write, chan_n); - _in->read_at (crossfade_buffer_in, mixdown_buffer, gain_buffer, start, to_write, chan_n); - - float* fiv = new float[to_write]; - float* fov = new float[to_write]; - - _fade_in.curve().get_vector (offset, offset+to_write, fiv, to_write); - _fade_out.curve().get_vector (offset, offset+to_write, fov, to_write); - - /* note: although we have not explicitly taken into account the return values - from _out->read_at() or _in->read_at(), the length() function does this - implicitly. why? because it computes a value based on the in+out regions' - position and length, and so we know precisely how much data they could return. - */ - - for (framecnt_t n = 0; n < to_write; ++n) { - buf[n] = (crossfade_buffer_out[n] * fov[n]) + (crossfade_buffer_in[n] * fiv[n]); - } - - delete [] fov; - delete [] fiv; - - return to_write; -} - -OverlapType -Crossfade::coverage (framepos_t start, framepos_t end) const -{ - framepos_t my_end = _position + _length; - - if ((start >= _position) && (end <= my_end)) { - return OverlapInternal; - } - if ((end >= _position) && (end <= my_end)) { - return OverlapStart; - } - if ((start >= _position) && (start <= my_end)) { - return OverlapEnd; - } - if ((_position >= start) && (_position <= end) && (my_end <= end)) { - return OverlapExternal; - } - return OverlapNone; -} - -void -Crossfade::set_active (bool yn) -{ - if (_active != yn) { - _active = yn; - PropertyChanged (PropertyChange (Properties::active)); - } -} - -bool -Crossfade::refresh () -{ - /* crossfades must be between non-muted regions */ - - if (_out->muted() || _in->muted()) { - Invalidated (shared_from_this ()); - return false; - } - - /* Top layer shouldn't be transparent */ - - if (!((layer_relation > 0 ? _in : _out)->opaque())) { - Invalidated (shared_from_this()); - return false; - } - - /* regions must cannot be identically sized and placed */ - - if (_in->position() == _out->position() && _in->length() == _out->length()) { - Invalidated (shared_from_this()); - return false; - } - - /* layer ordering cannot change */ - - int32_t new_layer_relation = (int32_t) (_in->layer() - _out->layer()); - - if (new_layer_relation * layer_relation < 0) { // different sign, layers rotated - Invalidated (shared_from_this ()); - return false; - } - - OverlapType ot = _in->coverage (_out->first_frame(), _out->last_frame()); - - if (ot == OverlapNone) { - Invalidated (shared_from_this ()); - return false; - } - - bool send_signal; - - if (ot != overlap_type) { - - if (_follow_overlap) { - - try { - compute (_in, _out, _session.config.get_xfade_model()); - } - - catch (NoCrossfadeHere& err) { - Invalidated (shared_from_this ()); - return false; - } - - send_signal = true; - - } else { - Invalidated (shared_from_this ()); - return false; - } - - } else { - - send_signal = update (); - } - - if (send_signal) { - PropertyChange bounds; - bounds.add (Properties::start); - bounds.add (Properties::position); - bounds.add (Properties::length); - PropertyChanged (bounds); /* EMIT SIGNAL */ - } - - _in_update = false; - - return true; -} - -bool -Crossfade::update () -{ - framecnt_t newlen; - - if (_follow_overlap) { - newlen = _out->first_frame() + _out->length() - _in->first_frame(); - } else { - newlen = _length; - } - - if (newlen == 0) { - Invalidated (shared_from_this ()); - return false; - } - - _in_update = true; - - if ((_follow_overlap && newlen != _length) || (_length > newlen)) { - - double factor = newlen / (double) _length; - - _fade_out.x_scale (factor); - _fade_in.x_scale (factor); - - _length = newlen; - } - - switch (_anchor_point) { - case StartOfIn: - _position = _in->first_frame(); - break; - - case EndOfIn: - _position = _in->last_frame() - _length; - break; - - case EndOfOut: - _position = _out->last_frame() - _length; - } - - return true; -} - -int -Crossfade::compute (boost::shared_ptr a, boost::shared_ptr b, CrossfadeModel model) -{ - boost::shared_ptr top; - boost::shared_ptr bottom; - framecnt_t short_xfade_length; - - short_xfade_length = _short_xfade_length; - - if (a->layer() < b->layer()) { - top = b; - bottom = a; - } else { - top = a; - bottom = b; - } - - /* first check for matching ends */ - - if (top->first_frame() == bottom->first_frame()) { - - /* Both regions start at the same point */ - - if (top->last_frame() < bottom->last_frame()) { - - /* top ends before bottom, so put an xfade - in at the end of top. - */ - - /* [-------- top ---------- ] - * {====== bottom =====================} - */ - - _in = bottom; - _out = top; - - if (top->last_frame() < short_xfade_length) { - _position = 0; - } else { - _position = top->last_frame() - short_xfade_length; - } - - set_xfade_length (min (short_xfade_length, top->length())); - _follow_overlap = false; - _anchor_point = EndOfIn; - _active = true; - _fixed = true; - - } else { - /* top ends after (or same time) as bottom - no xfade - */ - - /* [-------- top ------------------------ ] - * {====== bottom =====================} - */ - - throw NoCrossfadeHere(); - } - - } else if (top->last_frame() == bottom->last_frame()) { - - /* Both regions end at the same point */ - - if (top->first_frame() > bottom->first_frame()) { - - /* top starts after bottom, put an xfade in at the - start of top - */ - - /* [-------- top ---------- ] - * {====== bottom =====================} - */ - - _in = top; - _out = bottom; - _position = top->first_frame(); - set_xfade_length (min (short_xfade_length, top->length())); - _follow_overlap = false; - _anchor_point = StartOfIn; - _active = true; - _fixed = true; - - } else { - /* top starts before bottom - no xfade - */ - - /* [-------- top ------------------------ ] - * {====== bottom =====================} - */ - - throw NoCrossfadeHere(); - } - - } else { - - /* OK, time to do more regular overlapping */ - - OverlapType ot = top->coverage (bottom->first_frame(), bottom->last_frame()); - - switch (ot) { - case OverlapNone: - /* should be NOTREACHED as a precondition of creating - a new crossfade, but we need to handle it here. - */ - throw NoCrossfadeHere(); - break; - - case OverlapInternal: - case OverlapExternal: - /* should be NOTREACHED because of tests above */ - throw NoCrossfadeHere(); - break; - - case OverlapEnd: /* top covers start of bottom but ends within it */ - - /* [---- top ------------------------] - * { ==== bottom ============ } - */ - - _in = bottom; - _out = top; - _anchor_point = EndOfOut; - - if (model == FullCrossfade) { - _position = bottom->first_frame(); // "{" - set_xfade_length (_out->first_frame() + _out->length() - _in->first_frame()); - /* leave active alone */ - _follow_overlap = true; - } else { - set_xfade_length (min (short_xfade_length, top->length())); - _position = top->last_frame() - _length; // "]" - length - _active = true; - _follow_overlap = false; - - } - break; - - case OverlapStart: /* top starts within bottom but covers bottom's end */ - - /* { ==== top ============ } - * [---- bottom -------------------] - */ - - _in = top; - _out = bottom; - _position = top->first_frame(); - _anchor_point = StartOfIn; - - if (model == FullCrossfade) { - set_xfade_length (_out->first_frame() + _out->length() - _in->first_frame()); - /* leave active alone */ - _follow_overlap = true; - } else { - set_xfade_length (min (short_xfade_length, top->length())); - _active = true; - _follow_overlap = false; - - } - - break; - } - } - - return 0; -} - -XMLNode& -Crossfade::get_state () -{ - XMLNode* node = new XMLNode (X_("Crossfade")); - XMLNode* child; - char buf[64]; - LocaleGuard lg (X_("POSIX")); - - id().print (buf, sizeof (buf)); - node->add_property ("id", buf); - _out->id().print (buf, sizeof (buf)); - node->add_property ("out", buf); - _in->id().print (buf, sizeof (buf)); - node->add_property ("in", buf); - node->add_property ("active", (_active ? "yes" : "no")); - node->add_property ("follow-overlap", (_follow_overlap ? "yes" : "no")); - node->add_property ("fixed", (_fixed ? "yes" : "no")); - snprintf (buf, sizeof(buf), "%" PRId64, _length.val()); - node->add_property ("length", buf); - snprintf (buf, sizeof(buf), "%" PRIu32, (uint32_t) _anchor_point); - node->add_property ("anchor-point", buf); - snprintf (buf, sizeof(buf), "%" PRId64, _position.val()); - node->add_property ("position", buf); - - child = node->add_child ("FadeIn"); - - for (AutomationList::iterator ii = _fade_in.begin(); ii != _fade_in.end(); ++ii) { - XMLNode* pnode; - - pnode = new XMLNode ("point"); - - snprintf (buf, sizeof (buf), "%" PRId64, (framepos_t) floor ((*ii)->when)); - pnode->add_property ("x", buf); - snprintf (buf, sizeof (buf), "%.12g", (*ii)->value); - pnode->add_property ("y", buf); - child->add_child_nocopy (*pnode); - } - - child = node->add_child ("FadeOut"); - - for (AutomationList::iterator ii = _fade_out.begin(); ii != _fade_out.end(); ++ii) { - XMLNode* pnode; - - pnode = new XMLNode ("point"); - - snprintf (buf, sizeof (buf), "%" PRId64, (framepos_t) floor ((*ii)->when)); - pnode->add_property ("x", buf); - snprintf (buf, sizeof (buf), "%.12g", (*ii)->value); - pnode->add_property ("y", buf); - child->add_child_nocopy (*pnode); - } - - return *node; -} - -int -Crossfade::set_state (const XMLNode& node, int /*version*/) -{ - XMLNodeConstIterator i; - XMLNodeList children; - XMLNode* fi; - XMLNode* fo; - const XMLProperty* prop; - LocaleGuard lg (X_("POSIX")); - PropertyChange what_changed; - framepos_t val; - - set_id (node); - - if ((prop = node.property ("position")) != 0) { - sscanf (prop->value().c_str(), "%" PRId64, &val); - if (val != _position) { - _position = val; - what_changed.add (Properties::position); - } - } else { - warning << _("old-style crossfade information - no position information") << endmsg; - _position = _in->first_frame(); - } - - if ((prop = node.property ("active")) != 0) { - bool x = string_is_affirmative (prop->value()); - if (x != _active) { - _active = x; - what_changed.add (Properties::active); - } - } else { - _active = true; - } - - if ((prop = node.property ("follow-overlap")) != 0) { - _follow_overlap = string_is_affirmative (prop->value()); - } else { - _follow_overlap = false; - } - - if ((prop = node.property ("fixed")) != 0) { - _fixed = string_is_affirmative (prop->value()); - } else { - _fixed = false; - } - - if ((prop = node.property ("anchor-point")) != 0) { - _anchor_point = AnchorPoint (atoi ((prop->value().c_str()))); - } else { - _anchor_point = StartOfIn; - } - - if ((prop = node.property ("length")) != 0) { - - sscanf (prop->value().c_str(), "%" PRId64, &val); - if (val != _length) { - _length = val; - what_changed.add (Properties::length); - } - - } else { - - /* XXX this branch is legacy code from before - the point where we stored xfade lengths. - */ - - if ((_length = overlap_length()) == 0) { - throw failed_constructor(); - } - } - - if ((fi = find_named_node (node, "FadeIn")) == 0) { - return -1; - } - - if ((fo = find_named_node (node, "FadeOut")) == 0) { - return -1; - } - - /* fade in */ - - _fade_in.freeze (); - _fade_in.clear (); - - children = fi->children(); - - for (i = children.begin(); i != children.end(); ++i) { - if ((*i)->name() == "point") { - framepos_t x; - float y; - - prop = (*i)->property ("x"); - sscanf (prop->value().c_str(), "%" PRId64, &x); - - prop = (*i)->property ("y"); - sscanf (prop->value().c_str(), "%f", &y); - - _fade_in.add (x, y); - } - } - - if (_fade_in.size() < 2) { - /* fade state somehow saved with no points */ - return -1; - } - - _fade_in.front()->value = 0.0; - _fade_in.back()->value = 1.0; - - _fade_in.thaw (); - - /* fade out */ - - _fade_out.freeze (); - _fade_out.clear (); - - children = fo->children(); - - for (i = children.begin(); i != children.end(); ++i) { - if ((*i)->name() == "point") { - framepos_t x; - float y; - XMLProperty* prop; - - prop = (*i)->property ("x"); - sscanf (prop->value().c_str(), "%" PRId64, &x); - - prop = (*i)->property ("y"); - sscanf (prop->value().c_str(), "%f", &y); - - _fade_out.add (x, y); - } - } - - if (_fade_out.size() < 2) { - /* fade state somehow saved with no points */ - return -1; - } - - _fade_out.front()->value = 1.0; - _fade_out.back()->value = 0.0; - - _fade_out.thaw (); - - PropertyChanged (what_changed); /* EMIT SIGNAL */ - FadesChanged (); /* EMIT SIGNAL */ - - return 0; -} - -bool -Crossfade::can_follow_overlap () const -{ - return !_fixed; -} - -void -Crossfade::set_follow_overlap (bool yn) -{ - if (yn == _follow_overlap || _fixed) { - return; - } - - _follow_overlap = yn; - - if (!yn) { - set_xfade_length (_short_xfade_length); - } else { - set_xfade_length (_out->first_frame() + _out->length() - _in->first_frame()); - } - - PropertyChanged (PropertyChange (Properties::follow_overlap)); -} - -framecnt_t -Crossfade::set_xfade_length (framecnt_t len) -{ - framecnt_t limit = 0; - - switch (_anchor_point) { - case StartOfIn: - limit = _in->length(); - break; - - case EndOfIn: - limit = _in->length(); - break; - - case EndOfOut: - limit = _out->length(); - break; - - } - - len = min (limit, len); - - double factor = len / (double) _length; - - _in_update = true; - _fade_out.x_scale (factor); - _fade_in.x_scale (factor); - _in_update = false; - - _length = len; - - PropertyChanged (PropertyChange (Properties::length)); - - return len; -} - -framecnt_t -Crossfade::overlap_length () const -{ - if (_fixed) { - return _length; - } - return _out->first_frame() + _out->length() - _in->first_frame(); -} - -void -Crossfade::set_short_xfade_length (framecnt_t n) -{ - _short_xfade_length = n; -} diff --git a/libs/ardour/crossfade_binder.cc b/libs/ardour/crossfade_binder.cc deleted file mode 100644 index 34e7adab4b..0000000000 --- a/libs/ardour/crossfade_binder.cc +++ /dev/null @@ -1,63 +0,0 @@ -/* - Copyright (C) 2011 Paul Davis - Author: Carl Hetherington - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#include "ardour/crossfade_binder.h" -#include "ardour/session_playlists.h" -#include "ardour/crossfade.h" - -using namespace ARDOUR; - -CrossfadeBinder::CrossfadeBinder (boost::shared_ptr playlists, PBD::ID id) - : _playlists (playlists) - , _id (id) -{ - -} - - -CrossfadeBinder::CrossfadeBinder (XMLNode* node, boost::shared_ptr playlists) - : _playlists (playlists) -{ - XMLProperty* id = node->property ("crossfade-id"); - assert (id); - - _id = PBD::ID (id->value ()); -} - -ARDOUR::Crossfade * -CrossfadeBinder::get () const -{ - ARDOUR::Crossfade* c = _playlists->find_crossfade (_id).get (); - assert (c); - return c; -} - -std::string -CrossfadeBinder::type_name () const -{ - return "ARDOUR::Crossfade"; -} - -void -CrossfadeBinder::add_state (XMLNode* node) -{ - node->add_property ("crossfade-id", _id.to_s ()); -} - diff --git a/libs/ardour/delivery.cc b/libs/ardour/delivery.cc index 3b95c5ee3d..2fc8d82e47 100644 --- a/libs/ardour/delivery.cc +++ b/libs/ardour/delivery.cc @@ -446,7 +446,7 @@ Delivery::reset_panners () } void -Delivery::flush_buffers (framecnt_t nframes, framepos_t time) +Delivery::flush_buffers (framecnt_t nframes) { /* io_lock, not taken: function must be called from Session::process() calltree */ @@ -457,7 +457,7 @@ Delivery::flush_buffers (framecnt_t nframes, framepos_t time) PortSet& ports (_output->ports()); for (PortSet::iterator i = ports.begin(); i != ports.end(); ++i) { - i->flush_buffers (nframes, time); + i->flush_buffers (nframes); } } diff --git a/libs/ardour/diskstream.cc b/libs/ardour/diskstream.cc index 1cf4048c69..6e23eb81aa 100644 --- a/libs/ardour/diskstream.cc +++ b/libs/ardour/diskstream.cc @@ -685,15 +685,15 @@ Diskstream::route_going_away () } void -Diskstream::calculate_record_range(OverlapType ot, framepos_t transport_frame, framecnt_t nframes, - framecnt_t & rec_nframes, framecnt_t & rec_offset) +Diskstream::calculate_record_range (Evoral::OverlapType ot, framepos_t transport_frame, framecnt_t nframes, + framecnt_t & rec_nframes, framecnt_t & rec_offset) { switch (ot) { - case OverlapNone: + case Evoral::OverlapNone: rec_nframes = 0; break; - case OverlapInternal: + case Evoral::OverlapInternal: /* ---------- recrange |---| transrange */ @@ -701,7 +701,7 @@ Diskstream::calculate_record_range(OverlapType ot, framepos_t transport_frame, f rec_offset = 0; break; - case OverlapStart: + case Evoral::OverlapStart: /* |--------| recrange -----| transrange */ @@ -711,7 +711,7 @@ Diskstream::calculate_record_range(OverlapType ot, framepos_t transport_frame, f } break; - case OverlapEnd: + case Evoral::OverlapEnd: /* |--------| recrange |-------- transrange */ @@ -719,7 +719,7 @@ Diskstream::calculate_record_range(OverlapType ot, framepos_t transport_frame, f rec_offset = 0; break; - case OverlapExternal: + case Evoral::OverlapExternal: /* |--------| recrange -------------- transrange */ diff --git a/libs/ardour/enums.cc b/libs/ardour/enums.cc index d1eca7401c..4559ed457d 100644 --- a/libs/ardour/enums.cc +++ b/libs/ardour/enums.cc @@ -53,7 +53,6 @@ setup_enum_writer () vector i; vector s; - OverlapType _OverlapType; AlignStyle _AlignStyle; AlignChoice _AlignChoice; MeterPoint _MeterPoint; @@ -132,13 +131,6 @@ setup_enum_writer () #define REGISTER_ENUM(e) i.push_back (e); s.push_back (#e) #define REGISTER_CLASS_ENUM(t,e) i.push_back (t::e); s.push_back (#e) - REGISTER_ENUM (OverlapNone); - REGISTER_ENUM (OverlapInternal); - REGISTER_ENUM (OverlapStart); - REGISTER_ENUM (OverlapEnd); - REGISTER_ENUM (OverlapExternal); - REGISTER (_OverlapType); - REGISTER_ENUM (GainAutomation); REGISTER_ENUM (PanAzimuthAutomation); REGISTER_ENUM (PanElevationAutomation); diff --git a/libs/ardour/event_type_map.cc b/libs/ardour/event_type_map.cc index 4b21d32e58..7cf6045d86 100644 --- a/libs/ardour/event_type_map.cc +++ b/libs/ardour/event_type_map.cc @@ -159,6 +159,9 @@ EventTypeMap::new_parameter(uint32_t type, uint8_t channel, uint32_t id) const case PanFrontBackAutomation: case PanLFEAutomation: break; + case RecEnableAutomation: + /* default 0.0 - 1.0 is fine */ + break; case PluginAutomation: case SoloAutomation: case MuteAutomation: diff --git a/libs/ardour/globals.cc b/libs/ardour/globals.cc index c8c8199db3..3ad0f010c1 100644 --- a/libs/ardour/globals.cc +++ b/libs/ardour/globals.cc @@ -497,82 +497,6 @@ ARDOUR::setup_fpu () #endif } -ARDOUR::OverlapType -ARDOUR::coverage (framepos_t sa, framepos_t ea, - framepos_t sb, framepos_t eb) -{ - /* OverlapType returned reflects how the second (B) - range overlaps the first (A). - - The diagrams show various relative placements - of A and B for each OverlapType. - - Notes: - Internal: the start points cannot coincide - External: the start and end points can coincide - Start: end points can coincide - End: start points can coincide - - XXX Logically, Internal should disallow end - point equality. - */ - - /* - |--------------------| A - |------| B - |-----------------| B - - - "B is internal to A" - - */ - - if ((sb > sa) && (eb <= ea)) { - return OverlapInternal; - } - - /* - |--------------------| A - ----| B - -----------------------| B - --| B - - "B overlaps the start of A" - - */ - - if ((eb >= sa) && (eb <= ea)) { - return OverlapStart; - } - /* - |---------------------| A - |----------------- B - |----------------------- B - |- B - - "B overlaps the end of A" - - */ - if ((sb > sa) && (sb <= ea)) { - return OverlapEnd; - } - /* - |--------------------| A - -------------------------- B - |----------------------- B - ----------------------| B - |--------------------| B - - - "B overlaps all of A" - */ - if ((sa >= sb) && (sa <= eb) && (ea <= eb)) { - return OverlapExternal; - } - - return OverlapNone; -} - string ARDOUR::translation_kill_path () { diff --git a/libs/ardour/graph.cc b/libs/ardour/graph.cc index c38106506e..556748e2de 100644 --- a/libs/ardour/graph.cc +++ b/libs/ardour/graph.cc @@ -19,6 +19,7 @@ */ #include #include +#include #include "pbd/compose.h" #include "pbd/debug_rt_alloc.h" diff --git a/libs/ardour/import.cc b/libs/ardour/import.cc index dd1eefe4ef..67ed6da941 100644 --- a/libs/ardour/import.cc +++ b/libs/ardour/import.cc @@ -436,7 +436,7 @@ write_midi_data_to_new_files (Evoral::SMF* source, ImportStatus& status, const framepos_t pos = 0; const double length_beats = ceil(t / (double)source->ppqn()); BeatsFramesConverter converter(smfs->session().tempo_map(), pos); - smfs->update_length(pos, converter.to(length_beats)); + smfs->update_length(pos + converter.to(length_beats)); smfs->mark_streaming_write_completed (); if (status.cancel) { diff --git a/libs/ardour/interpolation.cc b/libs/ardour/interpolation.cc index 20ab584885..ccaaca7e76 100644 --- a/libs/ardour/interpolation.cc +++ b/libs/ardour/interpolation.cc @@ -10,21 +10,18 @@ framecnt_t LinearInterpolation::interpolate (int channel, framecnt_t nframes, Sample *input, Sample *output) { // index in the input buffers - framecnt_t i = 0; + framecnt_t i = 0; - double acceleration; - double distance = 0.0; + double acceleration = 0; if (_speed != _target_speed) { acceleration = _target_speed - _speed; - } else { - acceleration = 0.0; } - distance = phase[channel]; for (framecnt_t outsample = 0; outsample < nframes; ++outsample) { - i = floor(distance); - Sample fractional_phase_part = distance - i; + double const d = phase[channel] + outsample * (_speed + acceleration); + i = floor(d); + Sample fractional_phase_part = d - i; if (fractional_phase_part >= 1.0) { fractional_phase_part -= 1.0; i++; @@ -36,12 +33,11 @@ LinearInterpolation::interpolate (int channel, framecnt_t nframes, Sample *input input[i] * (1.0f - fractional_phase_part) + input[i+1] * fractional_phase_part; } - distance += _speed + acceleration; } + double const distance = phase[channel] + nframes * (_speed + acceleration); i = floor(distance); - phase[channel] = distance - floor(distance); - + phase[channel] = distance - i; return i; } diff --git a/libs/ardour/lv2/lv2plug.in/ns/ext/atom/atom.h b/libs/ardour/lv2/lv2plug.in/ns/ext/atom/atom.h deleted file mode 100644 index 3c7bee812b..0000000000 --- a/libs/ardour/lv2/lv2plug.in/ns/ext/atom/atom.h +++ /dev/null @@ -1,238 +0,0 @@ -/* - Copyright 2008-2012 David Robillard - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ - -/** - @file atom.h C header for the LV2 Atom extension - . -*/ - -#ifndef LV2_ATOM_H -#define LV2_ATOM_H - -#include -#include - -#define LV2_ATOM_URI "http://lv2plug.in/ns/ext/atom" -#define LV2_ATOM_PREFIX LV2_ATOM_URI "#" - -#define LV2_ATOM__Atom LV2_ATOM_PREFIX "Atom" -#define LV2_ATOM__AtomPort LV2_ATOM_PREFIX "AtomPort" -#define LV2_ATOM__AudioFrames LV2_ATOM_PREFIX "AudioFrames" -#define LV2_ATOM__Beats LV2_ATOM_PREFIX "Beats" -#define LV2_ATOM__Blank LV2_ATOM_PREFIX "Blank" -#define LV2_ATOM__Bool LV2_ATOM_PREFIX "Bool" -#define LV2_ATOM__Chunk LV2_ATOM_PREFIX "Chunk" -#define LV2_ATOM__Double LV2_ATOM_PREFIX "Double" -#define LV2_ATOM__Event LV2_ATOM_PREFIX "Event" -#define LV2_ATOM__Float LV2_ATOM_PREFIX "Float" -#define LV2_ATOM__Int LV2_ATOM_PREFIX "Int" -#define LV2_ATOM__Long LV2_ATOM_PREFIX "Long" -#define LV2_ATOM__Literal LV2_ATOM_PREFIX "Literal" -#define LV2_ATOM__Number LV2_ATOM_PREFIX "Number" -#define LV2_ATOM__Object LV2_ATOM_PREFIX "Object" -#define LV2_ATOM__Path LV2_ATOM_PREFIX "Path" -#define LV2_ATOM__Property LV2_ATOM_PREFIX "Property" -#define LV2_ATOM__Resource LV2_ATOM_PREFIX "Resource" -#define LV2_ATOM__Sequence LV2_ATOM_PREFIX "Sequence" -#define LV2_ATOM__Sound LV2_ATOM_PREFIX "Sound" -#define LV2_ATOM__String LV2_ATOM_PREFIX "String" -#define LV2_ATOM__TimeUnit LV2_ATOM_PREFIX "TimeUnit" -#define LV2_ATOM__Tuple LV2_ATOM_PREFIX "Tuple" -#define LV2_ATOM__URI LV2_ATOM_PREFIX "URI" -#define LV2_ATOM__URID LV2_ATOM_PREFIX "URID" -#define LV2_ATOM__Vector LV2_ATOM_PREFIX "Vector" -#define LV2_ATOM__beatTime LV2_ATOM_PREFIX "beatTime" -#define LV2_ATOM__bufferType LV2_ATOM_PREFIX "bufferType" -#define LV2_ATOM__childType LV2_ATOM_PREFIX "childType" -#define LV2_ATOM__eventTransfer LV2_ATOM_PREFIX "eventTransfer" -#define LV2_ATOM__frameTime LV2_ATOM_PREFIX "frameTime" -#define LV2_ATOM__supports LV2_ATOM_PREFIX "supports" -#define LV2_ATOM__timeUnit LV2_ATOM_PREFIX "timeUnit" - -#define LV2_ATOM_REFERENCE_TYPE 0 - -#ifdef __cplusplus -extern "C" { -#endif - -/** This expression will fail to compile if double does not fit in 64 bits. */ -typedef char lv2_atom_assert_double_fits_in_64_bits[ - ((sizeof(double) <= sizeof(uint64_t)) * 2) - 1]; - -/** - Return a pointer to the contents of an Atom. The "contents" of an atom - is the data past the complete type-specific header. - @param type The type of the atom, e.g. LV2_Atom_String. - @param atom A variable-sized atom. -*/ -#define LV2_ATOM_CONTENTS(type, atom) \ - ((void*)((uint8_t*)(atom) + sizeof(type))) - -/** - Return a pointer to the body of an Atom. The "body" of an atom is the - data just past the LV2_Atom head (i.e. the same offset for all types). -*/ -#define LV2_ATOM_BODY(atom) LV2_ATOM_CONTENTS(LV2_Atom, atom) - -/** The header of an atom:Atom. */ -typedef struct { - uint32_t size; /**< Size in bytes, not including type and size. */ - uint32_t type; /**< Type of this atom (mapped URI). */ -} LV2_Atom; - -/** An atom:Int or atom:Bool. May be cast to LV2_Atom. */ -typedef struct { - LV2_Atom atom; /**< Atom header. */ - int32_t body; /**< Integer value. */ -} LV2_Atom_Int; - -/** An atom:Long. May be cast to LV2_Atom. */ -typedef struct { - LV2_Atom atom; /**< Atom header. */ - int64_t body; /**< Integer value. */ -} LV2_Atom_Long; - -/** An atom:Float. May be cast to LV2_Atom. */ -typedef struct { - LV2_Atom atom; /**< Atom header. */ - float body; /**< Floating point value. */ -} LV2_Atom_Float; - -/** An atom:Double. May be cast to LV2_Atom. */ -typedef struct { - LV2_Atom atom; /**< Atom header. */ - double body; /**< Floating point value. */ -} LV2_Atom_Double; - -/** An atom:Bool. May be cast to LV2_Atom. */ -typedef LV2_Atom_Int LV2_Atom_Bool; - -/** An atom:URID. May be cast to LV2_Atom. */ -typedef struct { - LV2_Atom atom; /**< Atom header. */ - uint32_t body; /**< URID. */ -} LV2_Atom_URID; - -/** An atom:String. May be cast to LV2_Atom. */ -typedef struct { - LV2_Atom atom; /**< Atom header. */ - /* Contents (a null-terminated UTF-8 string) follow here. */ -} LV2_Atom_String; - -/** The body of an atom:Literal. */ -typedef struct { - uint32_t datatype; /**< Datatype URID. */ - uint32_t lang; /**< Language URID. */ - /* Contents (a null-terminated UTF-8 string) follow here. */ -} LV2_Atom_Literal_Body; - -/** An atom:Literal. May be cast to LV2_Atom. */ -typedef struct { - LV2_Atom atom; /**< Atom header. */ - LV2_Atom_Literal_Body body; /**< Body. */ -} LV2_Atom_Literal; - -/** An atom:Tuple. May be cast to LV2_Atom. */ -typedef struct { - LV2_Atom atom; /**< Atom header. */ - /* Contents (a series of complete atoms) follow here. */ -} LV2_Atom_Tuple; - -/** The body of an atom:Vector. */ -typedef struct { - uint32_t child_size; /**< The size of each element in the vector. */ - uint32_t child_type; /**< The type of each element in the vector. */ - /* Contents (a series of packed atom bodies) follow here. */ -} LV2_Atom_Vector_Body; - -/** An atom:Vector. May be cast to LV2_Atom. */ -typedef struct { - LV2_Atom atom; /**< Atom header. */ - LV2_Atom_Vector_Body body; /**< Body. */ -} LV2_Atom_Vector; - -/** The body of an atom:Property (e.g. in an atom:Object). */ -typedef struct { - uint32_t key; /**< Key (predicate) (mapped URI). */ - uint32_t context; /**< Context URID (may be, and generally is, 0). */ - LV2_Atom value; /**< Value atom header. */ - /* Value atom body follows here. */ -} LV2_Atom_Property_Body; - -/** An atom:Property. May be cast to LV2_Atom. */ -typedef struct { - LV2_Atom atom; /**< Atom header. */ - LV2_Atom_Property_Body body; /**< Body. */ -} LV2_Atom_Property; - -/** The body of an atom:Object. May be cast to LV2_Atom. */ -typedef struct { - uint32_t id; /**< URID (atom:Resource) or blank ID (atom:Blank). */ - uint32_t otype; /**< Type URID (same as rdf:type, for fast dispatch). */ - /* Contents (a series of property bodies) follow here. */ -} LV2_Atom_Object_Body; - -/** An atom:Object. May be cast to LV2_Atom. */ -typedef struct { - LV2_Atom atom; /**< Atom header. */ - LV2_Atom_Object_Body body; /**< Body. */ -} LV2_Atom_Object; - -/** The header of an atom:Event. Note this type is NOT an LV2_Atom. */ -typedef struct { - /** Time stamp. Which type is valid is determined by context. */ - union { - int64_t frames; /**< Time in audio frames. */ - double beats; /**< Time in beats. */ - } time; - LV2_Atom body; /**< Event body atom header. */ - /* Body atom contents follow here. */ -} LV2_Atom_Event; - -/** - The body of an atom:Sequence (a sequence of events). - - The unit field is either a URID that described an appropriate time stamp - type, or may be 0 where a default stamp type is known. For - LV2_Descriptor::run(), the default stamp type is atom:AudioFrames, i.e. - LV2_Atom_Audio_Time. - - The contents of a sequence is a series of LV2_Atom_Event, each aligned - to 64-bits, e.g.: -
-   | Event 1 (size 6)                              | Event 2
-   |       |       |       |       |       |       |       |       |
-   | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
-   |FRAMES |SUBFRMS|TYPE   |SIZE   |DATADATADATAPAD|FRAMES |SUBFRMS|...
-   
-*/ -typedef struct { - uint32_t unit; /**< URID of unit of event time stamps. */ - uint32_t pad; /**< Currently unused. */ - /* Contents (a series of events) follow here. */ -} LV2_Atom_Sequence_Body; - -/** An atom:Sequence. */ -typedef struct { - LV2_Atom atom; /**< Atom header. */ - LV2_Atom_Sequence_Body body; /**< Body. */ -} LV2_Atom_Sequence; - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif /* LV2_ATOM_H */ diff --git a/libs/ardour/lv2/lv2plug.in/ns/ext/atom/forge.h b/libs/ardour/lv2/lv2plug.in/ns/ext/atom/forge.h deleted file mode 100644 index ea5977e500..0000000000 --- a/libs/ardour/lv2/lv2plug.in/ns/ext/atom/forge.h +++ /dev/null @@ -1,618 +0,0 @@ -/* - Copyright 2008-2012 David Robillard - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ - -/** - @file forge.h An API for constructing LV2 atoms. - - This file provides an API for constructing Atoms which makes it relatively - simple to build nested atoms of arbitrary complexity without requiring - dynamic memory allocation. - - The API is based on successively appending the appropriate pieces to build a - complete Atom. The size of containers is automatically updated. Functions - that begin a container return (via their frame argument) a stack frame which - must be popped when the container is finished. - - All output is written to a user-provided buffer or sink function. This - makes it popssible to create create atoms on the stack, on the heap, in LV2 - port buffers, in a ringbuffer, or elsewhere, all using the same API. - - This entire API is realtime safe if used with a buffer or a realtime safe - sink, except lv2_atom_forge_init() which is only realtime safe if the URI - map function is. - - Note these functions are all static inline, do not take their address. - - This header is non-normative, it is provided for convenience. -*/ - -#ifndef LV2_ATOM_FORGE_H -#define LV2_ATOM_FORGE_H - -#include - -#include "lv2/lv2plug.in/ns/ext/atom/atom.h" -#include "lv2/lv2plug.in/ns/ext/atom/util.h" -#include "lv2/lv2plug.in/ns/ext/urid/urid.h" - -#ifdef __cplusplus -extern "C" { -#else -# include -#endif - -/** Handle for LV2_Atom_Forge_Sink. */ -typedef void* LV2_Atom_Forge_Sink_Handle; - -/** A reference to a chunk of written output. */ -typedef intptr_t LV2_Atom_Forge_Ref; - -/** Sink function for writing output. See lv2_atom_forge_set_sink(). */ -typedef LV2_Atom_Forge_Ref -(*LV2_Atom_Forge_Sink)(LV2_Atom_Forge_Sink_Handle handle, - const void* buf, - uint32_t size); - -/** Function for resolving a reference. See lv2_atom_forge_set_sink(). */ -typedef LV2_Atom* -(*LV2_Atom_Forge_Deref_Func)(LV2_Atom_Forge_Sink_Handle handle, - LV2_Atom_Forge_Ref ref); - -/** A stack frame used for keeping track of nested Atom containers. */ -typedef struct _LV2_Atom_Forge_Frame { - struct _LV2_Atom_Forge_Frame* parent; - LV2_Atom_Forge_Ref ref; -} LV2_Atom_Forge_Frame; - -/** A "forge" for creating atoms by appending to a buffer. */ -typedef struct { - uint8_t* buf; - uint32_t offset; - uint32_t size; - - LV2_Atom_Forge_Sink sink; - LV2_Atom_Forge_Deref_Func deref; - LV2_Atom_Forge_Sink_Handle handle; - - LV2_Atom_Forge_Frame* stack; - - LV2_URID Blank; - LV2_URID Bool; - LV2_URID Double; - LV2_URID Float; - LV2_URID Int; - LV2_URID Long; - LV2_URID Literal; - LV2_URID Path; - LV2_URID Property; - LV2_URID Resource; - LV2_URID Sequence; - LV2_URID String; - LV2_URID Tuple; - LV2_URID URI; - LV2_URID URID; - LV2_URID Vector; -} LV2_Atom_Forge; - -static inline void -lv2_atom_forge_set_buffer(LV2_Atom_Forge* forge, uint8_t* buf, size_t size); - -/** - Initialise @p forge. - - URIs will be mapped using @p map and stored, a reference to @p map itself is - not held. -*/ -static inline void -lv2_atom_forge_init(LV2_Atom_Forge* forge, LV2_URID_Map* map) -{ - lv2_atom_forge_set_buffer(forge, NULL, 0); - forge->Blank = map->map(map->handle, LV2_ATOM__Blank); - forge->Bool = map->map(map->handle, LV2_ATOM__Bool); - forge->Double = map->map(map->handle, LV2_ATOM__Double); - forge->Float = map->map(map->handle, LV2_ATOM__Float); - forge->Int = map->map(map->handle, LV2_ATOM__Int); - forge->Long = map->map(map->handle, LV2_ATOM__Long); - forge->Literal = map->map(map->handle, LV2_ATOM__Literal); - forge->Path = map->map(map->handle, LV2_ATOM__Path); - forge->Property = map->map(map->handle, LV2_ATOM__Property); - forge->Resource = map->map(map->handle, LV2_ATOM__Resource); - forge->Sequence = map->map(map->handle, LV2_ATOM__Sequence); - forge->String = map->map(map->handle, LV2_ATOM__String); - forge->Tuple = map->map(map->handle, LV2_ATOM__Tuple); - forge->URI = map->map(map->handle, LV2_ATOM__URI); - forge->URID = map->map(map->handle, LV2_ATOM__URID); - forge->Vector = map->map(map->handle, LV2_ATOM__Vector); -} - -static inline LV2_Atom* -lv2_atom_forge_deref(LV2_Atom_Forge* forge, LV2_Atom_Forge_Ref ref) -{ - if (forge->buf) { - return (LV2_Atom*)ref; - } else { - return forge->deref(forge->handle, ref); - } -} - -/** - @name Object Stack - @{ -*/ - -/** - Push a stack frame. - This is done automatically by container functions (which take a stack frame - pointer), but may be called by the user to push the top level container when - writing to an existing Atom. -*/ -static inline LV2_Atom_Forge_Ref -lv2_atom_forge_push(LV2_Atom_Forge* forge, - LV2_Atom_Forge_Frame* frame, - LV2_Atom_Forge_Ref ref) -{ - frame->parent = forge->stack; - frame->ref = ref; - forge->stack = frame; - return ref; -} - -/** Pop a stack frame. This must be called when a container is finished. */ -static inline void -lv2_atom_forge_pop(LV2_Atom_Forge* forge, LV2_Atom_Forge_Frame* frame) -{ - assert(frame == forge->stack); - forge->stack = frame->parent; -} - -/** Return true iff the top of the stack has the given type. */ -static inline bool -lv2_atom_forge_top_is(LV2_Atom_Forge* forge, uint32_t type) -{ - return forge->stack && - lv2_atom_forge_deref(forge, forge->stack->ref)->type == type; -} - -/** - @} - @name Output Configuration - @{ -*/ - -/** Set the output buffer where @p forge will write atoms. */ -static inline void -lv2_atom_forge_set_buffer(LV2_Atom_Forge* forge, uint8_t* buf, size_t size) -{ - forge->buf = buf; - forge->size = size; - forge->offset = 0; - forge->deref = NULL; - forge->sink = NULL; - forge->handle = NULL; - forge->stack = NULL; -} - -/** - Set the sink function where @p forge will write output. - - The return value of forge functions is an LV2_Atom_Forge_Ref which is an - integer type safe to use as a pointer but is otherwise opaque. The sink - function must return a ref that can be dereferenced to access as least - sizeof(LV2_Atom) bytes of the written data, so sizes can be updated. For - ringbuffers, this should be possible as long as the size of the buffer is a - multiple of sizeof(LV2_Atom), since atoms are always aligned. - - Note that 0 is an invalid reference, so if you are using a buffer offset be - sure to offset it such that 0 is never a valid reference. You will get - confusing errors otherwise. -*/ -static inline void -lv2_atom_forge_set_sink(LV2_Atom_Forge* forge, - LV2_Atom_Forge_Sink sink, - LV2_Atom_Forge_Deref_Func deref, - LV2_Atom_Forge_Sink_Handle handle) -{ - forge->buf = NULL; - forge->size = forge->offset = 0; - forge->deref = deref; - forge->sink = sink; - forge->handle = handle; - forge->stack = NULL; -} - -/** - @} - @name Low Level Output - @{ -*/ - -/** - Write raw output. This is used internally, but is also useful for writing - atom types not explicitly supported by the forge API. Note the caller is - responsible for ensuring the output is approriately padded. -*/ -static inline LV2_Atom_Forge_Ref -lv2_atom_forge_raw(LV2_Atom_Forge* forge, const void* data, uint32_t size) -{ - LV2_Atom_Forge_Ref out = 0; - if (forge->sink) { - out = forge->sink(forge->handle, data, size); - } else { - out = (LV2_Atom_Forge_Ref)forge->buf + forge->offset; - uint8_t* mem = forge->buf + forge->offset; - if (forge->offset + size > forge->size) { - return 0; - } - forge->offset += size; - memcpy(mem, data, size); - } - for (LV2_Atom_Forge_Frame* f = forge->stack; f; f = f->parent) { - lv2_atom_forge_deref(forge, f->ref)->size += size; - } - return out; -} - -/** Pad output accordingly so next write is 64-bit aligned. */ -static inline void -lv2_atom_forge_pad(LV2_Atom_Forge* forge, uint32_t written) -{ - const uint64_t pad = 0; - const uint32_t pad_size = lv2_atom_pad_size(written) - written; - lv2_atom_forge_raw(forge, &pad, pad_size); -} - -/** Write raw output, padding to 64-bits as necessary. */ -static inline LV2_Atom_Forge_Ref -lv2_atom_forge_write(LV2_Atom_Forge* forge, const void* data, uint32_t size) -{ - LV2_Atom_Forge_Ref out = lv2_atom_forge_raw(forge, data, size); - if (out) { - lv2_atom_forge_pad(forge, size); - } - return out; -} - -/** Write a null-terminated string body. */ -static inline LV2_Atom_Forge_Ref -lv2_atom_forge_string_body(LV2_Atom_Forge* forge, - const char* str, - uint32_t len) -{ - LV2_Atom_Forge_Ref out = lv2_atom_forge_raw(forge, str, len); - if (out && (out = lv2_atom_forge_raw(forge, "", 1))) { - lv2_atom_forge_pad(forge, len + 1); - } - return out; -} - -/** - @} - @name Atom Output - @{ -*/ - -/** Write an atom:Atom header. */ -static inline LV2_Atom_Forge_Ref -lv2_atom_forge_atom(LV2_Atom_Forge* forge, uint32_t size, uint32_t type) -{ - const LV2_Atom a = { size, type }; - return lv2_atom_forge_raw(forge, &a, sizeof(a)); -} - -/** Write a primitive (fixed-size) atom. */ -static inline LV2_Atom_Forge_Ref -lv2_atom_forge_primitive(LV2_Atom_Forge* forge, const LV2_Atom* a) -{ - if (lv2_atom_forge_top_is(forge, forge->Vector)) { - return lv2_atom_forge_raw(forge, LV2_ATOM_BODY(a), a->size); - } else { - return lv2_atom_forge_write(forge, a, sizeof(LV2_Atom) + a->size); - } -} - -/** Write an atom:Int. */ -static inline LV2_Atom_Forge_Ref -lv2_atom_forge_int(LV2_Atom_Forge* forge, int32_t val) -{ - const LV2_Atom_Int a = { { sizeof(val), forge->Int }, val }; - return lv2_atom_forge_primitive(forge, &a.atom); -} - -/** Write an atom:Long. */ -static inline LV2_Atom_Forge_Ref -lv2_atom_forge_long(LV2_Atom_Forge* forge, int64_t val) -{ - const LV2_Atom_Long a = { { sizeof(val), forge->Long }, val }; - return lv2_atom_forge_primitive(forge, &a.atom); -} - -/** Write an atom:Float. */ -static inline LV2_Atom_Forge_Ref -lv2_atom_forge_float(LV2_Atom_Forge* forge, float val) -{ - const LV2_Atom_Float a = { { sizeof(val), forge->Float }, val }; - return lv2_atom_forge_primitive(forge, &a.atom); -} - -/** Write an atom:Double. */ -static inline LV2_Atom_Forge_Ref -lv2_atom_forge_double(LV2_Atom_Forge* forge, double val) -{ - const LV2_Atom_Double a = { { sizeof(val), forge->Double }, val }; - return lv2_atom_forge_primitive(forge, &a.atom); -} - -/** Write an atom:Bool. */ -static inline LV2_Atom_Forge_Ref -lv2_atom_forge_bool(LV2_Atom_Forge* forge, bool val) -{ - const LV2_Atom_Bool a = { { sizeof(int32_t), forge->Bool }, val ? 1 : 0 }; - return lv2_atom_forge_primitive(forge, &a.atom); -} - -/** Write an atom:URID. */ -static inline LV2_Atom_Forge_Ref -lv2_atom_forge_urid(LV2_Atom_Forge* forge, LV2_URID id) -{ - const LV2_Atom_URID a = { { sizeof(id), forge->URID }, id }; - return lv2_atom_forge_primitive(forge, &a.atom); -} - -/** Write an atom compatible with atom:String. Used internally. */ -static inline LV2_Atom_Forge_Ref -lv2_atom_forge_typed_string(LV2_Atom_Forge* forge, - uint32_t type, - const char* str, - uint32_t len) -{ - const LV2_Atom_String a = { { len + 1, type } }; - LV2_Atom_Forge_Ref out = lv2_atom_forge_raw(forge, &a, sizeof(a)); - if (out) { - if (!lv2_atom_forge_string_body(forge, str, len)) { - LV2_Atom* atom = lv2_atom_forge_deref(forge, out); - atom->size = atom->type = 0; - out = 0; - } - } - return out; -} - -/** Write an atom:String. Note that @p str need not be NULL terminated. */ -static inline LV2_Atom_Forge_Ref -lv2_atom_forge_string(LV2_Atom_Forge* forge, const char* str, uint32_t len) -{ - return lv2_atom_forge_typed_string(forge, forge->String, str, len); -} - -/** - Write an atom:URI. Note that @p uri need not be NULL terminated. - This does not map the URI, but writes the complete URI string. To write - a mapped URI, use lv2_atom_forge_urid(). -*/ -static inline LV2_Atom_Forge_Ref -lv2_atom_forge_uri(LV2_Atom_Forge* forge, const char* uri, uint32_t len) -{ - return lv2_atom_forge_typed_string(forge, forge->URI, uri, len); -} - -/** Write an atom:Path. Note that @p path need not be NULL terminated. */ -static inline LV2_Atom_Forge_Ref -lv2_atom_forge_path(LV2_Atom_Forge* forge, const char* path, uint32_t len) -{ - return lv2_atom_forge_typed_string(forge, forge->Path, path, len); -} - -/** Write an atom:Literal. */ -static inline LV2_Atom_Forge_Ref -lv2_atom_forge_literal(LV2_Atom_Forge* forge, - const char* str, - uint32_t len, - uint32_t datatype, - uint32_t lang) -{ - const LV2_Atom_Literal a = { - { sizeof(LV2_Atom_Literal) - sizeof(LV2_Atom) + len + 1, - forge->Literal }, - { datatype, - lang } - }; - LV2_Atom_Forge_Ref out = lv2_atom_forge_raw(forge, &a, sizeof(a)); - if (out) { - if (!lv2_atom_forge_string_body(forge, str, len)) { - LV2_Atom* atom = lv2_atom_forge_deref(forge, out); - atom->size = atom->type = 0; - out = 0; - } - } - return out; -} - -/** Start an atom:Vector. */ -static inline LV2_Atom_Forge_Ref -lv2_atom_forge_vector_head(LV2_Atom_Forge* forge, - LV2_Atom_Forge_Frame* frame, - uint32_t child_size, - uint32_t child_type) -{ - const LV2_Atom_Vector a = { - { sizeof(LV2_Atom_Vector_Body), forge->Vector }, - { child_size, child_type } - }; - return lv2_atom_forge_push( - forge, frame, lv2_atom_forge_write(forge, &a, sizeof(a))); -} - -/** Write a complete atom:Vector. */ -static inline LV2_Atom_Forge_Ref -lv2_atom_forge_vector(LV2_Atom_Forge* forge, - uint32_t child_size, - uint32_t child_type, - uint32_t n_elems, - const void* elems) -{ - const LV2_Atom_Vector a = { - { sizeof(LV2_Atom_Vector_Body) + n_elems * child_size, forge->Vector }, - { child_size, child_type } - }; - LV2_Atom_Forge_Ref out = lv2_atom_forge_write(forge, &a, sizeof(a)); - if (out) { - lv2_atom_forge_write(forge, elems, child_size * n_elems); - } - return out; -} - -/** - Write the header of an atom:Tuple. - - The passed frame will be initialised to represent this tuple. To complete - the tuple, write a sequence of atoms, then pop the frame with - lv2_atom_forge_pop(). - - For example: - @code - // Write tuple (1, 2.0) - LV2_Atom_Forge_Frame frame; - LV2_Atom* tup = (LV2_Atom*)lv2_atom_forge_tuple(forge, &frame); - lv2_atom_forge_int32(forge, 1); - lv2_atom_forge_float(forge, 2.0); - lv2_atom_forge_pop(forge, &frame); - @endcode -*/ -static inline LV2_Atom_Forge_Ref -lv2_atom_forge_tuple(LV2_Atom_Forge* forge, LV2_Atom_Forge_Frame* frame) -{ - const LV2_Atom_Tuple a = { { 0, forge->Tuple } }; - return lv2_atom_forge_push( - forge, frame, lv2_atom_forge_write(forge, &a, sizeof(a))); -} - -/** - Write the header of an atom:Resource. - - The passed frame will be initialised to represent this object. To complete - the object, write a sequence of properties, then pop the frame with - lv2_atom_forge_pop(). - - For example: - @code - LV2_URID eg_Cat = map("http://example.org/Cat"); - LV2_URID eg_name = map("http://example.org/name"); - - // Write object header - LV2_Atom_Forge_Frame frame; - lv2_atom_forge_resource(forge, &frame, 1, eg_Cat); - - // Write property: eg:name = "Hobbes" - lv2_atom_forge_property_head(forge, eg_name, 0); - lv2_atom_forge_string(forge, "Hobbes", strlen("Hobbes")); - - // Finish object - lv2_atom_forge_pop(forge, &frame); - @endcode -*/ -static inline LV2_Atom_Forge_Ref -lv2_atom_forge_resource(LV2_Atom_Forge* forge, - LV2_Atom_Forge_Frame* frame, - LV2_URID id, - LV2_URID otype) -{ - const LV2_Atom_Object a = { - { sizeof(LV2_Atom_Object) - sizeof(LV2_Atom), forge->Resource }, - { id, otype } - }; - LV2_Atom_Forge_Ref out = lv2_atom_forge_write(forge, &a, sizeof(a)); - return lv2_atom_forge_push(forge, frame, out); -} - -/** - The same as lv2_atom_forge_resource(), but for object:Blank. -*/ -static inline LV2_Atom_Forge_Ref -lv2_atom_forge_blank(LV2_Atom_Forge* forge, - LV2_Atom_Forge_Frame* frame, - uint32_t id, - LV2_URID otype) -{ - const LV2_Atom_Object a = { - { sizeof(LV2_Atom_Object) - sizeof(LV2_Atom), forge->Blank }, - { id, otype } - }; - LV2_Atom_Forge_Ref out = lv2_atom_forge_write(forge, &a, sizeof(a)); - return lv2_atom_forge_push(forge, frame, out); -} - -/** - Write the header for a property body (likely in an Object). - See lv2_atom_forge_object() documentation for an example. -*/ -static inline LV2_Atom_Forge_Ref -lv2_atom_forge_property_head(LV2_Atom_Forge* forge, - LV2_URID key, - LV2_URID context) -{ - const LV2_Atom_Property_Body a = { key, context, { 0, 0 } }; - return lv2_atom_forge_write(forge, &a, 2 * sizeof(uint32_t)); -} - -/** - Write the header for a Sequence. - The size of the returned sequence will be 0, so passing it as the parent - parameter to other forge methods will do the right thing. -*/ -static inline LV2_Atom_Forge_Ref -lv2_atom_forge_sequence_head(LV2_Atom_Forge* forge, - LV2_Atom_Forge_Frame* frame, - uint32_t unit) -{ - const LV2_Atom_Sequence a = { - { sizeof(LV2_Atom_Sequence) - sizeof(LV2_Atom), forge->Sequence }, - { unit, 0 } - }; - LV2_Atom_Forge_Ref out = lv2_atom_forge_write(forge, &a, sizeof(a)); - return lv2_atom_forge_push(forge, frame, out); -} - -/** - Write the time stamp header of an Event (in a Sequence) in audio frames. - After this, call the appropriate forge method(s) to write the body, passing - the same @p parent parameter. Note the returned LV2_Event is NOT an Atom. -*/ -static inline LV2_Atom_Forge_Ref -lv2_atom_forge_frame_time(LV2_Atom_Forge* forge, int64_t frames) -{ - return lv2_atom_forge_write(forge, &frames, sizeof(frames)); -} - -/** - Write the time stamp header of an Event (in a Sequence) in beats. - After this, call the appropriate forge method(s) to write the body, passing - the same @p parent parameter. Note the returned LV2_Event is NOT an Atom. -*/ -static inline LV2_Atom_Forge_Ref -lv2_atom_forge_beat_time(LV2_Atom_Forge* forge, double beats) -{ - return lv2_atom_forge_write(forge, &beats, sizeof(beats)); -} - -/** - @} -*/ - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif /* LV2_ATOM_FORGE_H */ diff --git a/libs/ardour/lv2/lv2plug.in/ns/ext/atom/util.h b/libs/ardour/lv2/lv2plug.in/ns/ext/atom/util.h deleted file mode 100644 index 6b46a676f8..0000000000 --- a/libs/ardour/lv2/lv2plug.in/ns/ext/atom/util.h +++ /dev/null @@ -1,424 +0,0 @@ -/* - Copyright 2008-2012 David Robillard - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ - -/** - @file util.h Helper functions for the LV2 Atom extension. - - Note these functions are all static inline, do not take their address. - - This header is non-normative, it is provided for convenience. -*/ - -#ifndef LV2_ATOM_UTIL_H -#define LV2_ATOM_UTIL_H - -#include -#include -#include - -#include "lv2/lv2plug.in/ns/ext/atom/atom.h" - -#ifdef __cplusplus -extern "C" { -#else -# include -#endif - -/** Pad a size to 64 bits. */ -static inline uint32_t -lv2_atom_pad_size(uint32_t size) -{ - return (size + 7) & (~7); -} - -/** Return the total size of @p atom, including the header. */ -static inline uint32_t -lv2_atom_total_size(const LV2_Atom* atom) -{ - return sizeof(LV2_Atom) + atom->size; -} - -/** Return true iff @p atom is null. */ -static inline bool -lv2_atom_is_null(const LV2_Atom* atom) -{ - return !atom || (atom->type == 0 && atom->size == 0); -} - -/** Return true iff @p a is equal to @p b. */ -static inline bool -lv2_atom_equals(const LV2_Atom* a, const LV2_Atom* b) -{ - return (a == b) || ((a->type == b->type) && - (a->size == b->size) && - !memcmp(a + 1, b + 1, a->size)); -} - -/** - @name Sequence Iterator - @{ -*/ - -/** An iterator over the elements of an LV2_Atom_Sequence. */ -typedef LV2_Atom_Event* LV2_Atom_Sequence_Iter; - -/** Get an iterator pointing to the first element in a Sequence body. */ -static inline LV2_Atom_Sequence_Iter -lv2_sequence_body_begin(const LV2_Atom_Sequence_Body* body) -{ - return (LV2_Atom_Sequence_Iter)(body + 1); -} - -/** Get an iterator pointing to the first element in a Sequence. */ -static inline LV2_Atom_Sequence_Iter -lv2_sequence_begin(const LV2_Atom_Sequence* seq) -{ - return (LV2_Atom_Sequence_Iter)(seq + 1); -} - -/** Return true iff @p i has reached the end of @p body. */ -static inline bool -lv2_sequence_body_is_end(const LV2_Atom_Sequence_Body* body, - uint32_t size, - LV2_Atom_Sequence_Iter i) -{ - return (uint8_t*)i >= ((uint8_t*)body + size); -} - -/** Return true iff @p i has reached the end of @p seq. */ -static inline bool -lv2_sequence_is_end(const LV2_Atom_Sequence* seq, LV2_Atom_Sequence_Iter i) -{ - return (uint8_t*)i >= ((uint8_t*)seq + sizeof(LV2_Atom) + seq->atom.size); -} - -/** Return an iterator to the element following @p i. */ -static inline LV2_Atom_Sequence_Iter -lv2_sequence_iter_next(const LV2_Atom_Sequence_Iter i) -{ - return (LV2_Atom_Sequence_Iter)((uint8_t*)i - + sizeof(LV2_Atom_Event) - + lv2_atom_pad_size(i->body.size)); -} - -/** Return the element pointed to by @p i. */ -static inline LV2_Atom_Event* -lv2_sequence_iter_get(LV2_Atom_Sequence_Iter i) -{ - return (LV2_Atom_Event*)i; -} - -/** - A macro for iterating over all events in a Sequence. - @param sequence The sequence to iterate over - @param iter The name of the iterator - - This macro is used similarly to a for loop (which it expands to), e.g.: - @code - LV2_SEQUENCE_FOREACH(sequence, i) { - LV2_Atom_Event* ev = lv2_sequence_iter_get(i); - // Do something with ev here... - } - @endcode -*/ -#define LV2_SEQUENCE_FOREACH(sequence, iter) \ - for (LV2_Atom_Sequence_Iter (iter) = lv2_sequence_begin(sequence); \ - !lv2_sequence_is_end(sequence, (iter)); \ - (iter) = lv2_sequence_iter_next(iter)) - -/** A version of LV2_SEQUENCE_FOREACH for when only the body is available. */ -#define LV2_SEQUENCE_BODY_FOREACH(body, size, iter) \ - for (LV2_Atom_Sequence_Iter (iter) = lv2_sequence_body_begin(body); \ - !lv2_sequence_body_is_end(body, size, (iter)); \ - (iter) = lv2_sequence_iter_next(iter)) - -/** - @} - @name Tuple Iterator - @{ -*/ - -/** An iterator over the elements of an LV2_Atom_Tuple. */ -typedef LV2_Atom* LV2_Atom_Tuple_Iter; - -/** Get an iterator pointing to the first element in @p tup. */ -static inline LV2_Atom_Tuple_Iter -lv2_tuple_begin(const LV2_Atom_Tuple* tup) -{ - return (LV2_Atom_Tuple_Iter)(LV2_ATOM_BODY(tup)); -} - -/** Return true iff @p i has reached the end of @p body. */ -static inline bool -lv2_atom_tuple_body_is_end(const void* body, - uint32_t size, - LV2_Atom_Tuple_Iter i) -{ - return (uint8_t*)i >= ((uint8_t*)body + size); -} - -/** Return true iff @p i has reached the end of @p tup. */ -static inline bool -lv2_tuple_is_end(const LV2_Atom_Tuple* tup, LV2_Atom_Tuple_Iter i) -{ - return lv2_atom_tuple_body_is_end(LV2_ATOM_BODY(tup), tup->atom.size, i); -} - -/** Return an iterator to the element following @p i. */ -static inline LV2_Atom_Tuple_Iter -lv2_tuple_iter_next(const LV2_Atom_Tuple_Iter i) -{ - return (LV2_Atom_Tuple_Iter)( - (uint8_t*)i + sizeof(LV2_Atom) + lv2_atom_pad_size(i->size)); -} - -/** Return the element pointed to by @p i. */ -static inline LV2_Atom* -lv2_tuple_iter_get(LV2_Atom_Tuple_Iter i) -{ - return (LV2_Atom*)i; -} - -/** - A macro for iterating over all properties of a Tuple. - @param tuple The tuple to iterate over - @param iter The name of the iterator - - This macro is used similarly to a for loop (which it expands to), e.g.: - @code - LV2_TUPLE_FOREACH(tuple, i) { - LV2_Atom* elem = lv2_tuple_iter_get(i); - // Do something with elem here... - } - @endcode -*/ -#define LV2_TUPLE_FOREACH(tuple, iter) \ - for (LV2_Atom_Tuple_Iter (iter) = lv2_tuple_begin(tuple); \ - !lv2_tuple_is_end(tuple, (iter)); \ - (iter) = lv2_tuple_iter_next(iter)) - -/** A version of LV2_TUPLE_FOREACH for when only the body is available. */ -#define LV2_TUPLE_BODY_FOREACH(body, size, iter) \ - for (LV2_Atom_Tuple_Iter (iter) = (LV2_Atom_Tuple_Iter)body; \ - !lv2_atom_tuple_body_is_end(body, size, (iter)); \ - (iter) = lv2_tuple_iter_next(iter)) - -/** - @} - @name Object Iterator - @{ -*/ - -/** An iterator over the properties of an LV2_Atom_Object. */ -typedef LV2_Atom_Property_Body* LV2_Atom_Object_Iter; - -static inline LV2_Atom_Object_Iter -lv2_object_body_begin(const LV2_Atom_Object_Body* body) -{ - return (LV2_Atom_Object_Iter)(body + 1); -} - -/** Get an iterator pointing to the first property in @p obj. */ -static inline LV2_Atom_Object_Iter -lv2_object_begin(const LV2_Atom_Object* obj) -{ - return (LV2_Atom_Object_Iter)(obj + 1); -} - -static inline bool -lv2_atom_object_body_is_end(const LV2_Atom_Object_Body* body, - uint32_t size, - LV2_Atom_Object_Iter i) -{ - return (uint8_t*)i >= ((uint8_t*)body + size); -} - -/** Return true iff @p i has reached the end of @p obj. */ -static inline bool -lv2_object_is_end(const LV2_Atom_Object* obj, LV2_Atom_Object_Iter i) -{ - return (uint8_t*)i >= ((uint8_t*)obj + sizeof(LV2_Atom) + obj->atom.size); -} - -/** Return an iterator to the property following @p i. */ -static inline LV2_Atom_Object_Iter -lv2_object_iter_next(const LV2_Atom_Object_Iter i) -{ - const LV2_Atom* const value = (LV2_Atom*)((uint8_t*)i + sizeof(i)); - return (LV2_Atom_Object_Iter)((uint8_t*)i - + sizeof(LV2_Atom_Property_Body) - + lv2_atom_pad_size(value->size)); -} - -/** Return the property pointed to by @p i. */ -static inline LV2_Atom_Property_Body* -lv2_object_iter_get(LV2_Atom_Object_Iter i) -{ - return (LV2_Atom_Property_Body*)i; -} - -/** - A macro for iterating over all properties of an Object. - @param object The object to iterate over - @param iter The name of the iterator - - This macro is used similarly to a for loop (which it expands to), e.g.: - @code - LV2_OBJECT_FOREACH(object, i) { - LV2_Atom_Property_Body* prop = lv2_object_iter_get(i); - // Do something with prop here... - } - @endcode -*/ -#define LV2_OBJECT_FOREACH(object, iter) \ - for (LV2_Atom_Object_Iter (iter) = lv2_object_begin(object); \ - !lv2_object_is_end(object, (iter)); \ - (iter) = lv2_object_iter_next(iter)) - -/** A version of LV2_OBJECT_FOREACH for when only the body is available. */ -#define LV2_OBJECT_BODY_FOREACH(body, size, iter) \ - for (LV2_Atom_Object_Iter (iter) = lv2_object_body_begin(body); \ - !lv2_atom_object_body_is_end(body, size, (iter)); \ - (iter) = lv2_object_iter_next(iter)) - -/** - @} - @name Object Query - @{ -*/ - -/** A single entry in an Object query. */ -typedef struct { - uint32_t key; /**< Key to query (input set by user) */ - const LV2_Atom** value; /**< Found value (output set by query function) */ -} LV2_Atom_Object_Query; - -static const LV2_Atom_Object_Query LV2_OBJECT_QUERY_END = { 0, NULL }; - -/** - Get an object's values for various keys. - - The value pointer of each item in @p query will be set to the location of - the corresponding value in @p object. Every value pointer in @p query MUST - be initialised to NULL. This function reads @p object in a single linear - sweep. By allocating @p query on the stack, objects can be "queried" - quickly without allocating any memory. This function is realtime safe. - - This function can only do "flat" queries, it is not smart enough to match - variables in nested objects. - - For example: - @code - const LV2_Atom* name = NULL; - const LV2_Atom* age = NULL; - LV2_Atom_Object_Query q[] = { - { urids.eg_name, &name }, - { urids.eg_age, &age }, - LV2_OBJECT_QUERY_END - }; - lv2_object_query(obj, q); - // name and age are now set to the appropriate values in obj, or NULL. - @endcode -*/ -static inline int -lv2_object_query(const LV2_Atom_Object* object, LV2_Atom_Object_Query* query) -{ - int matches = 0; - int n_queries = 0; - - /* Count number of query keys so we can short-circuit when done */ - for (LV2_Atom_Object_Query* q = query; q->key; ++q) { - ++n_queries; - } - - LV2_OBJECT_FOREACH(object, o) { - const LV2_Atom_Property_Body* prop = lv2_object_iter_get(o); - for (LV2_Atom_Object_Query* q = query; q->key; ++q) { - if (q->key == prop->key && !*q->value) { - *q->value = &prop->value; - if (++matches == n_queries) { - return matches; - } - break; - } - } - } - return matches; -} - -/** - Variable argument version of lv2_object_get(). - - This is nicer-looking in code, but a bit more error-prone since it is not - type safe and the argument list must be terminated. - - The arguments should be a series of uint32_t key and const LV2_Atom** value - pairs, terminated by a zero key. The value pointers MUST be initialized to - NULL. For example: - - @code - const LV2_Atom* name = NULL; - const LV2_Atom* age = NULL; - lv2_object_get(obj, - uris.name_key, &name, - uris.age_key, &age, - 0); - @endcode -*/ -static inline int -lv2_object_get(const LV2_Atom_Object* object, ...) -{ - int matches = 0; - int n_queries = 0; - - /* Count number of keys so we can short-circuit when done */ - va_list args; - va_start(args, object); - for (n_queries = 0; va_arg(args, uint32_t); ++n_queries) { - if (!va_arg(args, const LV2_Atom**)) { - return -1; - } - } - va_end(args); - - LV2_OBJECT_FOREACH(object, o) { - const LV2_Atom_Property_Body* prop = lv2_object_iter_get(o); - va_start(args, object); - for (int i = 0; i < n_queries; ++i) { - uint32_t qkey = va_arg(args, uint32_t); - const LV2_Atom** qval = va_arg(args, const LV2_Atom**); - if (qkey == prop->key && !*qval) { - *qval = &prop->value; - if (++matches == n_queries) { - return matches; - } - break; - } - } - va_end(args); - } - return matches; -} - -/** - @} -*/ - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif /* LV2_ATOM_UTIL_H */ diff --git a/libs/ardour/lv2/lv2plug.in/ns/ext/event/event-helpers.h b/libs/ardour/lv2/lv2plug.in/ns/ext/event/event-helpers.h deleted file mode 100644 index 79f8a35fe8..0000000000 --- a/libs/ardour/lv2/lv2plug.in/ns/ext/event/event-helpers.h +++ /dev/null @@ -1,243 +0,0 @@ -/* lv2_event_helpers.h - Helper functions for the LV2 events extension. - * - * Copyright (C) 2008 David Robillard - * - * This header is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This header is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this header; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place, Suite 330, Boston, MA 01222-1307 USA - */ - -#ifndef LV2_EVENT_HELPERS_H -#define LV2_EVENT_HELPERS_H - -#include -#include -#include -#include -#include -#include "lv2/lv2plug.in/ns/ext/event/event.h" - -/** @file - * This header defines some helper functions for the the LV2 events extension - * with URI ('lv2ev'). - * - * These functions are provided for convenience only, use of them is not - * required for supporting lv2ev (i.e. the events extension is defined by the - * raw buffer format described in lv2_event.h and NOT by this API). - * - * Note that these functions are all static inline which basically means: - * do not take the address of these functions. */ - - -/** Pad a size to 64 bits (for event sizes) */ -static inline uint16_t -lv2_event_pad_size(uint16_t size) -{ - return (size + 7) & (~7); -} - - -/** Initialize (empty, reset..) an existing event buffer. - * The contents of buf are ignored entirely and overwritten, except capacity - * which is unmodified. */ -static inline void -lv2_event_buffer_reset(LV2_Event_Buffer* buf, uint16_t stamp_type, uint8_t *data) -{ - buf->data = data; - buf->header_size = sizeof(LV2_Event_Buffer); - buf->stamp_type = stamp_type; - buf->event_count = 0; - buf->size = 0; -} - - -/** Allocate a new, empty event buffer. */ -static inline LV2_Event_Buffer* -lv2_event_buffer_new(uint32_t capacity, uint16_t stamp_type) -{ - LV2_Event_Buffer* buf = (LV2_Event_Buffer*)malloc(sizeof(LV2_Event_Buffer) + capacity); - if (buf != NULL) { - buf->capacity = capacity; - lv2_event_buffer_reset(buf, stamp_type, (uint8_t *)(buf + 1)); - return buf; - } else { - return NULL; - } -} - - -/** An iterator over an LV2_Event_Buffer. - * - * Multiple simultaneous read iterators over a single buffer is fine, - * but changing the buffer invalidates all iterators (e.g. RW Lock). */ -typedef struct { - LV2_Event_Buffer* buf; - uint32_t offset; -} LV2_Event_Iterator; - - -/** Reset an iterator to point to the start of @a buf. - * @return True if @a iter is valid, otherwise false (buffer is empty) */ -static inline bool -lv2_event_begin(LV2_Event_Iterator* iter, - LV2_Event_Buffer* buf) -{ - iter->buf = buf; - iter->offset = 0; - return (buf->size > 0); -} - - -/** Check if @a iter is valid.. - * @return True if @a iter is valid, otherwise false (past end of buffer) */ -static inline bool -lv2_event_is_valid(LV2_Event_Iterator* iter) -{ - return (iter->offset < iter->buf->size); -} - - -/** Advance @a iter forward one event. - * @a iter must be valid. - * @return True if @a iter is valid, otherwise false (reached end of buffer) */ -static inline bool -lv2_event_increment(LV2_Event_Iterator* iter) -{ - assert(lv2_event_is_valid(iter)); - - LV2_Event* const ev = (LV2_Event*)( - (uint8_t*)iter->buf->data + iter->offset); - - iter->offset += lv2_event_pad_size(sizeof(LV2_Event) + ev->size); - - return true; -} - - -/** Dereference an event iterator (get the event currently pointed at). - * @a iter must be valid. - * @a data if non-NULL, will be set to point to the contents of the event - * returned. - * @return A Pointer to the event @a iter is currently pointing at, or NULL - * if the end of the buffer is reached (in which case @a data is - * also set to NULL). */ -static inline LV2_Event* -lv2_event_get(LV2_Event_Iterator* iter, - uint8_t** data) -{ - assert(lv2_event_is_valid(iter)); - - LV2_Event* const ev = (LV2_Event*)( - (uint8_t*)iter->buf->data + iter->offset); - - if (data) - *data = (uint8_t*)ev + sizeof(LV2_Event); - - return ev; -} - - -/** Write an event at @a iter. - * The event (if any) pointed to by @iter will be overwritten, and @a iter - * incremented to point to the following event (i.e. several calls to this - * function can be done in sequence without twiddling iter in-between). - * @return True if event was written, otherwise false (buffer is full). */ -static inline bool -lv2_event_write(LV2_Event_Iterator* iter, - uint32_t frames, - uint32_t subframes, - uint16_t type, - uint16_t size, - const uint8_t* data) -{ - if (iter->buf->capacity - iter->buf->size < sizeof(LV2_Event) + size) - return false; - - LV2_Event* const ev = (LV2_Event*)( - (uint8_t*)iter->buf->data + iter->offset); - - ev->frames = frames; - ev->subframes = subframes; - ev->type = type; - ev->size = size; - memcpy((uint8_t*)ev + sizeof(LV2_Event), data, size); - ++iter->buf->event_count; - - size = lv2_event_pad_size(sizeof(LV2_Event) + size); - iter->buf->size += size; - iter->offset += size; - - return true; -} - - -/** Reserve space for an event in the buffer and return a pointer to - the memory where the caller can write the event data, or NULL if there - is not enough room in the buffer. */ -static inline uint8_t* -lv2_event_reserve(LV2_Event_Iterator* iter, - uint32_t frames, - uint32_t subframes, - uint16_t type, - uint16_t size) -{ - size = lv2_event_pad_size(size); - if (iter->buf->capacity - iter->buf->size < sizeof(LV2_Event) + size) - return NULL; - - LV2_Event* const ev = (LV2_Event*)((uint8_t*)iter->buf->data + - iter->offset); - - ev->frames = frames; - ev->subframes = subframes; - ev->type = type; - ev->size = size; - ++iter->buf->event_count; - - size = lv2_event_pad_size(sizeof(LV2_Event) + size); - iter->buf->size += size; - iter->offset += size; - - return (uint8_t*)ev + sizeof(LV2_Event); -} - - -/** Write an event at @a iter. - * The event (if any) pointed to by @iter will be overwritten, and @a iter - * incremented to point to the following event (i.e. several calls to this - * function can be done in sequence without twiddling iter in-between). - * @return True if event was written, otherwise false (buffer is full). */ -static inline bool -lv2_event_write_event(LV2_Event_Iterator* iter, - const LV2_Event* ev, - const uint8_t* data) -{ - if (iter->buf->capacity - iter->buf->size < sizeof(LV2_Event) + ev->size) - return false; - - LV2_Event* const write_ev = (LV2_Event*)( - (uint8_t*)iter->buf->data + iter->offset); - - *write_ev = *ev; - memcpy((uint8_t*)write_ev + sizeof(LV2_Event), data, ev->size); - ++iter->buf->event_count; - - const uint16_t size = lv2_event_pad_size(sizeof(LV2_Event) + ev->size); - iter->buf->size += size; - iter->offset += size; - - return true; -} - -#endif // LV2_EVENT_HELPERS_H - diff --git a/libs/ardour/lv2/lv2plug.in/ns/ext/event/event.h b/libs/ardour/lv2/lv2plug.in/ns/ext/event/event.h deleted file mode 100644 index 701fecb534..0000000000 --- a/libs/ardour/lv2/lv2plug.in/ns/ext/event/event.h +++ /dev/null @@ -1,260 +0,0 @@ -/* lv2_event.h - C header file for the LV2 events extension. - * - * Copyright (C) 2006-2007 Lars Luthman - * Copyright (C) 2008 David Robillard - * - * This header is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This header is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this header; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place, Suite 330, Boston, MA 01222-1307 USA - */ - -#ifndef LV2_EVENT_H -#define LV2_EVENT_H - -#define LV2_EVENT_URI "http://lv2plug.in/ns/ext/event" -#define LV2_EVENT_AUDIO_STAMP 0 - -#include - -/** @file - * This header defines the code portion of the LV2 events extension with URI - * ('lv2ev'). - * - * This extension is a generic transport mechanism for time stamped events - * of any type (e.g. MIDI, OSC, ramps, etc). Each port can transport mixed - * events of any type; the type of events and timestamps are defined by a URI - * which is mapped to an integer by the host for performance reasons. - * - * This extension requires the host to support the LV2 URI Map extension. - * Any host which supports this extension MUST guarantee that any call to - * the LV2 URI Map uri_to_id function with the URI of this extension as the - * 'map' argument returns a value within the range of uint16_t. - */ - - -/** The best Pulses Per Quarter Note for tempo-based uint32_t timestmaps. - * Equal to 2^12 * 5 * 7 * 9 * 11 * 13 * 17, which is evenly divisble - * by all integers from 1 through 18 inclusive, and powers of 2 up to 2^12. - */ -static const uint32_t LV2_EVENT_PPQN = 3136573440U; - - -/** An LV2 event (header only). - * - * LV2 events are generic time-stamped containers for any type of event. - * The type field defines the format of a given event's contents. - * - * This struct defines the header of an LV2 event. An LV2 event is a single - * chunk of POD (plain old data), usually contained in a flat buffer - * (see LV2_EventBuffer below). Unless a required feature says otherwise, - * hosts may assume a deep copy of an LV2 event can be created safely - * using a simple: - * - * memcpy(ev_copy, ev, sizeof(LV2_Event) + ev->size); (or equivalent) - */ -typedef struct { - - /** The frames portion of timestamp. The units used here can optionally be - * set for a port (with the lv2ev:timeUnits property), otherwise this - * is audio frames, corresponding to the sample_count parameter of the - * LV2 run method (e.g. frame 0 is the first frame for that call to run). - */ - uint32_t frames; - - /** The sub-frames portion of timestamp. The units used here can - * optionally be set for a port (with the lv2ev:timeUnits property), - * otherwise this is 1/(2^32) of an audio frame. - */ - uint32_t subframes; - - /** The type of this event, as a number which represents some URI - * defining an event type. This value MUST be some value previously - * returned from a call to the uri_to_id function defined in the LV2 - * URI map extension (see lv2_uri_map.h). - * There are special rules which must be followed depending on the type - * of an event. If the plugin recognizes an event type, the definition - * of that event type will describe how to interpret the event, and - * any required behaviour. Otherwise, if the type is 0, this event is a - * non-POD event and lv2_event_unref MUST be called if the event is - * 'dropped' (see above). Even if the plugin does not understand an event, - * it may pass the event through to an output by simply copying (and NOT - * calling lv2_event_unref). These rules are designed to allow for generic - * event handling plugins and large non-POD events, but with minimal hassle - * on simple plugins that "don't care" about these more advanced features. - */ - uint16_t type; - - /** The size of the data portion of this event in bytes, which immediately - * follows. The header size (12 bytes) is not included in this value. - */ - uint16_t size; - - /* size bytes of data follow here */ - -} LV2_Event; - - - -/** A buffer of LV2 events (header only). - * - * Like events (which this contains) an event buffer is a single chunk of POD: - * the entire buffer (including contents) can be copied with a single memcpy. - * The first contained event begins sizeof(LV2_EventBuffer) bytes after - * the start of this struct. - * - * After this header, the buffer contains an event header (defined by struct - * LV2_Event), followed by that event's contents (padded to 64 bits), followed by - * another header, etc: - * - * | | | | | | | - * | | | | | | | | | | | | | | | | | | | | | | | | | - * |FRAMES |SUBFRMS|TYP|LEN|DATA..DATA..PAD|FRAMES | ... - */ -typedef struct { - - /** The contents of the event buffer. This may or may not reside in the - * same block of memory as this header, plugins must not assume either. - * The host guarantees this points to at least capacity bytes of allocated - * memory (though only size bytes of that are valid events). - */ - uint8_t* data; - - /** The size of this event header in bytes (including everything). - * - * This is to allow for extending this header in the future without - * breaking binary compatibility. Whenever this header is copied, - * it MUST be done using this field (and NOT the sizeof this struct). - */ - uint16_t header_size; - - /** The type of the time stamps for events in this buffer. - * As a special exception, '0' always means audio frames and subframes - * (1/UINT32_MAX'th of a frame) in the sample rate passed to instantiate. - * INPUTS: The host must set this field to the numeric ID of some URI - * defining the meaning of the frames/subframes fields of contained - * events (obtained by the LV2 URI Map uri_to_id function with the URI - * of this extension as the 'map' argument, see lv2_uri_map.h). - * The host must never pass a plugin a buffer which uses a stamp type - * the plugin does not 'understand'. The value of this field must - * never change, except when connect_port is called on the input - * port, at which time the host MUST have set the stamp_type field to - * the value that will be used for all subsequent run calls. - * OUTPUTS: The plugin may set this to any value that has been returned - * from uri_to_id with the URI of this extension for a 'map' argument. - * When connected to a buffer with connect_port, output ports MUST set - * this field to the type of time stamp they will be writing. On any - * call to connect_port on an event input port, the plugin may change - * this field on any output port, it is the responsibility of the host - * to check if any of these values have changed and act accordingly. - */ - uint16_t stamp_type; - - /** The number of events in this buffer. - * INPUTS: The host must set this field to the number of events - * contained in the data buffer before calling run(). - * The plugin must not change this field. - * OUTPUTS: The plugin must set this field to the number of events it - * has written to the buffer before returning from run(). - * Any initial value should be ignored by the plugin. - */ - uint32_t event_count; - - /** The size of the data buffer in bytes. - * This is set by the host and must not be changed by the plugin. - * The host is allowed to change this between run() calls. - */ - uint32_t capacity; - - /** The size of the initial portion of the data buffer containing data. - * INPUTS: The host must set this field to the number of bytes used - * by all events it has written to the buffer (including headers) - * before calling the plugin's run(). - * The plugin must not change this field. - * OUTPUTS: The plugin must set this field to the number of bytes - * used by all events it has written to the buffer (including headers) - * before returning from run(). - * Any initial value should be ignored by the plugin. - */ - uint32_t size; - -} LV2_Event_Buffer; - - -/** Opaque pointer to host data. */ -typedef void* LV2_Event_Callback_Data; - - -/** The data field of the LV2_Feature for this extension. - * - * To support this feature the host must pass an LV2_Feature struct to the - * plugin's instantiate method with URI "http://lv2plug.in/ns/ext/event" - * and data pointed to an instance of this struct. - */ -typedef struct { - - /** Opaque pointer to host data. - * - * The plugin MUST pass this to any call to functions in this struct. - * Otherwise, it must not be interpreted in any way. - */ - LV2_Event_Callback_Data callback_data; - - /** Take a reference to a non-POD event. - * - * If a plugin receives an event with type 0, it means the event is a - * pointer to some object in memory and not a flat sequence of bytes - * in the buffer. When receiving a non-POD event, the plugin already - * has an implicit reference to the event. If the event is stored AND - * passed to an output, lv2_event_ref MUST be called on that event. - * If the event is only stored OR passed through, this is not necessary - * (as the plugin already has 1 implicit reference). - * - * @param event An event received at an input that will not be copied to - * an output or stored in any way. - * @param context The calling context. (Like event types) this is a mapped - * URI, see lv2_context.h. Simple plugin with just a run() - * method should pass 0 here (the ID of the 'standard' LV2 - * run context). The host guarantees that this function is - * realtime safe iff @a context is realtime safe. - * - * PLUGINS THAT VIOLATE THESE RULES MAY CAUSE CRASHES AND MEMORY LEAKS. - */ - uint32_t (*lv2_event_ref)(LV2_Event_Callback_Data callback_data, - LV2_Event* event); - - /** Drop a reference to a non-POD event. - * - * If a plugin receives an event with type 0, it means the event is a - * pointer to some object in memory and not a flat sequence of bytes - * in the buffer. If the plugin does not pass the event through to - * an output or store it internally somehow, it MUST call this function - * on the event (more information on using non-POD events below). - * - * @param event An event received at an input that will not be copied to - * an output or stored in any way. - * @param context The calling context. (Like event types) this is a mapped - * URI, see lv2_context.h. Simple plugin with just a run() - * method should pass 0 here (the ID of the 'standard' LV2 - * run context). The host guarantees that this function is - * realtime safe iff @a context is realtime safe. - * - * PLUGINS THAT VIOLATE THESE RULES MAY CAUSE CRASHES AND MEMORY LEAKS. - */ - uint32_t (*lv2_event_unref)(LV2_Event_Callback_Data callback_data, - LV2_Event* event); - -} LV2_Event_Feature; - - -#endif // LV2_EVENT_H - diff --git a/libs/ardour/lv2/lv2plug.in/ns/ext/state/state.h b/libs/ardour/lv2/lv2plug.in/ns/ext/state/state.h deleted file mode 100644 index d8a5407bda..0000000000 --- a/libs/ardour/lv2/lv2plug.in/ns/ext/state/state.h +++ /dev/null @@ -1,359 +0,0 @@ -/* - Copyright 2010-2012 David Robillard - Copyright 2010 Leonard Ritter - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ - -/** - @file state.h - C API for the LV2 State extension . -*/ - -#ifndef LV2_STATE_H -#define LV2_STATE_H - -#include -#include - -#include "lv2/lv2plug.in/ns/lv2core/lv2.h" - -#define LV2_STATE_URI "http://lv2plug.in/ns/ext/state" -#define LV2_STATE_PREFIX LV2_STATE_URI "#" - -#define LV2_STATE__State LV2_STATE_PREFIX "State" -#define LV2_STATE__interface LV2_STATE_PREFIX "interface" -#define LV2_STATE__makePath LV2_STATE_PREFIX "makePath" -#define LV2_STATE__mapPath LV2_STATE_PREFIX "mapPath" -#define LV2_STATE__state LV2_STATE_PREFIX "state" - -#ifdef __cplusplus -extern "C" { -#else -# include -#endif - -typedef void* LV2_State_Handle; -typedef void* LV2_State_Map_Path_Handle; -typedef void* LV2_State_Make_Path_Handle; - -/** - Flags describing value characteristics. - - These flags are used along with the value's type URI to determine how to - (de-)serialise the value data, or whether it is even possible to do so. -*/ -typedef enum { - - /** - Plain Old Data. - - Values with this flag contain no pointers or references to other areas - of memory. It is safe to copy POD values with a simple memcpy and store - them for the duration of the process. A POD value is not necessarily - safe to trasmit between processes or machines (e.g. filenames are POD), - see LV2_STATE_IS_PORTABLE for details. - - Implementations MUST NOT attempt to copy or serialise a non-POD value if - they do not understand its type (and thus know how to correctly do so). - */ - LV2_STATE_IS_POD = 1, - - /** - Portable (architecture independent) data. - - Values with this flag are in a format that is usable on any - architecture. A portable value saved on one machine can be restored on - another machine regardless of architecture. The format of portable - values MUST NOT depend on architecture-specific properties like - endianness or alignment. Portable values MUST NOT contain filenames. - */ - LV2_STATE_IS_PORTABLE = 1 << 1, - - /** - Native data. - - This flag is used by the host to indicate that the saved data is only - going to be used locally in the currently running process (e.g. for - instance duplication or snapshots), so the plugin should use the most - efficient representation possible and not worry about serialisation - and portability. - */ - LV2_STATE_IS_NATIVE = 1 << 2 - -} LV2_State_Flags; - -/** A status code for state functions. */ -typedef enum { - LV2_STATE_SUCCESS = 0, /**< Completed successfully. */ - LV2_STATE_ERR_UNKNOWN = 1, /**< Unknown error. */ - LV2_STATE_ERR_BAD_TYPE = 2, /**< Failed due to unsupported type. */ - LV2_STATE_ERR_BAD_FLAGS = 3, /**< Failed due to unsupported flags. */ - LV2_STATE_ERR_NO_FEATURE = 4, /**< Failed due to missing features. */ - LV2_STATE_ERR_NO_PROPERTY = 5 /**< Failed due to missing property. */ -} LV2_State_Status; - -/** - A host-provided function to store a property. - @param handle Must be the handle passed to LV2_State_Interface.save(). - @param key The key to store @p value under (URID). - @param value Pointer to the value to be stored. - @param size The size of @p value in bytes. - @param type The type of @p value (URID). - @param flags LV2_State_Flags for @p value. - @return 0 on success, otherwise a non-zero error code. - - The host passes a callback of this type to LV2_State_Interface.save(). This - callback is called repeatedly by the plugin to store all the properties that - describe its current state. - - DO NOT INVENT NONSENSE URI SCHEMES FOR THE KEY. Best is to use keys from - existing vocabularies. If nothing appropriate is available, use http URIs - that point to somewhere you can host documents so documentation can be made - resolvable (e.g. a child of the plugin or project URI). If this is not - possible, invent a URN scheme, e.g. urn:myproj:whatever. The plugin MUST - NOT pass an invalid URI key. - - The host MAY fail to store a property for whatever reason, but SHOULD - store any property that is LV2_STATE_IS_POD and LV2_STATE_IS_PORTABLE. - Implementations SHOULD use the types from the LV2 Atom extension - (http://lv2plug.in/ns/ext/atom) wherever possible. The plugin SHOULD - attempt to fall-back and avoid the error if possible. - - Note that @p size MUST be > 0, and @p value MUST point to a valid region of - memory @p size bytes long (this is required to make restore unambiguous). - - The plugin MUST NOT attempt to use this function outside of the - LV2_State_Interface.restore() context. -*/ -typedef LV2_State_Status (*LV2_State_Store_Function)( - LV2_State_Handle handle, - uint32_t key, - const void* value, - size_t size, - uint32_t type, - uint32_t flags); - -/** - A host-provided function to retrieve a property. - @param handle Must be the handle passed to LV2_State_Interface.restore(). - @param key The key of the property to retrieve (URID). - @param size (Output) If non-NULL, set to the size of the restored value. - @param type (Output) If non-NULL, set to the type of the restored value. - @param flags (Output) If non-NULL, set to the flags for the restored value. - @return A pointer to the restored value (object), or NULL if no value - has been stored under @p key. - - A callback of this type is passed by the host to - LV2_State_Interface.restore(). This callback is called repeatedly by the - plugin to retrieve any properties it requires to restore its state. - - The returned value MUST remain valid until LV2_State_Interface.restore() - returns. The plugin MUST NOT attempt to use this function, or any value - returned from it, outside of the LV2_State_Interface.restore() context. -*/ -typedef const void* (*LV2_State_Retrieve_Function)( - LV2_State_Handle handle, - uint32_t key, - size_t* size, - uint32_t* type, - uint32_t* flags); - -/** - LV2 Plugin State Interface. - - When the plugin's extension_data is called with argument - LV2_STATE__interface, the plugin MUST return an LV2_State_Interface - structure, which remains valid for the lifetime of the plugin. - - The host can use the contained function pointers to save and restore the - state of a plugin instance at any time, provided the threading restrictions - of the functions are met. - - Stored data is only guaranteed to be compatible between instances of plugins - with the same URI (i.e. if a change to a plugin would cause a fatal error - when restoring state saved by a previous version of that plugin, the plugin - URI MUST change just as it must when ports change incompatibly). Plugin - authors should consider this possibility, and always store sensible data - with meaningful types to avoid such problems in the future. -*/ -typedef struct _LV2_State_Interface { - - /** - Save plugin state using a host-provided @p store callback. - - @param instance The instance handle of the plugin. - @param store The host-provided store callback. - @param handle An opaque pointer to host data which MUST be passed as the - handle parameter to @p store if it is called. - @param flags Flags describing desired properties of this save. These - flags may be used to determine the most appropriate values to store. - @param features Extensible parameter for passing any additional - features to be used for this save. - - The plugin is expected to store everything necessary to completely - restore its state later. Plugins SHOULD store simple POD data whenever - possible, and consider the possibility of state being restored much - later on a different machine. - - The @p handle pointer and @p store function MUST NOT be used - beyond the scope of save(). - - This function has its own special threading class: it may not be called - concurrently with any "Instantiation" function, but it may be called - concurrently with functions in any other class, unless the definition of - that class prohibits it (e.g. it may not be called concurrently with a - "Discovery" function, but it may be called concurrently with an "Audio" - function. The plugin is responsible for any locking or lock-free - techniques necessary to make this possible. - - Note that in the simple case where state is only modified by restore(), - there are no synchronization issues since save() is never called - concurrently with restore() (though run() may read it during a save). - - Plugins that dynamically modify state while running, however, must take - care to do so in such a way that a concurrent call to save() will save a - consistent representation of plugin state for a single instant in time. - */ - LV2_State_Status (*save)(LV2_Handle instance, - LV2_State_Store_Function store, - LV2_State_Handle handle, - uint32_t flags, - const LV2_Feature *const * features); - - /** - Restore plugin state using a host-provided @p retrieve callback. - - @param instance The instance handle of the plugin. - @param retrieve The host-provided retrieve callback. - @param handle An opaque pointer to host data which MUST be passed as the - handle parameter to @p retrieve if it is called. - @param flags Currently unused. - @param features Extensible parameter for passing any additional - features to be used for this restore. - - The plugin MAY assume a restored value was set by a previous call to - LV2_State_Interface.save() by a plugin with the same URI. - - The plugin MUST gracefully fall back to a default value when a value can - not be retrieved. This allows the host to reset the plugin state with - an empty map. - - The @p handle pointer and @p store function MUST NOT be used - beyond the scope of restore(). - - This function is in the "Instantiation" threading class as defined by - LV2. This means it MUST NOT be called concurrently with any other - function on the same plugin instance. - */ - LV2_State_Status (*restore)(LV2_Handle instance, - LV2_State_Retrieve_Function retrieve, - LV2_State_Handle handle, - uint32_t flags, - const LV2_Feature *const * features); - -} LV2_State_Interface; - -/** - Feature data for state:mapPath (LV2_STATE__mapPath). -*/ -typedef struct { - - /** - Opaque host data. - */ - LV2_State_Map_Path_Handle handle; - - /** - Map an absolute path to an abstract path for use in plugin state. - @param handle MUST be the @p handle member of this struct. - @param absolute_path The absolute path of a file. - @return An abstract path suitable for use in plugin state. - - The plugin MUST use this function to map any paths that will be stored - in plugin state. The returned value is an abstract path which MAY not - be an actual file system path; @ref absolute_path() MUST be used to map - it to an actual path in order to use the file. - - Plugins MUST NOT make any assumptions about abstract paths except that - they can be mapped back to the absolute path of the "same" file (though - not necessarily the same original path) using @ref absolute_path(). - - This function may only be called within the context of - LV2_State_Interface methods. The caller is responsible for freeing the - returned value with free(). - */ - char* (*abstract_path)(LV2_State_Map_Path_Handle handle, - const char* absolute_path); - - /** - Map an abstract path from plugin state to an absolute path. - @param handle MUST be the @p handle member of this struct. - @param abstract_path An abstract path (e.g. a path from plugin state). - @return An absolute file system path. - - The plugin MUST use this function in order to actually open or otherwise - use any paths loaded from plugin state. - - This function may only be called within the context of - LV2_State_Interface methods. The caller is responsible for freeing the - returned value with free(). - */ - char* (*absolute_path)(LV2_State_Map_Path_Handle handle, - const char* abstract_path); - -} LV2_State_Map_Path; - -/** - Feature data for state:makePath (@ref LV2_STATE__makePath). -*/ -typedef struct { - - /** - Opaque host data. - */ - LV2_State_Make_Path_Handle handle; - - /** - Return a path the plugin may use to create a new file. - @param handle MUST be the @p handle member of this struct. - @param path The path of the new file within a namespace unique to this - plugin instance. - @return The absolute path to use for the new file. - - This function can be used by plugins to create files and directories, - either at state saving time (if this feature is passed to - LV2_State_Interface.save()) or any time (if this feature is passed to - LV2_Descriptor.instantiate()). - - The host MUST do whatever is necessary for the plugin to be able to - create a file at the returned path (e.g. using fopen), including - creating any leading directories. - - If this function is passed to LV2_Descriptor.instantiate(), it may be - called from any non-realtime context. If it is passed to - LV2_State_Interface.save(), it may only be called within the dynamic - scope of that function call. - - The caller is responsible for freeing the returned value with free(). - */ - char* (*path)(LV2_State_Make_Path_Handle handle, - const char* path); - -} LV2_State_Make_Path; - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif /* LV2_STATE_H */ diff --git a/libs/ardour/lv2/lv2plug.in/ns/ext/uri-map/uri-map.h b/libs/ardour/lv2/lv2plug.in/ns/ext/uri-map/uri-map.h deleted file mode 100644 index 4066a2f468..0000000000 --- a/libs/ardour/lv2/lv2plug.in/ns/ext/uri-map/uri-map.h +++ /dev/null @@ -1,88 +0,0 @@ -/* lv2_uri_map.h - C header file for the LV2 URI Map extension. - * - * Copyright (C) 2008 David Robillard - * - * This header is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This header is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this header; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place, Suite 330, Boston, MA 01222-1307 USA - */ - -#ifndef LV2_URI_MAP_H -#define LV2_URI_MAP_H - -#define LV2_URI_MAP_URI "http://lv2plug.in/ns/ext/uri-map" - -#include - -/** @file - * This header defines the LV2 URI Map extension with the URI - * (preferred prefix 'lv2urimap'). - * - * This extension defines a simple mechanism for plugins to map URIs to - * integers, usually for performance reasons (e.g. processing events - * typed by URIs in real time). The expected use case is for plugins to - * map URIs to integers for things they 'understand' at instantiation time, - * and store those values for use in the audio thread without doing any string - * comparison. This allows the extensibility of RDF with the performance of - * integers (or centrally defined enumerations). - */ - - -/** Opaque pointer to host data. */ -typedef void* LV2_URI_Map_Callback_Data; - - -/** The data field of the LV2_Feature for this extension. - * - * To support this feature the host must pass an LV2_Feature struct to the - * plugin's instantiate method with URI "http://lv2plug.in/ns/ext/uri-map" - * and data pointed to an instance of this struct. - */ -typedef struct { - - /** Opaque pointer to host data. - * - * The plugin MUST pass this to any call to functions in this struct. - * Otherwise, it must not be interpreted in any way. - */ - LV2_URI_Map_Callback_Data callback_data; - - /** Get the numeric ID of a URI from the host. - * - * @param callback_data Must be the callback_data member of this struct. - * @param map The 'context' of this URI. Certain extensions may define a - * URI that must be passed here with certain restrictions on the - * return value (e.g. limited range). This value may be NULL if - * the plugin needs an ID for a URI in general. - * @param uri The URI to be mapped to an integer ID. - * - * This function is referentially transparent - any number of calls with - * the same arguments is guaranteed to return the same value over the life - * of a plugin instance (though the same URI may return different values - * with a different map parameter). However, this function is not - * necessarily very fast: plugins should cache any IDs they might need in - * performance critical situations. - * The return value 0 is reserved and means an ID for that URI could not - * be created for whatever reason. Extensions may define more precisely - * what this means, but in general plugins should gracefully handle 0 - * and consider whatever they wanted the URI for "unsupported". - */ - uint32_t (*uri_to_id)(LV2_URI_Map_Callback_Data callback_data, - const char* map, - const char* uri); - -} LV2_URI_Map_Feature; - - -#endif // LV2_URI_MAP_H - diff --git a/libs/ardour/lv2/lv2plug.in/ns/ext/urid/urid.h b/libs/ardour/lv2/lv2plug.in/ns/ext/urid/urid.h deleted file mode 100644 index 22c9a66ae4..0000000000 --- a/libs/ardour/lv2/lv2plug.in/ns/ext/urid/urid.h +++ /dev/null @@ -1,115 +0,0 @@ -/* - Copyright 2011 Gabriel M. Beddingfield - Copyright 2008-2011 David Robillard - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ - -/** - @file - C header for the LV2 URID extension -*/ - -#ifndef LV2_URID_H -#define LV2_URID_H - -#define LV2_URID_URI "http://lv2plug.in/ns/ext/urid" -#define LV2_URID_MAP_URI LV2_URID_URI "#map" -#define LV2_URID_UNMAP_URI LV2_URID_URI "#unmap" - -#include - -/** - Opaque pointer to host data for LV2_URID_Map. -*/ -typedef void* LV2_URID_Map_Handle; - -/** - Opaque pointer to host data for LV2_URID_Unmap. -*/ -typedef void* LV2_URID_Unmap_Handle; - -/** - URI mapped to an integer. -*/ -typedef uint32_t LV2_URID; - -/** - URI Map (http://lv2plug.in/ns/ext/urid#map). -*/ -typedef struct { - /** - Opaque pointer to host data. - - This MUST be passed to map_uri() whenever it is called. - Otherwise, it must not be interpreted in any way. - */ - LV2_URID_Map_Handle handle; - - /** - Get the numeric ID of a URI. - - If the ID does not already exist, it will be created. - - This function is referentially transparent; any number of calls with the - same arguments is guaranteed to return the same value over the life of a - plugin instance. Note, however, that several URIs MAY resolve to the - same ID if the host considers those URIs equivalent. - - This function is not necessarily very fast or RT-safe: plugins SHOULD - cache any IDs they might need in performance critical situations. - - The return value 0 is reserved and indicates that an ID for that URI - could not be created for whatever reason. However, hosts SHOULD NOT - return 0 from this function in non-exceptional circumstances (i.e. the - URI map SHOULD be dynamic). - - @param handle Must be the callback_data member of this struct. - @param uri The URI to be mapped to an integer ID. - */ - LV2_URID (*map)(LV2_URID_Map_Handle handle, - const char* uri); -} LV2_URID_Map; - -/** - URI Unmap (http://lv2plug.in/ns/ext/urid#unmap). -*/ -typedef struct { - /** - Opaque pointer to host data. - - This MUST be passed to unmap() whenever it is called. - Otherwise, it must not be interpreted in any way. - */ - LV2_URID_Unmap_Handle handle; - - /** - Get the URI for a previously mapped numeric ID. - - Returns NULL if @c urid is not yet mapped. Otherwise, the corresponding - URI is returned in a canonical form. This MAY not be the exact same - string that was originally passed to LV2_URID_Map::map(), but it MUST be - an identical URI according to the URI syntax specification (RFC3986). A - non-NULL return for a given @c urid will always be the same for the life - of the plugin. Plugins that intend to perform string comparison on - unmapped URIs SHOULD first canonicalise URI strings with a call to - map_uri() followed by a call to unmap_uri(). - - @param handle Must be the callback_data member of this struct. - @param urid The ID to be mapped back to the URI string. - */ - const char* (*unmap)(LV2_URID_Unmap_Handle handle, - LV2_URID urid); -} LV2_URID_Unmap; - -#endif /* LV2_URID_H */ diff --git a/libs/ardour/lv2_plugin.cc b/libs/ardour/lv2_plugin.cc index 91dd1578fb..0b3bc3cd18 100644 --- a/libs/ardour/lv2_plugin.cc +++ b/libs/ardour/lv2_plugin.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2008-2011 Paul Davis + Copyright (C) 2008-2012 Paul Davis Author: David Robillard This program is free software; you can redistribute it and/or modify @@ -43,6 +43,8 @@ #include "ardour/debug.h" #include "ardour/lv2_plugin.h" #include "ardour/session.h" +#include "ardour/tempo.h" +#include "ardour/worker.h" #include "i18n.h" #include @@ -50,7 +52,12 @@ #include #include "lv2/lv2plug.in/ns/ext/atom/atom.h" +#include "lv2/lv2plug.in/ns/ext/port-props/port-props.h" +#include "lv2/lv2plug.in/ns/ext/presets/presets.h" #include "lv2/lv2plug.in/ns/ext/state/state.h" +#include "lv2/lv2plug.in/ns/ext/time/time.h" +#include "lv2/lv2plug.in/ns/ext/worker/worker.h" +#include "lv2/lv2plug.in/ns/extensions/ui/ui.h" #include "lv2_evbuf.h" @@ -58,11 +65,6 @@ #include #endif -#define NS_DC "http://dublincore.org/documents/dcmi-namespace/" -#define NS_OLDPSET "http://lv2plug.in/ns/dev/presets#" -#define NS_PSET "http://lv2plug.in/ns/ext/presets#" -#define NS_UI "http://lv2plug.in/ns/extensions/ui#" - using namespace std; using namespace ARDOUR; using namespace PBD; @@ -107,27 +109,66 @@ public: LilvNode* lv2_sampleRate; LilvNode* lv2_toggled; LilvNode* midi_MidiEvent; + LilvNode* rdfs_comment; LilvNode* ui_GtkUI; LilvNode* ui_external; }; static LV2World _world; +/** Called by the plugin to schedule non-RT work. */ +static LV2_Worker_Status +work_schedule(LV2_Worker_Schedule_Handle handle, + uint32_t size, + const void* data) +{ + LV2Plugin* plugin = (LV2Plugin*)handle; + if (plugin->session().engine().freewheeling()) { + // Freewheeling, do the work immediately in this (audio) thread + return (LV2_Worker_Status)plugin->work(size, data); + } else { + // Enqueue message for the worker thread + return plugin->worker()->schedule(size, data) ? + LV2_WORKER_SUCCESS : LV2_WORKER_ERR_UNKNOWN; + } +} + +/** Called by the plugin to respond to non-RT work. */ +static LV2_Worker_Status +work_respond(LV2_Worker_Respond_Handle handle, + uint32_t size, + const void* data) +{ + LV2Plugin* plugin = (LV2Plugin*)handle; + if (plugin->session().engine().freewheeling()) { + // Freewheeling, respond immediately in this (audio) thread + return (LV2_Worker_Status)plugin->work_response(size, data); + } else { + // Enqueue response for the worker + return plugin->worker()->respond(size, data) ? + LV2_WORKER_SUCCESS : LV2_WORKER_ERR_UNKNOWN; + } +} + struct LV2Plugin::Impl { Impl() : plugin(0), ui(0), ui_type(0), name(0), author(0), instance(0) -#ifdef HAVE_NEW_LILV + , work_iface(0) , state(0) -#endif {} - LilvPlugin* plugin; - const LilvUI* ui; - const LilvNode* ui_type; - LilvNode* name; - LilvNode* author; - LilvInstance* instance; -#ifdef HAVE_NEW_LILV - LilvState* state; -#endif + + /** Find the LV2 input port with the given designation. + * If found, bufptrs[port_index] will be set to bufptr. + */ + LilvPort* designated_input (const char* uri, void** bufptrs[], void** bufptr); + + LilvPlugin* plugin; + const LilvUI* ui; + const LilvNode* ui_type; + LilvNode* name; + LilvNode* author; + LilvInstance* instance; + LV2_Worker_Interface* work_iface; + LilvState* state; }; LV2Plugin::LV2Plugin (AudioEngine& engine, @@ -137,6 +178,7 @@ LV2Plugin::LV2Plugin (AudioEngine& engine, : Plugin(engine, session) , _impl(new Impl()) , _features(NULL) + , _worker(NULL) , _insert_id("0") { init(c_plugin, rate); @@ -146,6 +188,7 @@ LV2Plugin::LV2Plugin (const LV2Plugin& other) : Plugin(other) , _impl(new Impl()) , _features(NULL) + , _worker(NULL) , _insert_id(other._insert_id) { init(other._impl->plugin, other._sample_rate); @@ -161,26 +204,29 @@ LV2Plugin::init(void* c_plugin, framecnt_t rate) { DEBUG_TRACE(DEBUG::LV2, "init\n"); - _impl->plugin = (LilvPlugin*)c_plugin; - _impl->ui = NULL; - _impl->ui_type = NULL; - _to_ui = NULL; - _from_ui = NULL; - _control_data = 0; - _shadow_data = 0; - _ev_buffers = 0; - _latency_control_port = 0; - _state_version = 0; - _was_activated = false; - _has_state_interface = false; + _impl->plugin = (LilvPlugin*)c_plugin; + _impl->ui = NULL; + _impl->ui_type = NULL; + _to_ui = NULL; + _from_ui = NULL; + _control_data = 0; + _shadow_data = 0; + _ev_buffers = 0; + _bpm_control_port = 0; + _freewheel_control_port = 0; + _latency_control_port = 0; + _state_version = 0; + _was_activated = false; + _has_state_interface = false; _instance_access_feature.URI = "http://lv2plug.in/ns/ext/instance-access"; _data_access_feature.URI = "http://lv2plug.in/ns/ext/data-access"; _make_path_feature.URI = LV2_STATE__makePath; + _work_schedule_feature.URI = LV2_WORKER__schedule; + _work_schedule_feature.data = NULL; LilvPlugin* plugin = _impl->plugin; -#ifdef HAVE_NEW_LILV LilvNode* state_iface_uri = lilv_new_uri(_world.world, LV2_STATE__interface); LilvNode* state_uri = lilv_new_uri(_world.world, LV2_STATE_URI); _has_state_interface = @@ -190,9 +236,8 @@ LV2Plugin::init(void* c_plugin, framecnt_t rate) || lilv_plugin_has_feature(plugin, state_uri); lilv_node_free(state_uri); lilv_node_free(state_iface_uri); -#endif - _features = (LV2_Feature**)malloc(sizeof(LV2_Feature*) * 7); + _features = (LV2_Feature**)malloc(sizeof(LV2_Feature*) * 8); _features[0] = &_instance_access_feature; _features[1] = &_data_access_feature; _features[2] = &_make_path_feature; @@ -200,6 +245,7 @@ LV2Plugin::init(void* c_plugin, framecnt_t rate) _features[4] = _uri_map.urid_map_feature(); _features[5] = _uri_map.urid_unmap_feature(); _features[6] = NULL; + _features[7] = NULL; LV2_State_Make_Path* make_path = (LV2_State_Make_Path*)malloc( sizeof(LV2_State_Make_Path)); @@ -207,6 +253,18 @@ LV2Plugin::init(void* c_plugin, framecnt_t rate) make_path->path = &lv2_state_make_path; _make_path_feature.data = make_path; + LilvNode* worker_schedule = lilv_new_uri(_world.world, LV2_WORKER__schedule); + if (lilv_plugin_has_feature(plugin, worker_schedule)) { + LV2_Worker_Schedule* schedule = (LV2_Worker_Schedule*)malloc( + sizeof(LV2_Worker_Schedule)); + _worker = new Worker(this, 4096); + schedule->handle = this; + schedule->schedule_work = work_schedule; + _work_schedule_feature.data = schedule; + _features[6] = &_work_schedule_feature; + } + lilv_node_free(worker_schedule); + _impl->instance = lilv_plugin_instantiate(plugin, rate, _features); _impl->name = lilv_plugin_get_name(plugin); _impl->author = lilv_plugin_get_author_name(plugin); @@ -220,6 +278,8 @@ LV2Plugin::init(void* c_plugin, framecnt_t rate) _data_access_extension_data.extension_data = _impl->instance->lv2_descriptor->extension_data; _data_access_feature.data = &_data_access_extension_data; + _impl->work_iface = (LV2_Worker_Interface*)extension_data(LV2_WORKER__interface); + if (lilv_plugin_has_feature(plugin, _world.lv2_inPlaceBroken)) { error << string_compose( _("LV2: \"%1\" cannot be used, since it cannot do inplace processing"), @@ -270,17 +330,25 @@ LV2Plugin::init(void* c_plugin, framecnt_t rate) _port_flags.push_back(flags); } - const bool latent = lilv_plugin_has_latency(plugin); - const uint32_t latency_port = (latent) - ? lilv_plugin_get_latency_port_index(plugin) - : 0; - _control_data = new float[num_ports]; _shadow_data = new float[num_ports]; _defaults = new float[num_ports]; _ev_buffers = new LV2_Evbuf*[num_ports]; memset(_ev_buffers, 0, sizeof(LV2_Evbuf*) * num_ports); + const bool latent = lilv_plugin_has_latency(plugin); + const uint32_t latency_index = (latent) + ? lilv_plugin_get_latency_port_index(plugin) + : 0; + + // Build an array of pointers to special parameter buffers + void*** params = new void**[num_ports]; + for (uint32_t i = 0; i < num_ports; ++i) { + params[i] = NULL; + } + _impl->designated_input (LV2_TIME__beatsPerMinute, params, (void**)&_bpm_control_port); + _impl->designated_input (LV2_CORE__freeWheeling, params, (void**)&_freewheel_control_port); + for (uint32_t i = 0; i < num_ports; ++i) { const LilvPort* port = lilv_plugin_get_port_by_index(plugin, i); const LilvNode* sym = lilv_port_get_symbol(plugin, port); @@ -300,19 +368,24 @@ LV2Plugin::init(void* c_plugin, framecnt_t rate) lilv_instance_connect_port(_impl->instance, i, &_control_data[i]); - if (latent && ( i == latency_port) ) { + if (latent && i == latency_index) { _latency_control_port = &_control_data[i]; *_latency_control_port = 0; } if (parameter_is_input(i)) { _shadow_data[i] = default_value(i); + if (params[i]) { + *params[i] = (void*)&_shadow_data[i]; + } } } else { _defaults[i] = 0.0f; } } + delete[] params; + LilvUIs* uis = lilv_plugin_get_uis(plugin); if (lilv_uis_size(uis) > 0) { #ifdef HAVE_SUIL @@ -369,6 +442,10 @@ LV2Plugin::~LV2Plugin () lilv_node_free(_impl->name); lilv_node_free(_impl->author); + free(_features); + free(_make_path_feature.data); + free(_work_schedule_feature.data); + delete _to_ui; delete _from_ui; @@ -390,9 +467,9 @@ bool LV2Plugin::ui_is_resizable () const { const LilvNode* s = lilv_ui_get_uri(_impl->ui); - LilvNode* p = lilv_new_uri(_world.world, NS_UI "optionalFeature"); - LilvNode* fs = lilv_new_uri(_world.world, NS_UI "fixedSize"); - LilvNode* nrs = lilv_new_uri(_world.world, NS_UI "noUserResize"); + LilvNode* p = lilv_new_uri(_world.world, LV2_CORE__optionalFeature); + LilvNode* fs = lilv_new_uri(_world.world, LV2_UI__fixedSize); + LilvNode* nrs = lilv_new_uri(_world.world, LV2_UI__noUserResize); LilvNodes* fs_matches = lilv_world_find_nodes(_world.world, s, p, fs); LilvNodes* nrs_matches = lilv_world_find_nodes(_world.world, s, p, nrs); @@ -466,6 +543,18 @@ LV2Plugin::port_symbol(uint32_t index) const return lilv_node_as_string(sym); } +uint32_t +LV2Plugin::port_index (const char* symbol) const +{ + const map::const_iterator i = _port_indices.find(symbol); + if (i != _port_indices.end()) { + return i->second; + } else { + warning << string_compose(_("LV2: Unknown port %1"), symbol) << endmsg; + return (uint32_t)-1; + } +} + void LV2Plugin::set_parameter(uint32_t which, float val) { @@ -495,6 +584,36 @@ LV2Plugin::get_parameter(uint32_t which) const return 0.0f; } +std::string +LV2Plugin::get_docs() const +{ + LilvNodes* comments = lilv_plugin_get_value(_impl->plugin, _world.rdfs_comment); + if (comments) { + const std::string docs(lilv_node_as_string(lilv_nodes_get_first(comments))); + lilv_nodes_free(comments); + return docs; + } + + return ""; +} + +std::string +LV2Plugin::get_parameter_docs(uint32_t which) const +{ + LilvNodes* comments = lilv_port_get_value( + _impl->plugin, + lilv_plugin_get_port_by_index(_impl->plugin, which), + _world.rdfs_comment); + + if (comments) { + const std::string docs(lilv_node_as_string(lilv_nodes_get_first(comments))); + lilv_nodes_free(comments); + return docs; + } + + return ""; +} + uint32_t LV2Plugin::nth_parameter(uint32_t n, bool& ok) const { @@ -633,7 +752,6 @@ LV2Plugin::add_state(XMLNode* root) const if (_has_state_interface) { cout << "LV2 " << name() << " has state interface" << endl; -#ifdef HAVE_NEW_LILV // Provisionally increment state version and create directory const std::string new_dir = state_dir(++_state_version); g_mkdir_with_parents(new_dir.c_str(), 0744); @@ -675,12 +793,6 @@ LV2Plugin::add_state(XMLNode* root) const } root->add_property("state-dir", string_compose("state%1", _state_version)); - -#else /* !HAVE_NEW_LILV */ - warning << string_compose( - _("Plugin \"%1\" has state, but Lilv is too old to save it"), - unique_id()) << endmsg; -#endif /* HAVE_NEW_LILV */ } else { cout << "LV2 " << name() << " has no state interface." << endl; } @@ -693,50 +805,35 @@ get_value(LilvWorld* world, const LilvNode* subject, const LilvNode* predicate) return vs ? lilv_nodes_get_first(vs) : NULL; } -static void -find_presets_helper(LilvWorld* world, - LilvPlugin* plugin, - std::map& out, - LilvNode* preset_pred, - LilvNode* title_pred) +void +LV2Plugin::find_presets() { - LilvNodes* presets = lilv_plugin_get_value(plugin, preset_pred); + LilvNode* lv2_appliesTo = lilv_new_uri(_world.world, LV2_CORE__appliesTo); + LilvNode* pset_Preset = lilv_new_uri(_world.world, LV2_PRESETS__Preset); + LilvNode* rdfs_label = lilv_new_uri(_world.world, LILV_NS_RDFS "label"); + + LilvNodes* presets = lilv_plugin_get_related(_impl->plugin, pset_Preset); LILV_FOREACH(nodes, i, presets) { const LilvNode* preset = lilv_nodes_get(presets, i); - const LilvNode* name = get_value(world, preset, title_pred); + lilv_world_load_resource(_world.world, preset); + const LilvNode* name = get_value(_world.world, preset, rdfs_label); if (name) { - out.insert(std::make_pair(lilv_node_as_string(preset), - Plugin::PresetRecord( - lilv_node_as_string(preset), - lilv_node_as_string(name)))); + _presets.insert(std::make_pair(lilv_node_as_string(preset), + Plugin::PresetRecord( + lilv_node_as_string(preset), + lilv_node_as_string(name)))); } else { warning << string_compose( _("Plugin \"%1\% preset \"%2%\" is missing a label\n"), - lilv_node_as_string(lilv_plugin_get_uri(plugin)), + lilv_node_as_string(lilv_plugin_get_uri(_impl->plugin)), lilv_node_as_string(preset)) << endmsg; } } lilv_nodes_free(presets); -} - -void -LV2Plugin::find_presets() -{ - LilvNode* dc_title = lilv_new_uri(_world.world, NS_DC "title"); - LilvNode* oldpset_hasPreset = lilv_new_uri(_world.world, NS_OLDPSET "hasPreset"); - LilvNode* pset_hasPreset = lilv_new_uri(_world.world, NS_PSET "hasPreset"); - LilvNode* rdfs_label = lilv_new_uri(_world.world, LILV_NS_RDFS "label"); - - find_presets_helper(_world.world, _impl->plugin, _presets, - oldpset_hasPreset, dc_title); - - find_presets_helper(_world.world, _impl->plugin, _presets, - pset_hasPreset, rdfs_label); lilv_node_free(rdfs_label); - lilv_node_free(pset_hasPreset); - lilv_node_free(oldpset_hasPreset); - lilv_node_free(dc_title); + lilv_node_free(pset_Preset); + lilv_node_free(lv2_appliesTo); } bool @@ -746,20 +843,16 @@ LV2Plugin::load_preset(PresetRecord r) std::map::iterator it; - LilvNode* lv2_port = lilv_new_uri(_world.world, LILV_NS_LV2 "port"); - LilvNode* lv2_symbol = lilv_new_uri(_world.world, LILV_NS_LV2 "symbol"); - LilvNode* oldpset_value = lilv_new_uri(_world.world, NS_OLDPSET "value"); - LilvNode* preset = lilv_new_uri(_world.world, r.uri.c_str()); - LilvNode* pset_value = lilv_new_uri(_world.world, NS_PSET "value"); + LilvNode* lv2_port = lilv_new_uri(_world.world, LILV_NS_LV2 "port"); + LilvNode* lv2_symbol = lilv_new_uri(_world.world, LILV_NS_LV2 "symbol"); + LilvNode* preset = lilv_new_uri(_world.world, r.uri.c_str()); + LilvNode* pset_value = lilv_new_uri(_world.world, LV2_PRESETS__value); LilvNodes* ports = lilv_world_find_nodes(_world.world, preset, lv2_port, NULL); LILV_FOREACH(nodes, i, ports) { const LilvNode* port = lilv_nodes_get(ports, i); const LilvNode* symbol = get_value(_world.world, port, lv2_symbol); const LilvNode* value = get_value(_world.world, port, pset_value); - if (!value) { - value = get_value(_world.world, port, oldpset_value); - } if (value && lilv_node_is_float(value)) { it = _port_indices.find(lilv_node_as_string(symbol)); if (it != _port_indices.end()) @@ -770,7 +863,6 @@ LV2Plugin::load_preset(PresetRecord r) lilv_node_free(pset_value); lilv_node_free(preset); - lilv_node_free(oldpset_value); lilv_node_free(lv2_symbol); lilv_node_free(lv2_port); @@ -850,6 +942,7 @@ LV2Plugin::write_to_ui(uint32_t index, uint32_t size, uint8_t* body) { + std::cerr << "WRITE TO UI" << std::endl; write_to(_to_ui, index, protocol, size, body); } @@ -887,6 +980,20 @@ LV2Plugin::emit_to_ui(void* controller, UIMessageSink sink) } } +int +LV2Plugin::work(uint32_t size, const void* data) +{ + return _impl->work_iface->work( + _impl->instance->lv2_handle, work_respond, this, size, data); +} + +int +LV2Plugin::work_response(uint32_t size, const void* data) +{ + return _impl->work_iface->work_response( + _impl->instance->lv2_handle, size, data); +} + void LV2Plugin::set_insert_info(const PluginInsert* insert) { @@ -946,7 +1053,6 @@ LV2Plugin::set_state(const XMLNode& node, int version) set_parameter(port_id, atof(value)); } -#ifdef HAVE_NEW_LILV _state_version = 0; if ((prop = node.property("state-dir")) != 0) { if (sscanf(prop->value().c_str(), "state%u", &_state_version) != 1) { @@ -965,7 +1071,6 @@ LV2Plugin::set_state(const XMLNode& node, int version) lilv_state_restore(state, _impl->instance, NULL, NULL, 0, NULL); } -#endif latency_compute_run(); @@ -1096,6 +1201,16 @@ LV2Plugin::connect_and_run(BufferSet& bufs, cycles_t then = get_cycles(); + if (_freewheel_control_port) { + *_freewheel_control_port = _session.engine().freewheeling (); + } + + if (_bpm_control_port) { + TempoMap& tmap (_session.tempo_map ()); + Tempo tempo = tmap.tempo_at (_session.transport_frame () + offset); + *_bpm_control_port = tempo.beats_per_minute (); + } + ChanCount bufs_count; bufs_count.set(DataType::AUDIO, 1); bufs_count.set(DataType::MIDI, 1); @@ -1297,6 +1412,13 @@ LV2Plugin::run(pframes_t nframes) } lilv_instance_run(_impl->instance, nframes); + + if (_impl->work_iface) { + _worker->emit_responses(); + if (_impl->work_iface->end_run) { + _impl->work_iface->end_run(_impl->instance->lv2_handle); + } + } } void @@ -1340,6 +1462,20 @@ LV2Plugin::latency_compute_run() deactivate(); } +LilvPort* +LV2Plugin::Impl::designated_input (const char* uri, void** bufptrs[], void** bufptr) +{ + LilvPort* port = NULL; + LilvNode* designation = lilv_new_uri(_world.world, uri); + port = lilv_plugin_get_port_by_designation( + plugin, _world.lv2_InputPort, designation); + lilv_node_free(designation); + if (port) { + bufptrs[lilv_port_get_index(plugin, port)] = bufptr; + } + return port; +} + LV2World::LV2World() : world(lilv_world_new()) { @@ -1350,19 +1486,20 @@ LV2World::LV2World() atom_bufferType = lilv_new_uri(world, LV2_ATOM__bufferType); atom_eventTransfer = lilv_new_uri(world, LV2_ATOM__eventTransfer); ev_EventPort = lilv_new_uri(world, LILV_URI_EVENT_PORT); - ext_logarithmic = lilv_new_uri(world, "http://lv2plug.in/ns/dev/extportinfo#logarithmic"); + ext_logarithmic = lilv_new_uri(world, LV2_PORT_PROPS__logarithmic); lv2_AudioPort = lilv_new_uri(world, LILV_URI_AUDIO_PORT); lv2_ControlPort = lilv_new_uri(world, LILV_URI_CONTROL_PORT); lv2_InputPort = lilv_new_uri(world, LILV_URI_INPUT_PORT); lv2_OutputPort = lilv_new_uri(world, LILV_URI_OUTPUT_PORT); - lv2_inPlaceBroken = lilv_new_uri(world, LILV_NS_LV2 "inPlaceBroken"); - lv2_integer = lilv_new_uri(world, LILV_NS_LV2 "integer"); - lv2_sampleRate = lilv_new_uri(world, LILV_NS_LV2 "sampleRate"); - lv2_toggled = lilv_new_uri(world, LILV_NS_LV2 "toggled"); - lv2_enumeration = lilv_new_uri(world, LILV_NS_LV2 "enumeration"); + lv2_inPlaceBroken = lilv_new_uri(world, LV2_CORE__inPlaceBroken); + lv2_integer = lilv_new_uri(world, LV2_CORE__integer); + lv2_sampleRate = lilv_new_uri(world, LV2_CORE__sampleRate); + lv2_toggled = lilv_new_uri(world, LV2_CORE__toggled); + lv2_enumeration = lilv_new_uri(world, LV2_CORE__enumeration); midi_MidiEvent = lilv_new_uri(world, LILV_URI_MIDI_EVENT); - ui_GtkUI = lilv_new_uri(world, NS_UI "GtkUI"); - ui_external = lilv_new_uri(world, NS_UI "external"); + rdfs_comment = lilv_new_uri(world, LILV_NS_RDFS "comment"); + ui_GtkUI = lilv_new_uri(world, LV2_UI__GtkUI); + ui_external = lilv_new_uri(world, "http://lv2plug.in/ns/extensions/ui#external"); } LV2World::~LV2World() diff --git a/libs/ardour/midi_buffer.cc b/libs/ardour/midi_buffer.cc index efb7bba317..403434b6cc 100644 --- a/libs/ardour/midi_buffer.cc +++ b/libs/ardour/midi_buffer.cc @@ -325,6 +325,7 @@ MidiBuffer::second_simultaneous_midi_byte_is_first (uint8_t a, uint8_t b) case MIDI_CMD_BENDER: b_first = true; } + break; case MIDI_CMD_NOTE_OFF: switch (a & 0xf0) { diff --git a/libs/ardour/midi_clock_slave.cc b/libs/ardour/midi_clock_slave.cc index 892bc800ff..87ea05738d 100644 --- a/libs/ardour/midi_clock_slave.cc +++ b/libs/ardour/midi_clock_slave.cc @@ -26,6 +26,7 @@ #include "pbd/error.h" #include "pbd/failed_constructor.h" #include "pbd/pthread_utils.h" +#include "pbd/convert.h" #include "midi++/port.h" diff --git a/libs/ardour/midi_diskstream.cc b/libs/ardour/midi_diskstream.cc index e62ec6837e..9b3b619d1c 100644 --- a/libs/ardour/midi_diskstream.cc +++ b/libs/ardour/midi_diskstream.cc @@ -339,7 +339,7 @@ MidiDiskstream::process (framepos_t transport_frame, pframes_t nframes, framecnt adjust_capture_position = 0; if (nominally_recording || (re && was_recording && _session.get_record_enabled() && _session.config.get_punch_in())) { - OverlapType ot = coverage (first_recordable_frame, last_recordable_frame, transport_frame, transport_frame + nframes); + Evoral::OverlapType ot = Evoral::coverage (first_recordable_frame, last_recordable_frame, transport_frame, transport_frame + nframes); calculate_record_range(ot, transport_frame, nframes, rec_nframes, rec_offset); diff --git a/libs/ardour/midi_playlist.cc b/libs/ardour/midi_playlist.cc index 84bcc70d0b..7e3d70bfe0 100644 --- a/libs/ardour/midi_playlist.cc +++ b/libs/ardour/midi_playlist.cc @@ -107,7 +107,11 @@ 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 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("++++++ %1 .. %2 +++++++ %3 trackers +++++++++++++++++\n", start, start + dur, _note_trackers.size())); @@ -128,13 +132,13 @@ MidiPlaylist::read (Evoral::EventSink& dst, framepos_t start, framec */ switch ((*i)->coverage (start, end)) { - case OverlapStart: - case OverlapInternal: - case OverlapExternal: + case Evoral::OverlapStart: + case Evoral::OverlapInternal: + case Evoral::OverlapExternal: regs.push_back (*i); break; - case OverlapEnd: + case Evoral::OverlapEnd: /* this region ends within the read range */ regs.push_back (*i); ended.push_back (*i); @@ -294,7 +298,11 @@ 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 for (NoteTrackers::iterator n = _note_trackers.begin(); n != _note_trackers.end(); ++n) { delete n->second; } @@ -316,26 +324,6 @@ MidiPlaylist::remove_dependents (boost::shared_ptr region) } } - -void -MidiPlaylist::refresh_dependents (boost::shared_ptr /*r*/) -{ - /* MIDI regions have no dependents (crossfades) */ -} - -void -MidiPlaylist::finalize_split_region (boost::shared_ptr /*original*/, boost::shared_ptr /*left*/, boost::shared_ptr /*right*/) -{ - /* No MIDI crossfading (yet?), so nothing to do here */ -} - -void -MidiPlaylist::check_dependents (boost::shared_ptr /*r*/, bool /*norefresh*/) -{ - /* MIDI regions have no dependents (crossfades) */ -} - - int MidiPlaylist::set_state (const XMLNode& node, int version) { @@ -419,7 +407,11 @@ 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 set ret; diff --git a/libs/ardour/midi_playlist_source.cc b/libs/ardour/midi_playlist_source.cc index 6270ad4339..208b2b0460 100644 --- a/libs/ardour/midi_playlist_source.cc +++ b/libs/ardour/midi_playlist_source.cc @@ -126,7 +126,7 @@ MidiPlaylistSource::length (framepos_t) const return extent.second - extent.first; } -framepos_t +framecnt_t MidiPlaylistSource::read_unlocked (Evoral::EventSink& dst, framepos_t /*position*/, framepos_t start, framecnt_t cnt, @@ -141,7 +141,7 @@ MidiPlaylistSource::read_unlocked (Evoral::EventSink& dst, return mp->read (dst, start, cnt); } -framepos_t +framecnt_t MidiPlaylistSource::write_unlocked (MidiRingBuffer& dst, framepos_t, framecnt_t) diff --git a/libs/ardour/midi_port.cc b/libs/ardour/midi_port.cc index 0f91dd3e36..9a7d3eeb27 100644 --- a/libs/ardour/midi_port.cc +++ b/libs/ardour/midi_port.cc @@ -128,10 +128,9 @@ MidiPort::cycle_split () void MidiPort::resolve_notes (void* jack_buffer, MidiBuffer::TimeType when) { - for (uint8_t channel = 0; channel <= 0xF; channel++) { - uint8_t ev[3] = { MIDI_CMD_CONTROL | channel, MIDI_CTL_SUSTAIN, 0 }; + uint8_t ev[3] = { ((uint8_t) (MIDI_CMD_CONTROL | channel)), MIDI_CTL_SUSTAIN, 0 }; /* we need to send all notes off AND turn the * sustain/damper pedal off to handle synths @@ -151,7 +150,7 @@ MidiPort::resolve_notes (void* jack_buffer, MidiBuffer::TimeType when) } void -MidiPort::flush_buffers (pframes_t nframes, framepos_t /*time*/) +MidiPort::flush_buffers (pframes_t nframes) { if (sends_output ()) { diff --git a/libs/ardour/midi_region.cc b/libs/ardour/midi_region.cc index 092915cefb..c61185fe3a 100644 --- a/libs/ardour/midi_region.cc +++ b/libs/ardour/midi_region.cc @@ -153,7 +153,7 @@ void MidiRegion::post_set (const PropertyChange& pc) { Region::post_set (pc); - + if (pc.contains (Properties::length) && !pc.contains (Properties::length_beats)) { update_length_beats (); } else if (pc.contains (Properties::start) && !pc.contains (Properties::start_beats)) { @@ -424,6 +424,7 @@ MidiRegion::fix_negative_start () model()->insert_silence_at_start (c.from (-_start)); _start = 0; + _start_beats = 0; } /** Transpose the notes in this region by a given number of semitones */ @@ -433,3 +434,10 @@ MidiRegion::transpose (int semitones) BeatsFramesConverter c (_session.tempo_map(), _start); model()->transpose (c.from (_start), c.from (_start + _length), semitones); } + +void +MidiRegion::set_start_internal (framecnt_t s) +{ + Region::set_start_internal (s); + set_start_beats_from_start_frames (); +} diff --git a/libs/ardour/midi_source.cc b/libs/ardour/midi_source.cc index ad07269b6c..0ca8183655 100644 --- a/libs/ardour/midi_source.cc +++ b/libs/ardour/midi_source.cc @@ -176,7 +176,7 @@ MidiSource::length (framepos_t pos) const } void -MidiSource::update_length (framepos_t /*pos*/, framecnt_t /*cnt*/) +MidiSource::update_length (framecnt_t) { // You're not the boss of me! } diff --git a/libs/ardour/midi_state_tracker.cc b/libs/ardour/midi_state_tracker.cc index 7537ca04d1..7bf23f9358 100644 --- a/libs/ardour/midi_state_tracker.cc +++ b/libs/ardour/midi_state_tracker.cc @@ -131,7 +131,7 @@ MidiStateTracker::resolve_notes (MidiBuffer &dst, framepos_t time) for (int channel = 0; channel < 16; ++channel) { for (int note = 0; note < 128; ++note) { while (_active_notes[note + 128 * channel]) { - uint8_t buffer[3] = { MIDI_CMD_NOTE_OFF | channel, note, 0 }; + uint8_t buffer[3] = { ((uint8_t) (MIDI_CMD_NOTE_OFF | channel)), uint8_t (note), 0 }; Evoral::MIDIEvent noteoff (MIDI_CMD_NOTE_OFF, time, 3, buffer, false); /* note that we do not care about failure from diff --git a/libs/ardour/midi_track.cc b/libs/ardour/midi_track.cc index 6000f64c35..7291a41212 100644 --- a/libs/ardour/midi_track.cc +++ b/libs/ardour/midi_track.cc @@ -107,16 +107,21 @@ MidiTrack::set_record_enabled (bool yn, void *src) void MidiTrack::set_diskstream (boost::shared_ptr ds) { + /* We have to do this here, as Track::set_diskstream will cause a buffer refill, + and the diskstream must be set up to fill its buffers using the correct _note_mode. + */ + boost::shared_ptr mds = boost::dynamic_pointer_cast (ds); + mds->set_note_mode (_note_mode); + Track::set_diskstream (ds); - midi_diskstream()->reset_tracker (); + mds->reset_tracker (); _diskstream->set_track (this); _diskstream->set_destructive (_mode == Destructive); _diskstream->set_record_enabled (false); _diskstream_data_recorded_connection.disconnect (); - boost::shared_ptr mds = boost::dynamic_pointer_cast (ds); mds->DataRecorded.connect_same_thread ( _diskstream_data_recorded_connection, boost::bind (&MidiTrack::diskstream_data_recorded, this, _1)); @@ -135,6 +140,16 @@ MidiTrack::set_state (const XMLNode& node, int version) { const XMLProperty *prop; + /* This must happen before Track::set_state(), as there will be a buffer + fill during that call, and we must fill buffers using the correct + _note_mode. + */ + if ((prop = node.property (X_("note-mode"))) != 0) { + _note_mode = NoteMode (string_2_enum (prop->value(), _note_mode)); + } else { + _note_mode = Sustained; + } + if (Track::set_state (node, version)) { return -1; } @@ -142,12 +157,6 @@ MidiTrack::set_state (const XMLNode& node, int version) // No destructive MIDI tracks (yet?) _mode = Normal; - if ((prop = node.property (X_("note-mode"))) != 0) { - _note_mode = NoteMode (string_2_enum (prop->value(), _note_mode)); - } else { - _note_mode = Sustained; - } - if ((prop = node.property ("midi-thru")) != 0) { set_midi_thru (string_is_affirmative (prop->value())); } @@ -369,7 +378,7 @@ MidiTrack::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { boost::shared_ptr d = boost::dynamic_pointer_cast (*i); if (d) { - d->flush_buffers (nframes, end_frame - start_frame - 1); + d->flush_buffers (nframes); } } @@ -478,7 +487,7 @@ MidiTrack::write_out_of_band_data (BufferSet& bufs, framepos_t /*start*/, framep } int -MidiTrack::export_stuff (BufferSet& /*bufs*/, framecnt_t /*nframes*/, framepos_t /*end_frame*/, +MidiTrack::export_stuff (BufferSet& /*bufs*/, framepos_t /*start_frame*/, framecnt_t /*nframes*/, boost::shared_ptr /*endpoint*/, bool /*include_endpoint*/, bool /*forexport*/) { return -1; @@ -525,7 +534,7 @@ MidiTrack::midi_panic() { DEBUG_TRACE (DEBUG::MidiIO, string_compose ("%1 delivers panic data\n", name())); for (uint8_t channel = 0; channel <= 0xF; channel++) { - uint8_t ev[3] = { MIDI_CMD_CONTROL | channel, MIDI_CTL_SUSTAIN, 0 }; + uint8_t ev[3] = { ((uint8_t) (MIDI_CMD_CONTROL | channel)), ((uint8_t) MIDI_CTL_SUSTAIN), 0 }; write_immediate_event(3, ev); ev[1] = MIDI_CTL_ALL_NOTES_OFF; write_immediate_event(3, ev); @@ -551,9 +560,9 @@ void MidiTrack::MidiControl::set_value(double val) { bool valid = false; - if (isinf(val)) { + if (std::isinf(val)) { cerr << "MIDIControl value is infinity" << endl; - } else if (isnan(val)) { + } else if (std::isnan(val)) { cerr << "MIDIControl value is NaN" << endl; } else if (val < _list->parameter().min()) { cerr << "MIDIControl value is < " << _list->parameter().min() << endl; @@ -570,7 +579,7 @@ MidiTrack::MidiControl::set_value(double val) assert(val <= _list->parameter().max()); if ( ! automation_playback()) { size_t size = 3; - uint8_t ev[3] = { _list->parameter().channel(), int(val), 0 }; + uint8_t ev[3] = { _list->parameter().channel(), uint8_t (val), 0 }; switch(_list->parameter().type()) { case MidiCCAutomation: ev[0] += MIDI_CMD_CONTROL; @@ -733,7 +742,7 @@ MidiTrack::act_on_mute () if ((1< plist = MIDI::Manager::instance()->get_midi_ports (); for (MIDI::Manager::PortList::const_iterator i = plist->begin(); i != plist->end(); ++i) { + + if (!(*i)->centrally_parsed()) { + continue; + } + int fd; if ((fd = (*i)->selectable ()) >= 0) { diff --git a/libs/ardour/mute_master.cc b/libs/ardour/mute_master.cc index a58bb8a421..9f2ed08dab 100644 --- a/libs/ardour/mute_master.cc +++ b/libs/ardour/mute_master.cc @@ -20,6 +20,7 @@ #include "pbd/enumwriter.h" #include "pbd/xml++.h" +#include "pbd/convert.h" #include "ardour/types.h" #include "ardour/mute_master.h" @@ -144,7 +145,7 @@ MuteMaster::set_state (const XMLNode& node, int /*version*/) } if ((prop = node.property ("muted")) != 0) { - _muted_by_self = string_is_affirmative (prop->value()); + _muted_by_self = PBD::string_is_affirmative (prop->value()); } else { _muted_by_self = (_mute_point != MutePoint (0)); } diff --git a/libs/ardour/playlist.cc b/libs/ardour/playlist.cc index 885f592bf2..cb40b4df91 100644 --- a/libs/ardour/playlist.cc +++ b/libs/ardour/playlist.cc @@ -210,35 +210,35 @@ Playlist::Playlist (boost::shared_ptr other, framepos_t start, f framepos_t position = 0; framecnt_t len = 0; string new_name; - OverlapType overlap; + Evoral::OverlapType overlap; region = *i; overlap = region->coverage (start, end); switch (overlap) { - case OverlapNone: + case Evoral::OverlapNone: continue; - case OverlapInternal: + case Evoral::OverlapInternal: offset = start - region->position(); position = 0; len = cnt; break; - case OverlapStart: + case Evoral::OverlapStart: offset = 0; position = region->position() - start; len = end - region->position(); break; - case OverlapEnd: + case Evoral::OverlapEnd: offset = start - region->position(); position = 0; len = region->length() - offset; break; - case OverlapExternal: + case Evoral::OverlapExternal: offset = 0; position = region->position() - start; len = region->length(); @@ -561,7 +561,6 @@ Playlist::notify_region_added (boost::shared_ptr r) void Playlist::flush_notifications (bool from_undo) { - set > dependent_checks_needed; set >::iterator s; bool regions_changed = false; @@ -575,6 +574,10 @@ Playlist::flush_notifications (bool from_undo) regions_changed = true; } + /* XXX: it'd be nice if we could use pending_bounds for + RegionsExtended and RegionsMoved. + */ + /* we have no idea what order the regions ended up in pending bounds (it could be based on selection order, for example). so, to preserve layering in the "most recently moved is higher" @@ -584,24 +587,26 @@ Playlist::flush_notifications (bool from_undo) // RegionSortByLayer cmp; // pending_bounds.sort (cmp); + list > crossfade_ranges; + for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) { - dependent_checks_needed.insert (*r); + crossfade_ranges.push_back ((*r)->last_range ()); + crossfade_ranges.push_back ((*r)->range ()); } for (s = pending_removes.begin(); s != pending_removes.end(); ++s) { + crossfade_ranges.push_back ((*s)->range ()); remove_dependents (*s); - // cerr << _name << " sends RegionRemoved\n"; RegionRemoved (boost::weak_ptr (*s)); /* EMIT SIGNAL */ } - + for (s = pending_adds.begin(); s != pending_adds.end(); ++s) { - // cerr << _name << " sends RegionAdded\n"; - /* don't emit RegionAdded signal until relayering is done, - so that the region is fully setup by the time - anyone hear's that its been added - */ - dependent_checks_needed.insert (*s); - } + crossfade_ranges.push_back ((*s)->range ()); + /* don't emit RegionAdded signal until relayering is done, + so that the region is fully setup by the time + anyone hears that its been added + */ + } if ( ((regions_changed || pending_contents_change) && !in_set_state) || @@ -621,11 +626,12 @@ Playlist::flush_notifications (bool from_undo) RegionAdded (boost::weak_ptr (*s)); /* EMIT SIGNAL */ } - for (s = dependent_checks_needed.begin(); s != dependent_checks_needed.end(); ++s) { - check_dependents (*s, false); - } + coalesce_and_check_crossfades (crossfade_ranges); if (!pending_range_moves.empty ()) { + /* We don't need to check crossfades for these as pending_bounds has + already covered it. + */ RangesMoved (pending_range_moves, from_undo); } @@ -754,7 +760,7 @@ Playlist::flush_notifications (bool from_undo) notify_region_added (region); if (!holding_state ()) { - check_dependents (region, false); + check_crossfades (region->range ()); } region->PropertyChanged.connect_same_thread (region_state_changed_connections, boost::bind (&Playlist::region_changed_proxy, this, _1, boost::weak_ptr (region))); @@ -879,7 +885,7 @@ Playlist::flush_notifications (bool from_undo) boost::shared_ptr current; string new_name; RegionList::iterator tmp; - OverlapType overlap; + Evoral::OverlapType overlap; framepos_t pos1, pos2, pos3, pos4; in_partition = true; @@ -915,7 +921,7 @@ Playlist::flush_notifications (bool from_undo) continue; } - if ((overlap = current->coverage (start, end)) == OverlapNone) { + if ((overlap = current->coverage (start, end)) == Evoral::OverlapNone) { continue; } @@ -924,7 +930,7 @@ Playlist::flush_notifications (bool from_undo) pos3 = end; pos4 = current->last_frame(); - if (overlap == OverlapInternal) { + if (overlap == Evoral::OverlapInternal) { /* split: we need 3 new regions, the front, middle and end. cut: we need 2 regions, the front and end. */ @@ -986,7 +992,7 @@ Playlist::flush_notifications (bool from_undo) thawlist.push_back (current); current->cut_end (pos2 - 1); - } else if (overlap == OverlapEnd) { + } else if (overlap == Evoral::OverlapEnd) { /* start end @@ -1026,7 +1032,7 @@ Playlist::flush_notifications (bool from_undo) thawlist.push_back (current); current->cut_end (pos2 - 1); - } else if (overlap == OverlapStart) { + } else if (overlap == Evoral::OverlapStart) { /* split: we need 2 regions: the front and the end. cut: just trim current to skip the cut area @@ -1069,7 +1075,7 @@ Playlist::flush_notifications (bool from_undo) current->suspend_property_changes (); thawlist.push_back (current); current->trim_front (pos3); - } else if (overlap == OverlapExternal) { + } else if (overlap == Evoral::OverlapExternal) { /* split: no split required. cut: remove the region. @@ -1098,9 +1104,7 @@ Playlist::flush_notifications (bool from_undo) in_partition = false; } - for (RegionList::iterator i = new_regions.begin(); i != new_regions.end(); ++i) { - check_dependents (*i, false); - } + check_crossfades (Evoral::Range (start, end)); } boost::shared_ptr @@ -1381,9 +1385,6 @@ Playlist::flush_notifications (bool from_undo) add_region_internal (left, region->position()); add_region_internal (right, region->position() + before); - - finalize_split_region (region, left, right); - remove_region_internal (region); _splicing = old_sp; @@ -1508,7 +1509,10 @@ Playlist::flush_notifications (bool from_undo) } else { notify_contents_changed (); relayer (); - check_dependents (region, false); + list > xf; + xf.push_back (Evoral::Range (region->last_range())); + xf.push_back (Evoral::Range (region->range())); + coalesce_and_check_crossfades (xf); } } } @@ -1556,7 +1560,7 @@ Playlist::flush_notifications (bool from_undo) } if (what_changed.contains (our_interests) && !what_changed.contains (pos_and_length)) { - check_dependents (region, false); + check_crossfades (region->range ()); } if (what_changed.contains (Properties::position) && !what_changed.contains (Properties::length)) { @@ -1703,148 +1707,13 @@ Playlist::regions_at (framepos_t frame) return region; } -boost::shared_ptr -Playlist::regions_to_read (framepos_t start, framepos_t end) -{ - /* Caller must hold lock */ - - RegionList covering; - set to_check; - set > unique; - - to_check.insert (start); - to_check.insert (end); - - DEBUG_TRACE (DEBUG::AudioPlayback, ">>>>> REGIONS TO READ\n"); - - for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) { - - /* find all/any regions that span start+end */ - - switch ((*i)->coverage (start, end)) { - case OverlapNone: - break; - - case OverlapInternal: - covering.push_back (*i); - DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OInternal)\n", (*i)->name())); - break; - - case OverlapStart: - to_check.insert ((*i)->position()); - if ((*i)->position() != 0) { - to_check.insert ((*i)->position()-1); - } - DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will check %1 for %2\n", (*i)->position(), (*i)->name())); - covering.push_back (*i); - break; - - case OverlapEnd: - to_check.insert ((*i)->last_frame()); - to_check.insert ((*i)->last_frame()+1); - DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OEnd)\n", (*i)->name())); - DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name())); - DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name())); - covering.push_back (*i); - break; - - case OverlapExternal: - covering.push_back (*i); - to_check.insert ((*i)->position()); - if ((*i)->position() != 0) { - to_check.insert ((*i)->position()-1); - } - to_check.insert ((*i)->last_frame()); - to_check.insert ((*i)->last_frame()+1); - DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OExt)\n", (*i)->name())); - DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->position(), (*i)->name())); - DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name())); - break; - } - - /* don't go too far */ - - if ((*i)->position() > end) { - break; - } - } - - boost::shared_ptr rlist (new RegionList); - - /* find all the regions that cover each position .... */ - - if (covering.size() == 1) { - - rlist->push_back (covering.front()); - DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Just one covering region (%1)\n", covering.front()->name())); - - } else { - - RegionList here; - for (set::iterator t = to_check.begin(); t != to_check.end(); ++t) { - - here.clear (); - - DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("++++ Considering %1\n", *t)); - - for (RegionList::iterator x = covering.begin(); x != covering.end(); ++x) { - - if ((*x)->covers (*t)) { - here.push_back (*x); - DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("region %1 covers %2\n", - (*x)->name(), - (*t))); - } else { - DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("region %1 does NOT covers %2\n", - (*x)->name(), - (*t))); - } - - } - - RegionSortByLayer cmp; - here.sort (cmp); - - /* ... and get the top/transparent regions at "here" */ - - for (RegionList::reverse_iterator c = here.rbegin(); c != here.rend(); ++c) { - - unique.insert (*c); - - if ((*c)->opaque()) { - - /* the other regions at this position are hidden by this one */ - DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("%1 is opaque, ignore all others\n", - (*c)->name())); - break; - } - } - } - - for (set >::iterator s = unique.begin(); s != unique.end(); ++s) { - rlist->push_back (*s); - } - - if (rlist->size() > 1) { - /* now sort by time order */ - - RegionSortByPosition cmp; - rlist->sort (cmp); - } - } - - DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("<<<<< REGIONS TO READ returns %1\n", rlist->size())); - - return rlist; - } - boost::shared_ptr Playlist::find_regions_at (framepos_t frame) { /* Caller must hold lock */ boost::shared_ptr rlist (new RegionList); - + for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) { if ((*i)->covers (frame)) { rlist->push_back (*i); @@ -1854,6 +1723,40 @@ Playlist::find_regions_at (framepos_t frame) return rlist; } +boost::shared_ptr +Playlist::regions_with_start_within (Evoral::Range range) +{ + RegionLock rlock (this); + boost::shared_ptr rlist (new RegionList); + + for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) { + if ((*i)->first_frame() >= range.from && (*i)->first_frame() <= range.to) { + rlist->push_back (*i); + } + } + + return rlist; +} + +boost::shared_ptr +Playlist::regions_with_end_within (Evoral::Range range) +{ + RegionLock rlock (this); + boost::shared_ptr rlist (new RegionList); + + for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) { + if ((*i)->last_frame() >= range.from && (*i)->last_frame() <= range.to) { + rlist->push_back (*i); + } + } + + return rlist; +} + +/** @param start Range start. + * @param end Range end. + * @return regions which have some part within this range. + */ boost::shared_ptr Playlist::regions_touched (framepos_t start, framepos_t end) { @@ -1861,12 +1764,12 @@ Playlist::regions_touched (framepos_t start, framepos_t end) boost::shared_ptr rlist (new RegionList); for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) { - if ((*i)->coverage (start, end) != OverlapNone) { + if ((*i)->coverage (start, end) != Evoral::OverlapNone) { rlist->push_back (*i); } } - return rlist; + return rlist; } framepos_t @@ -2205,7 +2108,7 @@ Playlist::regions_touched (framepos_t start, framepos_t end) */ for (RegionList::iterator r = regions.begin(); r != regions.end(); ++r) { - check_dependents (*r, false); + check_crossfades ((*r)->range ()); } } @@ -2674,6 +2577,8 @@ Playlist::shuffle (boost::shared_ptr region, int dir) _shuffling = true; + Evoral::Range old_range = region->range (); + { RegionLock rlock (const_cast (this)); @@ -2772,7 +2677,11 @@ Playlist::shuffle (boost::shared_ptr region, int dir) if (moved) { relayer (); - check_dependents (region, false); + + list > xf; + xf.push_back (old_range); + xf.push_back (region->range ()); + coalesce_and_check_crossfades (xf); notify_contents_changed(); } @@ -2936,10 +2845,6 @@ Playlist::combine (const RegionList& r) pre_combine (copies); - /* add any dependent regions to the new playlist */ - - copy_dependents (old_and_new_regions, pl.get()); - /* now create a new PlaylistSource for each channel in the new playlist */ SourceList sources; @@ -3081,13 +2986,13 @@ Playlist::uncombine (boost::shared_ptr target) modified_region = false; switch (original->coverage (adjusted_start, adjusted_end)) { - case OverlapNone: + case Evoral::OverlapNone: /* original region does not cover any part of the current state of the compound region */ continue; - case OverlapInternal: + case Evoral::OverlapInternal: /* overlap is just a small piece inside the * original so trim both ends */ @@ -3095,13 +3000,13 @@ Playlist::uncombine (boost::shared_ptr target) modified_region = true; break; - case OverlapExternal: + case Evoral::OverlapExternal: /* overlap fully covers original, so leave it as is */ break; - case OverlapEnd: + case Evoral::OverlapEnd: /* overlap starts within but covers end, so trim the front of the region */ @@ -3109,7 +3014,7 @@ Playlist::uncombine (boost::shared_ptr target) modified_region = true; break; - case OverlapStart: + case Evoral::OverlapStart: /* overlap covers start but ends within, so * trim the end of the region. */ @@ -3152,10 +3057,6 @@ Playlist::uncombine (boost::shared_ptr target) add_region ((*i), (*i)->position()); } - /* now move dependent regions back from the compound to this playlist */ - - pl->copy_dependents (old_and_new_regions, this); - in_partition = false; thaw (); } @@ -3195,16 +3096,33 @@ Playlist::set_orig_track_id (const PBD::ID& id) _orig_track_id = id; } -uint64_t -Playlist::highest_layering_index () const +void +Playlist::coalesce_and_check_crossfades (list > ranges) { - RegionLock rlock (const_cast (this)); + /* XXX: it's a shame that this coalesce algorithm also exists in + TimeSelection::consolidate(). + */ - uint64_t h = 0; - for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) { - h = max (h, (*i)->layering_index ()); + /* XXX: xfade: this is implemented in Evoral::RangeList */ + +restart: + for (list >::iterator i = ranges.begin(); i != ranges.end(); ++i) { + for (list >::iterator j = ranges.begin(); j != ranges.end(); ++j) { + + if (i == j) { + continue; + } + + if (Evoral::coverage (i->from, i->to, j->from, j->to) != Evoral::OverlapNone) { + i->from = min (i->from, j->from); + i->to = max (i->to, j->to); + ranges.erase (j); + goto restart; + } + } } - return h; + for (list >::iterator i = ranges.begin(); i != ranges.end(); ++i) { + check_crossfades (*i); + } } - diff --git a/libs/ardour/plugin_insert.cc b/libs/ardour/plugin_insert.cc index a03c25ee72..796ec9420c 100644 --- a/libs/ardour/plugin_insert.cc +++ b/libs/ardour/plugin_insert.cc @@ -340,9 +340,12 @@ PluginInsert::connect_and_run (BufferSet& bufs, pframes_t nframes, framecnt_t of /* copy the first stream's buffer contents to the others */ /* XXX: audio only */ - Sample const * mono = bufs.get_audio (in_map.get (DataType::AUDIO, 0, &valid)).data (offset); - for (uint32_t i = in_streams.n_audio(); i < natural_input_streams().n_audio(); ++i) { - memcpy (bufs.get_audio (in_map.get (DataType::AUDIO, i, &valid)).data (offset), mono, sizeof (Sample) * nframes); + uint32_t first_idx = in_map.get (DataType::AUDIO, 0, &valid); + if (valid) { + Sample const * mono = bufs.get_audio (first_idx).data (offset); + for (uint32_t i = in_streams.n_audio(); i < natural_input_streams().n_audio(); ++i) { + memcpy (bufs.get_audio (in_map.get (DataType::AUDIO, i, &valid)).data (offset), mono, sizeof (Sample) * nframes); + } } } diff --git a/libs/ardour/rc_configuration.cc b/libs/ardour/rc_configuration.cc index 8c4650b2e6..d5bfc7a9a8 100644 --- a/libs/ardour/rc_configuration.cc +++ b/libs/ardour/rc_configuration.cc @@ -36,6 +36,7 @@ #include "ardour/audio_diskstream.h" #include "ardour/control_protocol_manager.h" #include "ardour/filesystem_paths.h" +#include "ardour/session_metadata.h" #include "i18n.h" @@ -208,6 +209,8 @@ RCConfiguration::get_state () root->add_child_nocopy (get_variables ()); + root->add_child_nocopy (SessionMetadata::Metadata()->get_user_state()); + if (_extra_xml) { root->add_child_copy (*_extra_xml); } @@ -239,7 +242,7 @@ RCConfiguration::get_variables () } int -RCConfiguration::set_state (const XMLNode& root, int /*version*/) +RCConfiguration::set_state (const XMLNode& root, int version) { if (root.name() != "Ardour") { return -1; @@ -263,6 +266,8 @@ RCConfiguration::set_state (const XMLNode& root, int /*version*/) if (node->name() == "Config") { set_variables (*node); + } else if (node->name() == "Metadata") { + SessionMetadata::Metadata()->set_state (*node, version); } else if (node->name() == ControlProtocolManager::state_node_name) { _control_protocol_state = new XMLNode (*node); } else if (node->name() == MIDI::Port::state_node_name) { diff --git a/libs/ardour/region.cc b/libs/ardour/region.cc index 66c0b84ae9..efaa104d35 100644 --- a/libs/ardour/region.cc +++ b/libs/ardour/region.cc @@ -686,7 +686,7 @@ Region::set_start (framepos_t pos) return; } - _start = pos; + set_start_internal (pos); _whole_file = false; first_edit (); invalidate_transients (); @@ -701,6 +701,7 @@ Region::trim_start (framepos_t new_position) if (locked() || position_locked()) { return; } + framepos_t new_start; frameoffset_t const start_shift = new_position - _position; @@ -732,7 +733,7 @@ Region::trim_start (framepos_t new_position) return; } - _start = new_start; + set_start_internal (new_start); _whole_file = false; first_edit (); @@ -888,7 +889,7 @@ Region::trim_to_internal (framepos_t position, framecnt_t length) PropertyChange what_changed; if (_start != new_start) { - _start = new_start; + set_start_internal (new_start); what_changed.add (Properties::start); } @@ -1311,7 +1312,7 @@ Region::send_change (const PropertyChange& what_changed) bool Region::overlap_equivalent (boost::shared_ptr other) const { - return coverage (other->first_frame(), other->last_frame()) != OverlapNone; + return coverage (other->first_frame(), other->last_frame()) != Evoral::OverlapNone; } bool @@ -1648,3 +1649,8 @@ Region::post_set (const PropertyChange& pc) } } +void +Region::set_start_internal (framecnt_t s) +{ + _start = s; +} diff --git a/libs/ardour/region_factory.cc b/libs/ardour/region_factory.cc index 5e408b2786..f739483821 100644 --- a/libs/ardour/region_factory.cc +++ b/libs/ardour/region_factory.cc @@ -620,10 +620,17 @@ RegionFactory::remove_regions_using_source (boost::shared_ptr src) { Glib::Mutex::Lock lm (region_map_lock); - for (RegionMap::iterator i = region_map.begin(); i != region_map.end(); ++i) { + RegionMap::iterator i = region_map.begin(); + while (i != region_map.end()) { + + RegionMap::iterator j = i; + ++j; + if (i->second->uses_source (src)) { region_map.erase (i); } + + i = j; } } diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index 3cb71fad84..3f68e266eb 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -522,10 +522,13 @@ Route::process_output_buffers (BufferSet& bufs, /* if it has any inputs, make sure they match */ if (boost::dynamic_pointer_cast (*i) == 0 && (*i)->input_streams() != ChanCount::ZERO) { if (bufs.count() != (*i)->input_streams()) { - cerr << _name << " bufs = " << bufs.count() - << " input for " << (*i)->name() << " = " << (*i)->input_streams() - << endl; - abort (); + DEBUG_TRACE ( + DEBUG::Processors, string_compose ( + "%1 bufs = %2 input for %3 = %4\n", + _name, bufs.count(), (*i)->name(), (*i)->input_streams() + ) + ); + continue; } } #endif @@ -2964,7 +2967,7 @@ Route::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, in return 0; } - framecnt_t unused = 0; + framepos_t unused = 0; if ((nframes = check_initial_delay (nframes, unused)) == 0) { return 0; @@ -4051,3 +4054,15 @@ Route::has_external_redirects () const return false; } +boost::shared_ptr +Route::the_instrument () const +{ + Glib::RWLock::WriterLock lm (_processor_lock); + for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) { + if ((*i)->input_streams().n_midi() > 0 && + (*i)->output_streams().n_audio() > 0) { + return (*i); + } + } + return boost::shared_ptr(); +} diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index 73e8070099..1192e898f6 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -66,7 +66,6 @@ #include "ardour/click.h" #include "ardour/configuration.h" #include "ardour/control_protocol_manager.h" -#include "ardour/crossfade.h" #include "ardour/cycle_timer.h" #include "ardour/data_type.h" #include "ardour/debug.h" @@ -95,7 +94,6 @@ #include "ardour/session.h" #include "ardour/session_directory.h" #include "ardour/session_directory.h" -#include "ardour/session_metadata.h" #include "ardour/session_playlists.h" #include "ardour/slave.h" #include "ardour/smf_source.h" @@ -108,6 +106,7 @@ #include "ardour/operations.h" #include "midi++/port.h" +#include "midi++/jack_midi_port.h" #include "midi++/mmc.h" #include "midi++/manager.h" @@ -163,7 +162,6 @@ Session::Session (AudioEngine &eng, , click_data (0) , click_emphasis_data (0) , main_outs (0) - , _metadata (new SessionMetadata()) , _have_rec_enabled_track (false) , _suspend_timecode_transmission (0) { @@ -328,8 +326,6 @@ Session::destroy () delete *i; } - Crossfade::set_buffer_size (0); - /* not strictly necessary, but doing it here allows the shared_ptr debugging to work */ playlists.reset (); @@ -542,11 +538,21 @@ Session::when_engine_running () BootMessage (_("Setup signal flow and plugins")); + /* Reset all panners */ + + Delivery::reset_panners (); + + /* this will cause the CPM to instantiate any protocols that are in use + * (or mandatory), which will pass it this Session, and then call + * set_state() on each instantiated protocol to match stored state. + */ + ControlProtocolManager::instance().set_session (this); /* This must be done after the ControlProtocolManager set_session above, as it will set states for ports which the ControlProtocolManager creates. */ + MIDI::Manager::instance()->set_port_states (Config->midi_port_states ()); /* And this must be done after the MIDI::Manager::set_port_states as @@ -555,6 +561,12 @@ Session::when_engine_running () hookup_io (); + /* Let control protocols know that we are now all connected, so they + * could start talking to surfaces if they want to. + */ + + ControlProtocolManager::instance().midi_connectivity_established (); + if (_is_new && !no_auto_connect()) { Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock()); auto_connect_master_bus (); @@ -830,11 +842,7 @@ Session::hookup_io () /* Tell all IO objects to connect themselves together */ IO::enable_connecting (); - MIDI::Port::MakeConnections (); - - /* Now reset all panners */ - - Delivery::reset_panners (); + MIDI::JackMIDIPort::MakeConnections (); /* Anyone who cares about input state, wake up and do something */ @@ -2990,7 +2998,7 @@ Session::remove_source (boost::weak_ptr src) } } - if (!_state_of_the_state & InCleanup) { + if (!(_state_of_the_state & InCleanup)) { /* save state so we don't end up with a session file referring to non-existent sources. @@ -3419,12 +3427,9 @@ Session::audition_playlist () void Session::non_realtime_set_audition () { - if (!pending_audition_region) { - auditioner->audition_current_playlist (); - } else { - auditioner->audition_region (pending_audition_region); - pending_audition_region.reset (); - } + assert (pending_audition_region); + auditioner->audition_region (pending_audition_region); + pending_audition_region.reset (); AuditionActive (true); /* EMIT SIGNAL */ } @@ -4364,7 +4369,7 @@ Session::start_time_changed (framepos_t old) Location* l = _locations->auto_loop_location (); - if (l->start() == old) { + if (l && l->start() == old) { l->set_start (s->start(), true); } } diff --git a/libs/ardour/session_butler.cc b/libs/ardour/session_butler.cc index c92c0604c7..87e3c34fb5 100644 --- a/libs/ardour/session_butler.cc +++ b/libs/ardour/session_butler.cc @@ -35,7 +35,6 @@ #include "ardour/audioengine.h" #include "ardour/butler.h" #include "ardour/configuration.h" -#include "ardour/crossfade.h" #include "ardour/io.h" #include "ardour/midi_diskstream.h" #include "ardour/session.h" diff --git a/libs/ardour/session_command.cc b/libs/ardour/session_command.cc index e5ddd0a097..68aba1a689 100644 --- a/libs/ardour/session_command.cc +++ b/libs/ardour/session_command.cc @@ -34,8 +34,6 @@ #include "ardour/session_playlists.h" #include "ardour/region_factory.h" #include "ardour/midi_automation_list_binder.h" -#include "ardour/crossfade_binder.h" -#include "ardour/crossfade.h" #include "pbd/error.h" #include "pbd/id.h" #include "pbd/statefuldestructible.h" @@ -143,19 +141,6 @@ Session::memento_command_factory(XMLNode *n) cerr << "Alist " << id << " not found\n"; - } else if (obj_T == "ARDOUR::Crossfade") { - if (have_id) { - boost::shared_ptr c = playlists->find_crossfade (id); - if (c) { - return new MementoCommand (*c.get(), before, after); - } - } else { - return new MementoCommand ( - new CrossfadeBinder (n, playlists), - before, after - ); - } - } else if (registry.count(id)) { // For Editor and AutomationLine which are off-limits herea return new MementoCommand(*registry[id], before, after); } diff --git a/libs/ardour/session_metadata.cc b/libs/ardour/session_metadata.cc index 51dba7ceff..00f5d31ed8 100644 --- a/libs/ardour/session_metadata.cc +++ b/libs/ardour/session_metadata.cc @@ -26,6 +26,8 @@ using namespace std; using namespace Glib; using namespace ARDOUR; +SessionMetadata *SessionMetadata::_metadata = NULL; //singleton instance + SessionMetadata::SessionMetadata () { /*** General ***/ @@ -53,6 +55,10 @@ SessionMetadata::SessionMetadata () map.insert (Property ("mixer", "")); //map.insert (Property ("performers", "")); // Multiple values [instrument] + /*** Education... ***/ + map.insert (Property ("instructor", "")); + map.insert (Property ("course", "")); + /*** Album info ***/ map.insert (Property ("album", "")); map.insert (Property ("compilation", "")); @@ -80,6 +86,13 @@ SessionMetadata::SessionMetadata () //map.insert (Property ("album_artist_sort", "")); //map.insert (Property ("artist_sort", "")); //map.insert (Property ("title_sort", "")); + + /*** Globals ***/ + user_map.insert (Property ("user_name", "")); + user_map.insert (Property ("user_email", "")); + user_map.insert (Property ("user_web", "")); + user_map.insert (Property ("user_organization", "")); + user_map.insert (Property ("user_country", "")); } SessionMetadata::~SessionMetadata () @@ -107,9 +120,12 @@ SessionMetadata::get_value (const string & name) const { PropertyMap::const_iterator it = map.find (name); if (it == map.end()) { - // Should not be reached! - std::cerr << "Programming error in SessionMetadata::get_value" << std::endl; - return ""; + it = user_map.find (name); + if (it == user_map.end()) { + // Should not be reached! + std::cerr << "Programming error in SessionMetadata::get_value" << std::endl; + return ""; + } } return it->second; @@ -126,9 +142,12 @@ SessionMetadata::set_value (const string & name, const string & value) { PropertyMap::iterator it = map.find (name); if (it == map.end()) { - // Should not be reached! - std::cerr << "Programming error in SessionMetadata::set_value" << std::endl; - return; + it = user_map.find (name); + if (it == user_map.end()) { + // Should not be reached! + std::cerr << "Programming error in SessionMetadata::set_value" << std::endl; + return; + } } it->second = value; @@ -163,7 +182,7 @@ SessionMetadata::get_state () } int -SessionMetadata::set_state (const XMLNode & state, int /*version*/) +SessionMetadata::set_state (const XMLNode & state, int version_num) { const XMLNodeList & children = state.children(); string name; @@ -186,6 +205,22 @@ SessionMetadata::set_state (const XMLNode & state, int /*version*/) return 0; } + +XMLNode & +SessionMetadata::get_user_state () +{ + XMLNode * node = new XMLNode ("Metadata"); + XMLNode * prop; + + for (PropertyMap::const_iterator it = user_map.begin(); it != user_map.end(); ++it) { + if ((prop = get_xml (it->first))) { + node->add_child_nocopy (*prop); + } + } + + return *node; +} + /*** Accessing ***/ string SessionMetadata::comment () const @@ -343,6 +378,51 @@ SessionMetadata::genre () const return get_value("genre"); } +string +SessionMetadata::instructor () const +{ + return get_value("instructor"); +} + +string +SessionMetadata::course () const +{ + return get_value("course"); +} + + +string +SessionMetadata::user_name () const +{ + return get_value("user_name"); +} + +string +SessionMetadata::user_email () const +{ + return get_value("user_email"); +} + +string +SessionMetadata::user_web () const +{ + return get_value("user_web"); +} + +string +SessionMetadata::organization () const +{ + return get_value("user_organization"); +} + +string +SessionMetadata::country () const +{ + return get_value("user_country"); +} + + + /*** Editing ***/ void SessionMetadata::set_comment (const string & v) @@ -499,3 +579,44 @@ SessionMetadata::set_genre (const string & v) { set_value ("genre", v); } + +void +SessionMetadata::set_instructor (const string & v) +{ + set_value ("instructor", v); +} + +void +SessionMetadata::set_course (const string & v) +{ + set_value ("course", v); +} + +void +SessionMetadata::set_user_name (const string & v) +{ + set_value ("user_name", v); +} + +void +SessionMetadata::set_user_email (const string & v) +{ + set_value ("user_email", v); +} + +void +SessionMetadata::set_user_web (const string & v) +{ + set_value ("user_web", v); +} + +void +SessionMetadata::set_organization (const string & v) +{ + set_value ("user_organization", v); +} +void +SessionMetadata::set_country (const string & v) +{ + set_value ("user_country", v); +} diff --git a/libs/ardour/session_midi.cc b/libs/ardour/session_midi.cc index 453718c024..8b9da721f2 100644 --- a/libs/ardour/session_midi.cc +++ b/libs/ardour/session_midi.cc @@ -91,7 +91,7 @@ Session::setup_midi_control () } void -Session::spp_start (Parser &, framepos_t /*timestamp*/) +Session::spp_start () { if (Config->get_mmc_control ()) { request_transport_speed (1.0); @@ -99,13 +99,13 @@ Session::spp_start (Parser &, framepos_t /*timestamp*/) } void -Session::spp_continue (Parser& ignored, framepos_t timestamp) +Session::spp_continue () { - spp_start (ignored, timestamp); + spp_start (); } void -Session::spp_stop (Parser&, framepos_t /*timestamp*/) +Session::spp_stop () { if (Config->get_mmc_control ()) { request_stop (); diff --git a/libs/ardour/session_process.cc b/libs/ardour/session_process.cc index 60cb6ecee1..c002cc9008 100644 --- a/libs/ardour/session_process.cc +++ b/libs/ardour/session_process.cc @@ -47,6 +47,8 @@ #include "i18n.h" +#include + using namespace ARDOUR; using namespace PBD; using namespace std; @@ -60,8 +62,6 @@ Session::process (pframes_t nframes) { framepos_t transport_at_start = _transport_frame; - MIDI::Manager::instance()->cycle_start(nframes); - _silent = false; if (processing_blocked()) { @@ -96,8 +96,6 @@ Session::process (pframes_t nframes) } SendFeedback (); /* EMIT SIGNAL */ - - MIDI::Manager::instance()->cycle_end(); } int diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc index 013e07ada5..48e7f206e7 100644 --- a/libs/ardour/session_state.cc +++ b/libs/ardour/session_state.cc @@ -83,7 +83,6 @@ #include "ardour/butler.h" #include "ardour/configuration.h" #include "ardour/control_protocol_manager.h" -#include "ardour/crossfade.h" #include "ardour/cycle_timer.h" #include "ardour/directory_names.h" #include "ardour/filename_extensions.h" @@ -232,7 +231,6 @@ Session::first_stage_init (string fullpath, string snapshot_name) /* default short fade = 15ms */ - Crossfade::set_short_xfade_length ((framecnt_t) floor (config.get_short_xfade_seconds() * frame_rate())); SndFileSource::setup_standard_crossfades (*this, frame_rate()); last_mmc_step.tv_sec = 0; @@ -568,10 +566,6 @@ Session::create (const string& session_template, BusProfile* bus_profile) } - /* Instantiate metadata */ - - _metadata = new SessionMetadata (); - /* set initial start + end point */ _state_of_the_state = Clean; @@ -931,39 +925,38 @@ Session::load_state (string snapshot_name) /* no version implies very old version of Ardour */ Stateful::loading_state_version = 1000; } else { - int major; - int minor; - int micro; - - sscanf (prop->value().c_str(), "%d.%d.%d", &major, &minor, µ); - Stateful::loading_state_version = (major * 1000) + minor; + if (prop->value().find ('.')) { + /* old school version format - lock at 3000 */ + Stateful::loading_state_version = 3000; + } else { + Stateful::loading_state_version = atoi (prop->value()); + } } if (Stateful::loading_state_version < CURRENT_SESSION_FILE_VERSION) { sys::path backup_path(_session_dir->root_path()); - backup_path /= legalize_for_path (snapshot_name) + "-1" + statefile_suffix; + backup_path /= string_compose ("%1-%2%3", legalize_for_path (snapshot_name), Stateful::loading_state_version, statefile_suffix); - // only create a backup once - if (sys::exists (backup_path)) { - return 0; - } + // only create a backup for a given statefile version once - info << string_compose (_("Copying old session file %1 to %2\nUse %2 with %3 versions before 2.0 from now on"), - xmlpath.to_string(), backup_path.to_string(), PROGRAM_NAME) - << endmsg; - - try - { - sys::copy_file (xmlpath, backup_path); - } - catch(sys::filesystem_error& ex) - { - error << string_compose (_("Unable to make backup of state file %1 (%2)"), - xmlpath.to_string(), ex.what()) - << endmsg; - return -1; + if (!sys::exists (backup_path)) { + + info << string_compose (_("Copying old session file %1 to %2\nUse %2 with %3 versions before 2.0 from now on"), + xmlpath.to_string(), backup_path.to_string(), PROGRAM_NAME) + << endmsg; + + try { + sys::copy_file (xmlpath, backup_path); + + } catch (sys::filesystem_error& ex) { + + error << string_compose (_("Unable to make backup of state file %1 (%2)"), + xmlpath.to_string(), ex.what()) + << endmsg; + return -1; + } } } @@ -998,15 +991,14 @@ Session::get_template() } XMLNode& -Session::state(bool full_state) +Session::state (bool full_state) { XMLNode* node = new XMLNode("Session"); XMLNode* child; - // store libardour version, just in case char buf[16]; - snprintf(buf, sizeof(buf), "%d.%d.%d", libardour3_major_version, libardour3_minor_version, libardour3_micro_version); - node->add_property("version", string(buf)); + snprintf(buf, sizeof(buf), "%d", CURRENT_SESSION_FILE_VERSION); + node->add_property("version", buf); /* store configuration settings */ @@ -1060,7 +1052,7 @@ Session::state(bool full_state) node->add_child_nocopy (config.get_variables ()); - node->add_child_nocopy (_metadata->get_state()); + node->add_child_nocopy (ARDOUR::SessionMetadata::Metadata()->get_state()); child = node->add_child ("Sources"); @@ -1222,10 +1214,6 @@ Session::set_state (const XMLNode& node, int version) return -1; } - if ((prop = node.property ("version")) != 0) { - version = atoi (prop->value ()) * 1000; - } - if ((prop = node.property ("name")) != 0) { _name = prop->value (); } @@ -1277,7 +1265,7 @@ Session::set_state (const XMLNode& node, int version) if (version >= 3000) { if ((child = find_named_node (node, "Metadata")) == 0) { warning << _("Session: XML state has no metadata section") << endmsg; - } else if (_metadata->set_state (*child, version)) { + } else if ( ARDOUR::SessionMetadata::Metadata()->set_state (*child, version) ) { goto out; } } @@ -3054,7 +3042,7 @@ struct null_deleter { void operator()(void const *) const {} }; void Session::remove_controllable (Controllable* c) { - if (_state_of_the_state | Deletion) { + if (_state_of_the_state & Deletion) { return; } @@ -3579,6 +3567,8 @@ Session::config_changed (std::string p, bool ours) last_timecode_valid = false; } else if (p == "playback-buffer-seconds") { AudioSource::allocate_working_buffers (frame_rate()); + } else if (p == "automation-thinning-factor") { + Evoral::ControlList::set_thinning_factor (Config->get_automation_thinning_factor()); } set_dirty (); @@ -3641,9 +3631,9 @@ Session::setup_midi_machine_control () /* also handle MIDI SPP because its so common */ - mmc->SPPStart.connect_same_thread (*this, boost::bind (&Session::spp_start, this, _1, _2)); - mmc->SPPContinue.connect_same_thread (*this, boost::bind (&Session::spp_continue, this, _1, _2)); - mmc->SPPStop.connect_same_thread (*this, boost::bind (&Session::spp_stop, this, _1, _2)); + mmc->SPPStart.connect_same_thread (*this, boost::bind (&Session::spp_start, this)); + mmc->SPPContinue.connect_same_thread (*this, boost::bind (&Session::spp_continue, this)); + mmc->SPPStop.connect_same_thread (*this, boost::bind (&Session::spp_stop, this)); } boost::shared_ptr diff --git a/libs/ardour/session_vst.cc b/libs/ardour/session_vst.cc index af57976544..644fb19ffb 100644 --- a/libs/ardour/session_vst.cc +++ b/libs/ardour/session_vst.cc @@ -247,6 +247,8 @@ intptr_t Session::vst_callback ( case audioMasterOfflineStart: SHOW_CALLBACK ("amc: audioMasterOfflineStart\n"); + return 0; + case audioMasterOfflineRead: SHOW_CALLBACK ("amc: audioMasterOfflineRead\n"); // ptr points to offline structure, see below. return 0: error, 1 ok @@ -259,6 +261,8 @@ intptr_t Session::vst_callback ( case audioMasterOfflineGetCurrentPass: SHOW_CALLBACK ("amc: audioMasterOfflineGetCurrentPass\n"); + return 0; + case audioMasterOfflineGetCurrentMetaPass: SHOW_CALLBACK ("amc: audioMasterOfflineGetCurrentMetaPass\n"); return 0; diff --git a/libs/ardour/sndfilesource.cc b/libs/ardour/sndfilesource.cc index 1732a6f1a6..1f499bc61e 100644 --- a/libs/ardour/sndfilesource.cc +++ b/libs/ardour/sndfilesource.cc @@ -393,15 +393,13 @@ SndFileSource::nondestructive_write_unlocked (Sample *data, framecnt_t cnt) return 0; } - framecnt_t oldlen; int32_t frame_pos = _length; if (write_float (data, frame_pos, cnt) != cnt) { return 0; } - oldlen = _length; - update_length (oldlen, cnt); + update_length (_length + cnt); if (_build_peakfiles) { compute_and_write_peaks (data, frame_pos, cnt, false, true); @@ -488,7 +486,7 @@ SndFileSource::destructive_write_unlocked (Sample* data, framecnt_t cnt) } } - update_length (file_pos, cnt); + update_length (file_pos + cnt); if (_build_peakfiles) { compute_and_write_peaks (data, file_pos, cnt, false, true); diff --git a/libs/ardour/source.cc b/libs/ardour/source.cc index 4c582f3ad7..e9e3b00142 100644 --- a/libs/ardour/source.cc +++ b/libs/ardour/source.cc @@ -263,7 +263,7 @@ Source::mark_for_remove () } void -Source::set_timeline_position (int64_t pos) +Source::set_timeline_position (framepos_t pos) { _timeline_position = pos; } diff --git a/libs/ardour/strip_silence.cc b/libs/ardour/strip_silence.cc index 62b08cbb0a..a5b115205a 100644 --- a/libs/ardour/strip_silence.cc +++ b/libs/ardour/strip_silence.cc @@ -30,8 +30,7 @@ using namespace ARDOUR; /** Construct a StripSilence filter. * @param s Session. - * @param threshold Threshold below which audio is considered silence, in dBFS. - * @param minimum_length Minimum length of silence period to recognise, in samples. + * @param sm Silences to remove. * @param fade_length Length of fade in/out to apply to trimmed regions, in samples. */ diff --git a/libs/ardour/tempo.cc b/libs/ardour/tempo.cc index ef74c09c27..df3dddbd6d 100644 --- a/libs/ardour/tempo.cc +++ b/libs/ardour/tempo.cc @@ -1806,7 +1806,7 @@ TempoMap::framepos_plus_beats (framepos_t pos, Evoral::MusicalTime beats) const { Glib::RWLock::ReaderLock lm (lock); Metrics::const_iterator next_tempo; - const TempoSection* tempo; + const TempoSection* tempo = 0; /* Find the starting tempo metric */ @@ -1889,7 +1889,7 @@ TempoMap::framepos_plus_beats (framepos_t pos, Evoral::MusicalTime beats) const return pos; } -/** Subtract some (fractional) beats to a frame position, and return the result in frames */ +/** Subtract some (fractional) beats from a frame position, and return the result in frames */ framepos_t TempoMap::framepos_minus_beats (framepos_t pos, Evoral::MusicalTime beats) const { @@ -2177,10 +2177,18 @@ TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const while (distance) { /* End of this section */ - framepos_t const end = ((next_tempo == metrics.end()) ? max_framepos : (*next_tempo)->frame ()); + framepos_t end; + /* Distance to `end' in frames */ + framepos_t distance_to_end; - /* Distance to the end in frames */ - framecnt_t const distance_to_end = end - pos; + if (next_tempo == metrics.end ()) { + /* We can't do (end - pos) if end is max_framepos, as it will overflow if pos is -ve */ + end = max_framepos; + distance_to_end = max_framepos; + } else { + end = (*next_tempo)->frame (); + distance_to_end = end - pos; + } /* Amount to subtract this time */ double const sub = min (distance, distance_to_end); diff --git a/libs/ardour/test/audio_region_test.cc b/libs/ardour/test/audio_region_test.cc new file mode 100644 index 0000000000..72e886b4e1 --- /dev/null +++ b/libs/ardour/test/audio_region_test.cc @@ -0,0 +1,85 @@ +#include "ardour/playlist.h" +#include "ardour/region.h" +#include "ardour/audioregion.h" +#include "audio_region_test.h" +#include "test_globals.h" + +CPPUNIT_TEST_SUITE_REGISTRATION (AudioRegionTest); + +using namespace std; +using namespace ARDOUR; + +void +AudioRegionTest::readTest () +{ + int const N = 1024; + + Sample buf[N]; + Sample mbuf[N]; + float gbuf[N]; + + int const P = 100; + boost::shared_ptr ar = boost::dynamic_pointer_cast (_region[0]); + + /* 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)); + check_staircase (buf, 0, 256); + + for (int i = 0; i < N; ++i) { + buf[i] = 0; + } + + /* 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)); + check_staircase (buf, 128, 256); + + /* Simple read with a fade-in: 256 frames from start of region, with fades */ + ar->set_default_fade_in (); + CPPUNIT_ASSERT_EQUAL (double (64), ar->_fade_in->back()->when); + + for (int i = 0; i < N; ++i) { + buf[i] = 0; + } + + ar->read_at (buf, mbuf, gbuf, P, 256, 0); + for (int i = 0; i < 64; ++i) { + /* XXX: this isn't very accurate, but close enough for now; needs investigation */ + CPPUNIT_ASSERT_DOUBLES_EQUAL (float (i * i / 63.0), buf[i], 1e-4); + } + for (int i = 64; i < P; ++i) { + CPPUNIT_ASSERT_EQUAL (i, int (buf[i])); + } + + /* Offset read: 256 frames from 128 frames into the region, with fades + (though the fade should not affect it, as it is finished before the read starts) + */ + + for (int i = 0; i < N; ++i) { + buf[i] = 0; + } + + ar->read_at (buf, mbuf, gbuf, P + 128, 256, 0); + check_staircase (buf, 128, 256); +} + +void +AudioRegionTest::check_staircase (Sample* b, int offset, int N) +{ + for (int i = 0; i < N; ++i) { + int const j = i + offset; + CPPUNIT_ASSERT_EQUAL (j, int (b[i])); + } +} diff --git a/libs/ardour/test/audio_region_test.h b/libs/ardour/test/audio_region_test.h new file mode 100644 index 0000000000..e746b19ef3 --- /dev/null +++ b/libs/ardour/test/audio_region_test.h @@ -0,0 +1,15 @@ +#include "ardour/types.h" +#include "test_needing_playlist_and_regions.h" + +class AudioRegionTest : public TestNeedingPlaylistAndRegions +{ + CPPUNIT_TEST_SUITE (AudioRegionTest); + CPPUNIT_TEST (readTest); + CPPUNIT_TEST_SUITE_END (); + +public: + void readTest (); + +private: + void check_staircase (ARDOUR::Sample *, int, int); +}; diff --git a/libs/ardour/test/interpolation_test.cc b/libs/ardour/test/interpolation_test.cc index 0bbb697b85..23789f826f 100644 --- a/libs/ardour/test/interpolation_test.cc +++ b/libs/ardour/test/interpolation_test.cc @@ -54,15 +54,13 @@ InterpolationTest::linearInterpolationTest () result = linear.interpolate (0, NUM_SAMPLES, input, output); CPPUNIT_ASSERT_EQUAL ((framecnt_t)(NUM_SAMPLES * linear.speed()), result); - /* This one fails due too error accumulation - cout << "\nSpeed: 0.002"; - linear.reset(); - linear.set_speed (0.002); - linear.set_target_speed (linear.speed()); - result = linear.interpolate (0, NUM_SAMPLES, input, output); - linear.speed(); - CPPUNIT_ASSERT_EQUAL ((framecnt_t)(NUM_SAMPLES * linear.speed()), result); - */ +// cout << "\nSpeed: 0.002"; + linear.reset(); + linear.set_speed (0.002); + linear.set_target_speed (linear.speed()); + result = linear.interpolate (0, NUM_SAMPLES, input, output); + linear.speed(); + CPPUNIT_ASSERT_EQUAL ((framecnt_t)(NUM_SAMPLES * linear.speed()), result); // cout << "\nSpeed: 2.0"; linear.reset(); diff --git a/libs/ardour/test/midi_clock_slave_test.h b/libs/ardour/test/midi_clock_slave_test.h index 4b3572a613..56bda2ba58 100644 --- a/libs/ardour/test/midi_clock_slave_test.h +++ b/libs/ardour/test/midi_clock_slave_test.h @@ -66,7 +66,7 @@ class TestSlaveSessionProxy : public ISlaveSessionProxy { framepos_t audible_frame () const { return _transport_frame; } framepos_t transport_frame () const { return _transport_frame; } pframes_t frames_since_cycle_start () const { return 0; } - framecnt_t frame_time () const { return _frame_time; } + framepos_t frame_time () const { return _frame_time; } void request_locate (framepos_t frame, bool with_roll = false) { _transport_frame = frame; diff --git a/libs/ardour/test/playlist_read_test.cc b/libs/ardour/test/playlist_read_test.cc new file mode 100644 index 0000000000..f0d450e1b0 --- /dev/null +++ b/libs/ardour/test/playlist_read_test.cc @@ -0,0 +1,206 @@ +#include "ardour/playlist.h" +#include "ardour/region.h" +#include "ardour/audioplaylist.h" +#include "ardour/audioregion.h" +#include "ardour/session.h" +#include "playlist_read_test.h" +#include "test_globals.h" + +CPPUNIT_TEST_SUITE_REGISTRATION (PlaylistReadTest); + +using namespace std; +using namespace ARDOUR; + +void +PlaylistReadTest::setUp () +{ + TestNeedingPlaylistAndRegions::setUp (); + + _N = 1024; + _buf = new Sample[_N]; + _mbuf = new Sample[_N]; + _gbuf = new float[_N]; + + _session->config.set_auto_xfade (false); + + _apl = boost::dynamic_pointer_cast (_playlist); + + for (int i = 0; i < _N; ++i) { + _buf[i] = 0; + } +} + +void +PlaylistReadTest::tearDown () +{ + delete[] _buf; + delete[] _mbuf; + delete[] _gbuf; + + _apl.reset (); + + TestNeedingPlaylistAndRegions::tearDown (); +} + +void +PlaylistReadTest::singleReadTest () +{ + /* Single-region read with fades */ + + boost::shared_ptr ar0 = boost::dynamic_pointer_cast (_region[0]); + ar0->set_name ("ar0"); + _apl->add_region (ar0, 0); + ar0->set_default_fade_in (); + ar0->set_default_fade_out (); + CPPUNIT_ASSERT_EQUAL (double (64), ar0->_fade_in->back()->when); + CPPUNIT_ASSERT_EQUAL (double (64), ar0->_fade_out->back()->when); + ar0->set_length (1024); + _apl->read (_buf, _mbuf, _gbuf, 0, 256, 0); + + for (int i = 0; i < 64; ++i) { + /* Note: this specific float casting is necessary so that the rounding + is done here the same as it is done in AudioPlaylist. + */ + CPPUNIT_ASSERT_DOUBLES_EQUAL (float (i * float (i / 63.0)), _buf[i], 1e-16); + } + + for (int i = 64; i < 256; ++i) { + CPPUNIT_ASSERT_EQUAL (i, int (_buf[i])); + } +} + +void +PlaylistReadTest::overlappingReadTest () +{ + /* Overlapping read; ar0 and ar1 are both 1024 frames long, ar0 starts at 0, + ar1 starts at 128. We test a read from 0 to 256, which should consist + of the start of ar0, with its fade in, followed by ar1's fade in (mixed with ar0 + faded out with the inverse gain), and some more of ar1. + */ + + boost::shared_ptr ar0 = boost::dynamic_pointer_cast (_region[0]); + ar0->set_name ("ar0"); + _apl->add_region (ar0, 0); + ar0->set_default_fade_in (); + ar0->set_default_fade_out (); + CPPUNIT_ASSERT_EQUAL (double (64), ar0->_fade_in->back()->when); + CPPUNIT_ASSERT_EQUAL (double (64), ar0->_fade_out->back()->when); + ar0->set_length (1024); + + boost::shared_ptr ar1 = boost::dynamic_pointer_cast (_region[1]); + ar1->set_name ("ar1"); + _apl->add_region (ar1, 128); + ar1->set_default_fade_in (); + ar1->set_default_fade_out (); + + CPPUNIT_ASSERT_EQUAL (double (64), ar1->_fade_in->back()->when); + CPPUNIT_ASSERT_EQUAL (double (64), ar1->_fade_out->back()->when); + + ar1->set_length (1024); + _apl->read (_buf, _mbuf, _gbuf, 0, 256, 0); + + /* ar0's fade in */ + for (int i = 0; i < 64; ++i) { + /* Note: this specific float casting is necessary so that the rounding + is done here the same as it is done in AudioPlaylist; the gain factor + must be computed using double precision, with the result then cast + to float. + */ + CPPUNIT_ASSERT_DOUBLES_EQUAL (float (i * float (i / (double) 63)), _buf[i], 1e-16); + } + + /* bit of ar0 */ + for (int i = 64; i < 128; ++i) { + CPPUNIT_ASSERT_EQUAL (i, int (_buf[i])); + } + + /* ar1's fade in with faded-out ar0 */ + for (int i = 0; i < 64; ++i) { + /* Similar carry-on to above with float rounding */ + float const from_ar0 = (128 + i) * float (1 - float (i / (double) 63)); + float const from_ar1 = i * float (i / (double) 63); + CPPUNIT_ASSERT_DOUBLES_EQUAL (from_ar0 + from_ar1, _buf[i + 128], 1e-16); + } +} + +void +PlaylistReadTest::transparentReadTest () +{ + boost::shared_ptr ar0 = boost::dynamic_pointer_cast (_region[0]); + ar0->set_name ("ar0"); + _apl->add_region (ar0, 0); + ar0->set_default_fade_in (); + ar0->set_default_fade_out (); + CPPUNIT_ASSERT_EQUAL (double (64), ar0->_fade_in->back()->when); + CPPUNIT_ASSERT_EQUAL (double (64), ar0->_fade_out->back()->when); + ar0->set_length (1024); + + boost::shared_ptr ar1 = boost::dynamic_pointer_cast (_region[1]); + ar1->set_name ("ar1"); + _apl->add_region (ar1, 0); + ar1->set_default_fade_in (); + ar1->set_default_fade_out (); + CPPUNIT_ASSERT_EQUAL (double (64), ar1->_fade_in->back()->when); + CPPUNIT_ASSERT_EQUAL (double (64), ar1->_fade_out->back()->when); + ar1->set_length (1024); + ar1->set_opaque (false); + + _apl->read (_buf, _mbuf, _gbuf, 0, 1024, 0); + + /* ar0 and ar1 fade-ins; ar1 is on top, so its fade in will implicitly + fade in ar0 + */ + for (int i = 0; i < 64; ++i) { + float const fade = i / (double) 63; + float const ar0 = i * fade * (1 - fade); + float const ar1 = i * fade; + CPPUNIT_ASSERT_DOUBLES_EQUAL (ar0 + ar1, _buf[i], 1e-16); + } + + /* ar0 and ar1 bodies, mixed */ + for (int i = 64; i < (1024 - 64); ++i) { + CPPUNIT_ASSERT_DOUBLES_EQUAL (float (i * 2), _buf[i], 1e-16); + } + + /* ar0 and ar1 fade-outs, mixed (with implicit fade-in of ar0) */ + for (int i = (1024 - 64); i < 1024; ++i) { + float const fade = (1023 - i) / (double) 63; + float const ar0 = i * fade * (1 - fade); + float const ar1 = i * fade; + CPPUNIT_ASSERT_DOUBLES_EQUAL (ar0 + ar1, _buf[i], 1e-16); + } +} + +/* A few tests just to check that nothing nasty is happening with + memory corruption, really (for running with valgrind). +*/ +void +PlaylistReadTest::miscReadTest () +{ + boost::shared_ptr ar0 = boost::dynamic_pointer_cast (_region[0]); + ar0->set_name ("ar0"); + _apl->add_region (ar0, 0); + ar0->set_default_fade_in (); + ar0->set_default_fade_out (); + CPPUNIT_ASSERT_EQUAL (double (64), ar0->_fade_in->back()->when); + CPPUNIT_ASSERT_EQUAL (double (64), ar0->_fade_out->back()->when); + ar0->set_length (128); + + /* Read for just longer than the region */ + _apl->read (_buf, _mbuf, _gbuf, 0, 129, 0); + + /* Read for much longer than the region */ + _apl->read (_buf, _mbuf, _gbuf, 0, 1024, 0); + + /* Read one sample */ + _apl->read (_buf, _mbuf, _gbuf, 53, 54, 0); +} + +void +PlaylistReadTest::check_staircase (Sample* b, int offset, int N) +{ + for (int i = 0; i < N; ++i) { + int const j = i + offset; + CPPUNIT_ASSERT_EQUAL (j, int (b[i])); + } +} diff --git a/libs/ardour/test/playlist_read_test.h b/libs/ardour/test/playlist_read_test.h new file mode 100644 index 0000000000..be891a5004 --- /dev/null +++ b/libs/ardour/test/playlist_read_test.h @@ -0,0 +1,30 @@ +#include "ardour/types.h" +#include "test_needing_playlist_and_regions.h" + +class PlaylistReadTest : public TestNeedingPlaylistAndRegions +{ + CPPUNIT_TEST_SUITE (PlaylistReadTest); + CPPUNIT_TEST (singleReadTest); + CPPUNIT_TEST (overlappingReadTest); + CPPUNIT_TEST (transparentReadTest); + CPPUNIT_TEST (miscReadTest); + CPPUNIT_TEST_SUITE_END (); + +public: + void setUp (); + void tearDown (); + + void singleReadTest (); + void overlappingReadTest (); + void transparentReadTest (); + void miscReadTest (); + +private: + int _N; + ARDOUR::Sample* _buf; + ARDOUR::Sample* _mbuf; + float* _gbuf; + boost::shared_ptr _apl; + + void check_staircase (ARDOUR::Sample *, int, int); +}; diff --git a/libs/ardour/test/tempo_test.cc b/libs/ardour/test/tempo_test.cc index 845116470e..fa9ad0a520 100644 --- a/libs/ardour/test/tempo_test.cc +++ b/libs/ardour/test/tempo_test.cc @@ -41,15 +41,6 @@ TempoTest::recomputeMapTest () Meter meterB (3, 4); map.add_meter (meterB, BBT_Time (4, 1, 0)); - cout << "\n\n\n"; - for (list::iterator i = map.metrics.begin(); i != map.metrics.end(); ++i) { - if (dynamic_cast (*i)) { - cout << "\tTempo MS @ " << (*i)->start() << " " << (*i)->frame() << "\n"; - } else { - cout << "\tMeter MS @ " << (*i)->start() << " " << (*i)->frame() << "\n"; - } - } - list::iterator i = map.metrics.begin(); CPPUNIT_ASSERT_EQUAL (framepos_t (0), (*i)->frame ()); diff --git a/libs/ardour/test/test_globals.cc b/libs/ardour/test/test_globals.cc new file mode 100644 index 0000000000..ff00fbc2e5 --- /dev/null +++ b/libs/ardour/test/test_globals.cc @@ -0,0 +1,4 @@ +#include "test_globals.h" + +int const Fs = 44100; +int const sinusoid_frequency = 440; diff --git a/libs/ardour/test/test_globals.h b/libs/ardour/test/test_globals.h new file mode 100644 index 0000000000..8595e0c9b1 --- /dev/null +++ b/libs/ardour/test/test_globals.h @@ -0,0 +1,3 @@ + +extern int const Fs; +extern int const sinusoid_frequency; diff --git a/libs/ardour/test/test_needing_playlist_and_regions.cc b/libs/ardour/test/test_needing_playlist_and_regions.cc index fea08cc4d9..4e0e42c67b 100644 --- a/libs/ardour/test/test_needing_playlist_and_regions.cc +++ b/libs/ardour/test/test_needing_playlist_and_regions.cc @@ -3,7 +3,9 @@ #include "ardour/source_factory.h" #include "ardour/region.h" #include "ardour/region_factory.h" +#include "ardour/sndfilesource.h" #include "test_needing_playlist_and_regions.h" +#include "test_globals.h" using namespace std; using namespace PBD; @@ -14,9 +16,25 @@ TestNeedingPlaylistAndRegions::setUp () { TestNeedingSession::setUp (); + /* This is important, otherwise createWritable will mark the source immutable (hence unwritable) */ + unlink ("libs/ardour/test/test.wav"); string const test_wav_path = "libs/ardour/test/test.wav"; _playlist = PlaylistFactory::create (DataType::AUDIO, *_session, "test"); - _source = SourceFactory::createWritable (DataType::AUDIO, *_session, test_wav_path, "", false, 44100); + _source = SourceFactory::createWritable (DataType::AUDIO, *_session, test_wav_path, "", false, Fs); + + /* Write a staircase to the source */ + + boost::shared_ptr s = boost::dynamic_pointer_cast (_source); + assert (s); + + int const signal_length = 4096; + + Sample staircase[signal_length]; + for (int i = 0; i < signal_length; ++i) { + staircase[i] = i; + } + + s->write (staircase, signal_length); PropertyList plist; plist.add (Properties::start, 0); diff --git a/libs/ardour/ticker.cc b/libs/ardour/ticker.cc index 734b401356..5d078952a1 100644 --- a/libs/ardour/ticker.cc +++ b/libs/ardour/ticker.cc @@ -20,6 +20,7 @@ #include "pbd/stacktrace.h" #include "midi++/port.h" +#include "midi++/jack_midi_port.h" #include "midi++/manager.h" #include "evoral/midi_events.h" @@ -152,13 +153,13 @@ void MidiClockTicker::tick (const framepos_t& transport_frame) double next_tick = _last_tick + one_ppqn_in_frames (transport_frame); frameoffset_t next_tick_offset = llrint (next_tick) - transport_frame; + MIDI::JackMIDIPort* mp = dynamic_cast (_midi_port); + DEBUG_TRACE (PBD::DEBUG::MidiClock, string_compose ("Transport: %1, last tick time: %2, next tick time: %3, offset: %4, cycle length: %5\n", - transport_frame, _last_tick, next_tick, next_tick_offset, _midi_port->nframes_this_cycle() - ) - ); + transport_frame, _last_tick, next_tick, next_tick_offset, mp ? mp->nframes_this_cycle() : 0)); - if (next_tick_offset >= _midi_port->nframes_this_cycle()) { + if (!mp || (next_tick_offset >= mp->nframes_this_cycle())) { break; } diff --git a/libs/ardour/track.cc b/libs/ardour/track.cc index 5b6cb63091..c2750ccf98 100644 --- a/libs/ardour/track.cc +++ b/libs/ardour/track.cc @@ -44,7 +44,6 @@ Track::Track (Session& sess, string name, Route::Flag flag, TrackMode mode, Data , _saved_meter_point (_meter_point) , _mode (mode) , _monitoring (MonitorAuto) - , _rec_enable_control (new RecEnableControllable(*this)) { _freeze_record.state = NoFreeze; _declickable = true; @@ -64,6 +63,15 @@ Track::init () return -1; } + boost::shared_ptr rp (shared_from_this()); + boost::shared_ptr rt = boost::dynamic_pointer_cast (rp); + _rec_enable_control = boost::shared_ptr (new RecEnableControl(rt)); + _rec_enable_control->set_flags (Controllable::Toggle); + + /* don't add rec_enable_control to controls because we don't want it to + * appear as an automatable parameter + */ + return 0; } @@ -195,23 +203,34 @@ Track::freeze_state() const return _freeze_record.state; } -Track::RecEnableControllable::RecEnableControllable (Track& s) - : Controllable (X_("recenable"), Controllable::Toggle), track (s) +Track::RecEnableControl::RecEnableControl (boost::shared_ptr t) + : AutomationControl (t->session(), RecEnableAutomation, boost::shared_ptr(), X_("recenable")) + , track (t) { + boost::shared_ptr gl(new AutomationList(Evoral::Parameter(RecEnableAutomation))); + set_list (gl); } void -Track::RecEnableControllable::set_value (double val) +Track::RecEnableControl::set_value (double val) { - bool bval = ((val >= 0.5) ? true: false); - track.set_record_enabled (bval, this); + boost::shared_ptr t = track.lock (); + if (!t) { + return; + } + + t->set_record_enabled (val >= 0.5 ? true : false, this); } double -Track::RecEnableControllable::get_value (void) const +Track::RecEnableControl::get_value () const { - if (track.record_enabled()) { return 1.0; } - return 0.0; + boost::shared_ptr t = track.lock (); + if (!t) { + return 0; + } + + return (t->record_enabled() ? 1.0 : 0.0); } bool @@ -406,7 +425,7 @@ Track::no_roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { boost::shared_ptr d = boost::dynamic_pointer_cast (*i); if (d) { - d->flush_buffers (nframes, end_frame - start_frame - 1); + d->flush_buffers (nframes); } } @@ -568,13 +587,13 @@ Track::hidden () const } int -Track::can_internal_playback_seek (framepos_t p) +Track::can_internal_playback_seek (framecnt_t p) { return _diskstream->can_internal_playback_seek (p); } int -Track::internal_playback_seek (framepos_t p) +Track::internal_playback_seek (framecnt_t p) { return _diskstream->internal_playback_seek (p); } @@ -850,7 +869,7 @@ Track::maybe_declick (BufferSet& bufs, framecnt_t nframes, int declick) } framecnt_t -Track::check_initial_delay (framecnt_t nframes, framecnt_t& transport_frame) +Track::check_initial_delay (framecnt_t nframes, framepos_t& transport_frame) { if (_roll_delay > nframes) { diff --git a/libs/ardour/utils.cc b/libs/ardour/utils.cc index 4933cd7e5c..beb003e713 100644 --- a/libs/ardour/utils.cc +++ b/libs/ardour/utils.cc @@ -627,26 +627,6 @@ bool_as_string (bool yn) return (yn ? "yes" : "no"); } -bool -string_is_affirmative (const std::string& str) -{ - /* to be used only with XML data - not intended to handle user input */ - - if (str.empty ()) { - return false; - } - - /* the use of g_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())); -} - const char* native_header_format_extension (HeaderFormat hf, const DataType& type) { diff --git a/libs/ardour/worker.cc b/libs/ardour/worker.cc new file mode 100644 index 0000000000..c108f653c4 --- /dev/null +++ b/libs/ardour/worker.cc @@ -0,0 +1,115 @@ +/* + Copyright (C) 2012 Paul Davis + Author: David Robillard + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include + +#include "ardour/worker.h" +#include "pbd/error.h" + +namespace ARDOUR { + +Worker::Worker(Workee* workee, uint32_t ring_size) + : _workee(workee) + , _thread(Glib::Thread::create(sigc::mem_fun(*this, &Worker::run), true)) + , _requests(new RingBuffer(ring_size)) + , _responses(new RingBuffer(ring_size)) + , _response((uint8_t*)malloc(ring_size)) + , _sem(0) + , _exit(false) +{} + +Worker::~Worker() +{ + _exit = true; + _sem.post(); + _thread->join(); +} + +bool +Worker::schedule(uint32_t size, const void* data) +{ + if (_requests->write((const uint8_t*)&size, sizeof(size)) != sizeof(size)) { + return false; + } + if (_requests->write((const uint8_t*)data, size) != size) { + return false; // FIXME: corruption + } + _sem.post(); + return true; +} + +bool +Worker::respond(uint32_t size, const void* data) +{ + if (_responses->write((const uint8_t*)&size, sizeof(size)) != sizeof(size)) { + return false; + } + if (_responses->write((const uint8_t*)data, size) != size) { + return false; // FIXME: corruption + } + return true; +} + +void +Worker::emit_responses() +{ + uint32_t read_space = _responses->read_space(); + uint32_t size = 0; + while (read_space > sizeof(size)) { + _responses->read((uint8_t*)&size, sizeof(size)); + _responses->read(_response, size); + _workee->work_response(size, _response); + read_space -= sizeof(size) + size; + } +} + +void +Worker::run() +{ + void* buf = NULL; + size_t buf_size = 0; + while (true) { + _sem.wait(); + if (_exit) { + return; + } + + uint32_t size = 0; + if (_requests->read((uint8_t*)&size, sizeof(size)) < sizeof(size)) { + PBD::error << "Worker: Error reading size from request ring" + << endmsg; + continue; + } + + if (size > buf_size) { + buf = realloc(buf, size); + buf_size = size; + } + + if (_requests->read((uint8_t*)buf, size) < size) { + PBD::error << "Worker: Error reading body from request ring" + << endmsg; + continue; // TODO: This is probably fatal + } + + _workee->work(size, buf); + } +} + +} // namespace ARDOUR diff --git a/libs/ardour/wscript b/libs/ardour/wscript index f38e07667a..c493f830e7 100644 --- a/libs/ardour/wscript +++ b/libs/ardour/wscript @@ -18,7 +18,7 @@ LIBARDOUR_VERSION = "%s.%s.%s" % (MAJOR, MINOR, MICRO) LIBARDOUR_LIB_VERSION = '3.0.0' # default state file version for this build -CURRENT_SESSION_FILE_VERSION = 3000 +CURRENT_SESSION_FILE_VERSION = 3001 # Variables for 'waf dist' APPNAME = 'libardour3' @@ -70,8 +70,6 @@ libardour_sources = [ 'config_text.cc', 'control_protocol_manager.cc', 'control_protocol_search_path.cc', - 'crossfade.cc', - 'crossfade_binder.cc', 'cycle_timer.cc', 'data_type.cc', 'default_click.cc', @@ -219,6 +217,7 @@ libardour_sources = [ 'user_bundle.cc', 'utils.cc', 'version.cc', + 'worker.cc', ] def flac_supported(): @@ -259,12 +258,10 @@ def configure(conf): atleast_version='2.0') if Options.options.lv2: autowaf.check_pkg(conf, 'lilv-0', uselib_store='LILV', - atleast_version='0.0.0', mandatory=False) - autowaf.check_pkg(conf, 'lilv-0', uselib_store='NEW_LILV', - atleast_version='0.11.0', mandatory=False) + atleast_version='0.14.0', mandatory=False) if conf.is_defined('HAVE_LILV'): autowaf.check_pkg(conf, 'suil-0', uselib_store='SUIL', - atleast_version='0.2.0', mandatory=False) + atleast_version='0.6.0', mandatory=False) # autowaf.check_pkg(conf, 'soundtouch-1.0', uselib_store='SOUNDTOUCH', # mandatory=False) @@ -406,8 +403,7 @@ def build(bld): obj.source += [ 'audio_unit.cc' ] if Options.options.fpu_optimization: - if (bld.env['build_target'] == 'i386' - or bld.env['build_target'] == 'i686'): + if (bld.env['build_target'] == 'i386' or bld.env['build_target'] == 'i686'): obj.source += [ 'sse_functions_xmm.cc', 'sse_functions.s' ] elif bld.env['build_target'] == 'x86_64': obj.source += [ 'sse_functions_xmm.cc', 'sse_functions_64bit.s' ] @@ -428,6 +424,8 @@ def build(bld): test/dummy_lxvst.cc test/test_needing_session.cc test/test_needing_playlist_and_regions.cc + test/test_globals.cc + test/audio_region_test.cc test/bbt_test.cc test/tempo_test.cc test/interpolation_test.cc @@ -437,6 +435,7 @@ def build(bld): test/framepos_plus_beats_test.cc test/framepos_minus_beats_test.cc test/playlist_layering_test.cc + test/playlist_read_test.cc test/testrunner.cc '''.split() diff --git a/libs/clearlooks-newer/cairo-support.c b/libs/clearlooks-newer/cairo-support.c index 63245f4833..fa195776a2 100644 --- a/libs/clearlooks-newer/cairo-support.c +++ b/libs/clearlooks-newer/cairo-support.c @@ -491,7 +491,7 @@ void ge_cairo_polygon (cairo_t *cr, } } - if ((points[npoints-1].x != points[0].y) || + if ((points[npoints-1].x != points[0].x) || (points[npoints-1].y != points[0].y)) { cairo_line_to(cr, points[0].x, points[0].y); diff --git a/libs/evoral/evoral/ControlList.hpp b/libs/evoral/evoral/ControlList.hpp index 286974676f..30b9fca430 100644 --- a/libs/evoral/evoral/ControlList.hpp +++ b/libs/evoral/evoral/ControlList.hpp @@ -26,6 +26,7 @@ #include #include "pbd/signals.h" #include "evoral/types.hpp" +#include "evoral/Range.hpp" #include "evoral/Parameter.hpp" namespace Evoral { @@ -122,7 +123,6 @@ public: void fast_simple_add (double when, double value); void merge_nascent (double when); - void reset_range (double start, double end); void erase_range (double start, double end); void erase (iterator); void erase (iterator, iterator); @@ -165,9 +165,6 @@ public: (obj.*method)(*this); } - void set_max_xval (double); - double get_max_xval() const { return _max_xval; } - double eval (double where) { Glib::Mutex::Lock lm (_lock); return unlocked_eval (where); @@ -249,6 +246,9 @@ public: /** Emitted when our interpolation style changes */ PBD::Signal1 InterpolationChanged; + static void set_thinning_factor (double d); + static double thinning_factor() { return _thinning_factor; } + protected: /** Called by unlocked_eval() to handle cases of 3 or more control points. */ @@ -272,7 +272,6 @@ protected: mutable Glib::Mutex _lock; int8_t _frozen; bool _changed_when_thawed; - double _max_xval; double _min_yval; double _max_yval; double _default_value; @@ -292,6 +291,7 @@ protected: }; std::list nascent; + static double _thinning_factor; }; } // namespace Evoral diff --git a/libs/evoral/evoral/Range.hpp b/libs/evoral/evoral/Range.hpp new file mode 100644 index 0000000000..bc353a47d7 --- /dev/null +++ b/libs/evoral/evoral/Range.hpp @@ -0,0 +1,219 @@ +/* This file is part of Evoral. + * Copyright (C) 2008 David Robillard + * Copyright (C) 2000-2008 Paul Davis + * + * Evoral is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Evoral is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef EVORAL_RANGE_HPP +#define EVORAL_RANGE_HPP + +#include + +namespace Evoral { + +enum OverlapType { + OverlapNone, // no overlap + OverlapInternal, // the overlap is 100% with the object + OverlapStart, // overlap covers start, but ends within + OverlapEnd, // overlap begins within and covers end + OverlapExternal // overlap extends to (at least) begin+end +}; + +template +OverlapType coverage (T sa, T ea, T sb, T eb) { + /* OverlapType returned reflects how the second (B) + range overlaps the first (A). + + The diagrams show various relative placements + of A and B for each OverlapType. + + Notes: + Internal: the start points cannot coincide + External: the start and end points can coincide + Start: end points can coincide + End: start points can coincide + + XXX Logically, Internal should disallow end + point equality. + */ + + /* + |--------------------| A + |------| B + |-----------------| B + + + "B is internal to A" + + */ + + if ((sb > sa) && (eb <= ea)) { + return OverlapInternal; + } + + /* + |--------------------| A + ----| B + -----------------------| B + --| B + + "B overlaps the start of A" + + */ + + if ((eb >= sa) && (eb <= ea)) { + return OverlapStart; + } + /* + |---------------------| A + |----------------- B + |----------------------- B + |- B + + "B overlaps the end of A" + + */ + if ((sb > sa) && (sb <= ea)) { + return OverlapEnd; + } + /* + |--------------------| A + -------------------------- B + |----------------------- B + ----------------------| B + |--------------------| B + + + "B overlaps all of A" + */ + if ((sa >= sb) && (sa <= eb) && (ea <= eb)) { + return OverlapExternal; + } + + return OverlapNone; +} + +/** Type to describe a time range */ +template +struct Range { + Range (T f, T t) : from (f), to (t) {} + T from; ///< start of the range + T to; ///< end of the range +}; + +template +bool operator== (Range a, Range b) { + return a.from == b.from && a.to == b.to; +} + +template +class RangeList { +public: + RangeList () : _dirty (false) {} + + typedef std::list > List; + + List const & get () { + coalesce (); + return _list; + } + + void add (Range const & range) { + _dirty = true; + _list.push_back (range); + } + + bool empty () const { + return _list.empty (); + } + +private: + void coalesce () { + if (!_dirty) { + return; + } + + restart: + for (typename List::iterator i = _list.begin(); i != _list.end(); ++i) { + for (typename List::iterator j = _list.begin(); j != _list.end(); ++j) { + + if (i == j) { + continue; + } + + if (coverage (i->from, i->to, j->from, j->to) != OverlapNone) { + i->from = std::min (i->from, j->from); + i->to = std::max (i->to, j->to); + _list.erase (j); + goto restart; + } + } + } + + _dirty = false; + } + + List _list; + bool _dirty; +}; + +/** Type to describe the movement of a time range */ +template +struct RangeMove { + RangeMove (T f, double l, T t) : from (f), length (l), to (t) {} + T from; ///< start of the range + double length; ///< length of the range + T to; ///< new start of the range +}; + +template +RangeList subtract (Range range, RangeList sub) +{ + RangeList result; + + if (sub.empty ()) { + result.add (range); + return result; + } + + T x = range.from; + + typename RangeList::List s = sub.get (); + + for (typename RangeList::List::const_iterator i = s.begin(); i != s.end(); ++i) { + + if (coverage (range.from, range.to, i->from, i->to) == OverlapNone) { + continue; + } + + Range clamped (std::max (range.from, i->from), std::min (range.to, i->to)); + + if (clamped.from != x) { + result.add (Range (x, clamped.from - 1)); + } + + x = clamped.to; + } + + if (s.back().to < range.to) { + result.add (Range (x, range.to)); + } + + return result; +} + +} + +#endif diff --git a/libs/evoral/evoral/types.hpp b/libs/evoral/evoral/types.hpp index c3cb6a9c21..35dec6de0b 100644 --- a/libs/evoral/evoral/types.hpp +++ b/libs/evoral/evoral/types.hpp @@ -46,23 +46,6 @@ static inline bool musical_time_equal (MusicalTime a, MusicalTime b) { /** Type of an event (opaque, mapped by application) */ typedef uint32_t EventType; -/** Type to describe a time range */ -template -struct Range { - Range (T f, T t) : from (f), to (t) {} - T from; ///< start of the range - T to; ///< end of the range -}; - -/** Type to describe the movement of a time range */ -template -struct RangeMove { - RangeMove (T f, double l, T t) : from (f), length (l), to (t) {} - T from; ///< start of the range - double length; ///< length of the range - T to; ///< new start of the range -}; - } // namespace Evoral namespace PBD { diff --git a/libs/evoral/run-tests.sh b/libs/evoral/run-tests.sh index 35531a613e..8eb7ba8820 100755 --- a/libs/evoral/run-tests.sh +++ b/libs/evoral/run-tests.sh @@ -1,14 +1,14 @@ #!/bin/sh srcdir=`pwd` -export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$srcdir/../../build/default/libs/evoral:$srcdir/../../build/default/libs/pbd +export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$srcdir/../../build/libs/evoral:$srcdir/../../build/libs/pbd if [ ! -f './test/testdata/TakeFive.mid' ]; then echo "This script must be run from within the libs/evoral directory"; exit 1; fi # Make symlink to TakeFive.mid in build directory -cd ../../build/default/libs/evoral +cd ../../build/libs/evoral mkdir -p ./test/testdata ln -fs $srcdir/test/testdata/TakeFive.mid \ ./test/testdata/TakeFive.mid diff --git a/libs/evoral/src/ControlList.cpp b/libs/evoral/src/ControlList.cpp index c31bd3421d..9c38f67b29 100644 --- a/libs/evoral/src/ControlList.cpp +++ b/libs/evoral/src/ControlList.cpp @@ -33,6 +33,21 @@ inline bool event_time_less_than (ControlEvent* a, ControlEvent* b) return a->when < b->when; } +/* this has no units but corresponds to the area of a rectangle + computed between three points in the list. If the area is + large, it indicates significant non-linearity between the + points. + + during automation recording we thin the recorded points + using this value. if a point is sufficiently co-linear + with its neighbours (as defined by the area of the rectangle + formed by three of them), we will not include it in the + ControlList. a smaller value will exclude less points, + a larger value will exclude more points, so it effectively + measures the amount of thinning to be done. +*/ + +double ControlList::_thinning_factor = 20.0; ControlList::ControlList (const Parameter& id) : _parameter(id) @@ -43,7 +58,6 @@ ControlList::ControlList (const Parameter& id) _changed_when_thawed = false; _min_yval = id.min(); _max_yval = id.max(); - _max_xval = 0; // means "no limit" _default_value = 0; _lookup_cache.left = -1; _lookup_cache.range.first = _events.end(); @@ -61,7 +75,6 @@ ControlList::ControlList (const ControlList& other) _changed_when_thawed = false; _min_yval = other._min_yval; _max_yval = other._max_yval; - _max_xval = other._max_xval; _default_value = other._default_value; _lookup_cache.range.first = _events.end(); _search_cache.first = _events.end(); @@ -83,7 +96,6 @@ ControlList::ControlList (const ControlList& other, double start, double end) _changed_when_thawed = false; _min_yval = other._min_yval; _max_yval = other._max_yval; - _max_xval = other._max_xval; _default_value = other._default_value; _lookup_cache.range.first = _events.end(); _search_cache.first = _events.end(); @@ -143,7 +155,6 @@ ControlList::operator= (const ControlList& other) _min_yval = other._min_yval; _max_yval = other._max_yval; - _max_xval = other._max_xval; _default_value = other._default_value; mark_dirty (); @@ -411,9 +422,9 @@ ControlList::thin () { Glib::Mutex::Lock lm (_lock); - ControlEvent* prevprev; - ControlEvent* cur; - ControlEvent* prev; + ControlEvent* prevprev = 0; + ControlEvent* cur = 0; + ControlEvent* prev = 0; iterator pprev; int counter = 0; @@ -424,17 +435,11 @@ ControlList::thin () if (counter > 2) { - double area = fabs (0.5 * - (prevprev->when * (prev->value - cur->value)) + + double area = fabs ((prevprev->when * (prev->value - cur->value)) + (prev->when * (cur->value - prevprev->value)) + (cur->when * (prevprev->value - prev->value))); - /* the number 10.0 is an arbitrary value that needs to - * be controlled by some user-controllable - * configuration utility. - */ - - if (area < 10.0) { + if (area < _thinning_factor) { iterator tmp = pprev; /* pprev will change to current @@ -460,6 +465,8 @@ ControlList::fast_simple_add (double when, double value) /* to be used only for loading pre-sorted data from saved state */ _events.insert (_events.end(), new ControlEvent (when, value)); assert(_events.back()); + + mark_dirty (); } void @@ -550,37 +557,6 @@ ControlList::erase (double when, double value) maybe_signal_changed (); } -void -ControlList::reset_range (double start, double endt) -{ - bool reset = false; - - { - Glib::Mutex::Lock lm (_lock); - ControlEvent cp (start, 0.0f); - iterator s; - iterator e; - - if ((s = lower_bound (_events.begin(), _events.end(), &cp, time_comparator)) != _events.end()) { - - cp.when = endt; - e = upper_bound (_events.begin(), _events.end(), &cp, time_comparator); - - for (iterator i = s; i != e; ++i) { - (*i)->value = _default_value; - } - - reset = true; - - mark_dirty (); - } - } - - if (reset) { - maybe_signal_changed (); - } -} - void ControlList::erase_range (double start, double endt) { @@ -674,7 +650,7 @@ ControlList::modify (iterator iter, double when, double val) (*iter)->when = when; (*iter)->value = val; - if (isnan (val)) { + if (std::isnan (val)) { abort (); } @@ -723,12 +699,6 @@ ControlList::control_points_adjacent (double xval) return ret; } -void -ControlList::set_max_xval (double x) -{ - _max_xval = x; -} - void ControlList::freeze () { @@ -1550,5 +1520,11 @@ ControlList::set_interpolation (InterpolationStyle s) InterpolationChanged (s); /* EMIT SIGNAL */ } +void +ControlList::set_thinning_factor (double v) +{ + _thinning_factor = v; +} + } // namespace Evoral diff --git a/libs/evoral/src/Curve.cpp b/libs/evoral/src/Curve.cpp index dd327d488a..61f00198c1 100644 --- a/libs/evoral/src/Curve.cpp +++ b/libs/evoral/src/Curve.cpp @@ -190,7 +190,7 @@ Curve::get_vector (double x0, double x1, float *vec, int32_t veclen) void Curve::_get_vector (double x0, double x1, float *vec, int32_t veclen) { - double rx, dx, lx, hx, max_x, min_x; + double rx, lx, hx, max_x, min_x; int32_t i; int32_t original_veclen; int32_t npoints; @@ -276,26 +276,34 @@ Curve::_get_vector (double x0, double x1, float *vec, int32_t veclen) /* linear interpolation between 2 points */ - /* XXX I'm not sure that this is the right thing to - do here. but its not a common case for the envisaged - uses. + /* XXX: this numerator / denominator stuff is pretty grim, but it's the only + way I could get the maths to be accurate; doing everything with pure doubles + gives ~1e-17 errors in the vec[i] computation. */ + /* gradient of the line */ + double const m_num = _list.events().back()->value - _list.events().front()->value; + double const m_den = _list.events().back()->when - _list.events().front()->when; + + /* y intercept of the line */ + double const c = double (_list.events().back()->value) - (m_num * _list.events().back()->when / m_den); + + /* dx that we are using */ + double dx_num = 0; + double dx_den = 1; if (veclen > 1) { - dx = (hx - lx) / (veclen - 1) ; + dx_num = hx - lx; + dx_den = veclen - 1; + } + + if (veclen > 1) { + for (int i = 0; i < veclen; ++i) { + vec[i] = (lx * (m_num / m_den) + m_num * i * dx_num / (m_den * dx_den)) + c; + } } else { - dx = 0; // not used + vec[0] = lx; } - double slope = (_list.events().back()->value - _list.events().front()->value)/ - (_list.events().back()->when - _list.events().front()->when); - double yfrac = dx*slope; - - vec[0] = _list.events().front()->value + slope * (lx - _list.events().front()->when); - - for (i = 1; i < veclen; ++i) { - vec[i] = vec[i-1] + yfrac; - } return; } @@ -305,10 +313,9 @@ Curve::_get_vector (double x0, double x1, float *vec, int32_t veclen) rx = lx; + double dx = 0; if (veclen > 1) { dx = (hx - lx) / (veclen - 1); - } else { - dx = 0; } for (i = 0; i < veclen; ++i, rx += dx) { diff --git a/libs/evoral/test/RangeTest.cpp b/libs/evoral/test/RangeTest.cpp new file mode 100644 index 0000000000..ff9856a9b6 --- /dev/null +++ b/libs/evoral/test/RangeTest.cpp @@ -0,0 +1,84 @@ +#include "RangeTest.hpp" +#include "evoral/Range.hpp" + +CPPUNIT_TEST_SUITE_REGISTRATION (RangeTest); + +using namespace Evoral; + +void +RangeTest::coalesceTest () +{ + RangeList fred; + fred.add (Range (2, 4)); + fred.add (Range (5, 6)); + fred.add (Range (6, 8)); + + RangeList::List jim = fred.get (); + + RangeList::List::iterator i = jim.begin (); + CPPUNIT_ASSERT_EQUAL (2, i->from); + CPPUNIT_ASSERT_EQUAL (4, i->to); + + ++i; + CPPUNIT_ASSERT_EQUAL (5, i->from); + CPPUNIT_ASSERT_EQUAL (8, i->to); +} + +void +RangeTest::subtractTest1 () +{ + Range fred (0, 10); + + RangeList jim; + jim.add (Range (2, 4)); + jim.add (Range (7, 8)); + + RangeList sheila = subtract (fred, jim); + + RangeList::List s = sheila.get (); + CPPUNIT_ASSERT_EQUAL (size_t (3), s.size ()); + + RangeList::List::iterator i = s.begin (); + CPPUNIT_ASSERT_EQUAL (0, i->from); + CPPUNIT_ASSERT_EQUAL (1, i->to); + + ++i; + CPPUNIT_ASSERT_EQUAL (4, i->from); + CPPUNIT_ASSERT_EQUAL (6, i->to); + + ++i; + CPPUNIT_ASSERT_EQUAL (8, i->from); + CPPUNIT_ASSERT_EQUAL (10, i->to); +} + +void +RangeTest::subtractTest2 () +{ + Range fred (0, 10); + + RangeList jim; + jim.add (Range (12, 19)); + + RangeList sheila = subtract (fred, jim); + + RangeList::List s = sheila.get (); + CPPUNIT_ASSERT_EQUAL (size_t (1), s.size ()); + + RangeList::List::iterator i = s.begin (); + CPPUNIT_ASSERT_EQUAL (0, i->from); + CPPUNIT_ASSERT_EQUAL (10, i->to); +} + +void +RangeTest::subtractTest3 () +{ + Range fred (0, 10); + + RangeList jim; + jim.add (Range (0, 12)); + + RangeList sheila = subtract (fred, jim); + + RangeList::List s = sheila.get (); + CPPUNIT_ASSERT_EQUAL (size_t (0), s.size ()); +} diff --git a/libs/evoral/test/RangeTest.hpp b/libs/evoral/test/RangeTest.hpp new file mode 100644 index 0000000000..6b65ad02d2 --- /dev/null +++ b/libs/evoral/test/RangeTest.hpp @@ -0,0 +1,19 @@ +#include +#include + +class RangeTest : public CppUnit::TestFixture +{ + CPPUNIT_TEST_SUITE (RangeTest); + CPPUNIT_TEST (coalesceTest); + CPPUNIT_TEST (subtractTest1); + CPPUNIT_TEST (subtractTest3); + CPPUNIT_TEST_SUITE_END (); + +public: + void coalesceTest (); + void subtractTest1 (); + void subtractTest2 (); + void subtractTest3 (); +}; + + diff --git a/libs/evoral/wscript b/libs/evoral/wscript index e8bf097db2..9cc0356c7f 100644 --- a/libs/evoral/wscript +++ b/libs/evoral/wscript @@ -124,6 +124,7 @@ def build(bld): obj.source = ''' test/SequenceTest.cpp test/SMFTest.cpp + test/RangeTest.cpp test/testrunner.cpp ''' obj.includes = ['.', './src'] diff --git a/libs/gtkmm2ext/actions.cc b/libs/gtkmm2ext/actions.cc index 02e99f23e9..74533f96a4 100644 --- a/libs/gtkmm2ext/actions.cc +++ b/libs/gtkmm2ext/actions.cc @@ -246,14 +246,35 @@ ActionManager::get_widget (const char * name) RefPtr ActionManager::get_action (const char* path) { - GtkAction* _act; - RefPtr act; - - if ((_act = gtk_ui_manager_get_action (ui_manager->gobj(), path)) != 0) { - return Glib::wrap (_act, true); + if (!path) { + return RefPtr(); } - return act; + /* Skip / in path */ + + int len = strlen (path); + + if (len < 3) { + /* shortest possible path: "a/b" */ + return RefPtr(); + } + + if (len > 10 && !strncmp (path, "/", 10 )) { + path = path+10; + } else if (path[0] == '/') { + path++; + } + + char copy[len+1]; + strcpy (copy, path); + char* slash = strchr (copy, '/'); + if (!slash) { + return RefPtr (); + } + *slash = '\0'; + + return get_action (copy, ++slash); + } RefPtr @@ -289,6 +310,32 @@ ActionManager::get_action (const char* group_name, const char* action_name) return act; } +RefPtr +ActionManager::get_action_from_name (const char* name) +{ + /* the C++ API for functions used here appears to be broken in + gtkmm2.6, so we fall back to the C level. + */ + + GList* list = gtk_ui_manager_get_action_groups (ui_manager->gobj()); + GList* node; + GList* acts; + + for (node = list; node; node = g_list_next (node)) { + + GtkActionGroup* group = (GtkActionGroup*) node->data; + + for (acts = gtk_action_group_list_actions (group); acts; acts = g_list_next (acts)) { + GtkAction* action = (GtkAction*) acts->data; + if (!strcmp (gtk_action_get_name (action), name)) { + return Glib::wrap (action, true); + } + } + } + + return RefPtr(); +} + void ActionManager::set_sensitive (vector >& actions, bool state) { diff --git a/libs/gtkmm2ext/gtk_ui.cc b/libs/gtkmm2ext/gtk_ui.cc index 30bb011efe..255b18b2fb 100644 --- a/libs/gtkmm2ext/gtk_ui.cc +++ b/libs/gtkmm2ext/gtk_ui.cc @@ -104,7 +104,7 @@ UI::UI (string namestr, int *argc, char ***argv) errors->set_title (title.get_string()); errors->dismiss_button().set_name ("ErrorLogCloseButton"); - errors->signal_delete_event().connect (bind (sigc::ptr_fun (just_hide_it), (Window *) errors)); + errors->signal_delete_event().connect (sigc::bind (sigc::ptr_fun (just_hide_it), (Window *) errors)); errors->set_type_hint (Gdk::WINDOW_TYPE_HINT_UTILITY); //load_rcfile (rcfile); diff --git a/libs/gtkmm2ext/gtkmm2ext/actions.h b/libs/gtkmm2ext/gtkmm2ext/actions.h index 0c53e9c10d..e35ee1f442 100644 --- a/libs/gtkmm2ext/gtkmm2ext/actions.h +++ b/libs/gtkmm2ext/gtkmm2ext/actions.h @@ -43,6 +43,7 @@ namespace ActionManager { extern Gtk::Widget* get_widget (const char * name); extern Glib::RefPtr get_action (const char* group, const char* name); extern Glib::RefPtr get_action (const char* path); + extern Glib::RefPtr get_action_from_name (const char* name); extern void do_action (const char* group, const char* name); extern void set_toggle_action (const char* group, const char* name, bool); diff --git a/libs/gtkmm2ext/gtkmm2ext/dndvbox.h b/libs/gtkmm2ext/gtkmm2ext/dndvbox.h index 2f09779e71..bc5ca6c725 100644 --- a/libs/gtkmm2ext/gtkmm2ext/dndvbox.h +++ b/libs/gtkmm2ext/gtkmm2ext/dndvbox.h @@ -57,8 +57,8 @@ public: Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK ); - signal_button_press_event().connect (bind (mem_fun (*this, &DnDVBox::button_press), (T *) 0)); - signal_button_release_event().connect (bind (mem_fun (*this, &DnDVBox::button_release), (T *) 0)); + signal_button_press_event().connect (sigc::bind (mem_fun (*this, &DnDVBox::button_press), (T *) 0)); + signal_button_release_event().connect (sigc::bind (mem_fun (*this, &DnDVBox::button_release), (T *) 0)); signal_drag_motion().connect (mem_fun (*this, &DnDVBox::drag_motion)); signal_drag_leave().connect (mem_fun (*this, &DnDVBox::drag_leave)); @@ -571,8 +571,9 @@ private: { typename std::list::iterator x = find (_selection.begin(), _selection.end(), child); if (x != _selection.end()) { + T* c = *x; _selection.erase (x); - setup_child_state (*x); + setup_child_state (c); } } diff --git a/libs/gtkmm2ext/gtkmm2ext/keyboard.h b/libs/gtkmm2ext/gtkmm2ext/keyboard.h index db8100c081..9b083317e3 100644 --- a/libs/gtkmm2ext/gtkmm2ext/keyboard.h +++ b/libs/gtkmm2ext/gtkmm2ext/keyboard.h @@ -138,6 +138,8 @@ class Keyboard : public sigc::trackable, PBD::Stateful static void magic_widget_grab_focus (); static void magic_widget_drop_focus (); + static void close_current_dialog (); + static void keybindings_changed (); static void save_keybindings (); static bool load_keybindings (std::string path); diff --git a/libs/gtkmm2ext/keyboard.cc b/libs/gtkmm2ext/keyboard.cc index 0a3a1f7adc..e8f59af8c9 100644 --- a/libs/gtkmm2ext/keyboard.cc +++ b/libs/gtkmm2ext/keyboard.cc @@ -299,11 +299,8 @@ Keyboard::snooper (GtkWidget *widget, GdkEventKey *event) if (event->type == GDK_KEY_RELEASE && modifier_state_equals (event->state, PrimaryModifier)) { switch (event->keyval) { case GDK_w: - if (current_window) { - current_window->hide (); - current_window = 0; - ret = true; - } + close_current_dialog (); + ret = true; break; } } @@ -311,6 +308,15 @@ Keyboard::snooper (GtkWidget *widget, GdkEventKey *event) return ret; } +void +Keyboard::close_current_dialog () +{ + if (current_window) { + current_window->hide (); + current_window = 0; + } +} + bool Keyboard::key_is_down (uint32_t keyval) { @@ -556,4 +562,3 @@ Keyboard::load_keybindings (string path) return true; } - diff --git a/libs/midi++2/channel.cc b/libs/midi++2/channel.cc index ae889058c4..66ce5ed71c 100644 --- a/libs/midi++2/channel.cc +++ b/libs/midi++2/channel.cc @@ -25,7 +25,8 @@ using namespace MIDI; -Channel::Channel (byte channelnum, Port &p) : _port (p) +Channel::Channel (byte channelnum, Port &p) + : _port (p) { _channel_number = channelnum; diff --git a/libs/midi++2/ipmidi_port.cc b/libs/midi++2/ipmidi_port.cc new file mode 100644 index 0000000000..50777634f1 --- /dev/null +++ b/libs/midi++2/ipmidi_port.cc @@ -0,0 +1,293 @@ +/* + Copyright (C) 2012 Paul Davis + + Using code from Rui Nuno Capela's qmidinet as inspiration. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id: port.cc 12065 2012-04-23 16:23:48Z paul $ +*/ +#include +#include +#include +#include +#include +#include +#include + +#if defined(WIN32) +static WSADATA g_wsaData; +typedef int socklen_t; +#else +#include +#include +inline void closesocket(int s) { ::close(s); } +#endif + +#include +#include + +#include "pbd/xml++.h" +#include "pbd/error.h" +#include "pbd/failed_constructor.h" +#include "pbd/convert.h" +#include "pbd/compose.h" + +#include "midi++/types.h" +#include "midi++/ipmidi_port.h" +#include "midi++/channel.h" + +using namespace MIDI; +using namespace std; +using namespace PBD; + +IPMIDIPort::IPMIDIPort (int base_port, const string& iface) + : Port (string_compose ("IPmidi@%1", base_port), Port::Flags (Port::IsInput|Port::IsOutput)) + , sockin (-1) + , sockout (-1) +{ + if (!open_sockets (base_port, iface)) { + throw (failed_constructor ()); + } +} + +IPMIDIPort::IPMIDIPort (const XMLNode& node) + : Port (node) +{ + /* base class does not class set_state() */ + set_state (node); +} + +IPMIDIPort::~IPMIDIPort () +{ + close_sockets (); +} + +int +IPMIDIPort::selectable () const +{ + return sockin; +} + +XMLNode& +IPMIDIPort::get_state () const +{ + return Port::get_state (); +} + +void +IPMIDIPort::set_state (const XMLNode& node) +{ + Port::set_state (node); +} + +void +IPMIDIPort::close_sockets () +{ + if (sockin >= 0) { + ::closesocket (sockin); + sockin = -1; + } + + if (sockout >= 0) { + ::closesocket (sockout); + sockout = -1; + } +} + +static bool +get_address (int sock, struct in_addr *inaddr, const string& ifname ) +{ + // Get interface address from supplied name. + +#if !defined(WIN32) + struct ifreq ifr; + ::strncpy(ifr.ifr_name, ifname.c_str(), sizeof(ifr.ifr_name)); + + if (::ioctl(sock, SIOCGIFFLAGS, (char *) &ifr)) { + ::perror("ioctl(SIOCGIFFLAGS)"); + return false; + } + + if ((ifr.ifr_flags & IFF_UP) == 0) { + error << string_compose ("interface %1 is down", ifname) << endmsg; + return false; + } + + if (::ioctl(sock, SIOCGIFADDR, (char *) &ifr)) { + ::perror("ioctl(SIOCGIFADDR)"); + return false; + } + + struct sockaddr_in sa; + ::memcpy(&sa, &ifr.ifr_addr, sizeof(struct sockaddr_in)); + inaddr->s_addr = sa.sin_addr.s_addr; + + return true; + +#else + + return false; + +#endif // !WIN32 +} + +bool +IPMIDIPort::open_sockets (int base_port, const string& ifname) +{ + int protonum = 0; + struct protoent *proto = ::getprotobyname("IP"); + + if (proto) { + protonum = proto->p_proto; + } + + sockin = ::socket (PF_INET, SOCK_DGRAM, protonum); + if (sockin < 0) { + ::perror("socket(in)"); + return false; + } + + struct sockaddr_in addrin; + ::memset(&addrin, 0, sizeof(addrin)); + addrin.sin_family = AF_INET; + addrin.sin_addr.s_addr = htonl(INADDR_ANY); + addrin.sin_port = htons(base_port); + + if (::bind(sockin, (struct sockaddr *) (&addrin), sizeof(addrin)) < 0) { + ::perror("bind"); + return false; + } + + // Will Hall, 2007 + // INADDR_ANY will bind to default interface, + // specify alternate interface nameon which to bind... + struct in_addr if_addr_in; + if (!ifname.empty()) { + if (!get_address(sockin, &if_addr_in, ifname)) { + error << string_compose ("socket(in): could not find interface address for %1", ifname) << endmsg; + return false; + } + if (::setsockopt(sockin, IPPROTO_IP, IP_MULTICAST_IF, + (char *) &if_addr_in, sizeof(if_addr_in))) { + ::perror("setsockopt(IP_MULTICAST_IF)"); + return false; + } + } else { + if_addr_in.s_addr = htonl (INADDR_ANY); + } + + struct ip_mreq mreq; + mreq.imr_multiaddr.s_addr = ::inet_addr("225.0.0.37"); + mreq.imr_interface.s_addr = if_addr_in.s_addr; + if(::setsockopt (sockin, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &mreq, sizeof(mreq)) < 0) { + ::perror("setsockopt(IP_ADD_MEMBERSHIP)"); + fprintf(stderr, "socket(in): your kernel is probably missing multicast support.\n"); + return false; + } + + // Output socket... + + sockout = ::socket (AF_INET, SOCK_DGRAM, protonum); + + if (sockout < 0) { + ::perror("socket(out)"); + return false; + } + + // Will Hall, Oct 2007 + if (!ifname.empty()) { + struct in_addr if_addr_out; + if (!get_address(sockout, &if_addr_out, ifname)) { + error << string_compose ("socket(out): could not find interface address for %1", ifname) << endmsg; + return false; + } + if (::setsockopt(sockout, IPPROTO_IP, IP_MULTICAST_IF, (char *) &if_addr_out, sizeof(if_addr_out))) { + ::perror("setsockopt(IP_MULTICAST_IF)"); + return false; + } + } + + ::memset(&addrout, 0, sizeof(struct sockaddr_in)); + addrout.sin_family = AF_INET; + addrout.sin_addr.s_addr = ::inet_addr("225.0.0.37"); + addrout.sin_port = htons (base_port); + + // Turn off loopback... + int loop = 0; + if (::setsockopt(sockout, IPPROTO_IP, IP_MULTICAST_LOOP, (char *) &loop, sizeof (loop)) < 0) { + ::perror("setsockopt(IP_MULTICAST_LOOP)"); + return false; + } + + if (fcntl (sockin, F_SETFL, O_NONBLOCK)) { + error << "cannot set non-blocking mode for IP MIDI input socket (" << ::strerror (errno) << ')' << endmsg; + return false; + } + + if (fcntl (sockout, F_SETFL, O_NONBLOCK)) { + error << "cannot set non-blocking mode for IP MIDI output socket (" << ::strerror (errno) << ')' << endmsg; + return false; + } + + return true; +} + +int +IPMIDIPort::write (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) { + ::perror("sendto"); + return -1; + } + return msglen; + } + return 0; +} + +int +IPMIDIPort::read (byte* buf, size_t bufsize) +{ + /* nothing to do here - all handled by parse() */ + return 0; +} + +void +IPMIDIPort::parse (framecnt_t timestamp) +{ + /* input was detected on the socket, so go get it and hand it to the + * parser. This will emit appropriate signals that will be handled + * by anyone who cares. + */ + + unsigned char buf[1024]; + struct sockaddr_in sender; + socklen_t slen = sizeof(sender); + int r = ::recvfrom (sockin, (char *) buf, sizeof(buf), 0, (struct sockaddr *) &sender, &slen); + + if (r >= 0) { + + _parser->set_timestamp (timestamp); + + for (int i = 0; i < r; ++i) { + _parser->scanner (buf[i]); + } + } else { + ::perror ("failed to recv from socket"); + } +} + diff --git a/libs/midi++2/jack_midi_port.cc b/libs/midi++2/jack_midi_port.cc new file mode 100644 index 0000000000..729fc789d4 --- /dev/null +++ b/libs/midi++2/jack_midi_port.cc @@ -0,0 +1,463 @@ +/* + Copyright (C) 1998 Paul Barton-Davis + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id$ +*/ +#include +#include +#include +#include + +#include +#include + +#include "pbd/xml++.h" +#include "pbd/error.h" +#include "pbd/failed_constructor.h" +#include "pbd/convert.h" +#include "pbd/strsplit.h" +#include "pbd/stacktrace.h" + +#include "midi++/types.h" +#include "midi++/jack_midi_port.h" +#include "midi++/channel.h" + +using namespace MIDI; +using namespace std; +using namespace PBD; + +pthread_t JackMIDIPort::_process_thread; +Signal0 JackMIDIPort::JackHalted; +Signal0 JackMIDIPort::MakeConnections; + +JackMIDIPort::JackMIDIPort (string const & name, Flags flags, jack_client_t* jack_client) + : Port (name, flags) + , _currently_in_cycle (false) + , _nframes_this_cycle (0) + , _jack_client (jack_client) + , _jack_port (0) + , output_fifo (512) + , input_fifo (1024) + , xthread (true) +{ + assert (jack_client); + init (name, flags); +} + +JackMIDIPort::JackMIDIPort (const XMLNode& node, jack_client_t* jack_client) + : Port (node) + , _currently_in_cycle (false) + , _nframes_this_cycle (0) + , _jack_client (jack_client) + , _jack_port (0) + , output_fifo (512) + , input_fifo (1024) + , xthread (true) +{ + assert (jack_client); + + Descriptor desc (node); + init (desc.tag, desc.flags); + set_state (node); +} + +void +JackMIDIPort::init (string const & name, Flags flags) +{ + if (!create_port ()) { + _ok = true; + } + + MakeConnections.connect_same_thread (connect_connection, boost::bind (&JackMIDIPort::make_connections, this)); + JackHalted.connect_same_thread (halt_connection, boost::bind (&JackMIDIPort::jack_halted, this)); +} + + +JackMIDIPort::~JackMIDIPort () +{ + if (_jack_port) { + if (_jack_client) { + jack_port_unregister (_jack_client, _jack_port); + _jack_port = 0; + } + } +} + +void +JackMIDIPort::parse (framecnt_t timestamp) +{ + byte buf[512]; + + /* NOTE: parsing is done (if at all) by initiating a read from + the port. Each port implementation calls on the parser + once it has data ready. + */ + + _parser->set_timestamp (timestamp); + + while (1) { + + // cerr << "+++ READ ON " << name() << endl; + + int nread = read (buf, sizeof (buf)); + + // cerr << "-- READ (" << nread << " ON " << name() << endl; + + if (nread > 0) { + if ((size_t) nread < sizeof (buf)) { + break; + } else { + continue; + } + } else if (nread == 0) { + break; + } else if (errno == EAGAIN) { + break; + } else { + fatal << "Error reading from MIDI port " << name() << endmsg; + /*NOTREACHED*/ + } + } +} + +void +JackMIDIPort::cycle_start (pframes_t nframes) +{ + assert (_jack_port); + + _currently_in_cycle = true; + _nframes_this_cycle = nframes; + + assert(_nframes_this_cycle == nframes); + + if (sends_output()) { + void *buffer = jack_port_get_buffer (_jack_port, nframes); + jack_midi_clear_buffer (buffer); + flush (buffer); + } + + if (receives_input()) { + void* jack_buffer = jack_port_get_buffer(_jack_port, nframes); + const pframes_t event_count = jack_midi_get_event_count(jack_buffer); + + jack_midi_event_t ev; + timestamp_t cycle_start_frame = jack_last_frame_time (_jack_client); + + for (pframes_t i = 0; i < event_count; ++i) { + jack_midi_event_get (&ev, jack_buffer, i); + input_fifo.write (cycle_start_frame + ev.time, (Evoral::EventType) 0, ev.size, ev.buffer); + } + + if (event_count) { + xthread.wakeup (); + } + } +} + +void +JackMIDIPort::cycle_end () +{ + if (sends_output()) { + flush (jack_port_get_buffer (_jack_port, _nframes_this_cycle)); + } + + _currently_in_cycle = false; + _nframes_this_cycle = 0; +} + +void +JackMIDIPort::jack_halted () +{ + _jack_client = 0; + _jack_port = 0; +} + +void +JackMIDIPort::drain (int check_interval_usecs) +{ + RingBuffer< Evoral::Event >::rw_vector vec = { { 0, 0 }, { 0, 0} }; + + if (is_process_thread()) { + error << "Process thread called MIDI::JackMIDIPort::drain() - this cannot work" << endmsg; + return; + } + + while (1) { + output_fifo.get_write_vector (&vec); + if (vec.len[0] + vec.len[1] >= output_fifo.bufsize() - 1) { + break; + } + usleep (check_interval_usecs); + } +} + +int +JackMIDIPort::write(byte * msg, size_t msglen, timestamp_t timestamp) +{ + int ret = 0; + + if (!sends_output()) { + return ret; + } + + if (!is_process_thread()) { + + Glib::Mutex::Lock lm (output_fifo_lock); + RingBuffer< Evoral::Event >::rw_vector vec = { { 0, 0 }, { 0, 0} }; + + output_fifo.get_write_vector (&vec); + + if (vec.len[0] + vec.len[1] < 1) { + error << "no space in FIFO for non-process thread MIDI write" << endmsg; + return 0; + } + + if (vec.len[0]) { + if (!vec.buf[0]->owns_buffer()) { + vec.buf[0]->set_buffer (0, 0, true); + } + vec.buf[0]->set (msg, msglen, timestamp); + } else { + if (!vec.buf[1]->owns_buffer()) { + vec.buf[1]->set_buffer (0, 0, true); + } + vec.buf[1]->set (msg, msglen, timestamp); + } + + output_fifo.increment_write_idx (1); + + ret = msglen; + + } else { + + // XXX This had to be temporarily commented out to make export work again + if (!(timestamp < _nframes_this_cycle)) { + std::cerr << "attempting to write MIDI event of " << msglen << " bytes at time " + << timestamp << " of " << _nframes_this_cycle + << " (this will not work - needs a code fix)" + << std::endl; + } + + if (_currently_in_cycle) { + if (timestamp == 0) { + timestamp = _last_write_timestamp; + } + + if (jack_midi_event_write (jack_port_get_buffer (_jack_port, _nframes_this_cycle), + timestamp, msg, msglen) == 0) { + ret = msglen; + _last_write_timestamp = timestamp; + + } else { + ret = 0; + cerr << "write of " << msglen << " failed, port holds " + << jack_midi_get_event_count (jack_port_get_buffer (_jack_port, _nframes_this_cycle)) + << endl; + // PBD::stacktrace (cerr, 20); + } + } else { + cerr << "write to JACK midi port failed: not currently in a process cycle." << endl; + PBD::stacktrace (cerr, 20); + } + } + + if (ret > 0 && _parser) { + // ardour doesn't care about this and neither should your app, probably + // output_parser->raw_preparse (*output_parser, msg, ret); + for (int i = 0; i < ret; i++) { + _parser->scanner (msg[i]); + } + // ardour doesn't care about this and neither should your app, probably + // output_parser->raw_postparse (*output_parser, msg, ret); + } + + return ret; +} + +void +JackMIDIPort::flush (void* jack_port_buffer) +{ + RingBuffer< Evoral::Event >::rw_vector vec = { { 0, 0 }, { 0, 0 } }; + size_t written; + + output_fifo.get_read_vector (&vec); + + if (vec.len[0] + vec.len[1]) { + // cerr << "Flush " << vec.len[0] + vec.len[1] << " events from non-process FIFO\n"; + } + + if (vec.len[0]) { + Evoral::Event* evp = vec.buf[0]; + + for (size_t n = 0; n < vec.len[0]; ++n, ++evp) { + jack_midi_event_write (jack_port_buffer, + (timestamp_t) evp->time(), evp->buffer(), evp->size()); + } + } + + if (vec.len[1]) { + Evoral::Event* evp = vec.buf[1]; + + for (size_t n = 0; n < vec.len[1]; ++n, ++evp) { + jack_midi_event_write (jack_port_buffer, + (timestamp_t) evp->time(), evp->buffer(), evp->size()); + } + } + + if ((written = vec.len[0] + vec.len[1]) != 0) { + output_fifo.increment_read_idx (written); + } +} + +int +JackMIDIPort::read (byte *, size_t) +{ + if (!receives_input()) { + return 0; + } + + timestamp_t time; + Evoral::EventType type; + uint32_t size; + byte buffer[input_fifo.capacity()]; + + while (input_fifo.read (&time, &type, &size, buffer)) { + _parser->set_timestamp (time); + for (uint32_t i = 0; i < size; ++i) { + _parser->scanner (buffer[i]); + } + } + + return 0; +} + +int +JackMIDIPort::create_port () +{ + _jack_port = jack_port_register(_jack_client, _tagname.c_str(), JACK_DEFAULT_MIDI_TYPE, _flags, 0); + return _jack_port == 0 ? -1 : 0; +} + +XMLNode& +JackMIDIPort::get_state () const +{ + XMLNode& root = Port::get_state (); + +#if 0 + byte device_inquiry[6]; + + device_inquiry[0] = 0xf0; + device_inquiry[0] = 0x7e; + device_inquiry[0] = 0x7f; + device_inquiry[0] = 0x06; + device_inquiry[0] = 0x02; + device_inquiry[0] = 0xf7; + + write (device_inquiry, sizeof (device_inquiry), 0); +#endif + + if (_jack_port) { + + const char** jc = jack_port_get_connections (_jack_port); + string connection_string; + if (jc) { + for (int i = 0; jc[i]; ++i) { + if (i > 0) { + connection_string += ','; + } + connection_string += jc[i]; + } + free (jc); + } + + if (!connection_string.empty()) { + root.add_property ("connections", connection_string); + } + } else { + if (!_connections.empty()) { + root.add_property ("connections", _connections); + } + } + + return root; +} + +void +JackMIDIPort::set_state (const XMLNode& node) +{ + const XMLProperty* prop; + + if ((prop = node.property ("tag")) == 0 || prop->value() != _tagname) { + return; + } + + Port::set_state (node); + + if ((prop = node.property ("connections")) != 0) { + _connections = prop->value (); + } +} + +void +JackMIDIPort::make_connections () +{ + if (!_connections.empty()) { + vector ports; + split (_connections, ports, ','); + for (vector::iterator x = ports.begin(); x != ports.end(); ++x) { + if (_jack_client) { + if (receives_input()) { + jack_connect (_jack_client, (*x).c_str(), jack_port_name (_jack_port)); + } else { + jack_connect (_jack_client, jack_port_name (_jack_port), (*x).c_str()); + } + /* ignore failures */ + } + } + } + + connect_connection.disconnect (); +} + +void +JackMIDIPort::set_process_thread (pthread_t thr) +{ + _process_thread = thr; +} + +bool +JackMIDIPort::is_process_thread() +{ + return (pthread_self() == _process_thread); +} + +void +JackMIDIPort::reestablish (jack_client_t* jack) +{ + _jack_client = jack; + int const r = create_port (); + + if (r) { + PBD::error << "could not reregister ports for " << name() << endmsg; + } +} + +void +JackMIDIPort::reconnect () +{ + make_connections (); +} diff --git a/libs/midi++2/manager.cc b/libs/midi++2/manager.cc index 61d4c4c363..822c74e125 100644 --- a/libs/midi++2/manager.cc +++ b/libs/midi++2/manager.cc @@ -27,6 +27,7 @@ #include "midi++/manager.h" #include "midi++/channel.h" #include "midi++/port.h" +#include "midi++/jack_midi_port.h" #include "midi++/mmc.h" using namespace std; @@ -40,12 +41,12 @@ Manager::Manager (jack_client_t* jack) { _mmc = new MachineControl (this, jack); - _mtc_input_port = add_port (new MIDI::Port ("MTC in", Port::IsInput, jack)); - _mtc_output_port = add_port (new MIDI::Port ("MTC out", Port::IsOutput, jack)); - _midi_input_port = add_port (new MIDI::Port ("MIDI control in", Port::IsInput, jack)); - _midi_output_port = add_port (new MIDI::Port ("MIDI control out", Port::IsOutput, jack)); - _midi_clock_input_port = add_port (new MIDI::Port ("MIDI clock in", Port::IsInput, jack)); - _midi_clock_output_port = add_port (new MIDI::Port ("MIDI clock out", Port::IsOutput, jack)); + _mtc_input_port = add_port (new MIDI::JackMIDIPort ("MTC in", Port::IsInput, jack)); + _mtc_output_port = add_port (new MIDI::JackMIDIPort ("MTC out", Port::IsOutput, jack)); + _midi_input_port = add_port (new MIDI::JackMIDIPort ("MIDI control in", Port::IsInput, jack)); + _midi_output_port = add_port (new MIDI::JackMIDIPort ("MIDI control out", Port::IsOutput, jack)); + _midi_clock_input_port = add_port (new MIDI::JackMIDIPort ("MIDI clock in", Port::IsInput, jack)); + _midi_clock_output_port = add_port (new MIDI::JackMIDIPort ("MIDI clock out", Port::IsOutput, jack)); } Manager::~Manager () @@ -117,7 +118,10 @@ Manager::reestablish (jack_client_t* jack) boost::shared_ptr pr = _ports.reader (); for (PortList::const_iterator p = pr->begin(); p != pr->end(); ++p) { - (*p)->reestablish (jack); + JackMIDIPort* pp = dynamic_cast (*p); + if (pp) { + pp->reestablish (jack); + } } } @@ -128,7 +132,10 @@ Manager::reconnect () boost::shared_ptr pr = _ports.reader (); for (PortList::const_iterator p = pr->begin(); p != pr->end(); ++p) { - (*p)->reconnect (); + JackMIDIPort* pp = dynamic_cast (*p); + if (pp) { + pp->reconnect (); + } } } diff --git a/libs/midi++2/midi++/channel.h b/libs/midi++2/midi++/channel.h index 162eea1e9a..d00ce700c5 100644 --- a/libs/midi++2/midi++/channel.h +++ b/libs/midi++2/midi++/channel.h @@ -41,7 +41,7 @@ class Channel : public PBD::ScopedConnectionList { public: Channel (byte channel_number, Port &); - Port &midi_port() { return _port; } + Port &midi_port() { return _port; } byte channel() { return _channel_number; } byte program() { return _program_number; } byte bank() { return _bank_number; } @@ -115,7 +115,7 @@ class Channel : public PBD::ScopedConnectionList { void connect_signals (); private: - Port & _port; + Port& _port; /* Current channel values */ byte _channel_number; diff --git a/libs/midi++2/midi++/ipmidi_port.h b/libs/midi++2/midi++/ipmidi_port.h new file mode 100644 index 0000000000..7df9642321 --- /dev/null +++ b/libs/midi++2/midi++/ipmidi_port.h @@ -0,0 +1,79 @@ +/* + Copyright (C) 1998-2010 Paul Barton-Davis + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __libmidi_ipmidi_port_h__ +#define __libmidi_ipmidi_port_h__ + +#include +#include +#if defined(WIN32) +#include +#else +#include +#include +#endif + +#include + +#include + +#include "pbd/xml++.h" +#include "pbd/crossthread.h" +#include "pbd/signals.h" +#include "pbd/ringbuffer.h" + +#include "evoral/Event.hpp" +#include "evoral/EventRingBuffer.hpp" + +#include "midi++/types.h" +#include "midi++/parser.h" +#include "midi++/port.h" + +namespace MIDI { + +class IPMIDIPort : public Port { + public: + IPMIDIPort (int base_port = lowest_ipmidi_port_default, const std::string& ifname = std::string()); + IPMIDIPort (const XMLNode&); + ~IPMIDIPort (); + + XMLNode& get_state () const; + void set_state (const XMLNode&); + + int write (byte *msg, size_t msglen, timestamp_t timestamp); + int read (byte *buf, size_t bufsize); + void parse (framecnt_t timestamp); + int selectable () const; + + static const int lowest_ipmidi_port_default = 21928; + +private: + int sockin; + int sockout; + struct sockaddr_in addrout; + Glib::Mutex write_lock; + + bool open_sockets (int base_port, const std::string& ifname); + void close_sockets (); + + void init (std::string const &, Flags); +}; + +} // namespace MIDI + +#endif // __libmidi_ipmidi_port_h__ diff --git a/libs/midi++2/midi++/jack_midi_port.h b/libs/midi++2/midi++/jack_midi_port.h new file mode 100644 index 0000000000..e381120a99 --- /dev/null +++ b/libs/midi++2/midi++/jack_midi_port.h @@ -0,0 +1,103 @@ +/* + Copyright (C) 1998-2010 Paul Barton-Davis + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __libmidi_port_h__ +#define __libmidi_port_h__ + +#include +#include + +#include + +#include "pbd/xml++.h" +#include "pbd/crossthread.h" +#include "pbd/signals.h" +#include "pbd/ringbuffer.h" + +#include "evoral/Event.hpp" +#include "evoral/EventRingBuffer.hpp" + +#include "midi++/types.h" +#include "midi++/parser.h" +#include "midi++/port.h" + +namespace MIDI { + +class Channel; +class PortRequest; + +class JackMIDIPort : public Port { + public: + JackMIDIPort (std::string const &, Port::Flags, jack_client_t *); + JackMIDIPort (const XMLNode&, jack_client_t *); + ~JackMIDIPort (); + + XMLNode& get_state () const; + void set_state (const XMLNode&); + + void cycle_start (pframes_t nframes); + void cycle_end (); + + void parse (framecnt_t timestamp); + int write (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(); } + + pframes_t nframes_this_cycle() const { return _nframes_this_cycle; } + + void reestablish (jack_client_t *); + void reconnect (); + + static void set_process_thread (pthread_t); + static pthread_t get_process_thread () { return _process_thread; } + static bool is_process_thread(); + + static PBD::Signal0 MakeConnections; + static PBD::Signal0 JackHalted; + +private: + bool _currently_in_cycle; + pframes_t _nframes_this_cycle; + jack_client_t* _jack_client; + jack_port_t* _jack_port; + timestamp_t _last_write_timestamp; + RingBuffer< Evoral::Event > output_fifo; + Evoral::EventRingBuffer input_fifo; + Glib::Mutex output_fifo_lock; + CrossThreadChannel xthread; + + int create_port (); + + /** Channel used to signal to the MidiControlUI that input has arrived */ + + std::string _connections; + PBD::ScopedConnection connect_connection; + PBD::ScopedConnection halt_connection; + void flush (void* jack_port_buffer); + void jack_halted (); + void make_connections (); + void init (std::string const &, Flags); + + static pthread_t _process_thread; + +}; + +} // namespace MIDI + +#endif // __libmidi_port_h__ diff --git a/libs/midi++2/midi++/manager.h b/libs/midi++2/midi++/manager.h index bab4a18dd2..c37ba392b4 100644 --- a/libs/midi++2/midi++/manager.h +++ b/libs/midi++2/midi++/manager.h @@ -88,12 +88,12 @@ class Manager { static Manager *theManager; MIDI::MachineControl* _mmc; - MIDI::Port* _mtc_input_port; - MIDI::Port* _mtc_output_port; - MIDI::Port* _midi_input_port; - MIDI::Port* _midi_output_port; - MIDI::Port* _midi_clock_input_port; - MIDI::Port* _midi_clock_output_port; + MIDI::Port* _mtc_input_port; + MIDI::Port* _mtc_output_port; + MIDI::Port* _midi_input_port; + MIDI::Port* _midi_output_port; + MIDI::Port* _midi_clock_input_port; + MIDI::Port* _midi_clock_output_port; SerializedRCUManager _ports; }; diff --git a/libs/midi++2/midi++/mmc.h b/libs/midi++2/midi++/mmc.h index e54b69a04d..18204e3fa8 100644 --- a/libs/midi++2/midi++/mmc.h +++ b/libs/midi++2/midi++/mmc.h @@ -145,9 +145,9 @@ class MachineControl MMCSignal Wait; MMCSignal Resume; - TimestampedSignal SPPStart; - TimestampedSignal SPPContinue; - TimestampedSignal SPPStop; + PBD::Signal0 SPPStart; + PBD::Signal0 SPPContinue; + PBD::Signal0 SPPStop; /* The second argument is the shuttle speed, the third is true if the direction is "forwards", false for "reverse" @@ -271,9 +271,9 @@ class MachineControl int do_shuttle (byte *, size_t len); void write_track_status (byte *, size_t len, byte reg); - void spp_start (Parser&, framecnt_t); - void spp_continue (Parser&, framecnt_t); - void spp_stop (Parser&, framecnt_t); + void spp_start (); + void spp_continue (); + void spp_stop (); }; /** Class to describe a MIDI machine control command to be sent. diff --git a/libs/midi++2/midi++/parser.h b/libs/midi++2/midi++/parser.h index e57323e72d..f5f343e952 100644 --- a/libs/midi++2/midi++/parser.h +++ b/libs/midi++2/midi++/parser.h @@ -136,9 +136,9 @@ class Parser { void reset_mtc_state (); private: - Port &_port; + Port&_port; /* tracing */ - + std::ostream *trace_stream; std::string trace_prefix; void trace_event (Parser &p, byte *msg, size_t len); diff --git a/libs/midi++2/midi++/port.h b/libs/midi++2/midi++/port.h index f7bef36b1f..a2315f7284 100644 --- a/libs/midi++2/midi++/port.h +++ b/libs/midi++2/midi++/port.h @@ -16,8 +16,8 @@ */ -#ifndef __libmidi_port_h__ -#define __libmidi_port_h__ +#ifndef __libmidi_port_base_h__ +#define __libmidi_port_base_h__ #include #include @@ -47,9 +47,9 @@ class Port { IsOutput = JackPortIsOutput, }; - Port (std::string const &, Flags, jack_client_t *); - Port (const XMLNode&, jack_client_t *); - ~Port (); + Port (std::string const &, Flags); + Port (const XMLNode&); + virtual ~Port (); XMLNode& get_state () const; void set_state (const XMLNode&); @@ -57,9 +57,9 @@ class Port { // FIXME: make Manager a friend of port so these can be hidden? /* Only for use by MidiManager. Don't ever call this. */ - void cycle_start (pframes_t nframes); + virtual void cycle_start (pframes_t nframes) {} /* Only for use by MidiManager. Don't ever call this. */ - void cycle_end (); + virtual void cycle_end () {} /** Write a message to port. * @param msg Raw MIDI message to send @@ -67,17 +67,23 @@ class Port { * @param timestamp Time stamp in frames of this message (relative to cycle start) * @return number of bytes successfully written */ - int write (byte *msg, size_t msglen, timestamp_t timestamp); + virtual int write (byte *msg, size_t msglen, timestamp_t timestamp) = 0; /** Read raw bytes from a port. * @param buf memory to store read data in * @param bufsize size of @a buf * @return number of bytes successfully read, negative if error */ - int read (byte *buf, size_t bufsize); + virtual int read (byte *buf, size_t bufsize) = 0; + + /** block until the output FIFO used by non-process threads + * is empty, checking every @a check_interval_usecs usecs + * for current status. Not to be called by a thread that + * executes any part of a JACK process callback (will + * simply return immediately in that situation). + */ + virtual void drain (int check_interval_usecs) {} - void parse (framecnt_t timestamp); - /** Write a message to port. * @return true on success. * FIXME: describe semantics here @@ -86,6 +92,8 @@ class Port { return !(write (msg, len, timestamp) == (int) len); } + virtual void parse (framecnt_t timestamp) = 0; + bool clock (timestamp_t timestamp); /* select(2)/poll(2)-based I/O */ @@ -93,9 +101,7 @@ class Port { /** Get the file descriptor for port. * @return File descriptor, or -1 if not selectable. */ - int selectable () const { - return xthread.selectable(); - } + virtual int selectable () const = 0; Channel *channel (channel_t chn) { return _channel[chn&0x7F]; @@ -108,6 +114,9 @@ class Port { const char *name () const { return _tagname.c_str(); } bool ok () const { return _ok; } + virtual bool centrally_parsed() const; + void set_centrally_parsed (bool yn) { _centrally_parsed = yn; } + bool receives_input () const { return _flags == IsInput; } @@ -124,55 +133,17 @@ class Port { XMLNode& get_state(); }; - pframes_t nframes_this_cycle() const { return _nframes_this_cycle; } - - void reestablish (jack_client_t *); - void reconnect (); - - static void set_process_thread (pthread_t); - static pthread_t get_process_thread () { return _process_thread; } - static bool is_process_thread(); - static std::string state_node_name; - - static PBD::Signal0 MakeConnections; - static PBD::Signal0 JackHalted; -private: - bool _ok; - bool _currently_in_cycle; - pframes_t _nframes_this_cycle; - std::string _tagname; - size_t _number; - Channel *_channel[16]; - Parser *_parser; + protected: + bool _ok; + std::string _tagname; + Channel* _channel[16]; + Parser* _parser; + Flags _flags; + bool _centrally_parsed; - int create_port (); - - jack_client_t* _jack_client; - jack_port_t* _jack_port; - framecnt_t _last_read_index; - timestamp_t _last_write_timestamp; - - /** Channel used to signal to the MidiControlUI that input has arrived */ - CrossThreadChannel xthread; - - std::string _connections; - PBD::ScopedConnection connect_connection; - PBD::ScopedConnection halt_connection; - void flush (void* jack_port_buffer); - void jack_halted (); - void make_connections (); void init (std::string const &, Flags); - - static pthread_t _process_thread; - - RingBuffer< Evoral::Event > output_fifo; - Evoral::EventRingBuffer input_fifo; - - Glib::Mutex output_fifo_lock; - - Flags _flags; }; struct PortSet { @@ -182,8 +153,8 @@ struct PortSet { std::list ports; }; -std::ostream & operator << ( std::ostream & os, const Port & port ); +std::ostream & operator << (std::ostream& os, const Port& port); } // namespace MIDI -#endif // __libmidi_port_h__ +#endif // __libmidi_port_base_h__ diff --git a/libs/midi++2/mmc.cc b/libs/midi++2/mmc.cc index 292b6cc1b4..06eadb5b34 100644 --- a/libs/midi++2/mmc.cc +++ b/libs/midi++2/mmc.cc @@ -25,6 +25,7 @@ #include "pbd/error.h" #include "midi++/mmc.h" #include "midi++/port.h" +#include "midi++/jack_midi_port.h" #include "midi++/parser.h" #include "midi++/manager.h" @@ -202,13 +203,13 @@ MachineControl::MachineControl (Manager* m, jack_client_t* jack) _receive_device_id = 0x7f; _send_device_id = 0x7f; - _input_port = m->add_port (new Port ("MMC in", Port::IsInput, jack)); - _output_port = m->add_port (new Port ("MMC out", Port::IsOutput, jack)); + _input_port = m->add_port (new JackMIDIPort ("MMC in", Port::IsInput, jack)); + _output_port = m->add_port (new JackMIDIPort ("MMC out", Port::IsOutput, jack)); _input_port->parser()->mmc.connect_same_thread (port_connections, boost::bind (&MachineControl::process_mmc_message, this, _1, _2, _3)); - _input_port->parser()->start.connect_same_thread (port_connections, boost::bind (&MachineControl::spp_start, this, _1, _2)); - _input_port->parser()->contineu.connect_same_thread (port_connections, boost::bind (&MachineControl::spp_continue, this, _1, _2)); - _input_port->parser()->stop.connect_same_thread (port_connections, boost::bind (&MachineControl::spp_stop, this, _1, _2)); + _input_port->parser()->start.connect_same_thread (port_connections, boost::bind (&MachineControl::spp_start, this)); + _input_port->parser()->contineu.connect_same_thread (port_connections, boost::bind (&MachineControl::spp_continue, this)); + _input_port->parser()->stop.connect_same_thread (port_connections, boost::bind (&MachineControl::spp_stop, this)); } void @@ -649,21 +650,21 @@ MachineControl::send (MachineControlCommand const & c) } void -MachineControl::spp_start (Parser& parser, framecnt_t timestamp) +MachineControl::spp_start () { - SPPStart (parser, timestamp); /* EMIT SIGNAL */ + SPPStart (); /* EMIT SIGNAL */ } void -MachineControl::spp_continue (Parser& parser, framecnt_t timestamp) +MachineControl::spp_continue () { - SPPContinue (parser, timestamp); /* EMIT SIGNAL */ + SPPContinue (); /* EMIT SIGNAL */ } void -MachineControl::spp_stop (Parser& parser, framecnt_t timestamp) +MachineControl::spp_stop () { - SPPStop (parser, timestamp); /* EMIT SIGNAL */ + SPPStop (); /* EMIT SIGNAL */ } MachineControlCommand::MachineControlCommand (MachineControl::Command c) diff --git a/libs/midi++2/parser.cc b/libs/midi++2/parser.cc index cdd23a5306..8e3af64504 100644 --- a/libs/midi++2/parser.cc +++ b/libs/midi++2/parser.cc @@ -105,8 +105,7 @@ Parser::midi_event_type_name (eventType t) } Parser::Parser (Port &p) - : _port (p) - + : _port(p) { trace_stream = 0; trace_prefix = ""; diff --git a/libs/midi++2/port.cc b/libs/midi++2/port.cc index 378548ca03..3e7896631a 100644 --- a/libs/midi++2/port.cc +++ b/libs/midi++2/port.cc @@ -15,7 +15,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - $Id$ + $Id: port.cc 11871 2012-04-10 16:27:01Z paul $ */ #include #include @@ -40,43 +40,23 @@ using namespace MIDI; using namespace std; using namespace PBD; -pthread_t Port::_process_thread; -Signal0 Port::JackHalted; -Signal0 Port::MakeConnections; string Port::state_node_name = "MIDI-port"; -Port::Port (string const & name, Flags flags, jack_client_t* jack_client) - : _currently_in_cycle (false) - , _nframes_this_cycle (0) - , _jack_client (jack_client) - , _jack_port (0) - , _last_read_index (0) - , xthread (true) - , output_fifo (512) - , input_fifo (1024) - , _flags (flags) +Port::Port (string const & name, Flags flags) + : _flags (flags) + , _centrally_parsed (true) { - assert (jack_client); init (name, flags); } -Port::Port (const XMLNode& node, jack_client_t* jack_client) - : _currently_in_cycle (false) - , _nframes_this_cycle (0) - , _jack_client (jack_client) - , _jack_port (0) - , _last_read_index (0) - , xthread (true) - , output_fifo (512) - , input_fifo (1024) +Port::Port (const XMLNode& node) + : _centrally_parsed (true) { - assert (jack_client); - Descriptor desc (node); init (desc.tag, desc.flags); - set_state (node); + /* derived class must call ::set_state() */ } void @@ -97,65 +77,13 @@ Port::init (string const & name, Flags flags) _channel[i] = new Channel (i, *this); _channel[i]->connect_signals (); } - - if (!create_port ()) { - _ok = true; - } - - MakeConnections.connect_same_thread (connect_connection, boost::bind (&Port::make_connections, this)); - JackHalted.connect_same_thread (halt_connection, boost::bind (&Port::jack_halted, this)); } - Port::~Port () { for (int i = 0; i < 16; i++) { delete _channel[i]; } - - if (_jack_port) { - if (_jack_client) { - jack_port_unregister (_jack_client, _jack_port); - _jack_port = 0; - } - } -} - -void -Port::parse (framecnt_t timestamp) -{ - byte buf[512]; - - /* NOTE: parsing is done (if at all) by initiating a read from - the port. Each port implementation calls on the parser - once it has data ready. - */ - - _parser->set_timestamp (timestamp); - - while (1) { - - // cerr << "+++ READ ON " << name() << endl; - - int nread = read (buf, sizeof (buf)); - - // cerr << "-- READ (" << nread << " ON " << name() << endl; - - if (nread > 0) { - if ((size_t) nread < sizeof (buf)) { - break; - } else { - continue; - } - } else if (nread == 0) { - break; - } else if (errno == EAGAIN) { - break; - } else { - fatal << "Error reading from MIDI port " << name() << endmsg; - /*NOTREACHED*/ - } - } } /** Send a clock tick message. @@ -173,53 +101,6 @@ Port::clock (timestamp_t timestamp) return false; } -void -Port::cycle_start (pframes_t nframes) -{ - assert (_jack_port); - - _currently_in_cycle = true; - _nframes_this_cycle = nframes; - - assert(_nframes_this_cycle == nframes); - _last_read_index = 0; - _last_write_timestamp = 0; - - if (sends_output()) { - void *buffer = jack_port_get_buffer (_jack_port, nframes); - jack_midi_clear_buffer (buffer); - flush (buffer); - } - - if (receives_input()) { - void* jack_buffer = jack_port_get_buffer(_jack_port, nframes); - const pframes_t event_count = jack_midi_get_event_count(jack_buffer); - - jack_midi_event_t ev; - timestamp_t cycle_start_frame = jack_last_frame_time (_jack_client); - - for (pframes_t i = 0; i < event_count; ++i) { - jack_midi_event_get (&ev, jack_buffer, i); - input_fifo.write (cycle_start_frame + ev.time, (Evoral::EventType) 0, ev.size, ev.buffer); - } - - if (event_count) { - xthread.wakeup (); - } - } -} - -void -Port::cycle_end () -{ - if (sends_output()) { - flush (jack_port_get_buffer (_jack_port, _nframes_this_cycle)); - } - - _currently_in_cycle = false; - _nframes_this_cycle = 0; -} - std::ostream & MIDI::operator << ( std::ostream & os, const MIDI::Port & port ) { using namespace std; @@ -259,159 +140,6 @@ Port::Descriptor::Descriptor (const XMLNode& node) } } -void -Port::jack_halted () -{ - _jack_client = 0; - _jack_port = 0; -} - -int -Port::write(byte * msg, size_t msglen, timestamp_t timestamp) -{ - int ret = 0; - - if (!sends_output()) { - return ret; - } - - if (!is_process_thread()) { - - Glib::Mutex::Lock lm (output_fifo_lock); - RingBuffer< Evoral::Event >::rw_vector vec = { { 0, 0 }, { 0, 0} }; - - output_fifo.get_write_vector (&vec); - - if (vec.len[0] + vec.len[1] < 1) { - error << "no space in FIFO for non-process thread MIDI write" << endmsg; - return 0; - } - - if (vec.len[0]) { - if (!vec.buf[0]->owns_buffer()) { - vec.buf[0]->set_buffer (0, 0, true); - } - vec.buf[0]->set (msg, msglen, timestamp); - } else { - if (!vec.buf[1]->owns_buffer()) { - vec.buf[1]->set_buffer (0, 0, true); - } - vec.buf[1]->set (msg, msglen, timestamp); - } - - output_fifo.increment_write_idx (1); - - ret = msglen; - - } else { - - // XXX This had to be temporarily commented out to make export work again - if (!(timestamp < _nframes_this_cycle)) { - std::cerr << "attempting to write MIDI event of " << msglen << " bytes at time " - << timestamp << " of " << _nframes_this_cycle - << " (this will not work - needs a code fix)" - << std::endl; - } - - if (_currently_in_cycle) { - if (timestamp == 0) { - timestamp = _last_write_timestamp; - } - - if (jack_midi_event_write (jack_port_get_buffer (_jack_port, _nframes_this_cycle), - timestamp, msg, msglen) == 0) { - ret = msglen; - _last_write_timestamp = timestamp; - - } else { - ret = 0; - cerr << "write of " << msglen << " failed, port holds " - << jack_midi_get_event_count (jack_port_get_buffer (_jack_port, _nframes_this_cycle)) - << endl; - } - } else { - cerr << "write to JACK midi port failed: not currently in a process cycle." << endl; - PBD::stacktrace (cerr, 20); - } - } - - if (ret > 0 && _parser) { - // ardour doesn't care about this and neither should your app, probably - // output_parser->raw_preparse (*output_parser, msg, ret); - for (int i = 0; i < ret; i++) { - _parser->scanner (msg[i]); - } - // ardour doesn't care about this and neither should your app, probably - // output_parser->raw_postparse (*output_parser, msg, ret); - } - - return ret; -} - -void -Port::flush (void* jack_port_buffer) -{ - RingBuffer< Evoral::Event >::rw_vector vec = { { 0, 0 }, { 0, 0 } }; - size_t written; - - output_fifo.get_read_vector (&vec); - - if (vec.len[0] + vec.len[1]) { - // cerr << "Flush " << vec.len[0] + vec.len[1] << " events from non-process FIFO\n"; - } - - if (vec.len[0]) { - Evoral::Event* evp = vec.buf[0]; - - for (size_t n = 0; n < vec.len[0]; ++n, ++evp) { - jack_midi_event_write (jack_port_buffer, - (timestamp_t) evp->time(), evp->buffer(), evp->size()); - } - } - - if (vec.len[1]) { - Evoral::Event* evp = vec.buf[1]; - - for (size_t n = 0; n < vec.len[1]; ++n, ++evp) { - jack_midi_event_write (jack_port_buffer, - (timestamp_t) evp->time(), evp->buffer(), evp->size()); - } - } - - if ((written = vec.len[0] + vec.len[1]) != 0) { - output_fifo.increment_read_idx (written); - } -} - -int -Port::read (byte *, size_t) -{ - if (!receives_input()) { - return 0; - } - - timestamp_t time; - Evoral::EventType type; - uint32_t size; - byte buffer[input_fifo.capacity()]; - - while (input_fifo.read (&time, &type, &size, buffer)) { - _parser->set_timestamp (time); - for (uint32_t i = 0; i < size; ++i) { - _parser->scanner (buffer[i]); - } - } - - return 0; -} - -int -Port::create_port () -{ - _jack_port = jack_port_register(_jack_client, _tagname.c_str(), JACK_DEFAULT_MIDI_TYPE, _flags, 0); - return _jack_port == 0 ? -1 : 0; -} - XMLNode& Port::get_state () const { @@ -437,29 +165,6 @@ Port::get_state () const write (device_inquiry, sizeof (device_inquiry), 0); #endif - if (_jack_port) { - - const char** jc = jack_port_get_connections (_jack_port); - string connection_string; - if (jc) { - for (int i = 0; jc[i]; ++i) { - if (i > 0) { - connection_string += ','; - } - connection_string += jc[i]; - } - free (jc); - } - - if (!connection_string.empty()) { - root->add_property ("connections", connection_string); - } - } else { - if (!_connections.empty()) { - root->add_property ("connections", _connections); - } - } - return *root; } @@ -471,58 +176,10 @@ Port::set_state (const XMLNode& node) if ((prop = node.property ("tag")) == 0 || prop->value() != _tagname) { return; } - - if ((prop = node.property ("connections")) != 0 && _jack_port) { - _connections = prop->value (); - } -} - -void -Port::make_connections () -{ - if (!_connections.empty()) { - vector ports; - split (_connections, ports, ','); - for (vector::iterator x = ports.begin(); x != ports.end(); ++x) { - if (_jack_client) { - if (receives_input()) { - jack_connect (_jack_client, (*x).c_str(), jack_port_name (_jack_port)); - } else { - jack_connect (_jack_client, jack_port_name (_jack_port), (*x).c_str()); - } - /* ignore failures */ - } - } - } - - connect_connection.disconnect (); -} - -void -Port::set_process_thread (pthread_t thr) -{ - _process_thread = thr; } bool -Port::is_process_thread() +Port::centrally_parsed() const { - return (pthread_self() == _process_thread); -} - -void -Port::reestablish (jack_client_t* jack) -{ - _jack_client = jack; - int const r = create_port (); - - if (r) { - PBD::error << "could not reregister ports for " << name() << endmsg; - } -} - -void -Port::reconnect () -{ - make_connections (); + return _centrally_parsed; } diff --git a/libs/midi++2/wscript b/libs/midi++2/wscript index b810bdc802..d4f71124aa 100644 --- a/libs/midi++2/wscript +++ b/libs/midi++2/wscript @@ -47,6 +47,8 @@ def build(bld): obj.source = ''' midi.cc channel.cc + ipmidi_port.cc + jack_midi_port.cc manager.cc parser.cc port.cc diff --git a/libs/panners/1in2out/panner_1in2out.cc b/libs/panners/1in2out/panner_1in2out.cc index 2fb2df7e9a..d84af57ade 100644 --- a/libs/panners/1in2out/panner_1in2out.cc +++ b/libs/panners/1in2out/panner_1in2out.cc @@ -364,9 +364,13 @@ Panner1in2out::value_as_string (boost::shared_ptr ac) const This is pretty wierd, but its the way audio engineers expect it. Just remember that the center of the USA isn't Kansas, its (50LA, 50NY) and it will all make sense. + + This is designed to be as narrow as possible. Dedicated + panner GUIs can do their own version of this if they need + something less compact. */ - return string_compose (_("L:%1 R:%2"), (int) rint (100.0 * (1.0 - val)), + return string_compose (_("L%1R%2"), (int) rint (100.0 * (1.0 - val)), (int) rint (100.0 * val)); default: diff --git a/libs/panners/2in2out/panner_2in2out.cc b/libs/panners/2in2out/panner_2in2out.cc index c9dac08cf1..8e798315d0 100644 --- a/libs/panners/2in2out/panner_2in2out.cc +++ b/libs/panners/2in2out/panner_2in2out.cc @@ -478,11 +478,15 @@ Panner2in2out::value_as_string (boost::shared_ptr ac) const This is pretty wierd, but its the way audio engineers expect it. Just remember that the center of the USA isn't Kansas, its (50LA, 50NY) and it will all make sense. + + This is designed to be as narrow as possible. Dedicated + panner GUIs can do their own version of this if they need + something less compact. */ - return string_compose (_("L:%1 R:%2"), (int) rint (100.0 * (1.0 - val)), + return string_compose (_("L%1R%2"), (int) rint (100.0 * (1.0 - val)), (int) rint (100.0 * val)); - + case PanWidthAutomation: return string_compose (_("Width: %1%%"), (int) floor (100.0 * val)); diff --git a/libs/pbd/base_ui.cc b/libs/pbd/base_ui.cc index d56e4a31a4..6c21549e8b 100644 --- a/libs/pbd/base_ui.cc +++ b/libs/pbd/base_ui.cc @@ -25,6 +25,7 @@ #include #include "pbd/base_ui.h" +#include "pbd/debug.h" #include "pbd/pthread_utils.h" #include "pbd/error.h" #include "pbd/compose.h" @@ -72,6 +73,7 @@ BaseUI::new_request_type () void 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->run (); @@ -104,7 +106,7 @@ BaseUI::quit () bool BaseUI::request_handler (Glib::IOCondition ioc) { - /* check the transport request pipe */ + /* check the request pipe */ if (ioc & ~IO_IN) { _main_loop->quit (); diff --git a/libs/pbd/convert.cc b/libs/pbd/convert.cc index 3d968d0627..07407e2fad 100644 --- a/libs/pbd/convert.cc +++ b/libs/pbd/convert.cc @@ -255,6 +255,26 @@ strings_equal_ignore_case (const string& a, const string& b) return false; } +bool +string_is_affirmative (const std::string& str) +{ + /* to be used only with XML data - not intended to handle user input */ + + if (str.empty ()) { + return false; + } + + /* the use of g_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())); +} + /** A wrapper for dgettext that takes a msgid of the form Context|Text. * If Context|Text is translated, the translation is returned, otherwise * just Text is returned. Useful for getting translations of words or phrases diff --git a/libs/pbd/debug.cc b/libs/pbd/debug.cc index 0a2c1b0d72..6b526c33ad 100644 --- a/libs/pbd/debug.cc +++ b/libs/pbd/debug.cc @@ -36,6 +36,8 @@ uint64_t PBD::DEBUG::Stateful = PBD::new_debug_bit ("stateful"); uint64_t PBD::DEBUG::Properties = PBD::new_debug_bit ("properties"); uint64_t PBD::DEBUG::FileManager = PBD::new_debug_bit ("filemanager"); uint64_t PBD::DEBUG::Pool = PBD::new_debug_bit ("pool"); +uint64_t PBD::DEBUG::EventLoop = PBD::new_debug_bit ("eventloop"); +uint64_t PBD::DEBUG::AbstractUI = PBD::new_debug_bit ("abstractui"); uint64_t PBD::debug_bits = 0x0; diff --git a/libs/pbd/event_loop.cc b/libs/pbd/event_loop.cc index 5a9a220075..5c132037d3 100644 --- a/libs/pbd/event_loop.cc +++ b/libs/pbd/event_loop.cc @@ -20,14 +20,35 @@ EventLoop::set_event_loop_for_thread (EventLoop* loop) thread_event_loop.set (loop, do_not_delete_the_loop_pointer); } -/** Called when a sigc::trackable that was connected to using the invalidator() macro - * is destroyed. - */ void* EventLoop::invalidate_request (void* data) { InvalidationRecord* ir = (InvalidationRecord*) data; + /* Some of the requests queued with an EventLoop may involve functors + * that make method calls to objects whose lifetime is shorter + * than the EventLoop's. We do not want to make those calls if the + * object involve has been destroyed. To prevent this, we + * provide a way to invalidate those requests when the object is + * destroyed. + * + * An object was passed to __invalidator() which added a callback to + * EventLoop::invalidate_request() to its "notify when destroyed" + * list. __invalidator() returned an InvalidationRecord that has been + * to passed to this function as data. + * + * The object is currently being destroyed and so we want to + * mark all requests involving this object that are queued with + * any EventLoop as invalid. + * + * As of April 2012, we are usign sigc::trackable as the base object + * used to queue calls to ::invalidate_request() to be made upon + * destruction, via its ::add_destroy_notify_callback() API. This is + * not necessarily ideal, but it is very close to precisely what we + * want, and many of the objects we want to do this with already + * inherit (indirectly) from sigc::trackable. + */ + if (ir->event_loop) { Glib::Mutex::Lock lm (ir->event_loop->slot_invalidation_mutex()); for (list::iterator i = ir->requests.begin(); i != ir->requests.end(); ++i) { diff --git a/libs/pbd/pbd/abstract_ui.cc b/libs/pbd/pbd/abstract_ui.cc index 4c13ec1b09..b300a62dd8 100644 --- a/libs/pbd/pbd/abstract_ui.cc +++ b/libs/pbd/pbd/abstract_ui.cc @@ -5,6 +5,7 @@ #include "pbd/abstract_ui.h" #include "pbd/pthread_utils.h" #include "pbd/failed_constructor.h" +#include "pbd/debug.h" #include "i18n.h" @@ -17,6 +18,14 @@ template void cleanup_request_buffer (void* ptr) { RequestBuffer* rb = (RequestBuffer*) ptr; + + /* there is the question of why we don't simply erase the request + * buffer and delete it right here, since we have to take the lock + * anyway. + * + * as of april 24th 2012, i don't have a good answer to that. + */ + { Glib::Mutex::Lock lm (rb->ui.request_buffer_map_lock); @@ -40,10 +49,25 @@ AbstractUI::AbstractUI (const string& name) template void AbstractUI::register_thread (string target_gui, pthread_t thread_id, string /*thread name*/, uint32_t num_requests) { + /* the calling thread wants to register with the thread that runs this + * UI's event loop, so that it will have its own per-thread queue of + * requests. this means that when it makes a request to this UI it can + * do so in a realtime-safe manner (no locks). + */ + if (target_gui != name()) { + /* this UI is not the UI that the calling thread is trying to + register with + */ return; } + /* the per_thread_request_buffer is a thread-private variable. + See pthreads documentation for more on these, but the key + thing is that it is a variable that as unique value for + each thread, guaranteed. + */ + RequestBuffer* b = per_thread_request_buffer.get(); if (b) { @@ -52,13 +76,30 @@ AbstractUI::register_thread (string target_gui, pthread_t thread_ return; } + /* create a new request queue/ringbuffer */ + b = new RequestBuffer (num_requests, *this); { + /* add the new request queue (ringbuffer) to our map + so that we can iterate over it when the time is right. + This step is not RT-safe, but is assumed to be called + only at thread initialization time, not repeatedly, + and so this is of little consequence. + */ Glib::Mutex::Lock lm (request_buffer_map_lock); request_buffers[thread_id] = b; } + /* set this thread's per_thread_request_buffer to this new + queue/ringbuffer. remember that only this thread will + get this queue when it calls per_thread_request_buffer.get() + + the second argument is a function that will be called + when the thread exits, and ensures that the buffer is marked + dead. it will then be deleted during a call to handle_ui_requests() + */ + per_thread_request_buffer.set (b, cleanup_request_buffer); } @@ -68,20 +109,38 @@ AbstractUI::get_request (RequestType rt) RequestBuffer* rbuf = per_thread_request_buffer.get (); RequestBufferVector vec; + /* see comments in ::register_thread() above for an explanation of + the per_thread_request_buffer variable + */ + if (rbuf != 0) { - /* we have a per-thread FIFO, use it */ + + /* the calling thread has registered with this UI and therefore + * we have a per-thread request queue/ringbuffer. use it. this + * "allocation" of a request is RT-safe. + */ rbuf->get_write_vector (&vec); if (vec.len[0] == 0) { + DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1: no space in per thread pool for request of type %2\n", name(), rt)); return 0; } + DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1: allocated per-thread request of type %2, caller %3\n", name(), rt, pthread_self())); + vec.buf[0]->type = rt; vec.buf[0]->valid = true; return vec.buf[0]; } + /* calling thread has not registered, so just allocate a new request on + * the heap. the lack of registration implies that realtime constraints + * are not at work. + */ + + DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1: allocated normal heap request of type %2, caller %3\n", name(), rt, pthread_self())); + RequestObject* req = new RequestObject; req->type = rt; @@ -94,7 +153,7 @@ AbstractUI::handle_ui_requests () RequestBufferMapIterator i; RequestBufferVector vec; - /* per-thread buffers first */ + /* check all registered per-thread buffers first */ request_buffer_map_lock.lock (); @@ -134,6 +193,8 @@ AbstractUI::handle_ui_requests () for (i = request_buffers.begin(); i != request_buffers.end(); ) { if ((*i).second->dead) { + DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1/%2 deleting dead per-thread request buffer for %3 @ %4\n", + name(), pthread_self(), i->first, i->second)); delete (*i).second; RequestBufferMapIterator tmp = i; ++tmp; @@ -156,11 +217,12 @@ AbstractUI::handle_ui_requests () /* We need to use this lock, because its the one returned by slot_invalidation_mutex() and protects - against request invalidation. + against request invalidation. */ request_buffer_map_lock.lock (); if (!req->valid) { + DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1/%2 handling invalid heap request, type %3, deleting\n", name(), pthread_self(), req->type)); delete req; request_buffer_map_lock.unlock (); continue; @@ -172,17 +234,48 @@ AbstractUI::handle_ui_requests () */ if (req->invalidation) { + DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1/%2 remove request from its invalidation list\n", name(), pthread_self())); + + /* after this call, if the object referenced by the + * invalidation record is deleted, it will no longer + * try to mark the request as invalid. + */ + req->invalidation->requests.remove (req); } + /* at this point, an object involved in a functor could be + * deleted before we actually execute the functor. so there is + * a race condition that makes the invalidation architecture + * somewhat pointless. + * + * really, we should only allow functors containing shared_ptr + * references to objects to enter into the request queue. + */ + request_buffer_map_lock.unlock (); + + /* unlock the request lock while we execute the request, so + * that we don't needlessly block other threads (note: not RT + * threads since they have their own queue) from making requests. + */ lm.release (); + DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1/%2 execute request type %3\n", name(), pthread_self(), req->type)); + + /* and lets do it ... this is a virtual call so that each + * specific type of UI can have its own set of requests without + * some kind of central request type registration logic + */ + do_request (req); + DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1/%2 delete heap request type %3\n", name(), pthread_self(), req->type)); delete req; + /* re-acquire the list lock so that we check again */ + lm.acquire(); } } @@ -190,25 +283,53 @@ AbstractUI::handle_ui_requests () template void AbstractUI::send_request (RequestObject *req) { + /* This is called to ask a given UI to carry out a request. It may be + * called from the same thread that runs the UI's event loop (see the + * caller_is_self() case below), or from any other thread. + */ + if (base_instance() == 0) { return; /* XXX is this the right thing to do ? */ } if (caller_is_self ()) { + /* the thread that runs this UI's event loop is sending itself + a request: we dispatch it immediately and inline. + */ + DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1/%2 direct dispatch of request type %3\n", name(), pthread_self(), req->type)); do_request (req); } else { + + /* If called from a different thread, we first check to see if + * the calling thread is registered with this UI. If so, there + * is a per-thread ringbuffer of requests that ::get_request() + * just set up a new request in. If so, all we need do here is + * to advance the write ptr in that ringbuffer so that the next + * request by this calling thread will use the next slot in + * the ringbuffer. The ringbuffer has + * single-reader/single-writer semantics because the calling + * thread is the only writer, and the UI event loop is the only + * reader. + */ + RequestBuffer* rbuf = per_thread_request_buffer.get (); if (rbuf != 0) { + DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1/%2 send per-thread request type %3\n", name(), pthread_self(), req->type)); rbuf->increment_write_ptr (1); } else { /* no per-thread buffer, so just use a list with a lock so that it remains single-reader/single-writer semantics */ + DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1/%2 send heap request type %3\n", name(), pthread_self(), req->type)); Glib::Mutex::Lock lm (request_list_lock); request_list.push_back (req); } + /* send the UI event loop thread a wakeup so that it will look + at the per-thread and generic request lists. + */ + request_channel.wakeup (); } } @@ -217,6 +338,7 @@ template void AbstractUI::call_slot (InvalidationRecord* invalidation, const boost::function& f) { if (caller_is_self()) { + DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1/%2 direct dispatch of call slot via functor @ %3, invalidation %4\n", name(), pthread_self(), &f, invalidation)); f (); return; } @@ -227,7 +349,20 @@ AbstractUI::call_slot (InvalidationRecord* invalidation, const bo return; } + DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1/%2 queue call-slot using functor @ %3, invalidation %4\n", name(), pthread_self(), &f, invalidation)); + + /* copy semantics: copy the functor into the request object */ + req->the_slot = f; + + /* the invalidation record is an object which will carry out + * invalidation of any requests associated with it when it is + * destroyed. it can be null. if its not null, associate this + * request with the invalidation record. this allows us to + * "cancel" requests submitted to the UI because they involved + * a functor that uses an object that is being deleted. + */ + req->invalidation = invalidation; if (invalidation) { diff --git a/libs/pbd/pbd/base_ui.h b/libs/pbd/pbd/base_ui.h index ccd4878cd9..414c9970d9 100644 --- a/libs/pbd/pbd/base_ui.h +++ b/libs/pbd/pbd/base_ui.h @@ -32,6 +32,15 @@ #include "pbd/crossthread.h" #include "pbd/event_loop.h" +/** A BaseUI is an abstraction designed to be used with any "user + * interface" (not necessarily graphical) that needs to wait on + * events/requests and dispatch/process them as they arrive. + * + * This implementation starts up a thread that runs a Glib main loop + * to wait on events/requests etc. + */ + + class BaseUI : virtual public sigc::trackable, public PBD::EventLoop { public: @@ -52,7 +61,13 @@ class BaseUI : virtual public sigc::trackable, public PBD::EventLoop static RequestType CallSlot; static RequestType Quit; + /** start up a thread to run the main loop + */ void run (); + + /** stop the thread running the main loop (and block + * until it exits) + */ void quit (); protected: @@ -62,9 +77,21 @@ class BaseUI : virtual public sigc::trackable, public PBD::EventLoop Glib::RefPtr _main_loop; Glib::Thread* run_loop_thread; + /** Derived UI objects can implement thread_init() + * which will be called by the event loop thread + * immediately before it enters the event loop. + */ + virtual void thread_init () {}; + + /** Called when there input ready on the request_channel + */ bool request_handler (Glib::IOCondition); + /** Derived UI objects must implement this method, + * which will be called whenever there are requests + * to be dealt with. + */ virtual void handle_ui_requests () = 0; private: diff --git a/libs/pbd/pbd/convert.h b/libs/pbd/pbd/convert.h index 937b5a7a9d..fec0248157 100644 --- a/libs/pbd/pbd/convert.h +++ b/libs/pbd/pbd/convert.h @@ -53,6 +53,8 @@ to_string (T t, std::ios_base & (*f)(std::ios_base&)) return oss.str(); } +bool string_is_affirmative (const std::string&); + const char * sgettext (const char *, const char *); diff --git a/libs/pbd/pbd/crossthread.h b/libs/pbd/pbd/crossthread.h index a791be5798..13ab37d38d 100644 --- a/libs/pbd/pbd/crossthread.h +++ b/libs/pbd/pbd/crossthread.h @@ -26,20 +26,60 @@ #include +/** A simple abstraction of a mechanism of signalling one thread from another. + * The signaller calls ::wakeup() to tell the signalled thread to check for + * work to be done. + * + * This implementation provides both ::selectable() for use in direct + * poll/select-based event loops, and a Glib::IOSource via ::ios() for use + * in Glib main loop based situations. + */ + class CrossThreadChannel { public: - CrossThreadChannel(bool); + /** if @a non_blocking is true, the channel will not cause blocking + * when used in an event loop based on poll/select or the glib main + * loop. + */ + CrossThreadChannel(bool non_blocking); ~CrossThreadChannel(); + /** Tell the listening thread that is has work to do. + */ void wakeup(); - int selectable() const { return fds[0]; } - + + /* if the listening thread cares about the precise message + * it is being sent, then ::deliver() can be used to send + * a single byte message rather than a simple wakeup. These + * two mechanisms should not be used on the same CrossThreadChannel + * because there is no way to know which byte value will be used + * for ::wakeup() + */ int deliver (char msg); + + /** if using ::deliver() to wakeup the listening thread, then + * the listener should call ::receive() to fetch the message + * type from the channel. + */ int receive (char& msg); - + + /** empty the channel of all requests. + * Typically this is done as soon as input + * is noticed on the channel, because the + * handler will look at a separately managed work + * queue. The actual number of queued "wakeups" + * in the channel will not be important. + */ void drain (); static void drain (int fd); + /** File descriptor that can be used with poll/select to + * detect when wakeup() has been called on this channel. + * It be marked as readable/input-ready when this condition + * is true. It has already been marked non-blocking. + */ + int selectable() const { return fds[0]; } + /* glibmm 2.22 and earlier has a terrifying bug that will cause crashes whenever a Source is removed from a MainContext (including the destruction of the MainContext), @@ -47,15 +87,17 @@ class CrossThreadChannel { the RefPtr. I (Paul) have fixed this (https://bugzilla.gnome.org/show_bug.cgi?id=561885) but in the meantime, we need a hack to get around the issue. */ - Glib::RefPtr ios(); void drop_ios (); + /** returns true if the CrossThreadChannel was + * correctly constructed. + */ bool ok() const { return fds[0] >= 0 && fds[1] >= 0; } private: Glib::RefPtr* _ios; // lazily constructed - int fds[2]; + int fds[2]; // current implementation uses a pipe/fifo }; #endif /* __pbd__crossthread_h__ */ diff --git a/libs/pbd/pbd/debug.h b/libs/pbd/pbd/debug.h index 43fc9090cc..989cd42dd1 100644 --- a/libs/pbd/pbd/debug.h +++ b/libs/pbd/pbd/debug.h @@ -41,6 +41,8 @@ namespace PBD { extern uint64_t Properties; extern uint64_t FileManager; extern uint64_t Pool; + extern uint64_t EventLoop; + extern uint64_t AbstractUI; } } diff --git a/libs/pbd/pbd/event_loop.h b/libs/pbd/pbd/event_loop.h index 6e7e42e9dd..450bf5752f 100644 --- a/libs/pbd/pbd/event_loop.h +++ b/libs/pbd/pbd/event_loop.h @@ -27,6 +27,15 @@ namespace PBD { +/** An EventLoop is as basic abstraction designed to be used with any "user + * interface" (not necessarily graphical) that needs to wait on + * events/requests and dispatch/process them as they arrive. + * + * This is a very basic class that doesn't by itself provide an actual + * event loop or thread. See BaseUI for the "real" object to be used + * when something like this is needed (it inherits from EventLoop). + */ + class EventLoop { public: diff --git a/libs/pbd/pbd/ringbufferNPT.h b/libs/pbd/pbd/ringbufferNPT.h index 0aed05355d..e43f967592 100644 --- a/libs/pbd/pbd/ringbufferNPT.h +++ b/libs/pbd/pbd/ringbufferNPT.h @@ -231,6 +231,7 @@ RingBufferNPT::get_read_vector (RingBufferNPT::rw_vector *vec) vec->buf[0] = &buf[r]; vec->len[0] = free_cnt; + vec->buf[1] = 0; vec->len[1] = 0; } } diff --git a/libs/pbd/pbd/semaphore.h b/libs/pbd/pbd/semaphore.h new file mode 100644 index 0000000000..f1b07ea4f5 --- /dev/null +++ b/libs/pbd/pbd/semaphore.h @@ -0,0 +1,198 @@ +/* + Copyright (C) 2012 Paul Davis + Author: David Robillard + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __pbd_semaphore_h__ +#define __pbd_semaphore_h__ + +#ifdef __APPLE__ +# include +#elif defined(_WIN32) +# include +#else +# include +# include +#endif + +#include "pbd/failed_constructor.h" + +namespace PBD { + +/** + Unnamed (process local) counting semaphore. + + The civilized person's synchronisation primitive. A counting semaphore is + an integer which is always non-negative, so, an attempted decrement (or + "wait") will block if the value is 0, until another thread does an increment + (or "post"). + + At least on Lignux, the main advantage of this is that it is fast and the + only safe way to reliably signal from a real-time audio thread. The + counting semantics also complement ringbuffers of events nicely. +*/ +class Semaphore +{ +public: + /** + Create a new semaphore. + + Chances are you want 1 wait() per 1 post(), an initial value of 0. + */ + inline Semaphore(unsigned initial); + + inline ~Semaphore(); + + /** Post/Increment/Signal */ + inline void post(); + + /** Wait/Decrement. Returns false on error. */ + inline bool wait(); + + /** Attempt Wait/Decrement. Returns true iff a decrement occurred. */ + inline bool try_wait(); + +private: +#if defined(__APPLE__) + semaphore_t _sem; // sem_t is a worthless broken mess on OSX +#elif defined(_WIN32) + HANDLE _sem; // types are overrated anyway +#else + sem_t _sem; +#endif +}; + +#ifdef __APPLE__ + +inline +Semaphore::Semaphore(unsigned initial) +{ + if (semaphore_create(mach_task_self(), &sem->sem, SYNC_POLICY_FIFO, 0)) { + throw failed_constructor(); + } +} + +inline +Semaphore::~Semaphore() +{ + semaphore_destroy(mach_task_self(), _sem); +} + +inline void +Semaphore::post() +{ + semaphore_signal(_sem); +} + +inline bool +Semaphore::wait() +{ + if (semaphore_wait(_sem) != KERN_SUCCESS) { + return false; + } + return true; +} + +inline bool +Semaphore::try_wait() +{ + const mach_timespec_t zero = { 0, 0 }; + return semaphore_timedwait(_sem, zero) == KERN_SUCCESS; +} + +#elif defined(_WIN32) + +inline +Semaphore::Semaphore(unsigned initial) +{ + if (!(_sem = CreateSemaphore(NULL, initial, LONG_MAX, NULL))) { + throw failed_constructor(); + } +} + +inline +Semaphore::~Semaphore() +{ + CloseHandle(_sem); +} + +inline void +Semaphore::post() +{ + ReleaseSemaphore(_sem, 1, NULL); +} + +inline bool +Semaphore::wait() +{ + if (WaitForSingleObject(_sem, INFINITE) != WAIT_OBJECT_0) { + return false; + } + return true; +} + +inline bool +Semaphore::try_wait() +{ + return WaitForSingleObject(sem->sem, 0) == WAIT_OBJECT_0; +} + +#else /* !defined(__APPLE__) && !defined(_WIN32) */ + +Semaphore::Semaphore(unsigned initial) +{ + if (sem_init(&_sem, 0, initial)) { + throw failed_constructor(); + } +} + +inline +Semaphore::~Semaphore() +{ + sem_destroy(&_sem); +} + +inline void +Semaphore::post() +{ + sem_post(&_sem); +} + +inline bool +Semaphore::wait() +{ + while (sem_wait(&_sem)) { + if (errno != EINTR) { + return false; // We are all doomed + } + /* Otherwise, interrupted (rare/weird), so try again. */ + } + + return true; +} + +inline bool +Semaphore::try_wait() +{ + return (sem_trywait(&_sem) == 0); +} + +#endif + +} // namespace PBD + +#endif /* __pbd_semaphore_h__ */ diff --git a/libs/rubberband/src/StretcherImpl.cpp b/libs/rubberband/src/StretcherImpl.cpp index 7ec7c161f3..200c8771a8 100644 --- a/libs/rubberband/src/StretcherImpl.cpp +++ b/libs/rubberband/src/StretcherImpl.cpp @@ -834,7 +834,11 @@ RubberBandStretcher::Impl::study(const float *const *input, size_t samples, bool // cd.accumulator is not otherwise used during studying, // so we can use it as a temporary buffer here +#ifdef NDEBUG + inbuf.peek(cd.accumulator, m_windowSize); +#else size_t got = inbuf.peek(cd.accumulator, m_windowSize); +#endif assert(final || got == m_windowSize); m_window->cut(cd.accumulator); diff --git a/libs/surfaces/control_protocol/basic_ui.cc b/libs/surfaces/control_protocol/basic_ui.cc index 9d55b3ac53..2c514f188e 100644 --- a/libs/surfaces/control_protocol/basic_ui.cc +++ b/libs/surfaces/control_protocol/basic_ui.cc @@ -19,6 +19,7 @@ */ #include "pbd/pthread_utils.h" +#include "pbd/memento_command.h" #include "ardour/session.h" #include "ardour/location.h" @@ -91,22 +92,29 @@ BasicUI::goto_end () } void -BasicUI::add_marker () +BasicUI::add_marker (const std::string& markername) { - framepos_t when = session->audible_frame(); - session->locations()->add (new Location (*session, when, when, _("unnamed"), Location::IsMark)); + framepos_t where = session->audible_frame(); + Location *location = new Location (*session, where, where, markername, Location::IsMark); + session->begin_reversible_command (_("add marker")); + XMLNode &before = session->locations()->get_state(); + session->locations()->add (location, true); + XMLNode &after = session->locations()->get_state(); + session->add_command (new MementoCommand(*(session->locations()), &before, &after)); + session->commit_reversible_command (); } void BasicUI::rewind () { - session->request_transport_speed (-2.0f); + std::cerr << "request transport speed of " << session->transport_speed() - 1.5 << std::endl; + session->request_transport_speed (session->transport_speed() - 1.5); } void BasicUI::ffwd () { - session->request_transport_speed (2.0f); + session->request_transport_speed (session->transport_speed() + 1.5); } void diff --git a/libs/surfaces/control_protocol/control_protocol.cc b/libs/surfaces/control_protocol/control_protocol.cc index 983208343a..ec58dc2f21 100644 --- a/libs/surfaces/control_protocol/control_protocol.cc +++ b/libs/surfaces/control_protocol/control_protocol.cc @@ -35,37 +35,34 @@ Signal0 ControlProtocol::ZoomToSession; Signal0 ControlProtocol::ZoomOut; Signal0 ControlProtocol::ZoomIn; Signal0 ControlProtocol::Enter; +Signal0 ControlProtocol::Undo; +Signal0 ControlProtocol::Redo; Signal1 ControlProtocol::ScrollTimeline; -Signal1 ControlProtocol::SelectByRID; +Signal1 ControlProtocol::GotoView; +Signal0 ControlProtocol::CloseDialog; +PBD::Signal0 ControlProtocol::VerticalZoomInAll; +PBD::Signal0 ControlProtocol::VerticalZoomOutAll; +PBD::Signal0 ControlProtocol::VerticalZoomInSelected; +PBD::Signal0 ControlProtocol::VerticalZoomOutSelected; +PBD::Signal1 ControlProtocol::TrackSelectionChanged; +PBD::Signal1 ControlProtocol::AddRouteToSelection; +PBD::Signal1 ControlProtocol::SetRouteSelection; +PBD::Signal1 ControlProtocol::RemoveRouteFromSelection; +PBD::Signal0 ControlProtocol::ClearRouteSelection; +PBD::Signal0 ControlProtocol::StepTracksDown; +PBD::Signal0 ControlProtocol::StepTracksUp; -ControlProtocol::ControlProtocol (Session& s, string str, EventLoop* evloop) - : BasicUI (s), - _name (str) +ControlProtocol::ControlProtocol (Session& s, string str) + : BasicUI (s) + , _name (str) + , _active (false) { - if (evloop) { - _own_event_loop = false; - _event_loop = evloop; - } else { - _own_event_loop = true; - fatal << "programming error: cannot create control protocols without an existing event loop (yet)" << endmsg; - /*NOTREACHED*/ - } - - _active = false; - - session->RouteAdded.connect (*this, MISSING_INVALIDATOR, boost::protect (boost::bind (&ControlProtocol::add_strip, this, _1)), _event_loop); } ControlProtocol::~ControlProtocol () { } -void -ControlProtocol::add_strip (ARDOUR::RouteList&) -{ - route_list_changed(); -} - void ControlProtocol::next_track (uint32_t initial_id) { diff --git a/libs/surfaces/control_protocol/control_protocol/basic_ui.h b/libs/surfaces/control_protocol/control_protocol/basic_ui.h index 3bd5ea1c0a..96a1227463 100644 --- a/libs/surfaces/control_protocol/control_protocol/basic_ui.h +++ b/libs/surfaces/control_protocol/control_protocol/basic_ui.h @@ -42,7 +42,7 @@ class BasicUI { BasicUI (ARDOUR::Session&); virtual ~BasicUI (); - void add_marker (); + void add_marker (const std::string& = std::string()); void register_thread (std::string name); diff --git a/libs/surfaces/control_protocol/control_protocol/control_protocol.h b/libs/surfaces/control_protocol/control_protocol/control_protocol.h index c05e7e291a..b75a04db2a 100644 --- a/libs/surfaces/control_protocol/control_protocol/control_protocol.h +++ b/libs/surfaces/control_protocol/control_protocol/control_protocol.h @@ -24,10 +24,14 @@ #include #include #include + #include + #include "pbd/stateful.h" #include "pbd/signals.h" + #include "control_protocol/basic_ui.h" +#include "control_protocol/types.h" namespace ARDOUR { @@ -35,9 +39,9 @@ class Route; class Session; class Bundle; -class ControlProtocol : virtual public sigc::trackable, public PBD::Stateful, public PBD::ScopedConnectionList, public BasicUI { +class ControlProtocol : public PBD::Stateful, public PBD::ScopedConnectionList, public BasicUI { public: - ControlProtocol (Session&, std::string name, PBD::EventLoop* event_loop); + ControlProtocol (Session&, std::string name); virtual ~ControlProtocol(); std::string name() const { return _name; } @@ -48,7 +52,7 @@ class ControlProtocol : virtual public sigc::trackable, public PBD::Stateful, pu virtual int set_feedback (bool /*yn*/) { return 0; } virtual bool get_feedback () const { return false; } - virtual void route_list_changed () {} + virtual void midi_connectivity_established () {} PBD::Signal0 ActiveChanged; @@ -60,8 +64,29 @@ class ControlProtocol : virtual public sigc::trackable, public PBD::Stateful, pu static PBD::Signal0 ZoomIn; static PBD::Signal0 ZoomOut; static PBD::Signal0 Enter; + static PBD::Signal0 Undo; + static PBD::Signal0 Redo; static PBD::Signal1 ScrollTimeline; - static PBD::Signal1 SelectByRID; + static PBD::Signal1 GotoView; + static PBD::Signal0 CloseDialog; + static PBD::Signal0 VerticalZoomInAll; + static PBD::Signal0 VerticalZoomOutAll; + static PBD::Signal0 VerticalZoomInSelected; + static PBD::Signal0 VerticalZoomOutSelected; + static PBD::Signal0 StepTracksDown; + static PBD::Signal0 StepTracksUp; + + static PBD::Signal1 AddRouteToSelection; + static PBD::Signal1 SetRouteSelection; + static PBD::Signal1 RemoveRouteFromSelection; + static PBD::Signal0 ClearRouteSelection; + + /* signals that one UI (e.g. the GUI) can emit to get all other UI's to + respond. Typically this will always be GUI->"others" - the GUI pays + no attention to these signals. + */ + + static PBD::Signal1 TrackSelectionChanged; /* the model here is as follows: @@ -107,14 +132,10 @@ class ControlProtocol : virtual public sigc::trackable, public PBD::Stateful, pu virtual void tear_down_gui() { } protected: - PBD::EventLoop* _event_loop; - bool _own_event_loop; std::vector > route_table; std::string _name; bool _active; - void add_strip (std::list >&); - void next_track (uint32_t initial_id); void prev_track (uint32_t initial_id); diff --git a/libs/surfaces/control_protocol/control_protocol/types.h b/libs/surfaces/control_protocol/control_protocol/types.h new file mode 100644 index 0000000000..0f81d87d0a --- /dev/null +++ b/libs/surfaces/control_protocol/control_protocol/types.h @@ -0,0 +1,34 @@ +/* + Copyright (C) 2006 Paul Davis + + This program is free software; you can redistribute it + and/or modify it under the terms of the GNU Lesser + General Public License as published by the Free Software + Foundation; either version 2 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __ardour_control_protocol_types_h__ +#define __ardour_control_protocol_types_h__ + +#include +#include + +namespace ARDOUR { + class Route; + + typedef std::vector > RouteNotificationList; + typedef boost::shared_ptr RouteNotificationListPtr; +} + +#endif /* __ardour_control_protocol_types_h__ */ diff --git a/libs/surfaces/generic_midi/generic_midi_control_protocol.cc b/libs/surfaces/generic_midi/generic_midi_control_protocol.cc index bff46462a7..c2220c6fde 100644 --- a/libs/surfaces/generic_midi/generic_midi_control_protocol.cc +++ b/libs/surfaces/generic_midi/generic_midi_control_protocol.cc @@ -51,10 +51,9 @@ using namespace std; #include "i18n.h" #define midi_ui_context() MidiControlUI::instance() /* a UICallback-derived object that specifies the event loop for signal handling */ -#define ui_bind(x) boost::protect (boost::bind ((x))) GenericMidiControlProtocol::GenericMidiControlProtocol (Session& s) - : ControlProtocol (s, _("Generic MIDI"), midi_ui_context()) + : ControlProtocol (s, _("Generic MIDI")) , _motorised (false) , gui (0) { @@ -68,7 +67,10 @@ GenericMidiControlProtocol::GenericMidiControlProtocol (Session& s) _current_bank = 0; _bank_size = 0; - /* XXX is it right to do all these in the same thread as whatever emits the signal? */ + /* these signals are emitted by the MidiControlUI's event loop thread + * and we may as well handle them right there in the same the same + * thread + */ Controllable::StartLearning.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::start_learning, this, _1)); Controllable::StopLearning.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::stop_learning, this, _1)); @@ -76,6 +78,17 @@ GenericMidiControlProtocol::GenericMidiControlProtocol (Session& s) Controllable::DeleteBinding.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::delete_binding, this, _1)); Session::SendFeedback.connect (*this, MISSING_INVALIDATOR, boost::bind (&GenericMidiControlProtocol::send_feedback, this), midi_ui_context());; +#if 0 + /* XXXX SOMETHING GOES WRONG HERE (april 2012) - STILL DEBUGGING */ + /* this signal is emitted by the process() callback, and if + * send_feedback() is going to do anything, it should do it in the + * context of the process() callback itself. + */ + + Session::SendFeedback.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::send_feedback, this)); +#endif + /* this one is cross-thread */ + Route::RemoteControlIDChange.connect (*this, MISSING_INVALIDATOR, boost::bind (&GenericMidiControlProtocol::reset_controllables, this), midi_ui_context()); reload_maps (); @@ -236,6 +249,9 @@ GenericMidiControlProtocol::set_feedback_interval (microseconds_t ms) void GenericMidiControlProtocol::send_feedback () { + /* This is executed in RT "process" context", so no blocking calls + */ + if (!do_feedback) { return; } @@ -256,6 +272,9 @@ GenericMidiControlProtocol::send_feedback () void GenericMidiControlProtocol::_send_feedback () { + /* This is executed in RT "process" context", so no blocking calls + */ + const int32_t bufsize = 16 * 1024; /* XXX too big */ MIDI::byte buf[bufsize]; int32_t bsize = bufsize; diff --git a/libs/surfaces/generic_midi/midifunction.cc b/libs/surfaces/generic_midi/midifunction.cc index 926ee95a89..302ed0b552 100644 --- a/libs/surfaces/generic_midi/midifunction.cc +++ b/libs/surfaces/generic_midi/midifunction.cc @@ -131,8 +131,9 @@ MIDIFunction::execute () if (!_argument.empty()) { uint32_t rid; sscanf (_argument.c_str(), "%d", &rid); - _ui->SelectByRID (rid); + _ui->SetRouteSelection (rid); } + break; case TrackSetMute: break; case TrackSetSolo: diff --git a/libs/surfaces/mackie/README b/libs/surfaces/mackie/README deleted file mode 100644 index 5ffac15e5f..0000000000 --- a/libs/surfaces/mackie/README +++ /dev/null @@ -1,18 +0,0 @@ -For usage, see - -http://www.ardour.org/files/manual/sn-mackie.html - -unfortunately it's a bit outdated, so to get the latest manual, go to the manual -directory in the ardour source tree, and say "make html" to build the latest. -Then point your favourite browser at tmp/index.html - -NOTES: - -* support for alsa/sequencer ports is currently broken. We're working on it. - -* you'll need to make port changes in etc/ardour3/ardour_system.rc and -etc/ardour3/ardour.rc, otherwise they don't stay changed in ~/.ardour3/ardour.rc - - -John Anderson -panic@semiosix.com diff --git a/libs/surfaces/mackie/bcf_surface.cc b/libs/surfaces/mackie/bcf_surface.cc deleted file mode 100644 index 0898e95ba1..0000000000 --- a/libs/surfaces/mackie/bcf_surface.cc +++ /dev/null @@ -1,42 +0,0 @@ -#include "bcf_surface.h" -#include "surface_port.h" -#include "mackie_midi_builder.h" - -#include - -using namespace Mackie; - -void BcfSurface::display_bank_start( SurfacePort & port, MackieMidiBuilder & builder, uint32_t current_bank ) -{ - if ( current_bank == 0 ) - { - // send Ar. to 2-char display on the master - port.write( builder.two_char_display( "Ar", ".." ) ); - } - else - { - // write the current first remote_id to the 2-char display - port.write( builder.two_char_display( current_bank ) ); - } -} - -void BcfSurface::zero_all( SurfacePort & port, MackieMidiBuilder & builder ) -{ - // clear 2-char display - port.write( builder.two_char_display( "LC" ) ); - - // and the led ring for the master strip - blank_jog_ring( port, builder ); -} - -void BcfSurface::blank_jog_ring( SurfacePort & port, MackieMidiBuilder & builder ) -{ - Control & control = *controls_by_name["jog"]; - port.write( builder.build_led_ring( dynamic_cast( control ), off ) ); -} - -float BcfSurface::scaled_delta( const ControlState & state, float current_speed ) -{ - return state.sign * ( std::pow( float(state.ticks + 1), 2 ) + current_speed ) / 100.0; -} - diff --git a/libs/surfaces/mackie/bcf_surface.h b/libs/surfaces/mackie/bcf_surface.h deleted file mode 100644 index f6a1511aea..0000000000 --- a/libs/surfaces/mackie/bcf_surface.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef mackie_surface_bcf_h -#define mackie_surface_bcf_h -/* - Initially generated by scripts/generate-surface.rb -*/ - -#include "surface.h" - -namespace Mackie -{ - -class MackieButtonHandler; - -class BcfSurface : public Surface -{ -public: - BcfSurface (uint32_t max_strips) : Surface (max_strips, 7) {} - - virtual void handle_button( MackieButtonHandler & mbh, ButtonState bs, Button & button ); - virtual void init_controls(); - - virtual void display_bank_start( SurfacePort & port, MackieMidiBuilder & builder, uint32_t current_bank ); - virtual void zero_all( SurfacePort & port, MackieMidiBuilder & builder ); - virtual void blank_jog_ring( SurfacePort & port, MackieMidiBuilder & builder ); - virtual bool has_timecode_display() const { return false; } - - virtual float scrub_scaling_factor() { return 50.0; } - virtual float scaled_delta( const ControlState & state, float current_speed ); - -}; - -} - -#endif diff --git a/libs/surfaces/mackie/bcf_surface_generated.cc b/libs/surfaces/mackie/bcf_surface_generated.cc deleted file mode 100644 index 8d68313b7b..0000000000 --- a/libs/surfaces/mackie/bcf_surface_generated.cc +++ /dev/null @@ -1,1461 +0,0 @@ -/* - Generated by scripts/generate-surface.rb -*/ - -#include "bcf_surface.h" - -#include "controls.h" -#include "mackie_button_handler.h" - -using namespace Mackie; - -void Mackie::BcfSurface::init_controls() -{ - // intialise groups and strips - Group * group = 0; - - // make sure there are enough strips - strips.resize( 7 ); - - group = new Group ( "user" ); - groups["user"] = group; - - group = new Group ( "assignment" ); - groups["assignment"] = group; - - group = new Group ( "none" ); - groups["none"] = group; - - group = new MasterStrip ( "master", 0 ); - groups["master"] = group; - strips[0] = dynamic_cast( group ); - - group = new Strip ( "strip_1", 0 ); - groups["strip_1"] = group; - strips[0] = dynamic_cast( group ); - - group = new Group ( "cursor" ); - groups["cursor"] = group; - - group = new Strip ( "strip_2", 1 ); - groups["strip_2"] = group; - strips[1] = dynamic_cast( group ); - - group = new Group ( "automation" ); - groups["automation"] = group; - - group = new Group ( "functions" ); - groups["functions"] = group; - - group = new Strip ( "strip_3", 2 ); - groups["strip_3"] = group; - strips[2] = dynamic_cast( group ); - - group = new Group ( "display" ); - groups["display"] = group; - - group = new Strip ( "strip_4", 3 ); - groups["strip_4"] = group; - strips[3] = dynamic_cast( group ); - - group = new Strip ( "strip_5", 4 ); - groups["strip_5"] = group; - strips[4] = dynamic_cast( group ); - - group = new Strip ( "strip_6", 5 ); - groups["strip_6"] = group; - strips[5] = dynamic_cast( group ); - - group = new Group ( "transport" ); - groups["transport"] = group; - - group = new Strip ( "strip_7", 6 ); - groups["strip_7"] = group; - strips[6] = dynamic_cast( group ); - - group = new Group ( "modifiers" ); - groups["modifiers"] = group; - - group = new Group ( "bank" ); - groups["bank"] = group; - - - // initialise controls - Fader * fader = 0; - Pot * pot = 0; - Button * button = 0; - Led * led = 0; - - group = groups["strip_1"]; - fader = new Fader ( 0, 1, "gain", *group ); - faders[0x00] = fader; - controls.push_back( fader ); - group->add( *fader ); - - group = groups["strip_2"]; - fader = new Fader ( 1, 2, "gain", *group ); - faders[0x01] = fader; - controls.push_back( fader ); - group->add( *fader ); - - group = groups["strip_3"]; - fader = new Fader ( 2, 3, "gain", *group ); - faders[0x02] = fader; - controls.push_back( fader ); - group->add( *fader ); - - group = groups["strip_4"]; - fader = new Fader ( 3, 4, "gain", *group ); - faders[0x03] = fader; - controls.push_back( fader ); - group->add( *fader ); - - group = groups["strip_5"]; - fader = new Fader ( 4, 5, "gain", *group ); - faders[0x04] = fader; - controls.push_back( fader ); - group->add( *fader ); - - group = groups["strip_6"]; - fader = new Fader ( 5, 6, "gain", *group ); - faders[0x05] = fader; - controls.push_back( fader ); - group->add( *fader ); - - group = groups["strip_7"]; - fader = new Fader ( 6, 7, "gain", *group ); - faders[0x06] = fader; - controls.push_back( fader ); - group->add( *fader ); - - group = groups["master"]; - fader = new Fader ( 7, 1, "gain", *group ); - faders[0x07] = fader; - controls.push_back( fader ); - group->add( *fader ); - - group = groups["strip_1"]; - pot = new Pot ( 16, 1, "vpot", *group ); - pots[0x10] = pot; - controls.push_back( pot ); - group->add( *pot ); - - group = groups["strip_2"]; - pot = new Pot ( 17, 2, "vpot", *group ); - pots[0x11] = pot; - controls.push_back( pot ); - group->add( *pot ); - - group = groups["strip_3"]; - pot = new Pot ( 18, 3, "vpot", *group ); - pots[0x12] = pot; - controls.push_back( pot ); - group->add( *pot ); - - group = groups["strip_4"]; - pot = new Pot ( 19, 4, "vpot", *group ); - pots[0x13] = pot; - controls.push_back( pot ); - group->add( *pot ); - - group = groups["strip_5"]; - pot = new Pot ( 20, 5, "vpot", *group ); - pots[0x14] = pot; - controls.push_back( pot ); - group->add( *pot ); - - group = groups["strip_6"]; - pot = new Pot ( 21, 6, "vpot", *group ); - pots[0x15] = pot; - controls.push_back( pot ); - group->add( *pot ); - - group = groups["strip_7"]; - pot = new Pot ( 22, 7, "vpot", *group ); - pots[0x16] = pot; - controls.push_back( pot ); - group->add( *pot ); - - group = groups["none"]; - pot = new Jog ( 23, 1, "jog", *group ); - pots[0x17] = pot; - controls.push_back( pot ); - controls_by_name["jog"] = pot; - group->add( *pot ); - - group = groups["none"]; - pot = new Pot ( 46, 1, "external", *group ); - pots[0x2e] = pot; - controls.push_back( pot ); - controls_by_name["external"] = pot; - group->add( *pot ); - - group = groups["strip_1"]; - button = new Button ( 24, 1, "recenable", *group ); - buttons[0x18] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_2"]; - button = new Button ( 25, 2, "recenable", *group ); - buttons[0x19] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_3"]; - button = new Button ( 26, 3, "recenable", *group ); - buttons[0x1a] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_4"]; - button = new Button ( 27, 4, "recenable", *group ); - buttons[0x1b] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_5"]; - button = new Button ( 28, 5, "recenable", *group ); - buttons[0x1c] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_6"]; - button = new Button ( 29, 6, "recenable", *group ); - buttons[0x1d] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_7"]; - button = new Button ( 30, 7, "recenable", *group ); - buttons[0x1e] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_1"]; - button = new Button ( 32, 1, "solo", *group ); - buttons[0x20] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_2"]; - button = new Button ( 33, 2, "solo", *group ); - buttons[0x21] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_3"]; - button = new Button ( 34, 3, "solo", *group ); - buttons[0x22] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_4"]; - button = new Button ( 35, 4, "solo", *group ); - buttons[0x23] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_5"]; - button = new Button ( 36, 5, "solo", *group ); - buttons[0x24] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_6"]; - button = new Button ( 37, 6, "solo", *group ); - buttons[0x25] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_7"]; - button = new Button ( 38, 7, "solo", *group ); - buttons[0x26] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_1"]; - button = new Button ( 16, 1, "mute", *group ); - buttons[0x10] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_2"]; - button = new Button ( 17, 2, "mute", *group ); - buttons[0x11] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_3"]; - button = new Button ( 18, 3, "mute", *group ); - buttons[0x12] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_4"]; - button = new Button ( 19, 4, "mute", *group ); - buttons[0x13] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_5"]; - button = new Button ( 20, 5, "mute", *group ); - buttons[0x14] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_6"]; - button = new Button ( 21, 6, "mute", *group ); - buttons[0x15] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_7"]; - button = new Button ( 22, 7, "mute", *group ); - buttons[0x16] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_1"]; - button = new Button ( 0, 1, "select", *group ); - buttons[0x00] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_2"]; - button = new Button ( 1, 2, "select", *group ); - buttons[0x01] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_3"]; - button = new Button ( 2, 3, "select", *group ); - buttons[0x02] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_4"]; - button = new Button ( 3, 4, "select", *group ); - buttons[0x03] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_5"]; - button = new Button ( 4, 5, "select", *group ); - buttons[0x04] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_6"]; - button = new Button ( 5, 6, "select", *group ); - buttons[0x05] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_7"]; - button = new Button ( 6, 7, "select", *group ); - buttons[0x06] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_1"]; - button = new Button ( 8, 1, "vselect", *group ); - buttons[0x08] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_2"]; - button = new Button ( 9, 2, "vselect", *group ); - buttons[0x09] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_3"]; - button = new Button ( 10, 3, "vselect", *group ); - buttons[0x0a] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_4"]; - button = new Button ( 11, 4, "vselect", *group ); - buttons[0x0b] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_5"]; - button = new Button ( 12, 5, "vselect", *group ); - buttons[0x0c] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_6"]; - button = new Button ( 13, 6, "vselect", *group ); - buttons[0x0d] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_7"]; - button = new Button ( 14, 7, "vselect", *group ); - buttons[0x0e] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["assignment"]; - button = new Button ( 40, 1, "io", *group ); - buttons[0x28] = button; - controls.push_back( button ); - controls_by_name["io"] = button; - group->add( *button ); - - group = groups["assignment"]; - button = new Button ( 90, 1, "sends", *group ); - buttons[0x5a] = button; - controls.push_back( button ); - controls_by_name["sends"] = button; - group->add( *button ); - - group = groups["assignment"]; - button = new Button ( 89, 1, "pan", *group ); - buttons[0x59] = button; - controls.push_back( button ); - controls_by_name["pan"] = button; - group->add( *button ); - - group = groups["assignment"]; - button = new Button ( 87, 1, "plugin", *group ); - buttons[0x57] = button; - controls.push_back( button ); - controls_by_name["plugin"] = button; - group->add( *button ); - - group = groups["functions"]; - button = new Button ( 88, 1, "drop", *group ); - buttons[0x58] = button; - controls.push_back( button ); - controls_by_name["drop"] = button; - group->add( *button ); - - group = groups["assignment"]; - button = new Button ( 45, 1, "zoom", *group ); - buttons[0x2d] = button; - controls.push_back( button ); - controls_by_name["zoom"] = button; - group->add( *button ); - - group = groups["bank"]; - button = new Button ( 46, 1, "left", *group ); - buttons[0x2e] = button; - controls.push_back( button ); - controls_by_name["left"] = button; - group->add( *button ); - - group = groups["bank"]; - button = new Button ( 47, 1, "right", *group ); - buttons[0x2f] = button; - controls.push_back( button ); - controls_by_name["right"] = button; - group->add( *button ); - - group = groups["bank"]; - button = new Button ( 48, 1, "channel_left", *group ); - buttons[0x30] = button; - controls.push_back( button ); - controls_by_name["channel_left"] = button; - group->add( *button ); - - group = groups["bank"]; - button = new Button ( 49, 1, "channel_right", *group ); - buttons[0x31] = button; - controls.push_back( button ); - controls_by_name["channel_right"] = button; - group->add( *button ); - - group = groups["none"]; - button = new Button ( 50, 1, "scrub", *group ); - buttons[0x32] = button; - controls.push_back( button ); - controls_by_name["scrub"] = button; - group->add( *button ); - - group = groups["none"]; - button = new Button ( 86, 1, "edit", *group ); - buttons[0x56] = button; - controls.push_back( button ); - controls_by_name["edit"] = button; - group->add( *button ); - - group = groups["display"]; - button = new Button ( 52, 1, "name_value", *group ); - buttons[0x34] = button; - controls.push_back( button ); - controls_by_name["name_value"] = button; - group->add( *button ); - - group = groups["display"]; - button = new Button ( 53, 1, "timecode_beats", *group ); - buttons[0x35] = button; - controls.push_back( button ); - controls_by_name["timecode_beats"] = button; - group->add( *button ); - - group = groups["none"]; - button = new Button ( 54, 1, "F1", *group ); - buttons[0x36] = button; - controls.push_back( button ); - controls_by_name["F1"] = button; - group->add( *button ); - - group = groups["none"]; - button = new Button ( 55, 1, "F2", *group ); - buttons[0x37] = button; - controls.push_back( button ); - controls_by_name["F2"] = button; - group->add( *button ); - - group = groups["none"]; - button = new Button ( 56, 1, "F3", *group ); - buttons[0x38] = button; - controls.push_back( button ); - controls_by_name["F3"] = button; - group->add( *button ); - - group = groups["none"]; - button = new Button ( 57, 1, "F4", *group ); - buttons[0x39] = button; - controls.push_back( button ); - controls_by_name["F4"] = button; - group->add( *button ); - - group = groups["none"]; - button = new Button ( 58, 1, "F5", *group ); - buttons[0x3a] = button; - controls.push_back( button ); - controls_by_name["F5"] = button; - group->add( *button ); - - group = groups["none"]; - button = new Button ( 59, 1, "F6", *group ); - buttons[0x3b] = button; - controls.push_back( button ); - controls_by_name["F6"] = button; - group->add( *button ); - - group = groups["none"]; - button = new Button ( 60, 1, "F7", *group ); - buttons[0x3c] = button; - controls.push_back( button ); - controls_by_name["F7"] = button; - group->add( *button ); - - group = groups["none"]; - button = new Button ( 61, 1, "F8", *group ); - buttons[0x3d] = button; - controls.push_back( button ); - controls_by_name["F8"] = button; - group->add( *button ); - - group = groups["none"]; - button = new Button ( 62, 1, "F9", *group ); - buttons[0x3e] = button; - controls.push_back( button ); - controls_by_name["F9"] = button; - group->add( *button ); - - group = groups["none"]; - button = new Button ( 63, 1, "F10", *group ); - buttons[0x3f] = button; - controls.push_back( button ); - controls_by_name["F10"] = button; - group->add( *button ); - - group = groups["none"]; - button = new Button ( 64, 1, "F11", *group ); - buttons[0x40] = button; - controls.push_back( button ); - controls_by_name["F11"] = button; - group->add( *button ); - - group = groups["none"]; - button = new Button ( 65, 1, "F12", *group ); - buttons[0x41] = button; - controls.push_back( button ); - controls_by_name["F12"] = button; - group->add( *button ); - - group = groups["none"]; - button = new Button ( 66, 1, "F13", *group ); - buttons[0x42] = button; - controls.push_back( button ); - controls_by_name["F13"] = button; - group->add( *button ); - - group = groups["none"]; - button = new Button ( 67, 1, "F14", *group ); - buttons[0x43] = button; - controls.push_back( button ); - controls_by_name["F14"] = button; - group->add( *button ); - - group = groups["none"]; - button = new Button ( 68, 1, "F15", *group ); - buttons[0x44] = button; - controls.push_back( button ); - controls_by_name["F15"] = button; - group->add( *button ); - - group = groups["none"]; - button = new Button ( 69, 1, "F16", *group ); - buttons[0x45] = button; - controls.push_back( button ); - controls_by_name["F16"] = button; - group->add( *button ); - - group = groups["none"]; - button = new Button ( 39, 1, "global_solo", *group ); - buttons[0x27] = button; - controls.push_back( button ); - controls_by_name["global_solo"] = button; - group->add( *button ); - - group = groups["modifiers"]; - button = new Button ( 80, 1, "option", *group ); - buttons[0x50] = button; - controls.push_back( button ); - controls_by_name["option"] = button; - group->add( *button ); - - group = groups["modifiers"]; - button = new Button ( 73, 1, "cmd_alt", *group ); - buttons[0x49] = button; - controls.push_back( button ); - controls_by_name["cmd_alt"] = button; - group->add( *button ); - - group = groups["automation"]; - button = new Button ( 74, 1, "on", *group ); - buttons[0x4a] = button; - controls.push_back( button ); - controls_by_name["on"] = button; - group->add( *button ); - - group = groups["automation"]; - button = new Button ( 75, 1, "rec_ready", *group ); - buttons[0x4b] = button; - controls.push_back( button ); - controls_by_name["rec_ready"] = button; - group->add( *button ); - - group = groups["functions"]; - button = new Button ( 76, 1, "undo", *group ); - buttons[0x4c] = button; - controls.push_back( button ); - controls_by_name["undo"] = button; - group->add( *button ); - - group = groups["automation"]; - button = new Button ( 95, 1, "snapshot", *group ); - buttons[0x5f] = button; - controls.push_back( button ); - controls_by_name["snapshot"] = button; - group->add( *button ); - - group = groups["functions"]; - button = new Button ( 79, 1, "redo", *group ); - buttons[0x4f] = button; - controls.push_back( button ); - controls_by_name["redo"] = button; - group->add( *button ); - - group = groups["functions"]; - button = new Button ( 71, 1, "marker", *group ); - buttons[0x47] = button; - controls.push_back( button ); - controls_by_name["marker"] = button; - group->add( *button ); - - group = groups["functions"]; - button = new Button ( 81, 1, "enter", *group ); - buttons[0x51] = button; - controls.push_back( button ); - controls_by_name["enter"] = button; - group->add( *button ); - - group = groups["functions"]; - button = new Button ( 82, 1, "cancel", *group ); - buttons[0x52] = button; - controls.push_back( button ); - controls_by_name["cancel"] = button; - group->add( *button ); - - group = groups["functions"]; - button = new Button ( 83, 1, "mixer", *group ); - buttons[0x53] = button; - controls.push_back( button ); - controls_by_name["mixer"] = button; - group->add( *button ); - - group = groups["functions"]; - button = new Button ( 77, 1, "save", *group ); - buttons[0x4d] = button; - controls.push_back( button ); - controls_by_name["save"] = button; - group->add( *button ); - - group = groups["transport"]; - button = new Button ( 91, 1, "frm_left", *group ); - buttons[0x5b] = button; - controls.push_back( button ); - controls_by_name["frm_left"] = button; - group->add( *button ); - - group = groups["transport"]; - button = new Button ( 92, 1, "frm_right", *group ); - buttons[0x5c] = button; - controls.push_back( button ); - controls_by_name["frm_right"] = button; - group->add( *button ); - - group = groups["transport"]; - button = new Button ( 70, 1, "loop", *group ); - buttons[0x46] = button; - controls.push_back( button ); - controls_by_name["loop"] = button; - group->add( *button ); - - group = groups["transport"]; - button = new Button ( 72, 1, "punch_in", *group ); - buttons[0x48] = button; - controls.push_back( button ); - controls_by_name["punch_in"] = button; - group->add( *button ); - - group = groups["transport"]; - button = new Button ( 78, 1, "punch_out", *group ); - buttons[0x4e] = button; - controls.push_back( button ); - controls_by_name["punch_out"] = button; - group->add( *button ); - - group = groups["transport"]; - button = new Button ( 42, 1, "home", *group ); - buttons[0x2a] = button; - controls.push_back( button ); - controls_by_name["home"] = button; - group->add( *button ); - - group = groups["transport"]; - button = new Button ( 41, 1, "end", *group ); - buttons[0x29] = button; - controls.push_back( button ); - controls_by_name["end"] = button; - group->add( *button ); - - group = groups["transport"]; - button = new Button ( 44, 1, "rewind", *group ); - buttons[0x2c] = button; - controls.push_back( button ); - controls_by_name["rewind"] = button; - group->add( *button ); - - group = groups["transport"]; - button = new Button ( 43, 1, "ffwd", *group ); - buttons[0x2b] = button; - controls.push_back( button ); - controls_by_name["ffwd"] = button; - group->add( *button ); - - group = groups["transport"]; - button = new Button ( 93, 1, "stop", *group ); - buttons[0x5d] = button; - controls.push_back( button ); - controls_by_name["stop"] = button; - group->add( *button ); - - group = groups["transport"]; - button = new Button ( 94, 1, "play", *group ); - buttons[0x5e] = button; - controls.push_back( button ); - controls_by_name["play"] = button; - group->add( *button ); - - group = groups["transport"]; - button = new Button ( 31, 1, "record", *group ); - buttons[0x1f] = button; - controls.push_back( button ); - controls_by_name["record"] = button; - group->add( *button ); - - group = groups["cursor"]; - button = new Button ( 96, 1, "cursor_up", *group ); - buttons[0x60] = button; - controls.push_back( button ); - controls_by_name["cursor_up"] = button; - group->add( *button ); - - group = groups["cursor"]; - button = new Button ( 97, 1, "cursor_down", *group ); - buttons[0x61] = button; - controls.push_back( button ); - controls_by_name["cursor_down"] = button; - group->add( *button ); - - group = groups["cursor"]; - button = new Button ( 98, 1, "cursor_left", *group ); - buttons[0x62] = button; - controls.push_back( button ); - controls_by_name["cursor_left"] = button; - group->add( *button ); - - group = groups["cursor"]; - button = new Button ( 99, 1, "cursor_right", *group ); - buttons[0x63] = button; - controls.push_back( button ); - controls_by_name["cursor_right"] = button; - group->add( *button ); - - group = groups["none"]; - button = new Button ( 100, 1, "dyn", *group ); - buttons[0x64] = button; - controls.push_back( button ); - controls_by_name["dyn"] = button; - group->add( *button ); - - group = groups["none"]; - button = new Button ( 101, 1, "flip", *group ); - buttons[0x65] = button; - controls.push_back( button ); - controls_by_name["flip"] = button; - group->add( *button ); - - group = groups["user"]; - button = new Button ( 102, 1, "user_a", *group ); - buttons[0x66] = button; - controls.push_back( button ); - controls_by_name["user_a"] = button; - group->add( *button ); - - group = groups["user"]; - button = new Button ( 103, 1, "user_b", *group ); - buttons[0x67] = button; - controls.push_back( button ); - controls_by_name["user_b"] = button; - group->add( *button ); - - group = groups["strip_1"]; - button = new Button ( 104, 1, "fader_touch", *group ); - buttons[0x68] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_2"]; - button = new Button ( 105, 2, "fader_touch", *group ); - buttons[0x69] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_3"]; - button = new Button ( 106, 3, "fader_touch", *group ); - buttons[0x6a] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_4"]; - button = new Button ( 107, 4, "fader_touch", *group ); - buttons[0x6b] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_5"]; - button = new Button ( 108, 5, "fader_touch", *group ); - buttons[0x6c] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_6"]; - button = new Button ( 109, 6, "fader_touch", *group ); - buttons[0x6d] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_7"]; - button = new Button ( 110, 7, "fader_touch", *group ); - buttons[0x6e] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["master"]; - button = new Button ( 111, 1, "fader_touch", *group ); - buttons[0x6f] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["master"]; - button = new Button ( 23, 1, "mute", *group ); - buttons[0x17] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["none"]; - button = new Button ( 51, 1, "clicking", *group ); - buttons[0x33] = button; - controls.push_back( button ); - controls_by_name["clicking"] = button; - group->add( *button ); - - group = groups["none"]; - led = new Led ( 113, 1, "timecode", *group ); - leds[0x71] = led; - controls.push_back( led ); - controls_by_name["timecode"] = led; - group->add( *led ); - - group = groups["none"]; - led = new Led ( 114, 1, "beats", *group ); - leds[0x72] = led; - controls.push_back( led ); - controls_by_name["beats"] = led; - group->add( *led ); - - group = groups["none"]; - led = new Led ( 115, 1, "solo", *group ); - leds[0x73] = led; - controls.push_back( led ); - controls_by_name["solo"] = led; - group->add( *led ); - - group = groups["none"]; - led = new Led ( 118, 1, "relay_click", *group ); - leds[0x76] = led; - controls.push_back( led ); - controls_by_name["relay_click"] = led; - group->add( *led ); - -} - -void Mackie::BcfSurface::handle_button( MackieButtonHandler & mbh, ButtonState bs, Button & button ) -{ - if ( bs != press && bs != release ) - { - mbh.update_led( button, none ); - return; - } - - LedState ls; - switch ( button.id() ) - { - - case 0x9028: // io - switch ( bs ) { - case press: ls = mbh.io_press( button ); break; - case release: ls = mbh.io_release( button ); break; - case neither: break; - } - break; - - case 0x905a: // sends - switch ( bs ) { - case press: ls = mbh.sends_press( button ); break; - case release: ls = mbh.sends_release( button ); break; - case neither: break; - } - break; - - case 0x9059: // pan - switch ( bs ) { - case press: ls = mbh.pan_press( button ); break; - case release: ls = mbh.pan_release( button ); break; - case neither: break; - } - break; - - case 0x9057: // plugin - switch ( bs ) { - case press: ls = mbh.plugin_press( button ); break; - case release: ls = mbh.plugin_release( button ); break; - case neither: break; - } - break; - - case 0x9058: // drop - switch ( bs ) { - case press: ls = mbh.drop_press( button ); break; - case release: ls = mbh.drop_release( button ); break; - case neither: break; - } - break; - - case 0x902d: // zoom - switch ( bs ) { - case press: ls = mbh.zoom_press( button ); break; - case release: ls = mbh.zoom_release( button ); break; - case neither: break; - } - break; - - case 0x902e: // left - switch ( bs ) { - case press: ls = mbh.left_press( button ); break; - case release: ls = mbh.left_release( button ); break; - case neither: break; - } - break; - - case 0x902f: // right - switch ( bs ) { - case press: ls = mbh.right_press( button ); break; - case release: ls = mbh.right_release( button ); break; - case neither: break; - } - break; - - case 0x9030: // channel_left - switch ( bs ) { - case press: ls = mbh.channel_left_press( button ); break; - case release: ls = mbh.channel_left_release( button ); break; - case neither: break; - } - break; - - case 0x9031: // channel_right - switch ( bs ) { - case press: ls = mbh.channel_right_press( button ); break; - case release: ls = mbh.channel_right_release( button ); break; - case neither: break; - } - break; - - case 0x9032: // scrub - switch ( bs ) { - case press: ls = mbh.scrub_press( button ); break; - case release: ls = mbh.scrub_release( button ); break; - case neither: break; - } - break; - - case 0x9056: // edit - switch ( bs ) { - case press: ls = mbh.edit_press( button ); break; - case release: ls = mbh.edit_release( button ); break; - case neither: break; - } - break; - - case 0x9034: // name_value - switch ( bs ) { - case press: ls = mbh.name_value_press( button ); break; - case release: ls = mbh.name_value_release( button ); break; - case neither: break; - } - break; - - case 0x9035: // timecode_beats - switch ( bs ) { - case press: ls = mbh.timecode_beats_press( button ); break; - case release: ls = mbh.timecode_beats_release( button ); break; - case neither: break; - } - break; - - case 0x9036: // F1 - switch ( bs ) { - case press: ls = mbh.F1_press( button ); break; - case release: ls = mbh.F1_release( button ); break; - case neither: break; - } - break; - - case 0x9037: // F2 - switch ( bs ) { - case press: ls = mbh.F2_press( button ); break; - case release: ls = mbh.F2_release( button ); break; - case neither: break; - } - break; - - case 0x9038: // F3 - switch ( bs ) { - case press: ls = mbh.F3_press( button ); break; - case release: ls = mbh.F3_release( button ); break; - case neither: break; - } - break; - - case 0x9039: // F4 - switch ( bs ) { - case press: ls = mbh.F4_press( button ); break; - case release: ls = mbh.F4_release( button ); break; - case neither: break; - } - break; - - case 0x903a: // F5 - switch ( bs ) { - case press: ls = mbh.F5_press( button ); break; - case release: ls = mbh.F5_release( button ); break; - case neither: break; - } - break; - - case 0x903b: // F6 - switch ( bs ) { - case press: ls = mbh.F6_press( button ); break; - case release: ls = mbh.F6_release( button ); break; - case neither: break; - } - break; - - case 0x903c: // F7 - switch ( bs ) { - case press: ls = mbh.F7_press( button ); break; - case release: ls = mbh.F7_release( button ); break; - case neither: break; - } - break; - - case 0x903d: // F8 - switch ( bs ) { - case press: ls = mbh.F8_press( button ); break; - case release: ls = mbh.F8_release( button ); break; - case neither: break; - } - break; - - case 0x903e: // F9 - switch ( bs ) { - case press: ls = mbh.F9_press( button ); break; - case release: ls = mbh.F9_release( button ); break; - case neither: break; - } - break; - - case 0x903f: // F10 - switch ( bs ) { - case press: ls = mbh.F10_press( button ); break; - case release: ls = mbh.F10_release( button ); break; - case neither: break; - } - break; - - case 0x9040: // F11 - switch ( bs ) { - case press: ls = mbh.F11_press( button ); break; - case release: ls = mbh.F11_release( button ); break; - case neither: break; - } - break; - - case 0x9041: // F12 - switch ( bs ) { - case press: ls = mbh.F12_press( button ); break; - case release: ls = mbh.F12_release( button ); break; - case neither: break; - } - break; - - case 0x9042: // F13 - switch ( bs ) { - case press: ls = mbh.F13_press( button ); break; - case release: ls = mbh.F13_release( button ); break; - case neither: break; - } - break; - - case 0x9043: // F14 - switch ( bs ) { - case press: ls = mbh.F14_press( button ); break; - case release: ls = mbh.F14_release( button ); break; - case neither: break; - } - break; - - case 0x9044: // F15 - switch ( bs ) { - case press: ls = mbh.F15_press( button ); break; - case release: ls = mbh.F15_release( button ); break; - case neither: break; - } - break; - - case 0x9045: // F16 - switch ( bs ) { - case press: ls = mbh.F16_press( button ); break; - case release: ls = mbh.F16_release( button ); break; - case neither: break; - } - break; - - case 0x9027: // global_solo - switch ( bs ) { - case press: ls = mbh.global_solo_press( button ); break; - case release: ls = mbh.global_solo_release( button ); break; - case neither: break; - } - break; - - case 0x9050: // option - switch ( bs ) { - case press: ls = mbh.option_press( button ); break; - case release: ls = mbh.option_release( button ); break; - case neither: break; - } - break; - - case 0x9049: // cmd_alt - switch ( bs ) { - case press: ls = mbh.cmd_alt_press( button ); break; - case release: ls = mbh.cmd_alt_release( button ); break; - case neither: break; - } - break; - - case 0x904a: // on - switch ( bs ) { - case press: ls = mbh.on_press( button ); break; - case release: ls = mbh.on_release( button ); break; - case neither: break; - } - break; - - case 0x904b: // rec_ready - switch ( bs ) { - case press: ls = mbh.rec_ready_press( button ); break; - case release: ls = mbh.rec_ready_release( button ); break; - case neither: break; - } - break; - - case 0x904c: // undo - switch ( bs ) { - case press: ls = mbh.undo_press( button ); break; - case release: ls = mbh.undo_release( button ); break; - case neither: break; - } - break; - - case 0x905f: // snapshot - switch ( bs ) { - case press: ls = mbh.snapshot_press( button ); break; - case release: ls = mbh.snapshot_release( button ); break; - case neither: break; - } - break; - - case 0x904f: // redo - switch ( bs ) { - case press: ls = mbh.redo_press( button ); break; - case release: ls = mbh.redo_release( button ); break; - case neither: break; - } - break; - - case 0x9047: // marker - switch ( bs ) { - case press: ls = mbh.marker_press( button ); break; - case release: ls = mbh.marker_release( button ); break; - case neither: break; - } - break; - - case 0x9051: // enter - switch ( bs ) { - case press: ls = mbh.enter_press( button ); break; - case release: ls = mbh.enter_release( button ); break; - case neither: break; - } - break; - - case 0x9052: // cancel - switch ( bs ) { - case press: ls = mbh.cancel_press( button ); break; - case release: ls = mbh.cancel_release( button ); break; - case neither: break; - } - break; - - case 0x9053: // mixer - switch ( bs ) { - case press: ls = mbh.mixer_press( button ); break; - case release: ls = mbh.mixer_release( button ); break; - case neither: break; - } - break; - - case 0x904d: // save - switch ( bs ) { - case press: ls = mbh.save_press( button ); break; - case release: ls = mbh.save_release( button ); break; - case neither: break; - } - break; - - case 0x905b: // frm_left - switch ( bs ) { - case press: ls = mbh.frm_left_press( button ); break; - case release: ls = mbh.frm_left_release( button ); break; - case neither: break; - } - break; - - case 0x905c: // frm_right - switch ( bs ) { - case press: ls = mbh.frm_right_press( button ); break; - case release: ls = mbh.frm_right_release( button ); break; - case neither: break; - } - break; - - case 0x9046: // loop - switch ( bs ) { - case press: ls = mbh.loop_press( button ); break; - case release: ls = mbh.loop_release( button ); break; - case neither: break; - } - break; - - case 0x9048: // punch_in - switch ( bs ) { - case press: ls = mbh.punch_in_press( button ); break; - case release: ls = mbh.punch_in_release( button ); break; - case neither: break; - } - break; - - case 0x904e: // punch_out - switch ( bs ) { - case press: ls = mbh.punch_out_press( button ); break; - case release: ls = mbh.punch_out_release( button ); break; - case neither: break; - } - break; - - case 0x902a: // home - switch ( bs ) { - case press: ls = mbh.home_press( button ); break; - case release: ls = mbh.home_release( button ); break; - case neither: break; - } - break; - - case 0x9029: // end - switch ( bs ) { - case press: ls = mbh.end_press( button ); break; - case release: ls = mbh.end_release( button ); break; - case neither: break; - } - break; - - case 0x902c: // rewind - switch ( bs ) { - case press: ls = mbh.rewind_press( button ); break; - case release: ls = mbh.rewind_release( button ); break; - case neither: break; - } - break; - - case 0x902b: // ffwd - switch ( bs ) { - case press: ls = mbh.ffwd_press( button ); break; - case release: ls = mbh.ffwd_release( button ); break; - case neither: break; - } - break; - - case 0x905d: // stop - switch ( bs ) { - case press: ls = mbh.stop_press( button ); break; - case release: ls = mbh.stop_release( button ); break; - case neither: break; - } - break; - - case 0x905e: // play - switch ( bs ) { - case press: ls = mbh.play_press( button ); break; - case release: ls = mbh.play_release( button ); break; - case neither: break; - } - break; - - case 0x901f: // record - switch ( bs ) { - case press: ls = mbh.record_press( button ); break; - case release: ls = mbh.record_release( button ); break; - case neither: break; - } - break; - - case 0x9060: // cursor_up - switch ( bs ) { - case press: ls = mbh.cursor_up_press( button ); break; - case release: ls = mbh.cursor_up_release( button ); break; - case neither: break; - } - break; - - case 0x9061: // cursor_down - switch ( bs ) { - case press: ls = mbh.cursor_down_press( button ); break; - case release: ls = mbh.cursor_down_release( button ); break; - case neither: break; - } - break; - - case 0x9062: // cursor_left - switch ( bs ) { - case press: ls = mbh.cursor_left_press( button ); break; - case release: ls = mbh.cursor_left_release( button ); break; - case neither: break; - } - break; - - case 0x9063: // cursor_right - switch ( bs ) { - case press: ls = mbh.cursor_right_press( button ); break; - case release: ls = mbh.cursor_right_release( button ); break; - case neither: break; - } - break; - - case 0x9064: // dyn - switch ( bs ) { - case press: ls = mbh.dyn_press( button ); break; - case release: ls = mbh.dyn_release( button ); break; - case neither: break; - } - break; - - case 0x9065: // flip - switch ( bs ) { - case press: ls = mbh.flip_press( button ); break; - case release: ls = mbh.flip_release( button ); break; - case neither: break; - } - break; - - case 0x9066: // user_a - switch ( bs ) { - case press: ls = mbh.user_a_press( button ); break; - case release: ls = mbh.user_a_release( button ); break; - case neither: break; - } - break; - - case 0x9067: // user_b - switch ( bs ) { - case press: ls = mbh.user_b_press( button ); break; - case release: ls = mbh.user_b_release( button ); break; - case neither: break; - } - break; - - case 0x9033: // clicking - switch ( bs ) { - case press: ls = mbh.clicking_press( button ); break; - case release: ls = mbh.clicking_release( button ); break; - case neither: break; - } - break; - - } - mbh.update_led( button, ls ); -} diff --git a/libs/surfaces/mackie/button.cc b/libs/surfaces/mackie/button.cc new file mode 100644 index 0000000000..dc26429a63 --- /dev/null +++ b/libs/surfaces/mackie/button.cc @@ -0,0 +1,241 @@ +/* + Copyright (C) 2006,2007 John Anderson + Copyright (C) 2012 Paul Davis + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include + +#include "button.h" +#include "surface.h" +#include "control_group.h" + +using namespace Mackie; + +Control* +Button::factory (Surface& surface, Button::ID bid, int id, const std::string& name, Group& group) +{ + Button* b = new Button (bid, id, name, group); + /* store button with the device-specific ID */ + surface.buttons[id] = b; + surface.controls.push_back (b); + group.add (*b); + return b; +} + +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; } + + /* 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; } + + return -1; +} + +std::string +Button::id_to_name (Button::ID id) +{ + if (id == IO) { return "IO"; } + if (id == Sends) { return "Sends"; } + if (id == Pan) { return "Pan"; } + if (id == Plugin) { return "Plugin"; } + if (id == Eq) { return "Eq"; } + if (id == Dyn) { return "Dyn"; } + if (id == Left) { return "Bank Left"; } + if (id == Right) { return "Bank Right"; } + if (id == ChannelLeft) { return "Channel Left"; } + if (id == ChannelRight) { return "Channel Right"; } + if (id == Flip) { return "Flip"; } + if (id == Edit) { return "Edit"; } + if (id == NameValue) { return "Name/Value"; } + if (id == TimecodeBeats) { return "Timecode/Beats"; } + if (id == F1) { return "F1"; } + if (id == F2) { return "F2"; } + if (id == F3) { return "F3"; } + if (id == F4) { return "F4"; } + if (id == F5) { return "F5"; } + if (id == F6) { return "F6"; } + if (id == F7) { return "F7"; } + if (id == F8) { return "F8"; } + if (id == F9) { return "F9"; } + if (id == F10) { return "F10"; } + if (id == F11) { return "F11"; } + if (id == F12) { return "F12"; } + if (id == F13) { return "F13"; } + if (id == F14) { return "F14"; } + if (id == F15) { return "F15"; } + if (id == F16) { return "F16"; } + if (id == Shift) { return "Shift"; } + if (id == Option) { return "Option"; } + if (id == Ctrl) { return "Ctrl"; } + if (id == CmdAlt) { return "CmdAlt"; } + if (id == On) { return "On"; } + if (id == RecReady) { return "Record"; } + if (id == Undo) { return "Undo"; } + if (id == Save) { return "Save"; } + if (id == Touch) { return "Touch"; } + if (id == Redo) { return "Redo"; } + if (id == Marker) { return "Marker"; } + if (id == Enter) { return "Enter"; } + if (id == Cancel) { return "Cancel"; } + if (id == Mixer) { return "Mixer"; } + if (id == FrmLeft) { return "Frm Left"; } + if (id == FrmRight) { return "Frm Right"; } + if (id == Loop) { return "Loop"; } + if (id == PunchIn) { return "Punch In"; } + if (id == PunchOut) { return "Punch Out"; } + if (id == Home) { return "Home"; } + if (id == End) { return "End"; } + if (id == Rewind) { return "Rewind"; } + if (id == Ffwd) { return "FFwd"; } + if (id == Stop) { return "Stop"; } + if (id == Play) { return "Play"; } + if (id == Record) { return "Record"; } + if (id == CursorUp) { return "Cursor Up"; } + if (id == CursorDown) { return "Cursor Down"; } + if (id == CursorLeft) { return "Cursor Left"; } + if (id == CursorRight) { return "Cursor Right"; } + if (id == Zoom) { return "Zoom"; } + if (id == Scrub) { return "Scrub"; } + if (id == UserA) { return "User A"; } + if (id == UserB) { return "User B"; } + if (id == Snapshot) { return "Snapshot"; } + if (id == Read) { return "Read"; } + if (id == Write) { return "Write"; } + if (id == FdrGroup) { return "Fader Group"; } + if (id == ClearSolo) { return "Clear Solo"; } + if (id == Track) { return "Track"; } + if (id == Send) { return "Send"; } + if (id == MidiTracks) { return "Midi Tracks"; } + if (id == Inputs) { return "Inputs"; } + if (id == AudioTracks) { return "Audio Tracks"; } + if (id == AudioInstruments) { return "Audio Instruments"; } + if (id == Aux) { return "Aux"; } + if (id == Busses) { return "Busses"; } + if (id == Outputs) { return "Outputs"; } + if (id == User) { return "User"; } + if (id == Trim) { return "Trim"; } + if (id == Latch) { return "Latch"; } + if (id == Grp) { return "Group"; } + if (id == Nudge) { return "Nudge"; } + if (id == Drop) { return "Drop"; } + if (id == Replace) { return "Replace"; } + if (id == Click) { return "Click"; } + if (id == View) { return "View"; } + + if (id == RecEnable) { return "Record Enable"; } + if (id == Solo) { return "Solo"; } + if (id == Mute) { return "Mute"; } + if (id == Select) { return "Select"; } + if (id == VSelect) { return "V-Pot"; } + if (id == FaderTouch) { return "Fader Touch"; } + + return "???"; +} diff --git a/libs/surfaces/mackie/button.h b/libs/surfaces/mackie/button.h new file mode 100644 index 0000000000..b90f4b21a3 --- /dev/null +++ b/libs/surfaces/mackie/button.h @@ -0,0 +1,163 @@ +/* + Copyright (C) 2006,2007 John Anderson + Copyright (C) 2012 Paul Davis + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __ardour_mackie_control_protocol_button_h__ +#define __ardour_mackie_control_protocol_button_h__ + +#include "controls.h" +#include "led.h" + +namespace Mackie { + +class Surface; + +class Button : public Control +{ +public: +/* These values uniquely identify each possible button that an MCP device may + send. Each DeviceInfo object contains its own set of button definitions that + define what device ID will be sent for each button, and there is no reason + for them to be the same. */ + + enum ID { + /* Global Buttons */ + + IO, + Sends, + Pan, + Plugin, + Eq, + Dyn, + Left, + Right, + ChannelLeft, + ChannelRight, + Flip, + Edit, + NameValue, + TimecodeBeats, + F1, + F2, + F3, + F4, + F5, + F6, + F7, + F8, + F9, + F10, + F11, + F12, + F13, + F14, + F15, + F16, + Shift, + Option, + Ctrl, + CmdAlt, + On, + RecReady, + Undo, + Save, + Touch, + Redo, + Marker, + Enter, + Cancel, + Mixer, + FrmLeft, + FrmRight, + Loop, + PunchIn, + PunchOut, + Home, + End, + Rewind, + Ffwd, + Stop, + Play, + Record, + CursorUp, + CursorDown, + CursorLeft, + CursorRight, + Zoom, + Scrub, + UserA, + UserB, + Snapshot, + Read, + Write, + FdrGroup, + ClearSolo, + Track, + Send, + MidiTracks, + Inputs, + AudioTracks, + AudioInstruments, + Aux, + Busses, + Outputs, + User, + Trim, + Latch, + Grp, + Nudge, + Drop, + Replace, + Click, + View, + + FinalGlobalButton, + + /* Strip buttons */ + + RecEnable, + Solo, + Mute, + Select, + VSelect, + FaderTouch, + }; + + + Button (ID bid, int did, std::string name, Group & group) + : Control (did, name, group) + , _bid (bid) + , _led (did, name + "_led", group) {} + + MidiByteArray zero() { return _led.zero (); } + MidiByteArray set_state (LedState ls) { return _led.set_state (ls); } + + ID bid() const { return _bid; } + + static Control* factory (Surface& surface, Button::ID bid, int id, const std::string&, Group& group); + static int name_to_id (const std::string& name); + static std::string id_to_name (Button::ID); + +private: + ID _bid; /* device independent button ID */ + Led _led; +}; + +} + +#endif diff --git a/libs/surfaces/mackie/control_group.h b/libs/surfaces/mackie/control_group.h new file mode 100644 index 0000000000..1b73770fa2 --- /dev/null +++ b/libs/surfaces/mackie/control_group.h @@ -0,0 +1,42 @@ +#ifndef __ardour_mackie_control_protocol_control_group_h__ +#define __ardour_mackie_control_protocol_control_group_h__ + +#include + +namespace Mackie { + +class Control; + +/** + This is a loose group of controls, eg cursor buttons, + transport buttons, functions buttons etc. +*/ +class Group +{ +public: + Group (const std::string & name) + : _name (name) {} + + virtual ~Group() {} + + virtual bool is_strip() const { return false; } + virtual bool is_master() const { return false; } + + virtual void add (Control & control); + + const std::string & name() const { return _name; } + void set_name (const std::string & rhs) { _name = rhs; } + + typedef std::vector Controls; + const Controls & controls() const { return _controls; } + +protected: + Controls _controls; + +private: + std::string _name; +}; + +} + +#endif diff --git a/libs/surfaces/mackie/controls.cc b/libs/surfaces/mackie/controls.cc index ca7e086042..e5b8a28ab7 100644 --- a/libs/surfaces/mackie/controls.cc +++ b/libs/surfaces/mackie/controls.cc @@ -1,4 +1,4 @@ -/* + /* Copyright (C) 2006,2007 John Anderson This program is free software; you can redistribute it and/or modify @@ -15,168 +15,42 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include "controls.h" -#include "types.h" -#include "mackie_midi_builder.h" #include #include #include +#include "ardour/automation_control.h" + +#include "controls.h" +#include "types.h" +#include "surface.h" +#include "control_group.h" +#include "button.h" +#include "led.h" +#include "pot.h" +#include "fader.h" +#include "jog.h" +#include "meter.h" + + using namespace Mackie; using namespace std; +using ARDOUR::AutomationControl; -void Group::add( Control & control ) +void Group::add (Control& control) { - _controls.push_back( &control ); + _controls.push_back (&control); } -Strip::Strip( const std::string & name, int index ) - : Group( name ) - , _solo( 0 ) - , _recenable( 0 ) - , _mute( 0 ) - , _select( 0 ) - , _vselect( 0 ) - , _fader_touch( 0 ) - , _vpot( 0 ) - , _gain( 0 ) - , _index( index ) +Control::Control (int id, std::string name, Group & group) + : _id (id) + , _name (name) + , _group (group) + , _in_use (false) { } -/** - TODO could optimise this to use enum, but it's only - called during the protocol class instantiation. - - generated using - - irb -r controls.rb - sf=Surface.new - sf.parse - controls = sf.groups.find{|x| x[0] =~ /strip/}.each{|x| puts x[1]} - controls[1].each {|x| puts "\telse if ( control.name() == \"#{x.name}\" )\n\t{\n\t\t_#{x.name} = reinterpret_cast<#{x.class.name}*>(&control);\n\t}\n"} -*/ -void Strip::add( Control & control ) -{ - Group::add( control ); - if ( control.name() == "gain" ) - { - _gain = reinterpret_cast(&control); - } - else if ( control.name() == "vpot" ) - { - _vpot = reinterpret_cast(&control); - } - else if ( control.name() == "recenable" ) - { - _recenable = reinterpret_cast(&control); - } - else if ( control.name() == "solo" ) - { - _solo = reinterpret_cast(&control); - } - else if ( control.name() == "mute" ) - { - _mute = reinterpret_cast(&control); - } - else if ( control.name() == "select" ) - { - _select = reinterpret_cast(&control); - } - else if ( control.name() == "vselect" ) - { - _vselect = reinterpret_cast(&control); - } - else if ( control.name() == "fader_touch" ) - { - _fader_touch = reinterpret_cast(&control); - } - else if ( control.type() == Control::type_led || control.type() == Control::type_led_ring ) - { - // do nothing - cout << "Strip::add not adding " << control << endl; - } - else - { - ostringstream os; - os << "Strip::add: unknown control type " << control; - throw MackieControlException( os.str() ); - } -} - -Control::Control( int id, int ordinal, std::string name, Group & group ) -: _id( id ) -, _ordinal( ordinal ) -, _name( name ) -, _group( group ) -, _in_use( false ) -{ -} - -/** - generated with - -controls[1].each do |x| - puts < ac) { - os << typeid( strip ).name(); - os << " { "; - os << "has_solo: " << boolalpha << strip.has_solo(); - os << ", "; - os << "has_recenable: " << boolalpha << strip.has_recenable(); - os << ", "; - os << "has_mute: " << boolalpha << strip.has_mute(); - os << ", "; - os << "has_select: " << boolalpha << strip.has_select(); - os << ", "; - os << "has_vselect: " << boolalpha << strip.has_vselect(); - os << ", "; - os << "has_fader_touch: " << boolalpha << strip.has_fader_touch(); - os << ", "; - os << "has_vpot: " << boolalpha << strip.has_vpot(); - os << ", "; - os << "has_gain: " << boolalpha << strip.has_gain(); - os << " }"; - - return os; + normal_ac = ac; } + +void +Control::set_value (float val) +{ + if (normal_ac) { + normal_ac->set_value (normal_ac->interface_to_internal (val)); + } +} + +float +Control::get_value () +{ + if (!normal_ac) { + return 0.0f; + } + return normal_ac->internal_to_interface (normal_ac->get_value()); +} + +void +Control::start_touch (double when) +{ + if (normal_ac) { + return normal_ac->start_touch (when); + } +} + +void +Control::stop_touch (double when, bool mark) +{ + if (normal_ac) { + return normal_ac->stop_touch (when, mark); + } +} + diff --git a/libs/surfaces/mackie/controls.h b/libs/surfaces/mackie/controls.h index 8ccf7dbf03..38dad7ef7f 100644 --- a/libs/surfaces/mackie/controls.h +++ b/libs/surfaces/mackie/controls.h @@ -1,5 +1,6 @@ /* Copyright (C) 2006,2007 John Anderson + Copyright (C) 2012 Paul Davis This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -15,204 +16,42 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#ifndef mackie_controls_h -#define mackie_controls_h + +#ifndef __mackie_controls_h__ +#define __mackie_controls_h__ #include #include #include +#include + +#include #include "pbd/signals.h" #include "mackie_control_exception.h" +#include "midi_byte_array.h" + +namespace ARDOUR { + class AutomationControl; +} namespace Mackie { -class Control; +class Strip; +class Group; +class Surface; -/** - This is a loose group of controls, eg cursor buttons, - transport buttons, functions buttons etc. -*/ -class Group -{ -public: - Group( const std::string & name ) - : _name( name ) - { - } - - virtual ~Group() {} - - virtual bool is_strip() const - { - return false; - } - - virtual bool is_master() const - { - return false; - } - - virtual void add( Control & control ); - - const std::string & name() const - { - return _name; - } - - // This is for Surface only - void name( const std::string & rhs ) { _name = rhs; } - - typedef std::vector Controls; - const Controls & controls() const { return _controls; } - -protected: - Controls _controls; - -private: - std::string _name; -}; - -class Button; -class Pot; -class Fader; - -/** - This is the set of controls that make up a strip. -*/ -class Strip : public Group -{ -public: - /** - \param is the index of the strip. 0-based. - */ - Strip( const std::string & name, int index ); - - virtual bool is_strip() const - { - return true; - } - - virtual void add( Control & control ); - - /// This is the index of the strip. zero-based. - int index() const { return _index; } - - /// This is for Surface only - /// index is zero-based - void index( int rhs ) { _index = rhs; } - - Button & solo(); - Button & recenable(); - Button & mute(); - Button & select(); - Button & vselect(); - Button & fader_touch(); - Pot & vpot(); - Fader & gain(); - - bool has_solo() const { return _solo != 0; } - bool has_recenable() const { return _recenable != 0; } - bool has_mute() const { return _mute != 0; } - bool has_select() const { return _select != 0; } - bool has_vselect() const { return _vselect != 0; } - bool has_fader_touch() const { return _fader_touch != 0; } - bool has_vpot() const { return _vpot != 0; } - bool has_gain() const { return _gain != 0; } - -private: - Button * _solo; - Button * _recenable; - Button * _mute; - Button * _select; - Button * _vselect; - Button * _fader_touch; - Pot * _vpot; - Fader * _gain; - int _index; -}; - -std::ostream & operator << ( std::ostream &, const Strip & ); - -class MasterStrip : public Strip -{ -public: - MasterStrip( const std::string & name, int index ) - : Strip( name, index ) - { - } - - virtual bool is_master() const - { - return true; - } -}; - -class Led; - -/** - The base class for controls on the surface. They deliberately - don't know the midi protocol for updating them. -*/ class Control { public: - enum type_t { type_led, type_led_ring, type_fader = 0xe0, type_button = 0x90, type_pot = 0xb0 }; - - Control( int id, int ordinal, std::string name, Group & group ); + Control (int id, std::string name, Group& group); virtual ~Control() {} - virtual const Led & led() const - { - throw MackieControlException( "no led available" ); - } - - /// type() << 8 + midi id of the control. This - /// provides a unique id for any control on the surface. - int id() const - { - return ( type() << 8 ) + _id; - } - - /// the value of the second bytes of the message. It's - /// the id of the control, but only guaranteed to be - /// unique within the control type. - int raw_id() const { return _id; } - - /// The 1-based number of the control - int ordinal() const { return _ordinal; } - - const std::string & name() const - { - return _name; - } - - const Group & group() const - { - return _group; - } - - const Strip & strip() const - { - return dynamic_cast( _group ); - } - - Strip & strip() - { - return dynamic_cast( _group ); - } - - virtual bool accepts_feedback() const - { - return true; - } - - virtual type_t type() const = 0; - - /// Return true if this control is the one and only Jog Wheel - virtual bool is_jog() const { return false; } + int id() const { return _id; } + const std::string & name() const { return _name; } + Group & group() const { return _group; } bool in_use () const; void set_in_use (bool); @@ -220,107 +59,34 @@ public: /// Keep track of the timeout so it can be updated with more incoming events sigc::connection in_use_connection; + virtual MidiByteArray zero() = 0; + /** If we are doing an in_use timeout for a fader without touch, this * is its touch button control; otherwise 0. */ Control* in_use_touch_control; + + boost::shared_ptr control () const { return normal_ac; } + virtual void set_control (boost::shared_ptr); + + float get_value (); + void set_value (float val); -private: - int _id; - int _ordinal; + virtual void start_touch (double when); + virtual void stop_touch (double when, bool mark); + + protected: + boost::shared_ptr normal_ac; + + private: + int _id; /* possibly device-dependent ID */ std::string _name; - Group & _group; + Group& _group; bool _in_use; }; -std::ostream & operator << ( std::ostream & os, const Control & control ); - -class Fader : public Control -{ -public: - Fader( int id, int ordinal, std::string name, Group & group ) - : Control( id, ordinal, name, group ) - { - } - - virtual type_t type() const { return type_fader; } -}; - -class Led : public Control -{ -public: - Led( int id, int ordinal, std::string name, Group & group ) - : Control( id, ordinal, name, group ) - { - } - - virtual const Led & led() const { return *this; } - - virtual type_t type() const { return type_led; } -}; - -class Button : public Control -{ -public: - Button( int id, int ordinal, std::string name, Group & group ) - : Control( id, ordinal, name, group ) - , _led( id, ordinal, name + "_led", group ) - { - } - - virtual const Led & led() const - { - return _led; - } - - virtual type_t type() const { return type_button; }; - -private: - Led _led; -}; - -class LedRing : public Led -{ -public: - LedRing( int id, int ordinal, std::string name, Group & group ) - : Led( id, ordinal, name, group ) - { - } - - virtual type_t type() const { return type_led_ring; } -}; - -class Pot : public Control -{ -public: - Pot( int id, int ordinal, std::string name, Group & group ) - : Control( id, ordinal, name, group ) - , _led_ring( id, ordinal, name + "_ring", group ) - { - } - - virtual type_t type() const { return type_pot; } - - virtual const LedRing & led_ring() const - { - return _led_ring; - } - -private: - LedRing _led_ring; -}; - -class Jog : public Pot -{ -public: - Jog( int id, int ordinal, std::string name, Group & group ) - : Pot( id, ordinal, name, group ) - { - } - - virtual bool is_jog() const { return true; } -}; +std::ostream & operator << (std::ostream & os, const Control & control); } -#endif +#endif /* __mackie_controls_h__ */ diff --git a/libs/surfaces/mackie/device_info.cc b/libs/surfaces/mackie/device_info.cc new file mode 100644 index 0000000000..e9e5d1b04b --- /dev/null +++ b/libs/surfaces/mackie/device_info.cc @@ -0,0 +1,500 @@ +/* + Copyright (C) 2006,2007 John Anderson + Copyright (C) 2012 Paul Davis + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include + +#include "pbd/xml++.h" +#include "pbd/error.h" +#include "pbd/pathscanner.h" + +#include "ardour/filesystem_paths.h" + +#include "device_info.h" + +#include "i18n.h" + +using namespace Mackie; +using namespace PBD; +using namespace ARDOUR; +using std::string; +using std::vector; + +std::map DeviceInfo::device_info; + +DeviceInfo::DeviceInfo() + : _strip_cnt (8) + , _extenders (0) + , _has_two_character_display (true) + , _has_master_fader (true) + , _has_timecode_display (true) + , _has_global_controls (true) + , _has_jog_wheel (true) + , _has_touch_sense_faders (true) + , _uses_logic_control_buttons (false) + , _uses_ipmidi (false) + , _name (X_("Mackie Control Universal Pro")) +{ + mackie_control_buttons (); +} + +DeviceInfo::~DeviceInfo() +{ +} + +void +DeviceInfo::mackie_control_buttons () +{ + _global_buttons.clear (); + shared_buttons (); + + _global_buttons[Button::Edit] = GlobalButtonInfo ("edit", "none", 0x33); + + _global_buttons[Button::F9] = GlobalButtonInfo ("F9", "none", 0x3e); + _global_buttons[Button::F10] = GlobalButtonInfo ("F10", "none", 0x3f); + _global_buttons[Button::F11] = GlobalButtonInfo ("F11", "none", 0x40); + _global_buttons[Button::F12] = GlobalButtonInfo ("F12", "none", 0x41); + _global_buttons[Button::F13] = GlobalButtonInfo ("F13", "none", 0x42); + _global_buttons[Button::F14] = GlobalButtonInfo ("F14", "none", 0x43); + _global_buttons[Button::F15] = GlobalButtonInfo ("F15", "none", 0x44); + _global_buttons[Button::F16] = GlobalButtonInfo ("F16", "none", 0x45); + _global_buttons[Button::Ctrl] = GlobalButtonInfo ("ctrl", "modifiers", 0x46); + _global_buttons[Button::Option] = GlobalButtonInfo ("option", "modifiers", 0x47); + _global_buttons[Button::Snapshot] = GlobalButtonInfo ("snapshot", "modifiers", 0x48); + _global_buttons[Button::Shift] = GlobalButtonInfo ("shift", "modifiers", 0x49); + _global_buttons[Button::Read] = GlobalButtonInfo ("read", "automation", 0x4a); + _global_buttons[Button::Write] = GlobalButtonInfo ("write", "automation", 0x4b); + _global_buttons[Button::Undo] = GlobalButtonInfo ("undo", "functions", 0x4c); + _global_buttons[Button::Save] = GlobalButtonInfo ("save", "automation", 0x4d); + _global_buttons[Button::Touch] = GlobalButtonInfo ("touch", "automation", 0x4e); + _global_buttons[Button::Redo] = GlobalButtonInfo ("redo", "functions", 0x4f); + _global_buttons[Button::FdrGroup] = GlobalButtonInfo ("fader group", "functions", 0x50); + _global_buttons[Button::ClearSolo] = GlobalButtonInfo ("clear solo", "functions", 0x51); + _global_buttons[Button::Cancel] = GlobalButtonInfo ("cancel", "functions", 0x52); + _global_buttons[Button::Marker] = GlobalButtonInfo ("marker", "functions", 0x53); + _global_buttons[Button::Mixer] = GlobalButtonInfo ("mixer", "transport", 0x54); + _global_buttons[Button::FrmLeft] = GlobalButtonInfo ("frm left", "transport", 0x55); + _global_buttons[Button::FrmRight] = GlobalButtonInfo ("frm right", "transport", 0x56); + _global_buttons[Button::End] = GlobalButtonInfo ("end", "transport", 0x57); + _global_buttons[Button::PunchIn] = GlobalButtonInfo ("punch in", "transport", 0x58); + _global_buttons[Button::PunchOut] = GlobalButtonInfo ("punch out", "transport", 0x59); + _global_buttons[Button::Loop] = GlobalButtonInfo ("loop", "transport", 0x59); + _global_buttons[Button::Home] = GlobalButtonInfo ("home", "transport", 0x5a); + + _strip_buttons[Button::FaderTouch] = StripButtonInfo (0xe0, "fader touch"); +} + +void +DeviceInfo::logic_control_buttons () +{ + _global_buttons.clear (); + shared_buttons (); + + _global_buttons[Button::View] = GlobalButtonInfo ("view", "view", 0x33); + + _global_buttons[Button::MidiTracks] = GlobalButtonInfo ("miditracks", "view", 0x3e); + _global_buttons[Button::Inputs] = GlobalButtonInfo ("inputs", "view", 0x3f); + _global_buttons[Button::AudioTracks] = GlobalButtonInfo ("audiotracks", "view", 0x40); + _global_buttons[Button::AudioInstruments] = GlobalButtonInfo ("audio instruments", "view", 0x41); + _global_buttons[Button::Aux] = GlobalButtonInfo ("aux", "view", 0x42); + _global_buttons[Button::Busses] = GlobalButtonInfo ("busses", "view", 0x43); + _global_buttons[Button::Outputs] = GlobalButtonInfo ("outputs", "view", 0x44); + _global_buttons[Button::User] = GlobalButtonInfo ("user", "view", 0x45); + _global_buttons[Button::Shift] = GlobalButtonInfo ("shift", "modifiers", 0x46); + _global_buttons[Button::Option] = GlobalButtonInfo ("option", "modifiers", 0x47); + _global_buttons[Button::Ctrl] = GlobalButtonInfo ("ctrl", "modifiers", 0x48); + _global_buttons[Button::CmdAlt] = GlobalButtonInfo ("cmdalt", "modifiers", 0x49); + _global_buttons[Button::Read] = GlobalButtonInfo ("read", "automation", 0x4a); + _global_buttons[Button::Write] = GlobalButtonInfo ("write", "automation", 0x4b); + _global_buttons[Button::Trim] = GlobalButtonInfo ("trim", "automation", 0x4c); + _global_buttons[Button::Touch] = GlobalButtonInfo ("touch", "functions", 0x4d); + _global_buttons[Button::Latch] = GlobalButtonInfo ("latch", "functions", 0x4e); + _global_buttons[Button::Grp] = GlobalButtonInfo ("group", "functions", 0x4f); + _global_buttons[Button::Save] = GlobalButtonInfo ("save", "functions", 0x50); + _global_buttons[Button::Undo] = GlobalButtonInfo ("undo", "functions", 0x51); + _global_buttons[Button::Cancel] = GlobalButtonInfo ("cancel", "transport", 0x52); + _global_buttons[Button::Enter] = GlobalButtonInfo ("enter right", "transport", 0x53); + _global_buttons[Button::Marker] = GlobalButtonInfo ("marker", "transport", 0x54); + _global_buttons[Button::Nudge] = GlobalButtonInfo ("nudge", "transport", 0x55); + _global_buttons[Button::Loop] = GlobalButtonInfo ("cycle", "transport", 0x56); + _global_buttons[Button::Drop] = GlobalButtonInfo ("drop", "transport", 0x57); + _global_buttons[Button::Replace] = GlobalButtonInfo ("replace", "transport", 0x58); + _global_buttons[Button::Click] = GlobalButtonInfo ("click", "transport", 0x59); + _global_buttons[Button::Solo] = GlobalButtonInfo ("solo", "transport", 0x5a); + + _strip_buttons[Button::FaderTouch] = StripButtonInfo (0x68, "fader touch"); +} + +void +DeviceInfo::shared_buttons () +{ + _global_buttons[Button::Track] = GlobalButtonInfo ("track", "assignment", 0x28); + _global_buttons[Button::Send] = GlobalButtonInfo ("send", "assignment", 0x29); + _global_buttons[Button::Pan] = GlobalButtonInfo ("pan", "assignment", 0x2a); + _global_buttons[Button::Plugin] = GlobalButtonInfo ("plugin", "assignment", 0x2b); + _global_buttons[Button::Eq] = GlobalButtonInfo ("eq", "assignment", 0x2c); + _global_buttons[Button::Dyn] = GlobalButtonInfo ("dyn", "assignment", 0x2d); + _global_buttons[Button::Left] = GlobalButtonInfo ("left", "bank", 0x2e); + _global_buttons[Button::Right] = GlobalButtonInfo ("right", "bank", 0x2f); + _global_buttons[Button::ChannelLeft] = GlobalButtonInfo ("channelleft", "bank", 0x30); + _global_buttons[Button::ChannelRight] = GlobalButtonInfo ("channelright", "bank", 0x31); + _global_buttons[Button::Flip] = GlobalButtonInfo ("flip", "none", 0x32); + + _global_buttons[Button::NameValue] = GlobalButtonInfo ("name/value", "display", 0x34); + _global_buttons[Button::TimecodeBeats] = GlobalButtonInfo ("timecode/beats", "display", 0x35); + _global_buttons[Button::F1] = GlobalButtonInfo ("F1", "none", 0x36); + _global_buttons[Button::F2] = GlobalButtonInfo ("F2", "none", 0x37); + _global_buttons[Button::F3] = GlobalButtonInfo ("F3", "none", 0x38); + _global_buttons[Button::F4] = GlobalButtonInfo ("F4", "none", 0x39); + _global_buttons[Button::F5] = GlobalButtonInfo ("F5", "none", 0x3a); + _global_buttons[Button::F6] = GlobalButtonInfo ("F6", "none", 0x3b); + _global_buttons[Button::F7] = GlobalButtonInfo ("F7", "none", 0x3c); + _global_buttons[Button::F8] = GlobalButtonInfo ("F8", "none", 0x3d); + + _global_buttons[Button::Rewind] = GlobalButtonInfo ("rewind", "transport", 0x5b); + _global_buttons[Button::Ffwd] = GlobalButtonInfo ("ffwd", "transport", 0x5c); + _global_buttons[Button::Stop] = GlobalButtonInfo ("stop", "transport", 0x5d); + _global_buttons[Button::Play] = GlobalButtonInfo ("play", "transport", 0x5e); + _global_buttons[Button::Record] = GlobalButtonInfo ("record", "transport", 0x5f); + _global_buttons[Button::CursorUp] = GlobalButtonInfo ("cursor up", "cursor", 0x60); + _global_buttons[Button::CursorDown] = GlobalButtonInfo ("cursor down", "cursor", 0x61); + _global_buttons[Button::CursorLeft] = GlobalButtonInfo ("cursor left", "cursor", 0x62); + _global_buttons[Button::CursorRight] = GlobalButtonInfo ("cursor right", "cursor", 0x63); + _global_buttons[Button::Zoom] = GlobalButtonInfo ("zoom", "none", 0x64); + _global_buttons[Button::Scrub] = GlobalButtonInfo ("scrub", "none", 0x65); + _global_buttons[Button::UserA] = GlobalButtonInfo ("user a", "user", 0x66); + _global_buttons[Button::UserB] = GlobalButtonInfo ("user b", "user", 0x67); + + _strip_buttons[Button::RecEnable] = StripButtonInfo (0x0, "recenable"); + _strip_buttons[Button::Solo] = StripButtonInfo (0x08, "solo"); + _strip_buttons[Button::Mute] = StripButtonInfo (0x10, "mute"); + _strip_buttons[Button::Select] = StripButtonInfo (0x18, "select"); + _strip_buttons[Button::VSelect] = StripButtonInfo (0x20, "vselect"); +} + +int +DeviceInfo::set_state (const XMLNode& node, int /* version */) +{ + const XMLProperty* prop; + const XMLNode* child; + + if (node.name() != "MackieProtocolDevice") { + return -1; + } + + /* name is mandatory */ + + if ((child = node.child ("Name")) != 0) { + if ((prop = child->property ("value")) != 0) { + _name = prop->value(); + } else { + return -1; + } + } + + /* strip count is mandatory */ + + if ((child = node.child ("Strips")) != 0) { + if ((prop = child->property ("value")) != 0) { + if ((_strip_cnt = atoi (prop->value())) == 0) { + _strip_cnt = 8; + } + } + } else { + return -1; + } + + if ((child = node.child ("Extenders")) != 0) { + if ((prop = child->property ("value")) != 0) { + if ((_extenders = atoi (prop->value())) == 0) { + _extenders = 0; + } + } + } + + if ((child = node.child ("TwoCharacterDisplay")) != 0) { + if ((prop = child->property ("value")) != 0) { + _has_two_character_display = string_is_affirmative (prop->value()); + } + } + + if ((child = node.child ("MasterFader")) != 0) { + if ((prop = child->property ("value")) != 0) { + _has_master_fader = string_is_affirmative (prop->value()); + } + } + + if ((child = node.child ("TimecodeDisplay")) != 0) { + if ((prop = child->property ("value")) != 0) { + _has_timecode_display = string_is_affirmative (prop->value()); + } + } else { + _has_timecode_display = false; + } + + if ((child = node.child ("GlobalControls")) != 0) { + if ((prop = child->property ("value")) != 0) { + _has_global_controls = string_is_affirmative (prop->value()); + } + } else { + _has_global_controls = false; + } + + if ((child = node.child ("JogWheel")) != 0) { + if ((prop = child->property ("value")) != 0) { + _has_jog_wheel = string_is_affirmative (prop->value()); + } + } else { + _has_jog_wheel = false; + } + + if ((child = node.child ("TouchSenseFaders")) != 0) { + if ((prop = child->property ("value")) != 0) { + _has_touch_sense_faders = string_is_affirmative (prop->value()); + } + } else { + _has_touch_sense_faders = false; + } + + if ((child = node.child ("UsesIPMIDI")) != 0) { + if ((prop = child->property ("value")) != 0) { + _uses_ipmidi = string_is_affirmative (prop->value()); + } + } else { + _uses_ipmidi = false; + } + + if ((child = node.child ("LogicControlButtons")) != 0) { + if ((prop = child->property ("value")) != 0) { + _uses_logic_control_buttons = string_is_affirmative (prop->value()); + + if (_uses_logic_control_buttons) { + logic_control_buttons(); + } else { + mackie_control_buttons (); + } + } + } + + if ((child = node.child ("Buttons")) != 0) { + XMLNodeConstIterator i; + const XMLNodeList& nlist (child->children()); + + for (i = nlist.begin(); i != nlist.end(); ++i) { + if ((*i)->name() == "GlobalButton") { + if ((prop = (*i)->property ("name")) != 0) { + int id = Button::name_to_id (prop->value()); + if (id >= 0) { + Button::ID bid = (Button::ID) id; + if ((prop = (*i)->property ("id")) != 0) { + int val = strtol (prop->value().c_str(), 0, 0); + std::map::iterator b = _global_buttons.find (bid); + if (b != _global_buttons.end()) { + b->second.id = val; + + if ((prop = (*i)->property ("label")) != 0) { + b->second.label = prop->value(); + } + } + } + } + + } + + } else if ((*i)->name() == "StripButton") { + if ((prop = (*i)->property ("name")) != 0) { + int id = Button::name_to_id (prop->value()); + if (id >= 0) { + Button::ID bid = (Button::ID) id; + if ((prop = (*i)->property ("baseid")) != 0) { + int val = strtol (prop->value().c_str(), 0, 0); + std::map::iterator b = _strip_buttons.find (bid); + if (b != _strip_buttons.end()) { + b->second.base_id = val; + } + } + } + + } + + } + } + } + + return 0; +} + +const string& +DeviceInfo::name() const +{ + return _name; +} + +uint32_t +DeviceInfo::strip_cnt() const +{ + return _strip_cnt; +} + +uint32_t +DeviceInfo::extenders() const +{ + return _extenders; +} + +bool +DeviceInfo::has_master_fader() const +{ + return _has_master_fader; +} + +bool +DeviceInfo::has_two_character_display() const +{ + return _has_two_character_display; +} + +bool +DeviceInfo::has_timecode_display () const +{ + return _has_timecode_display; +} + +bool +DeviceInfo::uses_ipmidi () const +{ + return _uses_ipmidi; +} + +bool +DeviceInfo::has_global_controls () const +{ + return _has_global_controls; +} + +bool +DeviceInfo::has_jog_wheel () const +{ + return _has_jog_wheel; +} + +bool +DeviceInfo::has_touch_sense_faders () const +{ + return _has_touch_sense_faders; +} + +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 +system_devinfo_search_path () +{ + bool devinfo_path_defined = false; + sys::path spath_env (Glib::getenv (devinfo_env_variable_name, devinfo_path_defined)); + + if (devinfo_path_defined) { + return spath_env; + } + + 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; +} + +static sys::path +user_devinfo_directory () +{ + sys::path p(user_config_directory()); + p /= devinfo_dir_name; + + return p; +} + +static bool +devinfo_filter (const string &str, void */*arg*/) +{ + return (str.length() > strlen(devinfo_suffix) && + str.find (devinfo_suffix) == (str.length() - strlen (devinfo_suffix))); +} + +void +DeviceInfo::reload_device_info () +{ + DeviceInfo di; + vector s; + vector *devinfos; + PathScanner scanner; + SearchPath spath (system_devinfo_search_path()); + spath += user_devinfo_directory (); + + devinfos = scanner (spath.to_string(), devinfo_filter, 0, false, true); + device_info.clear (); + + if (!devinfos) { + error << "No MCP device info files found using " << spath.to_string() << endmsg; + std::cerr << "No MCP device info files found using " << spath.to_string() << std::endl; + return; + } + + if (devinfos->empty()) { + error << "No MCP device info files found using " << spath.to_string() << endmsg; + std::cerr << "No MCP device info files found using " << spath.to_string() << std::endl; + return; + } + + for (vector::iterator i = devinfos->begin(); i != devinfos->end(); ++i) { + string fullpath = *(*i); + + XMLTree tree; + + + if (!tree.read (fullpath.c_str())) { + continue; + } + + XMLNode* root = tree.root (); + if (!root) { + continue; + } + + if (di.set_state (*root, 3000) == 0) { /* version is ignored for now */ + device_info[di.name()] = di; + } + } + + delete devinfos; +} + +std::ostream& operator<< (std::ostream& os, const Mackie::DeviceInfo& di) +{ + os << di.name() << ' ' + << di.strip_cnt() << ' ' + << di.extenders() << ' ' + ; + return os; +} diff --git a/libs/surfaces/mackie/device_info.h b/libs/surfaces/mackie/device_info.h new file mode 100644 index 0000000000..4366cd09b1 --- /dev/null +++ b/libs/surfaces/mackie/device_info.h @@ -0,0 +1,104 @@ +/* + Copyright (C) 2006,2007 John Anderson + Copyright (C) 2012 Paul Davis + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __ardour_mackie_control_protocol_device_info_h__ +#define __ardour_mackie_control_protocol_device_info_h__ + +#include +#include +#include +#include + +#include "button.h" + +class XMLNode; + +namespace Mackie { + +struct GlobalButtonInfo { + std::string label; // visible to user + std::string group; // in case we want to present in a GUI + int32_t id; // value sent by device + + GlobalButtonInfo () : id (-1) {} + GlobalButtonInfo (const std::string& l, const std::string& g, uint32_t i) + : label (l), group (g), id (i) {} +}; + +struct StripButtonInfo { + int32_t base_id; + std::string name; + + StripButtonInfo () : base_id (-1) {} + StripButtonInfo (uint32_t i, const std::string& n) + : base_id (i), name (n) {} +}; + +class DeviceInfo +{ + public: + DeviceInfo(); + ~DeviceInfo(); + + int set_state (const XMLNode&, int version); + + uint32_t strip_cnt () const; + uint32_t extenders() const; + bool has_two_character_display() const; + bool has_master_fader () const; + bool has_timecode_display() const; + bool has_global_controls() const; + bool has_jog_wheel () const; + bool has_touch_sense_faders() const; + bool uses_ipmidi() const; + const std::string& name() const; + + static std::map device_info; + static void reload_device_info(); + + const std::map& global_buttons() const { return _global_buttons; } + const std::map& strip_buttons() const { return _strip_buttons; } + + private: + uint32_t _strip_cnt; + uint32_t _extenders; + bool _has_two_character_display; + bool _has_master_fader; + bool _has_timecode_display; + bool _has_global_controls; + bool _has_jog_wheel; + bool _has_touch_sense_faders; + bool _uses_logic_control_buttons; + bool _uses_ipmidi; + std::string _name; + + std::map _global_buttons; + std::map _strip_buttons; + + void logic_control_buttons (); + void mackie_control_buttons (); + void shared_buttons (); +}; + + +} + +std::ostream& operator<< (std::ostream& os, const Mackie::DeviceInfo& di); + +#endif /* __ardour_mackie_control_protocol_device_info_h__ */ diff --git a/libs/surfaces/mackie/device_profile.cc b/libs/surfaces/mackie/device_profile.cc new file mode 100644 index 0000000000..110038ca2b --- /dev/null +++ b/libs/surfaces/mackie/device_profile.cc @@ -0,0 +1,364 @@ +/* + Copyright (C) 2006,2007 John Anderson + Copyright (C) 2012 Paul Davis + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include +#include + +#include "pbd/xml++.h" +#include "pbd/error.h" +#include "pbd/pathscanner.h" +#include "pbd/replace_all.h" + +#include "ardour/filesystem_paths.h" + +#include "mackie_control_protocol.h" +#include "device_profile.h" + +#include "i18n.h" + +using namespace Mackie; +using namespace PBD; +using namespace ARDOUR; +using std::string; +using std::vector; + +std::map DeviceProfile::device_profiles; + +DeviceProfile::DeviceProfile (const string& n) + : _name (n) +{ +} + +DeviceProfile::~DeviceProfile() +{ +} + +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 +system_devprofile_search_path () +{ + bool devprofile_path_defined = false; + sys::path spath_env (Glib::getenv (devprofile_env_variable_name, devprofile_path_defined)); + + if (devprofile_path_defined) { + return spath_env; + } + + 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; +} + +static sys::path +user_devprofile_directory () +{ + sys::path p(user_config_directory()); + p /= devprofile_dir_name; + + return p; +} + +static bool +devprofile_filter (const string &str, void */*arg*/) +{ + return (str.length() > strlen(devprofile_suffix) && + str.find (devprofile_suffix) == (str.length() - strlen (devprofile_suffix))); +} + +void +DeviceProfile::reload_device_profiles () +{ + DeviceProfile dp; + vector s; + vector *devprofiles; + PathScanner scanner; + SearchPath spath (system_devprofile_search_path()); + spath += user_devprofile_directory (); + + devprofiles = scanner (spath.to_string(), devprofile_filter, 0, false, true); + device_profiles.clear (); + + if (!devprofiles) { + error << "No MCP device info files found using " << spath.to_string() << endmsg; + return; + } + + if (devprofiles->empty()) { + error << "No MCP device info files found using " << spath.to_string() << endmsg; + return; + } + + for (vector::iterator i = devprofiles->begin(); i != devprofiles->end(); ++i) { + string fullpath = *(*i); + + XMLTree tree; + + if (!tree.read (fullpath.c_str())) { + continue; + } + + XMLNode* root = tree.root (); + if (!root) { + continue; + } + + if (dp.set_state (*root, 3000) == 0) { /* version is ignored for now */ + dp.set_path (fullpath); + device_profiles[dp.name()] = dp; + } + } + + delete devprofiles; +} + +int +DeviceProfile::set_state (const XMLNode& node, int /* version */) +{ + const XMLProperty* prop; + const XMLNode* child; + + if (node.name() != "MackieDeviceProfile") { + return -1; + } + + /* name is mandatory */ + + if ((child = node.child ("Name")) == 0 || (prop = child->property ("value")) == 0) { + return -1; + } else { + _name = prop->value(); + } + + if ((child = node.child ("Buttons")) != 0) { + XMLNodeConstIterator i; + const XMLNodeList& nlist (child->children()); + + for (i = nlist.begin(); i != nlist.end(); ++i) { + + if ((*i)->name() == "Button") { + + if ((prop = (*i)->property ("name")) == 0) { + error << string_compose ("Button without name in device profile \"%1\" - ignored", _name) << endmsg; + continue; + } + + int id = Button::name_to_id (prop->value()); + if (id < 0) { + error << string_compose ("Unknow button ID \"%1\"", prop->value()) << endmsg; + continue; + } + + Button::ID bid = (Button::ID) id; + + ButtonActionMap::iterator b = _button_map.find (bid); + + if (b == _button_map.end()) { + b = _button_map.insert (_button_map.end(), std::pair (bid, ButtonActions())); + } + + if ((prop = (*i)->property ("plain")) != 0) { + b->second.plain = prop->value (); + } + if ((prop = (*i)->property ("control")) != 0) { + b->second.control = prop->value (); + } + if ((prop = (*i)->property ("shift")) != 0) { + b->second.shift = prop->value (); + } + if ((prop = (*i)->property ("option")) != 0) { + b->second.option = prop->value (); + } + if ((prop = (*i)->property ("cmdalt")) != 0) { + b->second.cmdalt = prop->value (); + } + if ((prop = (*i)->property ("shiftcontrol")) != 0) { + b->second.shiftcontrol = prop->value (); + } + } + } + } + + return 0; +} + +XMLNode& +DeviceProfile::get_state () const +{ + XMLNode* node = new XMLNode ("MackieDeviceProfile"); + XMLNode* child = new XMLNode ("Name"); + + child->add_property ("value", _name); + node->add_child_nocopy (*child); + + if (_button_map.empty()) { + return *node; + } + + XMLNode* buttons = new XMLNode ("Buttons"); + node->add_child_nocopy (*buttons); + + for (ButtonActionMap::const_iterator b = _button_map.begin(); b != _button_map.end(); ++b) { + XMLNode* n = new XMLNode ("Button"); + + n->add_property ("name", Button::id_to_name (b->first)); + + if (!b->second.plain.empty()) { + n->add_property ("plain", b->second.plain); + } + if (!b->second.control.empty()) { + n->add_property ("control", b->second.control); + } + if (!b->second.shift.empty()) { + n->add_property ("shift", b->second.shift); + } + if (!b->second.option.empty()) { + n->add_property ("option", b->second.option); + } + if (!b->second.cmdalt.empty()) { + n->add_property ("cmdalt", b->second.cmdalt); + } + if (!b->second.shiftcontrol.empty()) { + n->add_property ("shiftcontrol", b->second.shiftcontrol); + } + + buttons->add_child_nocopy (*n); + } + + return *node; +} + +string +DeviceProfile::get_button_action (Button::ID id, int modifier_state) const +{ + ButtonActionMap::const_iterator i = _button_map.find (id); + + if (i == _button_map.end()) { + return string(); + } + + if (modifier_state == MackieControlProtocol::MODIFIER_CONTROL) { + return i->second.control; + } else if (modifier_state == MackieControlProtocol::MODIFIER_SHIFT) { + return i->second.shift; + } else if (modifier_state == MackieControlProtocol::MODIFIER_OPTION) { + return i->second.option; + } else if (modifier_state == MackieControlProtocol::MODIFIER_CMDALT) { + return i->second.cmdalt; + } else if (modifier_state == (MackieControlProtocol::MODIFIER_CONTROL|MackieControlProtocol::MODIFIER_SHIFT)) { + return i->second.shiftcontrol; + } + + return i->second.plain; +} + +void +DeviceProfile::set_button_action (Button::ID id, int modifier_state, const string& act) +{ + ButtonActionMap::iterator i = _button_map.find (id); + + if (i == _button_map.end()) { + i = _button_map.insert (std::make_pair (id, ButtonActions())).first; + } + + string action (act); + replace_all (action, "/", ""); + + if (modifier_state == MackieControlProtocol::MODIFIER_CONTROL) { + i->second.control = action; + } else if (modifier_state == MackieControlProtocol::MODIFIER_SHIFT) { + i->second.shift = action; + } else if (modifier_state == MackieControlProtocol::MODIFIER_OPTION) { + i->second.option = action; + } else if (modifier_state == MackieControlProtocol::MODIFIER_CMDALT) { + i->second.cmdalt = action; + } else if (modifier_state == (MackieControlProtocol::MODIFIER_CONTROL|MackieControlProtocol::MODIFIER_SHIFT)) { + i->second.shiftcontrol = action; + } + + if (modifier_state == 0) { + i->second.plain = action; + } + + save (); +} + +const string& +DeviceProfile::name() const +{ + return _name; +} + +void +DeviceProfile::set_path (const string& p) +{ + _path = p; +} + +/* XXX copied from libs/ardour/utils.cc */ + +static string +legalize_for_path (const string& str) +{ + string::size_type pos; + string illegal_chars = "/\\"; /* DOS, POSIX. Yes, we're going to ignore HFS */ + string legal; + + legal = str; + pos = 0; + + while ((pos = legal.find_first_of (illegal_chars, pos)) != string::npos) { + legal.replace (pos, 1, "_"); + pos += 1; + } + + return string (legal); +} + + +void +DeviceProfile::save () +{ + sys::path fullpath = user_devprofile_directory(); + + if (g_mkdir_with_parents (fullpath.to_string().c_str(), 0755) < 0) { + error << string_compose(_("Session: cannot create user MCP profile folder \"%1\" (%2)"), fullpath.to_string(), strerror (errno)) << endmsg; + return; + } + + fullpath /= legalize_for_path (_name) + ".profile"; + + XMLTree tree; + tree.set_root (&get_state()); + + if (!tree.write (fullpath.to_string())) { + error << string_compose ("MCP profile not saved to %1", fullpath.to_string()) << endmsg; + } +} + diff --git a/libs/surfaces/mackie/device_profile.h b/libs/surfaces/mackie/device_profile.h new file mode 100644 index 0000000000..65aba48692 --- /dev/null +++ b/libs/surfaces/mackie/device_profile.h @@ -0,0 +1,72 @@ +/* + Copyright (C) 2012 Paul Davis + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __ardour_mackie_control_protocol_device_profile_h__ +#define __ardour_mackie_control_protocol_device_profile_h__ + +#include +#include +#include +#include + +#include "button.h" + +class XMLNode; + +namespace Mackie { + +class DeviceProfile +{ + public: + DeviceProfile (const std::string& name = ""); + ~DeviceProfile(); + + std::string get_button_action (Button::ID, int modifier_state) const; + void set_button_action (Button::ID, int modifier_state, const std::string&); + + const std::string& name() const; + void set_path (const std::string&); + + static void reload_device_profiles (); + static std::map device_profiles; + + private: + struct ButtonActions { + std::string plain; + std::string control; + std::string shift; + std::string option; + std::string cmdalt; + std::string shiftcontrol; + }; + + typedef std::map ButtonActionMap; + + std::string _name; + std::string _path; + ButtonActionMap _button_map; + + int set_state (const XMLNode&, int version); + XMLNode& get_state () const; + + void save (); +}; + +} + +#endif /* __ardour_mackie_control_protocol_device_profile_h__ */ diff --git a/libs/surfaces/mackie/dummy_port.cc b/libs/surfaces/mackie/dummy_port.cc deleted file mode 100644 index 7654f8f987..0000000000 --- a/libs/surfaces/mackie/dummy_port.cc +++ /dev/null @@ -1,58 +0,0 @@ -#include "dummy_port.h" - -#include "midi_byte_array.h" - -#include -#include - -#include - -using namespace Mackie; -using namespace std; - -DummyPort::DummyPort() -{ -} - -DummyPort::~DummyPort() -{ -} - - -void DummyPort::open() -{ - cout << "DummyPort::open" << endl; -} - - -void DummyPort::close() -{ - cout << "DummyPort::close" << endl; -} - - -MidiByteArray DummyPort::read() -{ - cout << "DummyPort::read" << endl; - return MidiByteArray(); -} - - -void DummyPort::write( const MidiByteArray & mba ) -{ - cout << "DummyPort::write " << mba << endl; -} - -MidiByteArray empty_midi_byte_array; - -const MidiByteArray & DummyPort::sysex_hdr() const -{ - cout << "DummyPort::sysex_hdr" << endl; - return empty_midi_byte_array; -} - -int DummyPort::strips() const -{ - cout << "DummyPort::strips" << endl; - return 0; -} diff --git a/libs/surfaces/mackie/dummy_port.h b/libs/surfaces/mackie/dummy_port.h deleted file mode 100644 index 4ed0a3043b..0000000000 --- a/libs/surfaces/mackie/dummy_port.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - Copyright (C) 2008 John Anderson - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ -#ifndef dummy_port_h -#define dummy_port_h - -#include "surface_port.h" - -#include "midi_byte_array.h" - -namespace MIDI { - class Port; -} - -namespace Mackie -{ - -/** - A Dummy Port, to catch things that shouldn't be sent. -*/ -class DummyPort : public SurfacePort -{ -public: - DummyPort(); - virtual ~DummyPort(); - - // when this is successful, active() should return true - virtual void open(); - - // subclasses should call this before doing their own close - virtual void close(); - - /// read bytes from the port. They'll either end up in the - /// parser, or if that's not active they'll be returned - virtual MidiByteArray read(); - - /// an easier way to output bytes via midi - virtual void write( const MidiByteArray & ); - - virtual const MidiByteArray & sysex_hdr() const; - virtual int strips() const; -}; - -} - -#endif diff --git a/libs/surfaces/mackie/fader.cc b/libs/surfaces/mackie/fader.cc new file mode 100644 index 0000000000..4a6c81f491 --- /dev/null +++ b/libs/surfaces/mackie/fader.cc @@ -0,0 +1,57 @@ +/* + Copyright (C) 2006,2007 John Anderson + Copyright (C) 2012 Paul Davis + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include + +#include "fader.h" +#include "surface.h" +#include "control_group.h" +#include "mackie_control_protocol.h" + +using namespace Mackie; + +Control* +Fader::factory (Surface& surface, int id, const char* name, Group& group) +{ + Fader* f = new Fader (id, name, group); + + surface.faders[id] = f; + surface.controls.push_back (f); + group.add (*f); + return f; +} + +MidiByteArray +Fader::set_position (float normalized) +{ + position = normalized; + return update_message (); +} + +MidiByteArray +Fader::update_message () +{ + if (MackieControlProtocol::instance()->flip_mode() == MackieControlProtocol::Zero) { + /* do not send messages to move the faders when in this mode */ + return MidiByteArray(); + } + + int posi = lrintf (0x3fff * position); + return MidiByteArray (3, 0xe0 + id(), posi & 0x7f, posi >> 7); +} diff --git a/libs/surfaces/mackie/fader.h b/libs/surfaces/mackie/fader.h new file mode 100644 index 0000000000..42a485036e --- /dev/null +++ b/libs/surfaces/mackie/fader.h @@ -0,0 +1,31 @@ +#ifndef __ardour_mackie_control_protocol_fader_h__ +#define __ardour_mackie_control_protocol_fader_h__ + +#include "controls.h" + +namespace Mackie { + +class Fader : public Control +{ + public: + + Fader (int id, std::string name, Group & group) + : Control (id, name, group) + , position (0.0) + { + } + + MidiByteArray set_position (float); + MidiByteArray zero() { return set_position (0.0); } + + MidiByteArray update_message (); + + static Control* factory (Surface&, int id, const char*, Group&); + + private: + float position; +}; + +} + +#endif diff --git a/libs/surfaces/mackie/gui.cc b/libs/surfaces/mackie/gui.cc index fe88355f58..8728b4a88c 100644 --- a/libs/surfaces/mackie/gui.cc +++ b/libs/surfaces/mackie/gui.cc @@ -20,27 +20,30 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include + +#include "pbd/strsplit.h" + #include "gtkmm2ext/utils.h" +#include "gtkmm2ext/actions.h" + #include "ardour/rc_configuration.h" + #include "mackie_control_protocol.h" +#include "device_info.h" +#include "gui.h" + #include "i18n.h" using namespace std; - -class MackieControlProtocolGUI : public Gtk::VBox -{ -public: - MackieControlProtocolGUI (MackieControlProtocol &); - -private: - - void surface_combo_changed (); - void extenders_changed (); - - MackieControlProtocol& _cp; - Gtk::ComboBoxText _surface_combo; - Gtk::SpinButton _extenders; -}; +using namespace Mackie; +using namespace Gtk; void* MackieControlProtocol::get_gui () const @@ -66,57 +69,444 @@ MackieControlProtocol::build_gui () MackieControlProtocolGUI::MackieControlProtocolGUI (MackieControlProtocol& p) : _cp (p) + , touch_sensitivity_adjustment (0, 0, 9, 1, 4) + , touch_sensitivity_scale (touch_sensitivity_adjustment) + , recalibrate_fader_button (_("Recalibrate Faders")) + , ipmidi_base_port_adjustment (_cp.ipmidi_base(), 0, 32767, 1, 1000) + , ipmidi_base_port_spinner (ipmidi_base_port_adjustment) { - Gtk::Table* table = Gtk::manage (new Gtk::Table (2, 2)); - table->set_spacings (4); - - table->attach (*manage (new Gtk::Label (_("Surface type:"))), 0, 1, 0, 1); - table->attach (_surface_combo, 1, 2, 0, 1); + Gtk::Label* l; + Gtk::Alignment* align; + + set_border_width (12); + + Gtk::Table* table = Gtk::manage (new Gtk::Table (2, 8)); + table->set_row_spacings (4); + table->set_col_spacings (6); + l = manage (new Gtk::Label (_("Device Type:"))); + l->set_alignment (1.0, 0.5); + table->attach (*l, 0, 1, 0, 1, AttachOptions(FILL|EXPAND), AttachOptions(0)); + table->attach (_surface_combo, 1, 2, 0, 1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 20); vector surfaces; - surfaces.push_back (_("Mackie Control")); - surfaces.push_back (_("Behringer BCF2000")); + + for (std::map::iterator i = DeviceInfo::device_info.begin(); i != DeviceInfo::device_info.end(); ++i) { + surfaces.push_back (i->first); + } Gtkmm2ext::set_popdown_strings (_surface_combo, surfaces); + _surface_combo.set_active_text (p.device_info().name()); + _surface_combo.signal_changed().connect (sigc::mem_fun (*this, &MackieControlProtocolGUI::surface_combo_changed)); - if (ARDOUR::Config->get_mackie_emulation () == X_("mcu")) { - _surface_combo.set_active_text (surfaces.front ()); - } else { - _surface_combo.set_active_text (surfaces.back ()); + RadioButtonGroup rb_group = absolute_touch_mode_button.get_group(); + touch_move_mode_button.set_group (rb_group); + + l = manage (new Gtk::Label (_("Button click"))); + l->set_alignment (1.0, 0.5); + table->attach (*l, 0, 1, 1, 2, AttachOptions(FILL|EXPAND), AttachOptions (0)); + align = manage (new Alignment); + align->set (0.0, 0.5); + align->add (relay_click_button); + table->attach (*align, 1, 2, 1, 2, AttachOptions(FILL|EXPAND), AttachOptions (0)); + l = manage (new Gtk::Label (_("Backlight"))); + l->set_alignment (1.0, 0.5); + table->attach (*l, 0, 1, 2, 3, AttachOptions(FILL|EXPAND), AttachOptions (0)); + align = manage (new Alignment); + align->set (0.0, 0.5); + align->add (backlight_button); + table->attach (*align, 1, 2, 2, 3, AttachOptions(FILL|EXPAND), AttachOptions (0)); + l = manage (new Gtk::Label (_("Send Fader Position Only When Touched"))); + l->set_alignment (1.0, 0.5); + table->attach (*l, 0, 1, 3, 4, AttachOptions(FILL|EXPAND), AttachOptions (0)); + align = manage (new Alignment); + align->set (0.0, 0.5); + align->add (absolute_touch_mode_button); + table->attach (*align, 1, 2, 3, 4, AttachOptions(FILL|EXPAND), AttachOptions (0)); + l = manage (new Gtk::Label (_("Send Fader Position When Moved"))); + l->set_alignment (1.0, 0.5); + table->attach (*l, 0, 1, 4, 5, AttachOptions(FILL|EXPAND), AttachOptions (0)); + align = manage (new Alignment); + align->set (0.0, 0.5); + align->add (touch_move_mode_button); + table->attach (*align, 1, 2, 4, 5, AttachOptions(FILL|EXPAND), AttachOptions (0)); + l = manage (new Gtk::Label (_("Fader Touch Sense Sensitivity"))); + l->set_alignment (1.0, 0.5); + table->attach (*l, 0, 1, 5, 6, AttachOptions(FILL|EXPAND), AttachOptions (0)); + touch_sensitivity_scale.property_digits() = 0; + touch_sensitivity_scale.property_draw_value() = false; + table->attach (touch_sensitivity_scale, 1, 2, 5, 6, AttachOptions(FILL|EXPAND), AttachOptions (0)); + table->attach (recalibrate_fader_button, 1, 2, 6, 7, AttachOptions(FILL|EXPAND), AttachOptions (0)); + + l = manage (new Gtk::Label (_("ipMIDI Port (lowest)"))); + l->set_alignment (1.0, 0.5); + table->attach (*l, 0, 1, 7, 8, AttachOptions(FILL|EXPAND), AttachOptions (0)); + table->attach (ipmidi_base_port_spinner, 1, 2, 7, 8, AttachOptions(FILL|EXPAND), AttachOptions (0)); + + 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)); + + vector profiles; + + profiles.push_back ("default"); + + for (std::map::iterator i = DeviceProfile::device_profiles.begin(); i != DeviceProfile::device_profiles.end(); ++i) { + profiles.push_back (i->first); + } + Gtkmm2ext::set_popdown_strings (_profile_combo, profiles); + _profile_combo.set_active_text (p.device_profile().name()); + _profile_combo.signal_changed().connect (sigc::mem_fun (*this, &MackieControlProtocolGUI::profile_combo_changed)); + + append_page (*table, _("Device Setup")); + table->show_all(); + + /* function key editor */ + + VBox* fkey_packer = manage (new VBox); + HBox* profile_packer = manage (new HBox); + + l = manage (new Gtk::Label (_("Profile/Settings:"))); + profile_packer->pack_start (*l, false, false); + profile_packer->pack_start (_profile_combo, true, true); + profile_packer->set_spacing (12); + profile_packer->set_border_width (12); + + fkey_packer->pack_start (*profile_packer, false, false); + fkey_packer->pack_start (function_key_scroller, true, true); + fkey_packer->set_spacing (12); + function_key_scroller.set_size_request (700,700); + function_key_scroller.property_shadow_type() = Gtk::SHADOW_NONE; + function_key_scroller.add (function_key_editor); + append_page (*fkey_packer, _("Function Keys")); + + build_available_action_menu (); + build_function_key_editor (); + refresh_function_key_editor (); + fkey_packer->show_all(); +} + +CellRendererCombo* +MackieControlProtocolGUI::make_action_renderer (Glib::RefPtr model, Gtk::TreeModelColumnBase column) +{ + CellRendererCombo* renderer = manage (new CellRendererCombo); + renderer->property_model() = model; + renderer->property_editable() = true; + renderer->property_text_column() = 0; + renderer->property_has_entry() = false; + renderer->signal_edited().connect (sigc::bind (sigc::mem_fun(*this, &MackieControlProtocolGUI::action_changed), column)); + + return renderer; +} + +void +MackieControlProtocolGUI::build_available_action_menu () +{ + /* build a model of all available actions (needs to be tree structured + * more) + */ + + available_action_model = TreeStore::create (available_action_columns); + + vector paths; + vector labels; + vector tooltips; + vector keys; + vector bindings; + typedef std::map NodeMap; + NodeMap nodes; + NodeMap::iterator r; + + ActionManager::get_all_actions (labels, paths, tooltips, keys, bindings); + + vector::iterator k; + vector::iterator p; + vector::iterator t; + vector::iterator l; + + available_action_model->clear (); + + for (l = labels.begin(), k = keys.begin(), p = paths.begin(), t = tooltips.begin(); l != labels.end(); ++k, ++p, ++t, ++l) { + + TreeModel::Row row; + vector parts; + + parts.clear (); + + split (*p, parts, '/'); + + if (parts.empty()) { + continue; + } + + //kinda kludgy way to avoid displaying menu items as mappable + if ( parts[1] == _("Main_menu") ) + continue; + if ( parts[1] == _("JACK") ) + continue; + if ( parts[1] == _("redirectmenu") ) + continue; + if ( parts[1] == _("Editor_menus") ) + continue; + if ( parts[1] == _("RegionList") ) + continue; + if ( parts[1] == _("ProcessorMenu") ) + continue; + + if ((r = nodes.find (parts[1])) == nodes.end()) { + + /* top level is missing */ + + TreeIter rowp; + TreeModel::Row parent; + rowp = available_action_model->append(); + nodes[parts[1]] = rowp; + parent = *(rowp); + parent[available_action_columns.name] = parts[1]; + + row = *(available_action_model->append (parent.children())); + + } else { + + row = *(available_action_model->append ((*r->second)->children())); + + } + + /* add this action */ + + if (l->empty ()) { + row[available_action_columns.name] = *t; + action_map[*t] = *p; + } else { + row[available_action_columns.name] = *l; + action_map[*l] = *p; + } + + row[available_action_columns.path] = (*p); + } +} + +void +MackieControlProtocolGUI::build_function_key_editor () +{ + function_key_editor.append_column (_("Key"), function_key_columns.name); + + TreeViewColumn* col; + CellRendererCombo* renderer; + + renderer = make_action_renderer (available_action_model, function_key_columns.plain); + col = manage (new TreeViewColumn (_("Plain"), *renderer)); + col->add_attribute (renderer->property_text(), function_key_columns.plain); + function_key_editor.append_column (*col); + + renderer = make_action_renderer (available_action_model, function_key_columns.shift); + col = manage (new TreeViewColumn (_("Shift"), *renderer)); + col->add_attribute (renderer->property_text(), function_key_columns.shift); + function_key_editor.append_column (*col); + + renderer = make_action_renderer (available_action_model, function_key_columns.control); + col = manage (new TreeViewColumn (_("Control"), *renderer)); + col->add_attribute (renderer->property_text(), function_key_columns.control); + function_key_editor.append_column (*col); + + renderer = make_action_renderer (available_action_model, function_key_columns.option); + col = manage (new TreeViewColumn (_("Option"), *renderer)); + col->add_attribute (renderer->property_text(), function_key_columns.option); + function_key_editor.append_column (*col); + + renderer = make_action_renderer (available_action_model, function_key_columns.cmdalt); + col = manage (new TreeViewColumn (_("Cmd/Alt"), *renderer)); + col->add_attribute (renderer->property_text(), function_key_columns.cmdalt); + function_key_editor.append_column (*col); + + renderer = make_action_renderer (available_action_model, function_key_columns.shiftcontrol); + col = manage (new TreeViewColumn (_("Shift+Control"), *renderer)); + col->add_attribute (renderer->property_text(), function_key_columns.shiftcontrol); + function_key_editor.append_column (*col); + + function_key_model = ListStore::create (function_key_columns); + function_key_editor.set_model (function_key_model); +} + +void +MackieControlProtocolGUI::refresh_function_key_editor () +{ + function_key_editor.set_model (Glib::RefPtr()); + function_key_model->clear (); + + /* now fill with data */ + + TreeModel::Row row; + DeviceProfile dp (_cp.device_profile()); + + for (int n = 0; n < Mackie::Button::FinalGlobalButton; ++n) { + + Mackie::Button::ID bid = (Mackie::Button::ID) n; + + row = *(function_key_model->append()); + row[function_key_columns.name] = Mackie::Button::id_to_name (bid); + row[function_key_columns.id] = bid; + + Glib::RefPtr act; + string action; + const string defstring = "\u2022"; + + action = dp.get_button_action (bid, 0); + if (action.empty()) { + row[function_key_columns.plain] = defstring; + } else { + act = ActionManager::get_action (action.c_str()); + if (act) { + row[function_key_columns.plain] = act->get_label(); + } else { + row[function_key_columns.plain] = defstring; + } + } + + action = dp.get_button_action (bid, MackieControlProtocol::MODIFIER_CONTROL); + if (action.empty()) { + row[function_key_columns.control] = defstring; + } else { + act = ActionManager::get_action (action.c_str()); + if (act) { + row[function_key_columns.control] = act->get_label(); + } else { + row[function_key_columns.control] = defstring; + } + } + + action = dp.get_button_action (bid, MackieControlProtocol::MODIFIER_SHIFT); + if (action.empty()) { + row[function_key_columns.shift] = defstring; + } else { + act = ActionManager::get_action (action.c_str()); + if (act) { + row[function_key_columns.shift] = act->get_label(); + } else { + row[function_key_columns.shift] = defstring; + } + } + + action = dp.get_button_action (bid, MackieControlProtocol::MODIFIER_OPTION); + if (action.empty()) { + row[function_key_columns.option] = defstring; + } else { + act = ActionManager::get_action (action.c_str()); + if (act) { + row[function_key_columns.option] = act->get_label(); + } else { + row[function_key_columns.option] = defstring; + } + } + + action = dp.get_button_action (bid, MackieControlProtocol::MODIFIER_CMDALT); + if (action.empty()) { + row[function_key_columns.cmdalt] = defstring; + } else { + act = ActionManager::get_action (action.c_str()); + if (act) { + row[function_key_columns.cmdalt] = act->get_label(); + } else { + row[function_key_columns.cmdalt] = defstring; + } + } + + action = dp.get_button_action (bid, (MackieControlProtocol::MODIFIER_SHIFT|MackieControlProtocol::MODIFIER_CONTROL)); + if (action.empty()) { + row[function_key_columns.shiftcontrol] = defstring; + } else { + act = ActionManager::get_action (action.c_str()); + if (act) { + row[function_key_columns.shiftcontrol] = act->get_label(); + } else { + row[function_key_columns.shiftcontrol] = defstring; + } + } } - _extenders.set_range (0, 8); - _extenders.set_increments (1, 4); + function_key_editor.set_model (function_key_model); +} - Gtk::Label* l = manage (new Gtk::Label (_("Extenders:"))); - l->set_alignment (0, 0.5); - table->attach (*l, 0, 1, 1, 2); - table->attach (_extenders, 1, 2, 1, 2); +void +MackieControlProtocolGUI::action_changed (const Glib::ustring &sPath, const Glib::ustring &text, TreeModelColumnBase col) +{ + Gtk::TreePath path(sPath); + Gtk::TreeModel::iterator row = function_key_model->get_iter(path); - pack_start (*table); + if (row) { - Gtk::Label* cop_out = manage (new Gtk::Label (_("You must restart Ardour for changes\nto these settings to take effect."))); - cop_out->set_use_markup (true); - pack_start (*cop_out); + std::map::iterator i = action_map.find (text); + + if (i == action_map.end()) { + return; + } - set_spacing (4); - show_all (); + cerr << "Changed to " << i->first << " aka " << i->second << endl; - _surface_combo.signal_changed().connect (sigc::mem_fun (*this, &MackieControlProtocolGUI::surface_combo_changed)); - _extenders.signal_changed().connect (sigc::mem_fun (*this, &MackieControlProtocolGUI::extenders_changed)); + Glib::RefPtr act = ActionManager::get_action (i->second.c_str()); + + if (act) { + /* update visible text, using string supplied by + available action model so that it matches and is found + within the model. + */ + (*row).set_value (col.index(), text); + + /* update the current DeviceProfile, using the full + * path + */ + + int modifier; + + switch (col.index()) { + case 3: + modifier = MackieControlProtocol::MODIFIER_SHIFT; + break; + case 4: + modifier = MackieControlProtocol::MODIFIER_CONTROL; + break; + case 5: + modifier = MackieControlProtocol::MODIFIER_OPTION; + break; + case 6: + modifier = MackieControlProtocol::MODIFIER_CMDALT; + break; + case 7: + modifier = (MackieControlProtocol::MODIFIER_SHIFT|MackieControlProtocol::MODIFIER_CONTROL); + break; + default: + modifier = 0; + } + + _cp.device_profile().set_button_action ((*row)[function_key_columns.id], modifier, i->second); + } else { + std::cerr << "no such action\n"; + } + } } void MackieControlProtocolGUI::surface_combo_changed () { - if (_surface_combo.get_active_text() == _("Mackie Control")) { - ARDOUR::Config->set_mackie_emulation (X_("mcu")); - } else { - ARDOUR::Config->set_mackie_emulation (X_("bcf")); - } + _cp.set_device (_surface_combo.get_active_text()); + + /* update ipMIDI field */ + + cerr << "New device called " << _cp.device_info().name() << " with ipMIDI ? " << _cp.device_info().uses_ipmidi() << endl; + + ipmidi_base_port_spinner.set_sensitive (_cp.device_info().uses_ipmidi()); } void -MackieControlProtocolGUI::extenders_changed () +MackieControlProtocolGUI::profile_combo_changed () { - ARDOUR::Config->set_mackie_extenders (_extenders.get_value_as_int ()); + string profile = _profile_combo.get_active_text(); + + _cp.set_profile (profile); + + refresh_function_key_editor (); +} + +void +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())); } diff --git a/libs/surfaces/mackie/gui.h b/libs/surfaces/mackie/gui.h new file mode 100644 index 0000000000..9af0c21cab --- /dev/null +++ b/libs/surfaces/mackie/gui.h @@ -0,0 +1,109 @@ +/* + Copyright (C) 2010-2012 Paul Davis + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Gtk { + class CellRendererCombo; +} + +class MackieControlProtocol; + +#include "button.h" + +#include "i18n.h" + +class MackieControlProtocolGUI : public Gtk::Notebook +{ + public: + MackieControlProtocolGUI (MackieControlProtocol &); + + private: + MackieControlProtocol& _cp; + Gtk::ComboBoxText _surface_combo; + Gtk::ComboBoxText _profile_combo; + + struct AvailableActionColumns : public Gtk::TreeModel::ColumnRecord { + AvailableActionColumns() { + add (name); + add (path); + } + Gtk::TreeModelColumn name; + Gtk::TreeModelColumn path; + }; + + struct FunctionKeyColumns : public Gtk::TreeModel::ColumnRecord { + FunctionKeyColumns() { + add (name); + add (id); + add (plain); + add (shift); + add (control); + add (option); + add (cmdalt); + add (shiftcontrol); + }; + Gtk::TreeModelColumn name; + Gtk::TreeModelColumn id; + Gtk::TreeModelColumn plain; + Gtk::TreeModelColumn shift; + Gtk::TreeModelColumn control; + Gtk::TreeModelColumn option; + Gtk::TreeModelColumn cmdalt; + Gtk::TreeModelColumn shiftcontrol; + }; + + AvailableActionColumns available_action_columns; + FunctionKeyColumns function_key_columns; + + Gtk::ScrolledWindow function_key_scroller; + Gtk::TreeView function_key_editor; + Glib::RefPtr function_key_model; + Glib::RefPtr available_action_model; + + void build_available_action_menu (); + void refresh_function_key_editor (); + void build_function_key_editor (); + void action_changed (const Glib::ustring &sPath, const Glib::ustring &text, Gtk::TreeModelColumnBase); + Gtk::CellRendererCombo* make_action_renderer (Glib::RefPtr model, Gtk::TreeModelColumnBase); + + void surface_combo_changed (); + void profile_combo_changed (); + void ipmidi_spinner_changed (); + + std::map action_map; // map from action names to paths + + Gtk::CheckButton relay_click_button; + Gtk::CheckButton backlight_button; + Gtk::RadioButton absolute_touch_mode_button; + Gtk::RadioButton touch_move_mode_button; + Gtk::Adjustment touch_sensitivity_adjustment; + Gtk::HScale touch_sensitivity_scale; + Gtk::Button recalibrate_fader_button; + Gtk::Adjustment ipmidi_base_port_adjustment; + Gtk::SpinButton ipmidi_base_port_spinner; +}; + diff --git a/libs/surfaces/mackie/interface.cc b/libs/surfaces/mackie/interface.cc index d3625cac51..294a222a6b 100644 --- a/libs/surfaces/mackie/interface.cc +++ b/libs/surfaces/mackie/interface.cc @@ -15,14 +15,15 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include "control_protocol/control_protocol.h" -#include "mackie_control_protocol.h" -#include "ardour/rc_configuration.h" +#include #include "pbd/error.h" -#include +#include "ardour/rc_configuration.h" + +#include "control_protocol/control_protocol.h" +#include "mackie_control_protocol.h" using namespace ARDOUR; using namespace PBD; @@ -35,7 +36,7 @@ new_mackie_protocol (ControlProtocolDescriptor*, Session* s) try { mcp = new MackieControlProtocol (*s); - mcp->set_active (true); + /* do not set active here - wait for set_state() */ } catch (exception & e) { error << "Error instantiating MackieControlProtocol: " << e.what() << endmsg; diff --git a/libs/surfaces/mackie/jog.cc b/libs/surfaces/mackie/jog.cc new file mode 100644 index 0000000000..dc832868ed --- /dev/null +++ b/libs/surfaces/mackie/jog.cc @@ -0,0 +1,36 @@ +/* + Copyright (C) 2006,2007 John Anderson + Copyright (C) 2012 Paul Davis + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "jog.h" +#include "surface.h" +#include "control_group.h" + +using namespace Mackie; + +const int Jog::ID = 0x3c; + +Control* +Jog::factory (Surface& surface, int id, const char* name, Group& group) +{ + Jog* j = new Jog (id, name, group); + surface.pots[id] = j; + surface.controls.push_back (j); + group.add (*j); + return j; +} diff --git a/libs/surfaces/mackie/jog.h b/libs/surfaces/mackie/jog.h new file mode 100644 index 0000000000..23451f92b2 --- /dev/null +++ b/libs/surfaces/mackie/jog.h @@ -0,0 +1,45 @@ +/* + Copyright (C) 2006,2007 John Anderson + Copyright (C) 2012 Paul Davis + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __ardour_mackie_control_protocol_jog_h__ +#define __ardour_mackie_control_protocol_jog_h__ + +#include "controls.h" +#include "pot.h" + +namespace Mackie { + +class Jog : public Pot +{ +public: + static const int ID; + + Jog (int id, std::string name, Group & group) + : Pot (id, name, group) + { + } + + MidiByteArray zero() { return MidiByteArray(); } + + static Control* factory (Surface&, int id, const char*, Group&); +}; + +} + +#endif /* __ardour_mackie_control_protocol_jog_h__ */ diff --git a/libs/surfaces/mackie/jog_wheel.cc b/libs/surfaces/mackie/jog_wheel.cc new file mode 100644 index 0000000000..6d8980d78e --- /dev/null +++ b/libs/surfaces/mackie/jog_wheel.cc @@ -0,0 +1,50 @@ +#include + +#include "ardour/session.h" + +#include "jog_wheel.h" +#include "mackie_control_protocol.h" +#include "surface_port.h" +#include "controls.h" +#include "surface.h" + +#include + +using namespace Mackie; + +JogWheel::JogWheel (MackieControlProtocol & mcp) + : _mcp (mcp) + , _mode (scroll) +{ +} + +void +JogWheel::set_mode (Mode m) +{ + _mode = m; +} + +void JogWheel::jog_event (float delta) +{ + if (_mcp.zoom_mode()) { + if (delta > 0) { + for (unsigned int i = 0; i < fabs (delta); ++i) { + _mcp.ZoomIn(); + } + } else { + for (unsigned int i = 0; i < fabs (delta); ++i) { + _mcp.ZoomOut(); + } + } + return; + } + + switch (_mode) { + case scroll: + _mcp.ScrollTimeline (delta/4.0); + break; + default: + break; + } +} + diff --git a/libs/surfaces/mackie/jog_wheel.h b/libs/surfaces/mackie/jog_wheel.h new file mode 100644 index 0000000000..0750775bdd --- /dev/null +++ b/libs/surfaces/mackie/jog_wheel.h @@ -0,0 +1,34 @@ +#ifndef mackie_jog_wheel +#define mackie_jog_wheel + +#include "timer.h" + +#include +#include +#include + +class MackieControlProtocol; + +namespace Mackie +{ + +class JogWheel +{ + public: + enum Mode { scroll }; + + JogWheel (MackieControlProtocol & mcp); + + /// As the wheel turns... + void jog_event (float delta); + void set_mode (Mode m); + Mode mode() const { return _mode; } + +private: + MackieControlProtocol & _mcp; + Mode _mode; +}; + +} + +#endif diff --git a/libs/surfaces/mackie/led.cc b/libs/surfaces/mackie/led.cc new file mode 100644 index 0000000000..38aa5c7f3b --- /dev/null +++ b/libs/surfaces/mackie/led.cc @@ -0,0 +1,64 @@ +/* + Copyright (C) 2006,2007 John Anderson + Copyright (C) 2012 Paul Davis + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "led.h" +#include "surface.h" +#include "control_group.h" + +using namespace Mackie; + +const int Led::FaderTouch = 0x70; +const int Led::Timecode = 0x71; +const int Led::Beats = 0x72; +const int Led::RudeSolo = 0x73; +const int Led::RelayClick = 0x74; + +Control* +Led::factory (Surface& surface, int id, const char* name, Group& group) +{ + Led* l = new Led (id, name, group); + surface.leds[id] = l; + surface.controls.push_back (l); + group.add (*l); + return l; +} + +MidiByteArray +Led::set_state (LedState new_state) +{ + state = new_state; + + MIDI::byte msg = 0; + + switch (state.state()) { + case LedState::on: + msg = 0x7f; + break; + case LedState::off: + msg = 0x00; + break; + case LedState::flashing: + msg = 0x01; + break; + case LedState::none: + return MidiByteArray (); + } + + return MidiByteArray (3, 0x90, id(), msg); +} diff --git a/libs/surfaces/mackie/scripts/generate-button-handlers-h.erb b/libs/surfaces/mackie/led.h similarity index 50% rename from libs/surfaces/mackie/scripts/generate-button-handlers-h.erb rename to libs/surfaces/mackie/led.h index 605b6c29dc..fdbeb8cd9c 100644 --- a/libs/surfaces/mackie/scripts/generate-button-handlers-h.erb +++ b/libs/surfaces/mackie/led.h @@ -1,5 +1,6 @@ -<%# +/* Copyright (C) 2006,2007 John Anderson + Copyright (C) 2012 Paul Davis This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -14,41 +15,43 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. --%> -<%- -require 'controls.rb' - -sf = Surface.new -sf.parse( File.open "mackie-controls.csv" ) -buttons = sf.controls.find_all{|x| x.class == Button && x.group.class != Strip} --%> -#ifndef mackie_button_handler_h -#define mackie_button_handler_h -/* - Generated by scripts/generate-button-handlers.erb */ +#ifndef __ardour_mackie_control_protocol_led_h__ +#define __ardour_mackie_control_protocol_led_h__ + +#include "controls.h" +#include "midi_byte_array.h" #include "types.h" -namespace Mackie -{ +namespace Mackie { -class MackieButtonHandler +class Led : public Control { public: - virtual ~MackieButtonHandler() {} + static const int FaderTouch; + static const int Timecode; + static const int Beats; + static const int RudeSolo; + static const int RelayClick; + + Led (int id, std::string name, Group & group) + : Control (id, name, group) + , state (off) + { + } - virtual LedState default_button_press( Button & button ); - virtual LedState default_button_release( Button & button ); + Led & led() { return *this; } + MidiByteArray set_state (LedState); + + MidiByteArray zero() { return set_state (off); } - virtual void update_led( Button & button, LedState ls ) = 0; - -<%- buttons.each do |button| %> - virtual LedState <%=button.name%>_press( Button & ); - virtual LedState <%=button.name%>_release( Button & ); -<% end %> + static Control* factory (Surface&, int id, const char*, Group&); + + private: + LedState state; }; } -#endif +#endif /* __ardour_mackie_control_protocol_led_h__ */ diff --git a/libs/surfaces/mackie/mackie_button_handler.cc b/libs/surfaces/mackie/mackie_button_handler.cc deleted file mode 100644 index 04f2e8e20b..0000000000 --- a/libs/surfaces/mackie/mackie_button_handler.cc +++ /dev/null @@ -1,711 +0,0 @@ -/* - Generated by scripts/generate-button-handlers.erb -*/ -#include "mackie_button_handler.h" -#include "controls.h" - -#include - -using namespace std; -using namespace Mackie; - -LedState MackieButtonHandler::default_button_press( Button & button ) -{ - cout << "press: " << button << endl; - return on; -} -LedState MackieButtonHandler::default_button_release( Button & button ) -{ - cout << "release: " << button << endl; - return off; -} - -LedState MackieButtonHandler::io_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::io_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::sends_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::sends_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::pan_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::pan_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::plugin_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::plugin_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::eq_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::eq_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::dyn_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::dyn_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::left_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::left_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::right_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::right_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::channel_left_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::channel_left_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::channel_right_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::channel_right_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::flip_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::flip_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::edit_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::edit_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::name_value_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::name_value_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::timecode_beats_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::timecode_beats_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::F1_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::F1_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::F2_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::F2_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::F3_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::F3_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::F4_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::F4_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::F5_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::F5_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::F6_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::F6_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::F7_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::F7_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::F8_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::F8_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::F9_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::F9_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::F10_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::F10_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::F11_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::F11_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::F12_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::F12_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::F13_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::F13_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::F14_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::F14_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::F15_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::F15_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::F16_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::F16_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::shift_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::shift_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::option_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::option_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::control_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::control_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::cmd_alt_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::cmd_alt_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::on_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::on_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::rec_ready_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::rec_ready_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::undo_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::undo_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::snapshot_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::snapshot_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::touch_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::touch_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::redo_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::redo_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::marker_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::marker_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::enter_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::enter_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::cancel_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::cancel_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::mixer_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::mixer_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::frm_left_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::frm_left_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::frm_right_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::frm_right_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::loop_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::loop_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::punch_in_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::punch_in_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::punch_out_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::punch_out_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::home_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::home_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::end_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::end_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::rewind_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::rewind_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::ffwd_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::ffwd_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::stop_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::stop_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::play_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::play_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::record_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::record_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::cursor_up_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::cursor_up_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::cursor_down_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::cursor_down_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::cursor_left_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::cursor_left_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::cursor_right_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::cursor_right_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::zoom_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::zoom_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::scrub_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::scrub_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::user_a_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::user_a_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::user_b_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::user_b_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::fader_touch_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::fader_touch_release( Button & button ) -{ - return default_button_release( button ); -} - -LedState MackieButtonHandler::clicking_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::clicking_release( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::global_solo_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::global_solo_release( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::drop_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::drop_release( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::save_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::save_release( Button & button ) -{ - return default_button_press( button ); -} diff --git a/libs/surfaces/mackie/mackie_button_handler.h b/libs/surfaces/mackie/mackie_button_handler.h deleted file mode 100644 index a41c7778cc..0000000000 --- a/libs/surfaces/mackie/mackie_button_handler.h +++ /dev/null @@ -1,233 +0,0 @@ -#ifndef mackie_button_handler_h -#define mackie_button_handler_h -/* - Generated by scripts/generate-button-handlers.erb -*/ - -#include "types.h" - -namespace Mackie -{ - -class MackieButtonHandler -{ -public: - virtual ~MackieButtonHandler() {} - - virtual LedState default_button_press( Button & button ); - virtual LedState default_button_release( Button & button ); - - virtual void update_led( Button & button, LedState ls ) = 0; - - - virtual LedState io_press( Button & ); - virtual LedState io_release( Button & ); - - virtual LedState sends_press( Button & ); - virtual LedState sends_release( Button & ); - - virtual LedState pan_press( Button & ); - virtual LedState pan_release( Button & ); - - virtual LedState plugin_press( Button & ); - virtual LedState plugin_release( Button & ); - - virtual LedState eq_press( Button & ); - virtual LedState eq_release( Button & ); - - virtual LedState dyn_press( Button & ); - virtual LedState dyn_release( Button & ); - - virtual LedState left_press( Button & ); - virtual LedState left_release( Button & ); - - virtual LedState right_press( Button & ); - virtual LedState right_release( Button & ); - - virtual LedState channel_left_press( Button & ); - virtual LedState channel_left_release( Button & ); - - virtual LedState channel_right_press( Button & ); - virtual LedState channel_right_release( Button & ); - - virtual LedState flip_press( Button & ); - virtual LedState flip_release( Button & ); - - virtual LedState edit_press( Button & ); - virtual LedState edit_release( Button & ); - - virtual LedState name_value_press( Button & ); - virtual LedState name_value_release( Button & ); - - virtual LedState timecode_beats_press( Button & ); - virtual LedState timecode_beats_release( Button & ); - - virtual LedState F1_press( Button & ); - virtual LedState F1_release( Button & ); - - virtual LedState F2_press( Button & ); - virtual LedState F2_release( Button & ); - - virtual LedState F3_press( Button & ); - virtual LedState F3_release( Button & ); - - virtual LedState F4_press( Button & ); - virtual LedState F4_release( Button & ); - - virtual LedState F5_press( Button & ); - virtual LedState F5_release( Button & ); - - virtual LedState F6_press( Button & ); - virtual LedState F6_release( Button & ); - - virtual LedState F7_press( Button & ); - virtual LedState F7_release( Button & ); - - virtual LedState F8_press( Button & ); - virtual LedState F8_release( Button & ); - - virtual LedState F9_press( Button & ); - virtual LedState F9_release( Button & ); - - virtual LedState F10_press( Button & ); - virtual LedState F10_release( Button & ); - - virtual LedState F11_press( Button & ); - virtual LedState F11_release( Button & ); - - virtual LedState F12_press( Button & ); - virtual LedState F12_release( Button & ); - - virtual LedState F13_press( Button & ); - virtual LedState F13_release( Button & ); - - virtual LedState F14_press( Button & ); - virtual LedState F14_release( Button & ); - - virtual LedState F15_press( Button & ); - virtual LedState F15_release( Button & ); - - virtual LedState F16_press( Button & ); - virtual LedState F16_release( Button & ); - - virtual LedState shift_press( Button & ); - virtual LedState shift_release( Button & ); - - virtual LedState option_press( Button & ); - virtual LedState option_release( Button & ); - - virtual LedState control_press( Button & ); - virtual LedState control_release( Button & ); - - virtual LedState cmd_alt_press( Button & ); - virtual LedState cmd_alt_release( Button & ); - - virtual LedState on_press( Button & ); - virtual LedState on_release( Button & ); - - virtual LedState rec_ready_press( Button & ); - virtual LedState rec_ready_release( Button & ); - - virtual LedState undo_press( Button & ); - virtual LedState undo_release( Button & ); - - virtual LedState snapshot_press( Button & ); - virtual LedState snapshot_release( Button & ); - - virtual LedState touch_press( Button & ); - virtual LedState touch_release( Button & ); - - virtual LedState redo_press( Button & ); - virtual LedState redo_release( Button & ); - - virtual LedState marker_press( Button & ); - virtual LedState marker_release( Button & ); - - virtual LedState enter_press( Button & ); - virtual LedState enter_release( Button & ); - - virtual LedState cancel_press( Button & ); - virtual LedState cancel_release( Button & ); - - virtual LedState mixer_press( Button & ); - virtual LedState mixer_release( Button & ); - - virtual LedState frm_left_press( Button & ); - virtual LedState frm_left_release( Button & ); - - virtual LedState frm_right_press( Button & ); - virtual LedState frm_right_release( Button & ); - - virtual LedState loop_press( Button & ); - virtual LedState loop_release( Button & ); - - virtual LedState punch_in_press( Button & ); - virtual LedState punch_in_release( Button & ); - - virtual LedState punch_out_press( Button & ); - virtual LedState punch_out_release( Button & ); - - virtual LedState home_press( Button & ); - virtual LedState home_release( Button & ); - - virtual LedState end_press( Button & ); - virtual LedState end_release( Button & ); - - virtual LedState rewind_press( Button & ); - virtual LedState rewind_release( Button & ); - - virtual LedState ffwd_press( Button & ); - virtual LedState ffwd_release( Button & ); - - virtual LedState stop_press( Button & ); - virtual LedState stop_release( Button & ); - - virtual LedState play_press( Button & ); - virtual LedState play_release( Button & ); - - virtual LedState record_press( Button & ); - virtual LedState record_release( Button & ); - - virtual LedState cursor_up_press( Button & ); - virtual LedState cursor_up_release( Button & ); - - virtual LedState cursor_down_press( Button & ); - virtual LedState cursor_down_release( Button & ); - - virtual LedState cursor_left_press( Button & ); - virtual LedState cursor_left_release( Button & ); - - virtual LedState cursor_right_press( Button & ); - virtual LedState cursor_right_release( Button & ); - - virtual LedState zoom_press( Button & ); - virtual LedState zoom_release( Button & ); - - virtual LedState scrub_press( Button & ); - virtual LedState scrub_release( Button & ); - - virtual LedState user_a_press( Button & ); - virtual LedState user_a_release( Button & ); - - virtual LedState user_b_press( Button & ); - virtual LedState user_b_release( Button & ); - - virtual LedState fader_touch_press( Button & ); - virtual LedState fader_touch_release( Button & ); - - virtual LedState clicking_press( Button & ); - virtual LedState clicking_release( Button & ); - - virtual LedState global_solo_press( Button & ); - virtual LedState global_solo_release( Button & ); - - virtual LedState drop_press( Button & ); - virtual LedState drop_release( Button & ); - - virtual LedState save_press( Button & ); - virtual LedState save_release( Button & ); -}; - -} - -#endif diff --git a/libs/surfaces/mackie/mackie_control_protocol.cc b/libs/surfaces/mackie/mackie_control_protocol.cc index 7e208686ba..c1f5317976 100644 --- a/libs/surfaces/mackie/mackie_control_protocol.cc +++ b/libs/surfaces/mackie/mackie_control_protocol.cc @@ -1,5 +1,6 @@ /* Copyright (C) 2006,2007 John Anderson + Copyright (C) 2012 Paul Davis This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -34,21 +35,23 @@ #include "midi++/types.h" #include "midi++/port.h" -#include "midi++/manager.h" +#include "midi++/ipmidi_port.h" #include "pbd/pthread_utils.h" #include "pbd/error.h" #include "pbd/memento_command.h" #include "pbd/convert.h" +#include "ardour/automation_control.h" #include "ardour/dB.h" #include "ardour/debug.h" #include "ardour/location.h" -#include "ardour/midi_ui.h" +#include "ardour/meter.h" #include "ardour/panner.h" #include "ardour/panner_shell.h" #include "ardour/route.h" #include "ardour/session.h" #include "ardour/tempo.h" +#include "ardour/track.h" #include "ardour/types.h" #include "ardour/audioengine.h" @@ -56,53 +59,77 @@ #include "midi_byte_array.h" #include "mackie_control_exception.h" -#include "route_signal.h" -#include "mackie_midi_builder.h" +#include "device_profile.h" #include "surface_port.h" #include "surface.h" -#include "bcf_surface.h" -#include "mackie_surface.h" +#include "strip.h" +#include "control_group.h" +#include "meter.h" +#include "button.h" +#include "fader.h" +#include "pot.h" using namespace ARDOUR; using namespace std; using namespace Mackie; using namespace PBD; +using namespace Glib; #include "i18n.h" #include "pbd/abstract_ui.cc" // instantiate template -MackieMidiBuilder builder; +const int MackieControlProtocol::MODIFIER_OPTION = 0x1; +const int MackieControlProtocol::MODIFIER_CONTROL = 0x2; +const int MackieControlProtocol::MODIFIER_SHIFT = 0x4; +const int MackieControlProtocol::MODIFIER_CMDALT = 0x8; -#define midi_ui_context() MidiControlUI::instance() /* a UICallback-derived object that specifies the event loop for signal handling */ -#define ui_bind(f, ...) boost::protect (boost::bind (f, __VA_ARGS__)) +MackieControlProtocol* MackieControlProtocol::_instance = 0; -extern PBD::EventLoop::InvalidationRecord* __invalidator (sigc::trackable& trackable, const char*, int); -#define invalidator(x) __invalidator ((x), __FILE__, __LINE__) +bool MackieControlProtocol::probe() +{ + return true; +} MackieControlProtocol::MackieControlProtocol (Session& session) - : ControlProtocol (session, X_("Mackie"), MidiControlUI::instance()) + : ControlProtocol (session, X_("Mackie")) , AbstractUI ("mackie") , _current_initial_bank (0) - , _surface (0) - , _jog_wheel (*this) , _timecode_type (ARDOUR::AnyTime::BBT) , _input_bundle (new ARDOUR::Bundle (_("Mackie Control In"), true)) , _output_bundle (new ARDOUR::Bundle (_("Mackie Control Out"), false)) , _gui (0) + , _zoom_mode (false) + , _scrub_mode (false) + , _flip_mode (false) + , _view_mode (Mixer) + , _current_selected_track (-1) + , _modifier_state (0) + , _ipmidi_base (MIDI::IPMIDIPort::lowest_ipmidi_port_default) + , needs_ipmidi_restart (false) { DEBUG_TRACE (DEBUG::MackieControl, "MackieControlProtocol::MackieControlProtocol\n"); - AudioEngine::instance()->PortConnectedOrDisconnected.connect ( - audio_engine_connections, invalidator (*this), ui_bind (&MackieControlProtocol::port_connected_or_disconnected, this, _2, _4, _5), - midi_ui_context () - ); + DeviceInfo::reload_device_info (); + DeviceProfile::reload_device_profiles (); + + TrackSelectionChanged.connect (gui_connections, MISSING_INVALIDATOR, boost::bind (&MackieControlProtocol::gui_track_selection_changed, this, _1), this); + + _instance = this; + + build_button_map (); } MackieControlProtocol::~MackieControlProtocol() { DEBUG_TRACE (DEBUG::MackieControl, "MackieControlProtocol::~MackieControlProtocol\n"); + _active = false; + + /* stop event loop */ + + BaseUI::quit (); + try { close(); } @@ -114,34 +141,33 @@ MackieControlProtocol::~MackieControlProtocol() } DEBUG_TRACE (DEBUG::MackieControl, "finished ~MackieControlProtocol::MackieControlProtocol\n"); + + _instance = 0; } -Mackie::Surface& -MackieControlProtocol::surface() +void +MackieControlProtocol::thread_init () { - if (_surface == 0) { - throw MackieControlException ("_surface is 0 in MackieControlProtocol::surface"); - } - return *_surface; -} + struct sched_param rtparam; -const Mackie::SurfacePort& -MackieControlProtocol::mcu_port() const -{ - if (_ports.size() < 1) { - return _dummy_port; - } else { - return dynamic_cast (*_ports[0]); + pthread_set_name (X_("MackieControl")); + + PBD::notify_gui_about_thread_creation (X_("gui"), pthread_self(), X_("MackieControl"), 2048); + ARDOUR::SessionEvent::create_per_thread_pool (X_("MackieControl"), 128); + + memset (&rtparam, 0, sizeof (rtparam)); + rtparam.sched_priority = 9; /* XXX should be relative to audio (JACK) thread */ + + if (pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam) != 0) { + // do we care? not particularly. } } -Mackie::SurfacePort& -MackieControlProtocol::mcu_port() +void +MackieControlProtocol::midi_connectivity_established () { - if (_ports.size() < 1) { - return _dummy_port; - } else { - return dynamic_cast (*_ports[0]); + for (Surfaces::const_iterator si = surfaces.begin(); si != surfaces.end(); ++si) { + (*si)->say_hello (); } } @@ -151,7 +177,6 @@ void MackieControlProtocol::prev_track() { if (_current_initial_bank >= 1) { - session->set_dirty(); switch_banks (_current_initial_bank - 1); } } @@ -162,36 +187,20 @@ void MackieControlProtocol::next_track() { Sorted sorted = get_sorted_routes(); - if (_current_initial_bank + route_table.size() < sorted.size()) { - session->set_dirty(); + if (_current_initial_bank + n_strips() < sorted.size()) { switch_banks (_current_initial_bank + 1); } } -void -MackieControlProtocol::clear_route_signals() +bool +MackieControlProtocol::route_is_locked_to_strip (boost::shared_ptr r) const { - for (RouteSignals::iterator it = route_signals.begin(); it != route_signals.end(); ++it) { - delete *it; + for (Surfaces::const_iterator si = surfaces.begin(); si != surfaces.end(); ++si) { + if ((*si)->route_is_locked_to_strip (r)) { + return true; + } } - route_signals.clear(); -} - -// return the port for a given id - 0 based -// throws an exception if no port found -MackiePort& -MackieControlProtocol::port_for_id (uint32_t index) -{ - uint32_t current_max = 0; - for (MackiePorts::iterator it = _ports.begin(); it != _ports.end(); ++it) { - current_max += (*it)->strips(); - if (index < current_max) return **it; - } - - // oops - no matching port - ostringstream os; - os << "No port for index " << index; - throw MackieControlException (os.str()); + return false; } // predicate for sort call in get_sorted_routes @@ -228,19 +237,50 @@ MackieControlProtocol::get_sorted_routes() // sort in remote_id order, and exclude master, control and hidden routes // and any routes that are already set. + for (RouteList::iterator it = routes->begin(); it != routes->end(); ++it) { - Route & route = **it; - if ( - route.active() - && !route.is_master() - && !route.is_hidden() - && !route.is_monitor() - && remote_ids.find (route.remote_control_id()) == remote_ids.end() - ) { - sorted.push_back (*it); - remote_ids.insert (route.remote_control_id()); + + boost::shared_ptr route = *it; + + if (remote_ids.find (route->remote_control_id()) != remote_ids.end()) { + continue; } + + if (route->is_hidden() || route->is_master() || route->is_monitor()) { + continue; + } + + /* don't include locked routes */ + + if (route_is_locked_to_strip(route)) { + continue; + } + + switch (_view_mode) { + case Mixer: + break; + case AudioTracks: + break; + case Busses: + break; + case MidiTracks: + break; + case Dynamics: + break; + case EQ: + break; + case Loop: + break; + case Sends: + break; + case Plugins: + break; + } + + sorted.push_back (*it); + remote_ids.insert (route->remote_control_id()); } + sort (sorted.begin(), sorted.end(), RouteByRemoteId()); return sorted; } @@ -248,99 +288,73 @@ MackieControlProtocol::get_sorted_routes() void MackieControlProtocol::refresh_current_bank() { - switch_banks (_current_initial_bank); + switch_banks (_current_initial_bank, true); +} + +uint32_t +MackieControlProtocol::n_strips (bool with_locked_strips) const +{ + uint32_t strip_count = 0; + + for (Surfaces::const_iterator si = surfaces.begin(); si != surfaces.end(); ++si) { + strip_count += (*si)->n_strips (with_locked_strips); + } + + return strip_count; } void -MackieControlProtocol::switch_banks (int initial) +MackieControlProtocol::switch_banks (uint32_t initial, bool force) { - // DON'T prevent bank switch if initial == _current_initial_bank - // because then this method can't be used as a refresh + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("switch banking to start at %1 force ? %2 current = %3\n", initial, force, _current_initial_bank)); - // sanity checking - Sorted sorted = get_sorted_routes(); - int delta = sorted.size() - route_table.size(); - if (initial < 0 || (delta > 0 && initial > delta)) { - DEBUG_TRACE (DEBUG::MackieControl, string_compose ("not switching to %1\n", initial)); + if (initial == _current_initial_bank && !force) { return; } + + Sorted sorted = get_sorted_routes(); + 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 */ + return; + } + _current_initial_bank = initial; + _current_selected_track = -1; - // first clear the signals from old routes - // taken care of by the RouteSignal destructors - clear_route_signals(); + // Map current bank of routes onto each surface(+strip) - // now set the signals for new routes if (_current_initial_bank <= sorted.size()) { - // fetch the bank start and end to switch to - uint32_t end_pos = min (route_table.size(), sorted.size()); - Sorted::iterator it = sorted.begin() + _current_initial_bank; - Sorted::iterator end = sorted.begin() + _current_initial_bank + end_pos; - DEBUG_TRACE (DEBUG::MackieControl, string_compose ("switch to %1, %2\n", _current_initial_bank, end_pos)); - - // clear out routes from our table in case any have been deleted - for (vector >::iterator i = route_table.begin(); i != route_table.end(); ++i) { - i->reset (); - } + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("switch to %1, %2, available routes %3\n", _current_initial_bank, strip_cnt, sorted.size())); // link routes to strips - uint32_t i = 0; - for (; it != end && it != sorted.end(); ++it, ++i) { - boost::shared_ptr route = *it; - assert (surface().strips[i]); - Strip & strip = *surface().strips[i]; + Sorted::iterator r = sorted.begin() + _current_initial_bank; + + for (Surfaces::iterator si = surfaces.begin(); si != surfaces.end(); ++si) { + vector > routes; + uint32_t added = 0; - DEBUG_TRACE (DEBUG::MackieControl, string_compose ("remote id %1 connecting %2 to %3 with port %4\n", - route->remote_control_id(), route->name(), strip.name(), port_for_id(i))); - route_table[i] = route; - RouteSignal * rs = new RouteSignal (route, *this, strip, port_for_id(i)); - route_signals.push_back (rs); - // update strip from route - rs->notify_all(); - } + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("surface has %1 unlockedstrips\n", (*si)->n_strips (false))); - // create dead strips if there aren't enough routes to - // fill a bank - for (; i < route_table.size(); ++i) { - Strip & strip = *surface().strips[i]; - // send zero for this strip - MackiePort & port = port_for_id(i); - port.write (builder.zero_strip (port, strip)); + for (; r != sorted.end() && added < (*si)->n_strips (false); ++r, ++added) { + routes.push_back (*r); + } + + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("give surface %1 routes\n", routes.size())); + + (*si)->map_routes (routes); } } - // display the current start bank. - surface().display_bank_start (mcu_port(), builder, _current_initial_bank); -} - -void -MackieControlProtocol::zero_all() -{ - // TODO turn off Timecode displays - - // zero all strips - for (Surface::Strips::iterator it = surface().strips.begin(); it != surface().strips.end(); ++it) { - MackiePort & port = port_for_id ((*it)->index()); - port.write (builder.zero_strip (port, **it)); - } - - // and the master strip - mcu_port().write (builder.zero_strip (dynamic_cast (mcu_port()), master_strip())); - - // turn off global buttons and leds - // global buttons are only ever on mcu_port, so we don't have - // to figure out which port. - for (Surface::Controls::iterator it = surface().controls.begin(); it != surface().controls.end(); ++it) { - Control & control = **it; - if (!control.group().is_strip() && control.accepts_feedback()) { - mcu_port().write (builder.zero_control (control)); - } - } - - // any hardware-specific stuff - surface().zero_all (mcu_port(), builder); + /* reset this to get the right display of view mode after the switch */ + set_view_mode (_view_mode); + + /* current bank has not been saved */ + session->set_dirty(); } int @@ -349,127 +363,75 @@ MackieControlProtocol::set_active (bool yn) if (yn == _active) { return 0; } - - try - { - // the reason for the locking and unlocking is that - // glibmm can't do a condition wait on a RecMutex - if (yn) { - // TODO what happens if this fails half way? - - // create MackiePorts - { - Glib::Mutex::Lock lock (update_mutex); - create_ports(); - } - - // now initialise MackiePorts - ie exchange sysex messages - for (MackiePorts::iterator it = _ports.begin(); it != _ports.end(); ++it) { - (*it)->open(); - } - - // wait until all ports are active - // TODO a more sophisticated approach would - // allow things to start up with only an MCU, even if - // extenders were specified but not responding. - for (MackiePorts::iterator it = _ports.begin(); it != _ports.end(); ++it) { - (*it)->wait_for_init(); - } - - // create surface object. This depends on the ports being - // correctly initialised - initialize_surface(); - connect_session_signals(); - - // yeehah! - _active = true; - - // send current control positions to surface - // must come after _active = true otherwise it won't run - update_surface(); - } else { - close(); - _active = false; - } - } - - catch (exception & e) { - DEBUG_TRACE (DEBUG::MackieControl, string_compose ("set_active to false because exception caught: %1\n", e.what())); + + if (yn) { + + /* start event loop */ + + BaseUI::run (); + + create_surfaces (); + connect_session_signals (); + _active = true; + update_surfaces (); + + /* set up periodic task for metering and automation + */ + + Glib::RefPtr periodic_timeout = Glib::TimeoutSource::create (100); // milliseconds + periodic_connection = periodic_timeout->connect (sigc::mem_fun (*this, &MackieControlProtocol::periodic)); + periodic_timeout->attach (main_loop()->get_context()); + + } else { + + BaseUI::quit (); + close (); _active = false; - throw; + } return 0; } -bool -MackieControlProtocol::handle_strip_button (SurfacePort & port, Control & control, ButtonState bs, boost::shared_ptr route) +bool +MackieControlProtocol::periodic () { - bool state = false; - - if (bs == press) { - if (control.name() == "recenable") { - state = !route->record_enabled(); - route->set_record_enabled (state, this); - } else if (control.name() == "mute") { - state = !route->muted(); - route->set_mute (state, this); - } else if (control.name() == "solo") { - state = !route->soloed(); - route->set_solo (state, this); - } else if (control.name() == "select") { - // TODO make the track selected. Whatever that means. - //state = default_button_press (dynamic_cast (control)); - } else if (control.name() == "vselect") { - // TODO could be used to select different things to apply the pot to? - //state = default_button_press (dynamic_cast (control)); - } + if (!_active) { + return false; } - if (control.name() == "fader_touch") { - state = bs == press; - control.strip().gain().set_in_use (state); - - if (ARDOUR::Config->get_mackie_emulation() == "bcf" && state) { - /* BCF faders don't support touch, so add a timeout to reset - their `in_use' state. - */ - port.add_in_use_timeout (control.strip().gain(), &control.strip().fader_touch()); - } + if (needs_ipmidi_restart) { + ipmidi_restart (); + return true; } - return state; + struct timeval now; + uint64_t now_usecs; + gettimeofday (&now, 0); + + now_usecs = (now.tv_sec * 1000000) + now.tv_usec; + + for (Surfaces::iterator s = surfaces.begin(); s != surfaces.end(); ++s) { + (*s)->periodic (now_usecs); + } + + update_timecode_display (); + + return true; } -void -MackieControlProtocol::update_led (Mackie::Button & button, Mackie::LedState ls) -{ - if (ls != none) { - SurfacePort * port = 0; - if (button.group().is_strip()) { - if (button.group().is_master()) { - port = &mcu_port(); - } else { - port = &port_for_id (dynamic_cast (button.group()).index()); - } - } else { - port = &mcu_port(); - } - port->write (builder.build_led (button, ls)); - } -} void MackieControlProtocol::update_timecode_beats_led() { switch (_timecode_type) { case ARDOUR::AnyTime::BBT: - update_global_led ("beats", on); - update_global_led ("timecode", off); + update_global_led (Led::Beats, on); + update_global_led (Led::Timecode, off); break; case ARDOUR::AnyTime::Timecode: - update_global_led ("timecode", on); - update_global_led ("beats", off); + update_global_led (Led::Timecode, on); + update_global_led (Led::Beats, off); break; default: ostringstream os; @@ -479,30 +441,44 @@ MackieControlProtocol::update_timecode_beats_led() } void -MackieControlProtocol::update_global_button (const string & name, LedState ls) +MackieControlProtocol::update_global_button (int id, LedState ls) { - if (surface().controls_by_name.find (name) != surface().controls_by_name.end()) { - Button * button = dynamic_cast (surface().controls_by_name[name]); - mcu_port().write (builder.build_led (button->led(), ls)); + boost::shared_ptr surface = surfaces.front(); + + if (!surface->type() == mcu) { + return; + } + + map::iterator x = surface->controls_by_device_independent_id.find (id); + if (x != surface->controls_by_device_independent_id.end()) { + Button * button = dynamic_cast (x->second); + surface->write (button->set_state (ls)); } else { - DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Button %1 not found\n", name)); + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Button %1 not found\n", id)); } } void -MackieControlProtocol::update_global_led (const string & name, LedState ls) +MackieControlProtocol::update_global_led (int id, LedState ls) { - if (surface().controls_by_name.find (name) != surface().controls_by_name.end()) { - Led * led = dynamic_cast (surface().controls_by_name[name]); - mcu_port().write (builder.build_led (*led, ls)); + boost::shared_ptr surface = surfaces.front(); + + if (!surface->type() == mcu) { + return; + } + + map::iterator x = surface->controls_by_device_independent_id.find (id); + if (x != surface->controls_by_device_independent_id.end()) { + Led * led = dynamic_cast (x->second); + surface->write (led->set_state (ls)); } else { - DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Led %1 not found\n", name)); + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Led %1 not found\n", id)); } } // send messages to surface to set controls to correct values void -MackieControlProtocol::update_surface() +MackieControlProtocol::update_surfaces() { if (!_active) { return; @@ -510,22 +486,13 @@ MackieControlProtocol::update_surface() // do the initial bank switch to connect signals // _current_initial_bank is initialised by set_state - switch_banks (_current_initial_bank); - - /* Create a RouteSignal for the master route, if we don't already have one */ - if (!master_route_signal) { - boost::shared_ptr mr = master_route (); - if (mr) { - master_route_signal = boost::shared_ptr (new RouteSignal (mr, *this, master_strip(), mcu_port())); - // update strip from route - master_route_signal->notify_all(); - } - } + switch_banks (_current_initial_bank, true); // sometimes the jog wheel is a pot - surface().blank_jog_ring (mcu_port(), builder); + surfaces.front()->blank_jog_ring (); // update global buttons and displays + notify_record_state_changed(); notify_transport_state_changed(); update_timecode_beats_led(); @@ -535,198 +502,164 @@ void MackieControlProtocol::connect_session_signals() { // receive routes added - session->RouteAdded.connect(session_connections, MISSING_INVALIDATOR, ui_bind (&MackieControlProtocol::notify_route_added, this, _1), midi_ui_context()); + session->RouteAdded.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&MackieControlProtocol::notify_route_added, this, _1), this); // receive record state toggled - session->RecordStateChanged.connect(session_connections, MISSING_INVALIDATOR, ui_bind (&MackieControlProtocol::notify_record_state_changed, this), midi_ui_context()); + session->RecordStateChanged.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&MackieControlProtocol::notify_record_state_changed, this), this); // receive transport state changed - session->TransportStateChange.connect(session_connections, MISSING_INVALIDATOR, ui_bind (&MackieControlProtocol::notify_transport_state_changed, this), midi_ui_context()); + session->TransportStateChange.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&MackieControlProtocol::notify_transport_state_changed, this), this); + session->TransportLooped.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&MackieControlProtocol::notify_loop_state_changed, this), this); // receive punch-in and punch-out - Config->ParameterChanged.connect(session_connections, MISSING_INVALIDATOR, ui_bind (&MackieControlProtocol::notify_parameter_changed, this, _1), midi_ui_context()); - session->config.ParameterChanged.connect (session_connections, MISSING_INVALIDATOR, ui_bind (&MackieControlProtocol::notify_parameter_changed, this, _1), midi_ui_context()); + Config->ParameterChanged.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&MackieControlProtocol::notify_parameter_changed, this, _1), this); + session->config.ParameterChanged.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&MackieControlProtocol::notify_parameter_changed, this, _1), this); // receive rude solo changed - session->SoloActive.connect(session_connections, MISSING_INVALIDATOR, ui_bind (&MackieControlProtocol::notify_solo_active_changed, this, _1), midi_ui_context()); + session->SoloActive.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&MackieControlProtocol::notify_solo_active_changed, this, _1), this); // make sure remote id changed signals reach here // see also notify_route_added Sorted sorted = get_sorted_routes(); for (Sorted::iterator it = sorted.begin(); it != sorted.end(); ++it) { - (*it)->RemoteControlIDChanged.connect (route_connections, MISSING_INVALIDATOR, ui_bind(&MackieControlProtocol::notify_remote_id_changed, this), midi_ui_context()); + (*it)->RemoteControlIDChanged.connect (route_connections, MISSING_INVALIDATOR, boost::bind (&MackieControlProtocol::notify_remote_id_changed, this), this); } } -void -MackieControlProtocol::add_port (MIDI::Port & midi_input_port, MIDI::Port & midi_output_port, int number) +void +MackieControlProtocol::set_profile (const string& profile_name) { - DEBUG_TRACE (DEBUG::MackieControl, string_compose ("add port %1 %2\n", midi_input_port.name(), midi_output_port.name())); - - MackiePort * sport = new MackiePort (*this, midi_input_port, midi_output_port, number); - _ports.push_back (sport); - - sport->init_event.connect_same_thread (port_connections, boost::bind (&MackieControlProtocol::handle_port_init, this, sport)); - sport->active_event.connect_same_thread (port_connections, boost::bind (&MackieControlProtocol::handle_port_active, this, sport)); - sport->inactive_event.connect_same_thread (port_connections, boost::bind (&MackieControlProtocol::handle_port_inactive, this, sport)); - - _input_bundle->add_channel ( - midi_input_port.name(), - ARDOUR::DataType::MIDI, - session->engine().make_port_name_non_relative (midi_input_port.name()) - ); - - _output_bundle->add_channel ( - midi_output_port.name(), - ARDOUR::DataType::MIDI, - session->engine().make_port_name_non_relative (midi_output_port.name()) - ); -} - -void -MackieControlProtocol::create_ports() -{ - MIDI::Manager * mm = MIDI::Manager::instance(); - MIDI::Port * midi_input_port = mm->add_port ( - new MIDI::Port (string_compose (_("%1 in"), default_port_name), MIDI::Port::IsInput, session->engine().jack()) - ); - MIDI::Port * midi_output_port = mm->add_port ( - new MIDI::Port (string_compose (_("%1 out"), default_port_name), MIDI::Port::IsOutput, session->engine().jack()) - ); - - /* Create main port */ - - if (!midi_input_port->ok() || !midi_output_port->ok()) { - ostringstream os; - os << _("Mackie control MIDI ports could not be created; Mackie control disabled"); - error << os.str() << endmsg; - throw MackieControlException (os.str()); + if (profile_name == "default") { + /* reset to default */ + _device_profile = DeviceProfile (profile_name); } - add_port (*midi_input_port, *midi_output_port, 0); + map::iterator d = DeviceProfile::device_profiles.find (profile_name); - /* Create extender ports */ + if (d == DeviceProfile::device_profiles.end()) { + return; + } + + _device_profile = d->second; +} - for (uint32_t index = 1; index <= Config->get_mackie_extenders(); ++index) { - MIDI::Port * midi_input_port = mm->add_port ( - new MIDI::Port (string_compose (_("mcu_xt_%1 in"), index), MIDI::Port::IsInput, session->engine().jack()) - ); - MIDI::Port * midi_output_port = mm->add_port ( - new MIDI::Port (string_compose (_("mcu_xt_%1 out"), index), MIDI::Port::IsOutput, session->engine().jack()) - ); - if (midi_input_port->ok() && midi_output_port->ok()) { - add_port (*midi_input_port, *midi_output_port, index); +void +MackieControlProtocol::set_device (const string& device_name, bool allow_activation) +{ + map::iterator d = DeviceInfo::device_info.find (device_name); + + if (d == DeviceInfo::device_info.end()) { + return; + } + + if (_active) { + clear_ports (); + surfaces.clear (); + } + + _device_info = d->second; + + if (allow_activation) { + set_active (true); + } else { + if (_active) { + create_surfaces (); + switch_banks (0, true); } } } -boost::shared_ptr -MackieControlProtocol::master_route() -{ - return session->master_out (); -} - -Strip& -MackieControlProtocol::master_strip() -{ - return dynamic_cast (*surface().groups["master"]); -} - void -MackieControlProtocol::initialize_surface() +MackieControlProtocol::create_surfaces () { - // set up the route table - int strips = 0; - for (MackiePorts::iterator it = _ports.begin(); it != _ports.end(); ++it) { - strips += (*it)->strips(); - } + string device_name; + surface_type_t stype = mcu; + char buf[128]; - set_route_table_size (strips); - - // TODO same as code in mackie_port.cc - string emulation = ARDOUR::Config->get_mackie_emulation(); - if (emulation == "bcf") { - _surface = new BcfSurface (strips); - } else if (emulation == "mcu") { - _surface = new MackieSurface (strips); + if (_device_info.extenders() == 0) { + device_name = X_("mackie control"); } else { - ostringstream os; - os << "no Surface class found for emulation: " << emulation; - throw MackieControlException (os.str()); + device_name = X_("mackie control #1"); } - _surface->init(); + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Create %1 surfaces\n", 1 + _device_info.extenders())); - // Connect events. Must be after route table otherwise there will be trouble + for (uint32_t n = 0; n < 1 + _device_info.extenders(); ++n) { - for (MackiePorts::iterator it = _ports.begin(); it != _ports.end(); ++it) { - (*it)->control_event.connect_same_thread (port_connections, boost::bind (&MackieControlProtocol::handle_control_event, this, _1, _2, _3)); + boost::shared_ptr surface (new Surface (*this, device_name, n, stype)); + surfaces.push_back (surface); + + /* next device will be an extender */ + + if (_device_info.extenders() < 2) { + device_name = X_("mackie control #2"); + } else { + snprintf (buf, sizeof (buf), X_("mackie control #%d"), n+2); + device_name = buf; + } + stype = ext; + + if (!_device_info.uses_ipmidi()) { + _input_bundle->add_channel ( + surface->port().input_port().name(), + ARDOUR::DataType::MIDI, + session->engine().make_port_name_non_relative (surface->port().input_port().name()) + ); + + _output_bundle->add_channel ( + surface->port().output_port().name(), + ARDOUR::DataType::MIDI, + session->engine().make_port_name_non_relative (surface->port().output_port().name()) + ); + } + + int fd; + MIDI::Port& input_port (surface->port().input_port()); + + if ((fd = input_port.selectable ()) >= 0) { + Glib::RefPtr psrc = IOSource::create (fd, IO_IN|IO_HUP|IO_ERR); + + psrc->connect (sigc::bind (sigc::mem_fun (this, &MackieControlProtocol::midi_input_handler), &input_port)); + psrc->attach (main_loop()->get_context()); + + // glibmm hack: for now, store only the GSource* + + port_sources.push_back (psrc->gobj()); + g_source_ref (psrc->gobj()); + } } } void MackieControlProtocol::close() { - - // must be before other shutdown otherwise polling loop - // calls methods on objects that are deleted + clear_ports (); port_connections.drop_connections (); session_connections.drop_connections (); route_connections.drop_connections (); + periodic_connection.disconnect (); - if (_surface != 0) { - // These will fail if the port has gone away. - // So catch the exception and do the rest of the - // close afterwards - // because the bcf doesn't respond to the next 3 sysex messages - try { - zero_all(); - } - - catch (exception & e) { - DEBUG_TRACE (DEBUG::MackieControl, string_compose ("MackieControlProtocol::close caught exception: %1\n", e.what())); - } - - for (MackiePorts::iterator it = _ports.begin(); it != _ports.end(); ++it) { - try { - MackiePort & port = **it; - // faders to minimum - port.write_sysex (0x61); - // All LEDs off - port.write_sysex (0x62); - // Reset (reboot into offline mode) - port.write_sysex (0x63); - } - catch (exception & e) { - DEBUG_TRACE (DEBUG::MackieControl, string_compose ("MackieControlProtocol::close caught exception: %1\n", e.what())); - } - } - - // disconnect routes from strips - clear_route_signals(); - delete _surface; - _surface = 0; - } - - // shut down MackiePorts - for (MackiePorts::iterator it = _ports.begin(); it != _ports.end(); ++it) { - delete *it; - } - - _ports.clear(); + surfaces.clear (); } XMLNode& MackieControlProtocol::get_state() { DEBUG_TRACE (DEBUG::MackieControl, "MackieControlProtocol::get_state\n"); + char buf[16]; // add name of protocol XMLNode* node = new XMLNode (X_("Protocol")); node->add_property (X_("name"), ARDOUR::ControlProtocol::_name); // add current bank - ostringstream os; - os << _current_initial_bank; - node->add_property (X_("bank"), os.str()); + snprintf (buf, sizeof (buf), "%d", _current_initial_bank); + node->add_property (X_("bank"), buf); + + // ipMIDI base port (possibly not used) + snprintf (buf, sizeof (buf), "%d", _ipmidi_base); + node->add_property (X_("ipmidi-base"), buf); + + node->add_property (X_("device-profile"), _device_profile.name()); + node->add_property (X_("device-name"), _device_info.name()); return *node; } @@ -737,123 +670,40 @@ MackieControlProtocol::set_state (const XMLNode & node, int /*version*/) DEBUG_TRACE (DEBUG::MackieControl, string_compose ("MackieControlProtocol::set_state: active %1\n", _active)); int retval = 0; + const XMLProperty* prop; + uint32_t bank; + bool active = _active; + + if ((prop = node.property (X_("ipmidi-base"))) != 0) { + set_ipmidi_base (atoi (prop->value())); + } // fetch current bank + if ((prop = node.property (X_("bank"))) != 0) { + bank = atoi (prop->value()); + } + + if ((prop = node.property (X_("active"))) != 0) { + active = string_is_affirmative (prop->value()); + } - if (node.property (X_("bank")) != 0) { - string bank = node.property (X_("bank"))->value(); - try { - set_active (true); - uint32_t new_bank = atoi (bank.c_str()); - if (_current_initial_bank != new_bank) { - switch_banks (new_bank); - } - } - catch (exception & e) { - DEBUG_TRACE (DEBUG::MackieControl, string_compose ("exception in MackieControlProtocol::set_state: %1\n", e.what())); - return -1; - } + if ((prop = node.property (X_("device-name"))) != 0) { + set_device (prop->value(), false); + } + + if ((prop = node.property (X_("device-profile"))) != 0) { + set_profile (prop->value()); + } + + set_active (active); + + if (_active) { + switch_banks (bank, true); } return retval; } -void -MackieControlProtocol::handle_control_event (SurfacePort & port, Control & control, const ControlState & state) -{ - // find the route for the control, if there is one - boost::shared_ptr route; - if (control.group().is_strip()) { - if (control.group().is_master()) { - route = master_route(); - } else { - uint32_t index = control.ordinal() - 1 + (port.number() * port.strips()); - if (index < route_table.size()) - route = route_table[index]; - else - cerr << "Warning: index is " << index << " which is not in the route table, size: " << route_table.size() << endl; - } - } - - // This handles control element events from the surface - // the state of the controls on the surface is usually updated - // from UI events. - switch (control.type()) { - case Control::type_fader: - // find the route in the route table for the id - // if the route isn't available, skip it - // at which point the fader should just reset itself - if (route != 0) - { - route->gain_control()->set_value (slider_position_to_gain (state.pos)); - - if (ARDOUR::Config->get_mackie_emulation() == "bcf") { - /* reset the timeout while we're still moving the fader */ - port.add_in_use_timeout (control, control.in_use_touch_control); - } - - // must echo bytes back to slider now, because - // the notifier only works if the fader is not being - // touched. Which it is if we're getting input. - port.write (builder.build_fader ((Fader&)control, state.pos)); - } - break; - - case Control::type_button: - if (control.group().is_strip()) { - // strips - if (route != 0) { - handle_strip_button (port, control, state.button_state, route); - } else { - // no route so always switch the light off - // because no signals will be emitted by a non-route - port.write (builder.build_led (control.led(), off)); - } - } else if (control.group().is_master()) { - // master fader touch - if (route != 0) { - handle_strip_button (port, control, state.button_state, route); - } - } else { - // handle all non-strip buttons - surface().handle_button (*this, state.button_state, dynamic_cast (control)); - } - break; - - // pot (jog wheel, external control) - case Control::type_pot: - if (control.group().is_strip()) { - if (route) { - boost::shared_ptr panner = route->panner_shell()->panner(); - // pan for mono input routes, or stereo linked panners - if (panner) { - double p = panner->position (); - - // calculate new value, and adjust - p += state.delta * state.sign; - p = min (1.0, p); - p = max (0.0, p); - panner->set_position (p); - } - } else { - // it's a pot for an umnapped route, so turn all the lights off - port.write (builder.build_led_ring (dynamic_cast (control), off)); - } - } - else - { - if (control.is_jog()) { - _jog_wheel.jog_event (port, control, state); - } else { - cout << "external controller" << state.ticks * state.sign << endl; - } - } - break; - - default: - cout << "Control::type not handled: " << control.type() << endl; - } -} ///////////////////////////////////////////////// // handlers for Route signals @@ -862,149 +712,7 @@ MackieControlProtocol::handle_control_event (SurfacePort & port, Control & contr // from Route, but they're also used in polling for automation ///////////////////////////////////////////////// -void -MackieControlProtocol::notify_solo_changed (RouteSignal * route_signal) -{ - try { - Button & button = route_signal->strip().solo(); - route_signal->port().write (builder.build_led (button, route_signal->route()->soloed())); - } - catch (exception & e) { - cout << e.what() << endl; - } -} - -void -MackieControlProtocol::notify_mute_changed (RouteSignal * route_signal) -{ - try { - Button & button = route_signal->strip().mute(); - route_signal->port().write (builder.build_led (button, route_signal->route()->muted())); - } - catch (exception & e) { - cout << e.what() << endl; - } -} - -void -MackieControlProtocol::notify_record_enable_changed (RouteSignal * route_signal) -{ - try { - Button & button = route_signal->strip().recenable(); - route_signal->port().write (builder.build_led (button, route_signal->route()->record_enabled())); - } - catch (exception & e) { - cout << e.what() << endl; - } -} - -void MackieControlProtocol::notify_active_changed (RouteSignal *) -{ - try { - DEBUG_TRACE (DEBUG::MackieControl, "MackieControlProtocol::notify_active_changed\n"); - refresh_current_bank(); - } - catch (exception & e) { - cout << e.what() << endl; - } -} - -void -MackieControlProtocol::notify_gain_changed (RouteSignal * route_signal, bool force_update) -{ - try { - Fader & fader = route_signal->strip().gain(); - if (!fader.in_use()) { - float gain_value = gain_to_slider_position (route_signal->route()->gain_control()->get_value()); - // check that something has actually changed - if (force_update || gain_value != route_signal->last_gain_written()) { - route_signal->port().write (builder.build_fader (fader, gain_value)); - route_signal->last_gain_written (gain_value); - } - } - } - catch (exception & e) { - cout << e.what() << endl; - } -} - -void -MackieControlProtocol::notify_property_changed (const PropertyChange& what_changed, RouteSignal * route_signal) -{ - if (!what_changed.contains (Properties::name)) { - return; - } - - try { - Strip & strip = route_signal->strip(); - - /* XXX: not sure about this check to only display stuff for strips of index < 8 */ - if (!strip.is_master() && strip.index() < 8) { - string line1; - string fullname = route_signal->route()->name(); - - if (fullname.length() <= 6) { - line1 = fullname; - } else { - line1 = PBD::short_version (fullname, 6); - } - - SurfacePort & port = route_signal->port(); - port.write (builder.strip_display (port, strip, 0, line1)); - port.write (builder.strip_display_blank (port, strip, 1)); - } - } - catch (exception & e) { - cout << e.what() << endl; - } -} - -void -MackieControlProtocol::notify_panner_changed (RouteSignal * route_signal, bool force_update) -{ - try { - Pot & pot = route_signal->strip().vpot(); - boost::shared_ptr panner = route_signal->route()->panner(); - if (panner) { - double pos = panner->position (); - - // cache the MidiByteArray here, because the mackie led control is much lower - // resolution than the panner control. So we save lots of byte - // sends in spite of more work on the comparison - MidiByteArray bytes = builder.build_led_ring (pot, ControlState (on, pos), MackieMidiBuilder::midi_pot_mode_dot); - // check that something has actually changed - if (force_update || bytes != route_signal->last_pan_written()) - { - route_signal->port().write (bytes); - route_signal->last_pan_written (bytes); - } - } else { - route_signal->port().write (builder.zero_control (pot)); - } - } - catch (exception & e) { - cout << e.what() << endl; - } -} - // TODO handle plugin automation polling -void -MackieControlProtocol::update_automation (RouteSignal & rs) -{ - ARDOUR::AutoState gain_state = rs.route()->gain_control()->automation_state(); - - if (gain_state == Touch || gain_state == Play) { - notify_gain_changed (&rs, false); - } - - if (rs.route()->panner()) { - ARDOUR::AutoState panner_state = rs.route()->panner()->automation_state(); - if (panner_state == Touch || panner_state == Play) { - notify_panner_changed (&rs, false); - } - } -} - string MackieControlProtocol::format_bbt_timecode (framepos_t now_frame) { @@ -1019,7 +727,7 @@ MackieControlProtocol::format_bbt_timecode (framepos_t now_frame) os << setw(2) << setfill('0') << bbt_time.beats; // figure out subdivisions per beat - const Meter & meter = session->tempo_map().meter_at (now_frame); + const ARDOUR::Meter & meter = session->tempo_map().meter_at (now_frame); int subdiv = 2; if (meter.note_divisor() == 8 && (meter.divisions_per_bar() == 12.0 || meter.divisions_per_bar() == 9.0 || meter.divisions_per_bar() == 6.0)) { subdiv = 3; @@ -1055,295 +763,37 @@ MackieControlProtocol::format_timecode_timecode (framepos_t now_frame) void MackieControlProtocol::update_timecode_display() { - if (surface().has_timecode_display()) { - // do assignment here so current_frame is fixed - framepos_t current_frame = session->transport_frame(); - string timecode; - - switch (_timecode_type) { - case ARDOUR::AnyTime::BBT: - timecode = format_bbt_timecode (current_frame); - break; - case ARDOUR::AnyTime::Timecode: - timecode = format_timecode_timecode (current_frame); - break; - default: - ostringstream os; - os << "Unknown timecode: " << _timecode_type; - throw runtime_error (os.str()); - } - - // only write the timecode string to the MCU if it's changed - // since last time. This is to reduce midi bandwidth used. - if (timecode != _timecode_last) { - surface().display_timecode (mcu_port(), builder, timecode, _timecode_last); - _timecode_last = timecode; - } - } -} - -void -MackieControlProtocol::poll_session_data() -{ - // XXX need to attach this to a timer in the MIDI UI event loop (20msec) - - if (_active) { - // do all currently mapped routes - for (RouteSignals::iterator it = route_signals.begin(); it != route_signals.end(); ++it) { - update_automation (**it); - } - - // and the master strip - if (master_route_signal != 0) { - update_automation (*master_route_signal); - } - - update_timecode_display(); - } -} - -///////////////////////////////////// -// Transport Buttons -///////////////////////////////////// - -LedState -MackieControlProtocol::frm_left_press (Button &) -{ - // can use first_mark_before/after as well - unsigned long elapsed = _frm_left_last.restart(); - - Location * loc = session->locations()->first_location_before ( - session->transport_frame() - ); - - // allow a quick double to go past a previous mark - if (session->transport_rolling() && elapsed < 500 && loc != 0) { - Location * loc_two_back = session->locations()->first_location_before (loc->start()); - if (loc_two_back != 0) - { - loc = loc_two_back; - } + if (surfaces.empty()) { + return; } - // move to the location, if it's valid - if (loc != 0) { - session->request_locate (loc->start(), session->transport_rolling()); + boost::shared_ptr surface = surfaces.front(); + + if (surface->type() != mcu || !_device_info.has_timecode_display()) { + return; } - return on; -} + // do assignment here so current_frame is fixed + framepos_t current_frame = session->transport_frame(); + string timecode; -LedState -MackieControlProtocol::frm_left_release (Button &) -{ - return off; -} - -LedState -MackieControlProtocol::frm_right_press (Button &) -{ - // can use first_mark_before/after as well - Location * loc = session->locations()->first_location_after (session->transport_frame()); + switch (_timecode_type) { + case ARDOUR::AnyTime::BBT: + timecode = format_bbt_timecode (current_frame); + break; + case ARDOUR::AnyTime::Timecode: + timecode = format_timecode_timecode (current_frame); + break; + default: + return; + } - if (loc != 0) { - session->request_locate (loc->start(), session->transport_rolling()); + // only write the timecode string to the MCU if it's changed + // since last time. This is to reduce midi bandwidth used. + if (timecode != _timecode_last) { + surface->display_timecode (timecode, _timecode_last); + _timecode_last = timecode; } - - return on; -} - -LedState -MackieControlProtocol::frm_right_release (Button &) -{ - return off; -} - -LedState -MackieControlProtocol::stop_press (Button &) -{ - session->request_stop(); - return on; -} - -LedState -MackieControlProtocol::stop_release (Button &) -{ - return session->transport_stopped(); -} - -LedState -MackieControlProtocol::play_press (Button &) -{ - session->request_transport_speed (1.0); - return on; -} - -LedState -MackieControlProtocol::play_release (Button &) -{ - return session->transport_rolling(); -} - -LedState -MackieControlProtocol::record_press (Button &) -{ - if (session->get_record_enabled()) { - session->disable_record (false); - } else { - session->maybe_enable_record(); - } - return on; -} - -LedState -MackieControlProtocol::record_release (Button &) -{ - if (session->get_record_enabled()) { - if (session->transport_rolling()) { - return on; - } else { - return flashing; - } - } else { - return off; - } -} - -LedState -MackieControlProtocol::rewind_press (Button &) -{ - _jog_wheel.push (JogWheel::speed); - _jog_wheel.transport_direction (-1); - session->request_transport_speed (-_jog_wheel.transport_speed()); - return on; -} - -LedState -MackieControlProtocol::rewind_release (Button &) -{ - _jog_wheel.pop(); - _jog_wheel.transport_direction (0); - if (_transport_previously_rolling) { - session->request_transport_speed (1.0); - } else { - session->request_stop(); - } - return off; -} - -LedState -MackieControlProtocol::ffwd_press (Button &) -{ - _jog_wheel.push (JogWheel::speed); - _jog_wheel.transport_direction (1); - session->request_transport_speed (_jog_wheel.transport_speed()); - return on; -} - -LedState -MackieControlProtocol::ffwd_release (Button &) -{ - _jog_wheel.pop(); - _jog_wheel.transport_direction (0); - if (_transport_previously_rolling) { - session->request_transport_speed (1.0); - } else { - session->request_stop(); - } - return off; -} - -LedState -MackieControlProtocol::loop_press (Button &) -{ - session->request_play_loop (!session->get_play_loop()); - return on; -} - -LedState -MackieControlProtocol::loop_release (Button &) -{ - return session->get_play_loop(); -} - -LedState -MackieControlProtocol::punch_in_press (Button &) -{ - bool const state = !session->config.get_punch_in(); - session->config.set_punch_in (state); - return state; -} - -LedState -MackieControlProtocol::punch_in_release (Button &) -{ - return session->config.get_punch_in(); -} - -LedState -MackieControlProtocol::punch_out_press (Button &) -{ - bool const state = !session->config.get_punch_out(); - session->config.set_punch_out (state); - return state; -} - -LedState -MackieControlProtocol::punch_out_release (Button &) -{ - return session->config.get_punch_out(); -} - -LedState -MackieControlProtocol::home_press (Button &) -{ - session->goto_start(); - return on; -} - -LedState -MackieControlProtocol::home_release (Button &) -{ - return off; -} - -LedState -MackieControlProtocol::end_press (Button &) -{ - session->goto_end(); - return on; -} - -LedState -MackieControlProtocol::end_release (Button &) -{ - return off; -} - -LedState -MackieControlProtocol::clicking_press (Button &) -{ - bool state = !Config->get_clicking(); - Config->set_clicking (state); - return state; -} - -LedState -MackieControlProtocol::clicking_release (Button &) -{ - return Config->get_clicking(); -} - -LedState MackieControlProtocol::global_solo_press (Button &) -{ - bool state = !session->soloing(); - session->set_solo (session->get_routes(), state); - return state; -} - -LedState MackieControlProtocol::global_solo_release (Button &) -{ - return session->soloing(); } /////////////////////////////////////////// @@ -1353,11 +803,11 @@ LedState MackieControlProtocol::global_solo_release (Button &) void MackieControlProtocol::notify_parameter_changed (std::string const & p) { if (p == "punch-in") { - update_global_button ("punch_in", session->config.get_punch_in()); + update_global_button (Button::PunchIn, session->config.get_punch_in()); } else if (p == "punch-out") { - update_global_button ("punch_out", session->config.get_punch_out()); + update_global_button (Button::PunchOut, session->config.get_punch_out()); } else if (p == "clicking") { - update_global_button ("clicking", Config->get_clicking()); + // update_global_button (Button::RelayClick, Config->get_clicking()); } else { DEBUG_TRACE (DEBUG::MackieControl, string_compose ("parameter changed: %1\n", p)); } @@ -1369,36 +819,45 @@ MackieControlProtocol::notify_route_added (ARDOUR::RouteList & rl) { // currently assigned banks are less than the full set of // strips, so activate the new strip now. - if (route_signals.size() < route_table.size()) { - refresh_current_bank(); - } + + refresh_current_bank(); + // otherwise route added, but current bank needs no updating // make sure remote id changes in the new route are handled typedef ARDOUR::RouteList ARS; for (ARS::iterator it = rl.begin(); it != rl.end(); ++it) { - (*it)->RemoteControlIDChanged.connect (route_connections, MISSING_INVALIDATOR, ui_bind (&MackieControlProtocol::notify_remote_id_changed, this), midi_ui_context()); + (*it)->RemoteControlIDChanged.connect (route_connections, MISSING_INVALIDATOR, boost::bind (&MackieControlProtocol::notify_remote_id_changed, this), this); } } void MackieControlProtocol::notify_solo_active_changed (bool active) { - Button * rude_solo = reinterpret_cast (surface().controls_by_name["solo"]); - mcu_port().write (builder.build_led (*rude_solo, active ? flashing : off)); + boost::shared_ptr surface = surfaces.front(); + + map::iterator x = surface->controls_by_device_independent_id.find (Led::RudeSolo); + if (x != surface->controls_by_device_independent_id.end()) { + Led* rude_solo = dynamic_cast (x->second); + if (rude_solo) { + surface->write (rude_solo->set_state (active ? flashing : off)); + } + } } void MackieControlProtocol::notify_remote_id_changed() { Sorted sorted = get_sorted_routes(); + uint32_t sz = n_strips(); // if a remote id has been moved off the end, we need to shift // the current bank backwards. - if (sorted.size() - _current_initial_bank < route_signals.size()) { + + if (sorted.size() - _current_initial_bank < sz) { // but don't shift backwards past the zeroth channel - switch_banks (max((Sorted::size_type) 0, sorted.size() - route_signals.size())); + switch_banks (max((Sorted::size_type) 0, sorted.size() - sz)); } else { // Otherwise just refresh the current bank refresh_current_bank(); @@ -1410,262 +869,57 @@ MackieControlProtocol::notify_remote_id_changed() /////////////////////////////////////////// void -MackieControlProtocol::notify_record_state_changed() +MackieControlProtocol::notify_loop_state_changed() { - // switch rec button on / off / flashing - Button * rec = reinterpret_cast (surface().controls_by_name["record"]); - mcu_port().write (builder.build_led (*rec, record_release (*rec))); + update_global_button (Button::Loop, session->get_play_loop()); } void MackieControlProtocol::notify_transport_state_changed() { // switch various play and stop buttons on / off - update_global_button ("play", session->transport_rolling()); - update_global_button ("stop", !session->transport_rolling()); - update_global_button ("loop", session->get_play_loop()); + update_global_button (Button::Loop, session->get_play_loop()); + update_global_button (Button::Play, session->transport_speed() == 1.0); + update_global_button (Button::Stop, !session->transport_rolling()); + update_global_button (Button::Rewind, session->transport_speed() < 0.0); + update_global_button (Button::Ffwd, session->transport_speed() > 1.0); _transport_previously_rolling = session->transport_rolling(); - - // rec is special because it's tristate - Button * rec = reinterpret_cast (surface().controls_by_name["record"]); - mcu_port().write (builder.build_led (*rec, record_release (*rec))); } -///////////////////////////////////// -// Bank Switching -///////////////////////////////////// -LedState -MackieControlProtocol::left_press (Button &) +void +MackieControlProtocol::notify_record_state_changed () { - Sorted sorted = get_sorted_routes(); - if (sorted.size() > route_table.size()) { - int new_initial = _current_initial_bank - route_table.size(); - if (new_initial < 0) { - new_initial = 0; + boost::shared_ptr surface = surfaces.front(); + + /* rec is a tristate */ + + map::iterator x = surface->controls_by_device_independent_id.find (Button::Record); + if (x != surface->controls_by_device_independent_id.end()) { + Button * rec = dynamic_cast (x->second); + if (rec) { + LedState ls; + + switch (session->record_status()) { + case Session::Disabled: + DEBUG_TRACE (DEBUG::MackieControl, "record state changed to disabled, LED off\n"); + ls = off; + break; + case Session::Recording: + DEBUG_TRACE (DEBUG::MackieControl, "record state changed to recording, LED on\n"); + ls = on; + break; + case Session::Enabled: + DEBUG_TRACE (DEBUG::MackieControl, "record state changed to enabled, LED flashing\n"); + ls = flashing; + break; + } + + surface->write (rec->set_state (ls)); } - - if (new_initial != int (_current_initial_bank)) { - session->set_dirty(); - switch_banks (new_initial); - } - - return on; - } else { - return flashing; } } -LedState -MackieControlProtocol::left_release (Button &) -{ - return off; -} - -LedState -MackieControlProtocol::right_press (Button &) -{ - Sorted sorted = get_sorted_routes(); - if (sorted.size() > route_table.size()) { - uint32_t delta = sorted.size() - (route_table.size() + _current_initial_bank); - - if (delta > route_table.size()) { - delta = route_table.size(); - } - - if (delta > 0) { - session->set_dirty(); - switch_banks (_current_initial_bank + delta); - } - - return on; - } else { - return flashing; - } -} - -LedState -MackieControlProtocol::right_release (Button &) -{ - return off; -} - -LedState -MackieControlProtocol::channel_left_press (Button &) -{ - Sorted sorted = get_sorted_routes(); - if (sorted.size() > route_table.size()) { - prev_track(); - return on; - } else { - return flashing; - } -} - -LedState -MackieControlProtocol::channel_left_release (Button &) -{ - return off; -} - -LedState -MackieControlProtocol::channel_right_press (Button &) -{ - Sorted sorted = get_sorted_routes(); - if (sorted.size() > route_table.size()) { - next_track(); - return on; - } else { - return flashing; - } -} - -LedState -MackieControlProtocol::channel_right_release (Button &) -{ - return off; -} - -///////////////////////////////////// -// Functions -///////////////////////////////////// -LedState -MackieControlProtocol::marker_press (Button &) -{ - // cut'n'paste from LocationUI::add_new_location() - string markername; - framepos_t where = session->audible_frame(); - session->locations()->next_available_name(markername,"mcu"); - Location *location = new Location (*session, where, where, markername, Location::IsMark); - session->begin_reversible_command (_("add marker")); - XMLNode &before = session->locations()->get_state(); - session->locations()->add (location, true); - XMLNode &after = session->locations()->get_state(); - session->add_command (new MementoCommand(*(session->locations()), &before, &after)); - session->commit_reversible_command (); - return on; -} - -LedState -MackieControlProtocol::marker_release (Button &) -{ - return off; -} - -void -jog_wheel_state_display (JogWheel::State state, SurfacePort & port) -{ - switch (state) { - case JogWheel::zoom: - port.write (builder.two_char_display ("Zm")); - break; - case JogWheel::scroll: - port.write (builder.two_char_display ("Sc")); - break; - case JogWheel::scrub: - port.write (builder.two_char_display ("Sb")); - break; - case JogWheel::shuttle: - port.write (builder.two_char_display ("Sh")); - break; - case JogWheel::speed: - port.write (builder.two_char_display ("Sp")); - break; - case JogWheel::select: - port.write (builder.two_char_display ("Se")); - break; - } -} - -Mackie::LedState -MackieControlProtocol::zoom_press (Mackie::Button &) -{ - _jog_wheel.zoom_state_toggle(); - update_global_button ("scrub", _jog_wheel.jog_wheel_state() == JogWheel::scrub); - jog_wheel_state_display (_jog_wheel.jog_wheel_state(), mcu_port()); - return _jog_wheel.jog_wheel_state() == JogWheel::zoom; -} - -Mackie::LedState -MackieControlProtocol::zoom_release (Mackie::Button &) -{ - return _jog_wheel.jog_wheel_state() == JogWheel::zoom; -} - -Mackie::LedState -MackieControlProtocol::scrub_press (Mackie::Button &) -{ - _jog_wheel.scrub_state_cycle(); - update_global_button ("zoom", _jog_wheel.jog_wheel_state() == JogWheel::zoom); - jog_wheel_state_display (_jog_wheel.jog_wheel_state(), mcu_port()); - return ( - _jog_wheel.jog_wheel_state() == JogWheel::scrub - || - _jog_wheel.jog_wheel_state() == JogWheel::shuttle - ); -} - -Mackie::LedState -MackieControlProtocol::scrub_release (Mackie::Button &) -{ - return ( - _jog_wheel.jog_wheel_state() == JogWheel::scrub - || - _jog_wheel.jog_wheel_state() == JogWheel::shuttle - ); -} - -LedState -MackieControlProtocol::drop_press (Button &) -{ - session->remove_last_capture(); - return on; -} - -LedState -MackieControlProtocol::drop_release (Button &) -{ - return off; -} - -LedState -MackieControlProtocol::save_press (Button &) -{ - session->save_state (""); - return on; -} - -LedState -MackieControlProtocol::save_release (Button &) -{ - return off; -} - -LedState -MackieControlProtocol::timecode_beats_press (Button &) -{ - switch (_timecode_type) { - case ARDOUR::AnyTime::BBT: - _timecode_type = ARDOUR::AnyTime::Timecode; - break; - case ARDOUR::AnyTime::Timecode: - _timecode_type = ARDOUR::AnyTime::BBT; - break; - default: - ostringstream os; - os << "Unknown Anytime::Type " << _timecode_type; - throw runtime_error (os.str()); - } - update_timecode_beats_led(); - return on; -} - -LedState -MackieControlProtocol::timecode_beats_release (Button &) -{ - return off; -} - list > MackieControlProtocol::bundles () { @@ -1675,34 +929,6 @@ MackieControlProtocol::bundles () return b; } -void -MackieControlProtocol::port_connected_or_disconnected (string a, string b, bool connected) -{ - /* If something is connected to one of our output ports, send MIDI to update the surface - to whatever state it should have. - */ - - if (!connected) { - return; - } - - MackiePorts::const_iterator i = _ports.begin(); - while (i != _ports.end()) { - - string const n = AudioEngine::instance()->make_port_name_non_relative ((*i)->output_port().name ()); - - if (a == n || b == n) { - break; - } - - ++i; - } - - if (i != _ports.end ()) { - update_surface (); - } -} - void MackieControlProtocol::do_request (MackieControlUIRequest* req) { @@ -1723,3 +949,480 @@ MackieControlProtocol::stop () return 0; } + +void +MackieControlProtocol::update_led (Surface& surface, Button& button, Mackie::LedState ls) +{ + if (ls != none) { + surface.port().write (button.set_state (ls)); + } +} + +void +MackieControlProtocol::build_button_map () +{ + /* this maps our device-independent button codes to the methods that handle them. + */ + +#define DEFINE_BUTTON_HANDLER(b,p,r) button_map.insert (pair ((b), ButtonHandlers ((p),(r)))); + + DEFINE_BUTTON_HANDLER (Button::IO, &MackieControlProtocol::io_press, &MackieControlProtocol::io_release); + DEFINE_BUTTON_HANDLER (Button::Sends, &MackieControlProtocol::sends_press, &MackieControlProtocol::sends_release); + DEFINE_BUTTON_HANDLER (Button::Pan, &MackieControlProtocol::pan_press, &MackieControlProtocol::pan_release); + DEFINE_BUTTON_HANDLER (Button::Plugin, &MackieControlProtocol::plugin_press, &MackieControlProtocol::plugin_release); + DEFINE_BUTTON_HANDLER (Button::Eq, &MackieControlProtocol::eq_press, &MackieControlProtocol::eq_release); + DEFINE_BUTTON_HANDLER (Button::Dyn, &MackieControlProtocol::dyn_press, &MackieControlProtocol::dyn_release); + DEFINE_BUTTON_HANDLER (Button::Left, &MackieControlProtocol::left_press, &MackieControlProtocol::left_release); + DEFINE_BUTTON_HANDLER (Button::Right, &MackieControlProtocol::right_press, &MackieControlProtocol::right_release); + DEFINE_BUTTON_HANDLER (Button::ChannelLeft, &MackieControlProtocol::channel_left_press, &MackieControlProtocol::channel_left_release); + DEFINE_BUTTON_HANDLER (Button::ChannelRight, &MackieControlProtocol::channel_right_press, &MackieControlProtocol::channel_right_release); + DEFINE_BUTTON_HANDLER (Button::Flip, &MackieControlProtocol::flip_press, &MackieControlProtocol::flip_release); + DEFINE_BUTTON_HANDLER (Button::Edit, &MackieControlProtocol::edit_press, &MackieControlProtocol::edit_release); + DEFINE_BUTTON_HANDLER (Button::NameValue, &MackieControlProtocol::name_value_press, &MackieControlProtocol::name_value_release); + DEFINE_BUTTON_HANDLER (Button::TimecodeBeats, &MackieControlProtocol::timecode_beats_press, &MackieControlProtocol::timecode_beats_release); + DEFINE_BUTTON_HANDLER (Button::F1, &MackieControlProtocol::F1_press, &MackieControlProtocol::F1_release); + DEFINE_BUTTON_HANDLER (Button::F2, &MackieControlProtocol::F2_press, &MackieControlProtocol::F2_release); + DEFINE_BUTTON_HANDLER (Button::F3, &MackieControlProtocol::F3_press, &MackieControlProtocol::F3_release); + DEFINE_BUTTON_HANDLER (Button::F4, &MackieControlProtocol::F4_press, &MackieControlProtocol::F4_release); + DEFINE_BUTTON_HANDLER (Button::F5, &MackieControlProtocol::F5_press, &MackieControlProtocol::F5_release); + DEFINE_BUTTON_HANDLER (Button::F6, &MackieControlProtocol::F6_press, &MackieControlProtocol::F6_release); + DEFINE_BUTTON_HANDLER (Button::F7, &MackieControlProtocol::F7_press, &MackieControlProtocol::F7_release); + DEFINE_BUTTON_HANDLER (Button::F8, &MackieControlProtocol::F8_press, &MackieControlProtocol::F8_release); + DEFINE_BUTTON_HANDLER (Button::F9, &MackieControlProtocol::F9_press, &MackieControlProtocol::F9_release); + DEFINE_BUTTON_HANDLER (Button::F10, &MackieControlProtocol::F10_press, &MackieControlProtocol::F10_release); + DEFINE_BUTTON_HANDLER (Button::F11, &MackieControlProtocol::F11_press, &MackieControlProtocol::F11_release); + DEFINE_BUTTON_HANDLER (Button::F12, &MackieControlProtocol::F12_press, &MackieControlProtocol::F12_release); + DEFINE_BUTTON_HANDLER (Button::F13, &MackieControlProtocol::F13_press, &MackieControlProtocol::F13_release); + DEFINE_BUTTON_HANDLER (Button::F14, &MackieControlProtocol::F14_press, &MackieControlProtocol::F14_release); + DEFINE_BUTTON_HANDLER (Button::F15, &MackieControlProtocol::F15_press, &MackieControlProtocol::F15_release); + DEFINE_BUTTON_HANDLER (Button::F16, &MackieControlProtocol::F16_press, &MackieControlProtocol::F16_release); + DEFINE_BUTTON_HANDLER (Button::Shift, &MackieControlProtocol::shift_press, &MackieControlProtocol::shift_release); + DEFINE_BUTTON_HANDLER (Button::Option, &MackieControlProtocol::option_press, &MackieControlProtocol::option_release); + DEFINE_BUTTON_HANDLER (Button::Ctrl, &MackieControlProtocol::control_press, &MackieControlProtocol::control_release); + DEFINE_BUTTON_HANDLER (Button::CmdAlt, &MackieControlProtocol::cmd_alt_press, &MackieControlProtocol::cmd_alt_release); + DEFINE_BUTTON_HANDLER (Button::On, &MackieControlProtocol::on_press, &MackieControlProtocol::on_release); + DEFINE_BUTTON_HANDLER (Button::RecReady, &MackieControlProtocol::rec_ready_press, &MackieControlProtocol::rec_ready_release); + DEFINE_BUTTON_HANDLER (Button::Undo, &MackieControlProtocol::undo_press, &MackieControlProtocol::undo_release); + DEFINE_BUTTON_HANDLER (Button::Save, &MackieControlProtocol::save_press, &MackieControlProtocol::save_release); + DEFINE_BUTTON_HANDLER (Button::Touch, &MackieControlProtocol::touch_press, &MackieControlProtocol::touch_release); + DEFINE_BUTTON_HANDLER (Button::Redo, &MackieControlProtocol::redo_press, &MackieControlProtocol::redo_release); + DEFINE_BUTTON_HANDLER (Button::Marker, &MackieControlProtocol::marker_press, &MackieControlProtocol::marker_release); + DEFINE_BUTTON_HANDLER (Button::Enter, &MackieControlProtocol::enter_press, &MackieControlProtocol::enter_release); + DEFINE_BUTTON_HANDLER (Button::Cancel, &MackieControlProtocol::cancel_press, &MackieControlProtocol::cancel_release); + DEFINE_BUTTON_HANDLER (Button::Mixer, &MackieControlProtocol::mixer_press, &MackieControlProtocol::mixer_release); + DEFINE_BUTTON_HANDLER (Button::FrmLeft, &MackieControlProtocol::frm_left_press, &MackieControlProtocol::frm_left_release); + DEFINE_BUTTON_HANDLER (Button::FrmRight, &MackieControlProtocol::frm_right_press, &MackieControlProtocol::frm_right_release); + DEFINE_BUTTON_HANDLER (Button::Loop, &MackieControlProtocol::loop_press, &MackieControlProtocol::loop_release); + DEFINE_BUTTON_HANDLER (Button::PunchIn, &MackieControlProtocol::punch_in_press, &MackieControlProtocol::punch_in_release); + DEFINE_BUTTON_HANDLER (Button::PunchOut, &MackieControlProtocol::punch_out_press, &MackieControlProtocol::punch_out_release); + DEFINE_BUTTON_HANDLER (Button::Home, &MackieControlProtocol::home_press, &MackieControlProtocol::home_release); + DEFINE_BUTTON_HANDLER (Button::End, &MackieControlProtocol::end_press, &MackieControlProtocol::end_release); + DEFINE_BUTTON_HANDLER (Button::Rewind, &MackieControlProtocol::rewind_press, &MackieControlProtocol::rewind_release); + DEFINE_BUTTON_HANDLER (Button::Ffwd, &MackieControlProtocol::ffwd_press, &MackieControlProtocol::ffwd_release); + DEFINE_BUTTON_HANDLER (Button::Stop, &MackieControlProtocol::stop_press, &MackieControlProtocol::stop_release); + DEFINE_BUTTON_HANDLER (Button::Play, &MackieControlProtocol::play_press, &MackieControlProtocol::play_release); + DEFINE_BUTTON_HANDLER (Button::Record, &MackieControlProtocol::record_press, &MackieControlProtocol::record_release); + DEFINE_BUTTON_HANDLER (Button::CursorUp, &MackieControlProtocol::cursor_up_press, &MackieControlProtocol::cursor_up_release); + DEFINE_BUTTON_HANDLER (Button::CursorDown, &MackieControlProtocol::cursor_down_press, &MackieControlProtocol::cursor_down_release); + DEFINE_BUTTON_HANDLER (Button::CursorLeft, &MackieControlProtocol::cursor_left_press, &MackieControlProtocol::cursor_left_release); + DEFINE_BUTTON_HANDLER (Button::CursorRight, &MackieControlProtocol::cursor_right_press, &MackieControlProtocol::cursor_right_release); + DEFINE_BUTTON_HANDLER (Button::Zoom, &MackieControlProtocol::zoom_press, &MackieControlProtocol::zoom_release); + DEFINE_BUTTON_HANDLER (Button::Scrub, &MackieControlProtocol::scrub_press, &MackieControlProtocol::scrub_release); + DEFINE_BUTTON_HANDLER (Button::UserA, &MackieControlProtocol::user_a_press, &MackieControlProtocol::user_a_release); + DEFINE_BUTTON_HANDLER (Button::UserB, &MackieControlProtocol::user_b_press, &MackieControlProtocol::user_b_release); + + DEFINE_BUTTON_HANDLER (Button::Snapshot, &MackieControlProtocol::snapshot_press, &MackieControlProtocol::snapshot_release); + DEFINE_BUTTON_HANDLER (Button::Read, &MackieControlProtocol::read_press, &MackieControlProtocol::read_release); + DEFINE_BUTTON_HANDLER (Button::Write, &MackieControlProtocol::write_press, &MackieControlProtocol::write_release); + DEFINE_BUTTON_HANDLER (Button::FdrGroup, &MackieControlProtocol::fdrgroup_press, &MackieControlProtocol::fdrgroup_release); + DEFINE_BUTTON_HANDLER (Button::ClearSolo, &MackieControlProtocol::clearsolo_press, &MackieControlProtocol::clearsolo_release); + DEFINE_BUTTON_HANDLER (Button::Track, &MackieControlProtocol::track_press, &MackieControlProtocol::track_release); + DEFINE_BUTTON_HANDLER (Button::Send, &MackieControlProtocol::send_press, &MackieControlProtocol::send_release); + DEFINE_BUTTON_HANDLER (Button::MidiTracks, &MackieControlProtocol::miditracks_press, &MackieControlProtocol::miditracks_release); + DEFINE_BUTTON_HANDLER (Button::Inputs, &MackieControlProtocol::inputs_press, &MackieControlProtocol::inputs_release); + DEFINE_BUTTON_HANDLER (Button::AudioTracks, &MackieControlProtocol::audiotracks_press, &MackieControlProtocol::audiotracks_release); + DEFINE_BUTTON_HANDLER (Button::AudioInstruments, &MackieControlProtocol::audioinstruments_press, &MackieControlProtocol::audioinstruments_release); + DEFINE_BUTTON_HANDLER (Button::Aux, &MackieControlProtocol::aux_press, &MackieControlProtocol::aux_release); + DEFINE_BUTTON_HANDLER (Button::Busses, &MackieControlProtocol::busses_press, &MackieControlProtocol::busses_release); + DEFINE_BUTTON_HANDLER (Button::Outputs, &MackieControlProtocol::outputs_press, &MackieControlProtocol::outputs_release); + DEFINE_BUTTON_HANDLER (Button::User, &MackieControlProtocol::user_press, &MackieControlProtocol::user_release); + DEFINE_BUTTON_HANDLER (Button::Trim, &MackieControlProtocol::trim_press, &MackieControlProtocol::trim_release); + DEFINE_BUTTON_HANDLER (Button::Latch, &MackieControlProtocol::latch_press, &MackieControlProtocol::latch_release); + DEFINE_BUTTON_HANDLER (Button::Grp, &MackieControlProtocol::grp_press, &MackieControlProtocol::grp_release); + DEFINE_BUTTON_HANDLER (Button::Nudge, &MackieControlProtocol::nudge_press, &MackieControlProtocol::nudge_release); + DEFINE_BUTTON_HANDLER (Button::Drop, &MackieControlProtocol::drop_press, &MackieControlProtocol::drop_release); + DEFINE_BUTTON_HANDLER (Button::Replace, &MackieControlProtocol::replace_press, &MackieControlProtocol::replace_release); + DEFINE_BUTTON_HANDLER (Button::Click, &MackieControlProtocol::click_press, &MackieControlProtocol::click_release); + DEFINE_BUTTON_HANDLER (Button::View, &MackieControlProtocol::view_press, &MackieControlProtocol::view_release); +} + +void +MackieControlProtocol::handle_button_event (Surface& surface, Button& button, ButtonState bs) +{ + if (bs != press && bs != release) { + update_led (surface, button, none); + return; + } + + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Handling %1 for button %2 (%3)\n", (bs == press ? "press" : "release"), button.id(), + Button::id_to_name (button.bid()))); + + /* check profile first */ + + string action = _device_profile.get_button_action (button.bid(), _modifier_state); + + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Looked up action for button %1 with modifier %2, got [%3]\n", + button.bid(), _modifier_state, action)); + + if (!action.empty()) { + /* if there is a bound action for this button, and this is a press event, + carry out the action. If its a release event, do nothing since we + don't bind to them at all but don't want any other handling to + occur either. + */ + if (bs == press) { + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("executing action %1\n", action)); + access_action (action); + } + return; + } + + /* lookup using the device-INDEPENDENT button ID */ + + ButtonMap::iterator b = button_map.find (button.bid()); + + if (b != button_map.end()) { + + ButtonHandlers& bh (b->second); + + switch (bs) { + case press: + surface.write (button.set_state ((this->*(bh.press)) (button))); + break; + case release: + surface.write (button.set_state ((this->*(bh.release)) (button))); + break; + default: + break; + } + } else { + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("no button handlers for button ID %1 (device ID %2)\n", + button.bid(), button.id())); + error << string_compose ("no button handlers for button ID %1 (device ID %2)\n", + button.bid(), button.id()) << endmsg; + } +} + +bool +MackieControlProtocol::midi_input_handler (IOCondition ioc, MIDI::Port* port) +{ + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("something happend on %1\n", port->name())); + + if (ioc & ~IO_IN) { + return false; + } + + if (ioc & IO_IN) { + + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("data available on %1\n", port->name())); + framepos_t now = session->engine().frame_time(); + port->parse (now); + } + + return true; +} + +void +MackieControlProtocol::clear_ports () +{ + _input_bundle->remove_channels (); + _output_bundle->remove_channels (); + + for (PortSources::iterator i = port_sources.begin(); i != port_sources.end(); ++i) { + g_source_destroy (*i); + g_source_unref (*i); + } + + port_sources.clear (); +} + +void +MackieControlProtocol::set_view_mode (ViewMode m) +{ + _view_mode = m; + + for (Surfaces::iterator s = surfaces.begin(); s != surfaces.end(); ++s) { + (*s)->update_view_mode_display (); + } + +} + +void +MackieControlProtocol::set_flip_mode (bool yn) +{ + _flip_mode = yn; + + for (Surfaces::iterator s = surfaces.begin(); s != surfaces.end(); ++s) { + (*s)->update_flip_mode_display (); + } +} + +void +MackieControlProtocol::set_master_on_surface_strip (uint32_t surface, uint32_t strip_number) +{ + force_special_route_to_strip (session->master_out(), surface, strip_number); +} + +void +MackieControlProtocol::set_monitor_on_surface_strip (uint32_t surface, uint32_t strip_number) +{ + force_special_route_to_strip (session->monitor_out(), surface, strip_number); +} + +void +MackieControlProtocol::force_special_route_to_strip (boost::shared_ptr r, uint32_t surface, uint32_t strip_number) +{ + if (!r) { + return; + } + + for (Surfaces::iterator s = surfaces.begin(); s != surfaces.end(); ++s) { + if ((*s)->number() == surface) { + Strip* strip = (*s)->nth_strip (strip_number); + if (strip) { + strip->set_route (session->master_out()); + strip->lock_controls (); + } + } + } +} + +void +MackieControlProtocol::gui_track_selection_changed (ARDOUR::RouteNotificationListPtr rl) +{ + for (Surfaces::iterator s = surfaces.begin(); s != surfaces.end(); ++s) { + (*s)->gui_selection_changed (rl); + } +} + +framepos_t +MackieControlProtocol::transport_frame() const +{ + return session->transport_frame(); +} + +void +MackieControlProtocol::add_down_select_button (int surface, int strip) +{ + _down_select_buttons.insert ((surface<<8)|(strip&0xf)); +} + +void +MackieControlProtocol::remove_down_select_button (int surface, int strip) +{ + DownButtonList::iterator x = find (_down_select_buttons.begin(), _down_select_buttons.end(), (surface<<8)|(strip&0xf)); + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("removing surface %1 strip %2 from down select buttons\n", surface, strip)); + if (x != _down_select_buttons.end()) { + _down_select_buttons.erase (x); + } else { + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("surface %1 strip %2 not found in down select buttons\n", + surface, strip)); + } +} + +void +MackieControlProtocol::select_range () +{ + RouteList routes; + + pull_route_range (_down_select_buttons, routes); + + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("select range: found %1 routes\n", routes.size())); + + if (!routes.empty()) { + for (RouteList::iterator r = routes.begin(); r != routes.end(); ++r) { + if (r == routes.begin()) { + SetRouteSelection ((*r)->remote_control_id()); + } else { + AddRouteToSelection ((*r)->remote_control_id()); + } + } + } +} + +void +MackieControlProtocol::add_down_button (AutomationType a, int surface, int strip) +{ + DownButtonMap::iterator m = _down_buttons.find (a); + + if (m == _down_buttons.end()) { + _down_buttons[a] = DownButtonList(); + } + + _down_buttons[a].insert ((surface<<8)|(strip&0xf)); +} + +void +MackieControlProtocol::remove_down_button (AutomationType a, int surface, int strip) +{ + DownButtonMap::iterator m = _down_buttons.find (a); + + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("removing surface %1 strip %2 from down buttons for %3\n", surface, strip, (int) a)); + + if (m == _down_buttons.end()) { + return; + } + + DownButtonList& l (m->second); + DownButtonList::iterator x = find (l.begin(), l.end(), (surface<<8)|(strip&0xf)); + + if (x != l.end()) { + l.erase (x); + } else { + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("surface %1 strip %2 not found in down buttons for %3\n", + surface, strip, (int) a)); + } +} + +MackieControlProtocol::ControlList +MackieControlProtocol::down_controls (AutomationType p) +{ + ControlList controls; + RouteList routes; + + DownButtonMap::iterator m = _down_buttons.find (p); + + if (m == _down_buttons.end()) { + return controls; + } + + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("looking for down buttons for %1, got %2\n", + p, m->second.size())); + + pull_route_range (m->second, routes); + + switch (p) { + case GainAutomation: + for (RouteList::iterator r = routes.begin(); r != routes.end(); ++r) { + controls.push_back ((*r)->gain_control()); + } + break; + case SoloAutomation: + for (RouteList::iterator r = routes.begin(); r != routes.end(); ++r) { + controls.push_back ((*r)->solo_control()); + } + break; + case MuteAutomation: + for (RouteList::iterator r = routes.begin(); r != routes.end(); ++r) { + controls.push_back ((*r)->mute_control()); + } + break; + case RecEnableAutomation: + for (RouteList::iterator r = routes.begin(); r != routes.end(); ++r) { + boost::shared_ptr trk = boost::dynamic_pointer_cast (*r); + if (trk) { + controls.push_back (trk->rec_enable_control()); + } + } + break; + default: + break; + } + + return controls; + +} + +struct ButtonRangeSorter { + bool operator() (const uint32_t& a, const uint32_t& b) { + return (a>>8) < (b>>8) // a.surface < b.surface + || + ((a>>8) == (b>>8) && (a&0xf) < (b&0xf)); // a.surface == b.surface && a.strip < b.strip + } +}; + +void +MackieControlProtocol::pull_route_range (DownButtonList& down, RouteList& selected) +{ + ButtonRangeSorter cmp; + + if (down.empty()) { + return; + } + + list ldown; + ldown.insert (ldown.end(), down.begin(), down.end()); + ldown.sort (cmp); + + uint32_t first = ldown.front(); + uint32_t last = ldown.back (); + + uint32_t first_surface = first>>8; + uint32_t first_strip = first&0xf; + + uint32_t last_surface = last>>8; + uint32_t last_strip = last&0xf; + + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("PRR %5 in list %1.%2 - %3.%4\n", first_surface, first_strip, last_surface, last_strip, + down.size())); + + for (Surfaces::const_iterator s = surfaces.begin(); s != surfaces.end(); ++s) { + + if ((*s)->number() >= first_surface && (*s)->number() <= last_surface) { + + uint32_t fs; + uint32_t ls; + + if ((*s)->number() == first_surface) { + fs = first_strip; + } else { + fs = 0; + } + + if ((*s)->number() == last_surface) { + ls = last_strip; + ls += 1; + } else { + ls = (*s)->n_strips (); + } + + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("adding strips for surface %1 (%2 .. %3)\n", + (*s)->number(), fs, ls)); + + for (uint32_t n = fs; n < ls; ++n) { + boost::shared_ptr r = (*s)->nth_strip (n)->route(); + if (r) { + selected.push_back (r); + } + } + } + } +} + +void +MackieControlProtocol::set_ipmidi_base (int16_t portnum) +{ + /* this will not be saved without a session save, so .. */ + + session->set_dirty (); + + _ipmidi_base = portnum; + + /* if the current device uses ipMIDI we need + to restart. + */ + + if (_active && _device_info.uses_ipmidi()) { + needs_ipmidi_restart = true; + } +} + +void +MackieControlProtocol::ipmidi_restart () +{ + clear_ports (); + surfaces.clear (); + create_surfaces (); + switch_banks (_current_initial_bank, true); + needs_ipmidi_restart = false; +} diff --git a/libs/surfaces/mackie/mackie_control_protocol.h b/libs/surfaces/mackie/mackie_control_protocol.h index cecfc5ea6c..0df770b36e 100644 --- a/libs/surfaces/mackie/mackie_control_protocol.h +++ b/libs/surfaces/mackie/mackie_control_protocol.h @@ -20,27 +20,32 @@ #define ardour_mackie_control_protocol_h #include +#include +#include +#include #include #include +#include #include #include "pbd/abstract_ui.h" - -#include "ardour/types.h" -#include "ardour/midi_ui.h" #include "midi++/types.h" - +#include "ardour/types.h" #include "control_protocol/control_protocol.h" + +#include "types.h" #include "midi_byte_array.h" #include "controls.h" -#include "dummy_port.h" -#include "route_signal.h" -#include "mackie_button_handler.h" -#include "mackie_port.h" -#include "mackie_jog_wheel.h" +#include "jog_wheel.h" #include "timer.h" +#include "device_info.h" +#include "device_profile.h" + +namespace ARDOUR { + class AutomationControl; +} namespace MIDI { class Port; @@ -48,6 +53,9 @@ namespace MIDI { namespace Mackie { class Surface; + class Control; + class SurfacePort; + class Button; } /** @@ -79,61 +87,83 @@ public: class MackieControlProtocol : public ARDOUR::ControlProtocol , public AbstractUI - , public Mackie::MackieButtonHandler { public: + static const int MODIFIER_OPTION; + static const int MODIFIER_CONTROL; + static const int MODIFIER_SHIFT; + static const int MODIFIER_CMDALT; + + enum ViewMode { + Mixer, + Dynamics, + EQ, + Loop, + AudioTracks, + MidiTracks, + Busses, + Sends, + Plugins, + }; + + enum FlipMode { + Normal, /* fader controls primary, vpot controls secondary */ + Mirror, /* fader + vpot control secondary */ + Swap, /* fader controls secondary, vpot controls primary */ + Zero, /* fader controls primary, but doesn't move, vpot controls secondary */ + }; + MackieControlProtocol(ARDOUR::Session &); virtual ~MackieControlProtocol(); + static MackieControlProtocol* instance() { return _instance; } + + const Mackie::DeviceInfo& device_info() const { return _device_info; } + Mackie::DeviceProfile& device_profile() { return _device_profile; } + int set_active (bool yn); + void set_device (const std::string&, bool allow_activation = true); + void set_profile (const std::string&); + + bool flip_mode () const { return _flip_mode; } + ViewMode view_mode () const { return _view_mode; } + bool zoom_mode () const { return _zoom_mode; } + + void set_view_mode (ViewMode); + void set_flip_mode (bool); XMLNode& get_state (); int set_state (const XMLNode&, int version); static bool probe(); - Mackie::Surface & surface(); + typedef std::list > Surfaces; + Surfaces surfaces; std::list > bundles (); + void set_master_on_surface_strip (uint32_t surface, uint32_t strip); + void set_monitor_on_surface_strip (uint32_t surface, uint32_t strip); + + uint32_t n_strips (bool with_locked_strips = true) const; + bool has_editor () const { return true; } void* get_gui () const; void tear_down_gui (); - - // control events - void handle_control_event(Mackie::SurfacePort & port, Mackie::Control & control, const Mackie::ControlState & state); - // strip/route related stuff - public: - /// Signal handler for Route::solo - void notify_solo_changed(Mackie::RouteSignal *); - /// Signal handler for Route::mute - void notify_mute_changed(Mackie::RouteSignal *); - /// Signal handler for Route::record_enable_changed - void notify_record_enable_changed(Mackie::RouteSignal *); - /// Signal handler for Route::gain_changed (from IO) - void notify_gain_changed(Mackie::RouteSignal *, bool force_update = true); - /// Signal handler for Route::name_change - void notify_property_changed(const PBD::PropertyChange&, Mackie::RouteSignal *); - /// Signal handler from Panner::Change - void notify_panner_changed(Mackie::RouteSignal *, bool force_update = true); - /// Signal handler for new routes added - void notify_route_added(ARDOUR::RouteList &); - /// Signal handler for Route::active_changed - void notify_active_changed(Mackie::RouteSignal *); - + void handle_button_event (Mackie::Surface&, Mackie::Button& button, Mackie::ButtonState); + + void notify_route_added (ARDOUR::RouteList &); void notify_remote_id_changed(); /// rebuild the current bank. Called on route added/removed and /// remote id changed. void refresh_current_bank(); - // global buttons (ie button not part of strips) - - public: // button-related signals void notify_record_state_changed(); void notify_transport_state_changed(); + void notify_loop_state_changed(); // mainly to pick up punch-in and punch-out void notify_parameter_changed(std::string const &); void notify_solo_active_changed(bool); @@ -143,108 +173,36 @@ class MackieControlProtocol void update_timecode_beats_led(); /// this is called to generate the midi to send in response to a button press. - void update_led(Mackie::Button & button, Mackie::LedState); + void update_led(Mackie::Surface&, Mackie::Button & button, Mackie::LedState); - void update_global_button(const std::string & name, Mackie::LedState); - void update_global_led(const std::string & name, Mackie::LedState); - - // transport button handler methods from MackieButtonHandler - virtual Mackie::LedState frm_left_press(Mackie::Button &); - virtual Mackie::LedState frm_left_release(Mackie::Button &); + void update_global_button (int id, Mackie::LedState); + void update_global_led (int id, Mackie::LedState); - virtual Mackie::LedState frm_right_press(Mackie::Button &); - virtual Mackie::LedState frm_right_release(Mackie::Button &); - - virtual Mackie::LedState stop_press(Mackie::Button &); - virtual Mackie::LedState stop_release(Mackie::Button &); - - virtual Mackie::LedState play_press(Mackie::Button &); - virtual Mackie::LedState play_release(Mackie::Button &); - - virtual Mackie::LedState record_press(Mackie::Button &); - virtual Mackie::LedState record_release(Mackie::Button &); - - virtual Mackie::LedState loop_press(Mackie::Button &); - virtual Mackie::LedState loop_release(Mackie::Button &); - - virtual Mackie::LedState punch_in_press(Mackie::Button &); - virtual Mackie::LedState punch_in_release(Mackie::Button &); - - virtual Mackie::LedState punch_out_press(Mackie::Button &); - virtual Mackie::LedState punch_out_release(Mackie::Button &); - - virtual Mackie::LedState home_press(Mackie::Button &); - virtual Mackie::LedState home_release(Mackie::Button &); - - virtual Mackie::LedState end_press(Mackie::Button &); - virtual Mackie::LedState end_release(Mackie::Button &); - - virtual Mackie::LedState rewind_press(Mackie::Button & button); - virtual Mackie::LedState rewind_release(Mackie::Button & button); - - virtual Mackie::LedState ffwd_press(Mackie::Button & button); - virtual Mackie::LedState ffwd_release(Mackie::Button & button); - - // bank switching button handler methods from MackieButtonHandler - virtual Mackie::LedState left_press(Mackie::Button &); - virtual Mackie::LedState left_release(Mackie::Button &); - - virtual Mackie::LedState right_press(Mackie::Button &); - virtual Mackie::LedState right_release(Mackie::Button &); - - virtual Mackie::LedState channel_left_press(Mackie::Button &); - virtual Mackie::LedState channel_left_release(Mackie::Button &); - - virtual Mackie::LedState channel_right_press(Mackie::Button &); - virtual Mackie::LedState channel_right_release(Mackie::Button &); - - virtual Mackie::LedState clicking_press(Mackie::Button &); - virtual Mackie::LedState clicking_release(Mackie::Button &); - - virtual Mackie::LedState global_solo_press(Mackie::Button &); - virtual Mackie::LedState global_solo_release(Mackie::Button &); - - // function buttons - virtual Mackie::LedState marker_press(Mackie::Button &); - virtual Mackie::LedState marker_release(Mackie::Button &); - - virtual Mackie::LedState drop_press(Mackie::Button &); - virtual Mackie::LedState drop_release(Mackie::Button &); - - virtual Mackie::LedState save_press(Mackie::Button &); - virtual Mackie::LedState save_release(Mackie::Button &); - - virtual Mackie::LedState timecode_beats_press(Mackie::Button &); - virtual Mackie::LedState timecode_beats_release(Mackie::Button &); - - // jog wheel states - virtual Mackie::LedState zoom_press(Mackie::Button &); - virtual Mackie::LedState zoom_release(Mackie::Button &); - - virtual Mackie::LedState scrub_press(Mackie::Button &); - virtual Mackie::LedState scrub_release(Mackie::Button &); - - /// This is the main MCU port, ie not an extender port - /// Only for use by JogWheel - const Mackie::SurfacePort & mcu_port() const; - Mackie::SurfacePort & mcu_port(); ARDOUR::Session & get_session() { return *session; } - + framepos_t transport_frame() const; + + int modifier_state() const { return _modifier_state; } + + typedef std::list > ControlList; + + void add_down_button (ARDOUR::AutomationType, int surface, int strip); + void remove_down_button (ARDOUR::AutomationType, int surface, int strip); + ControlList down_controls (ARDOUR::AutomationType); + + void add_down_select_button (int surface, int strip); + void remove_down_select_button (int surface, int strip); + void select_range (); + + int16_t ipmidi_base() const { return _ipmidi_base; } + void set_ipmidi_base (int16_t); + protected: - // create instances of MackiePort, depending on what's found in ardour.rc - void create_ports(); - // shut down the surface void close(); - // create the Surface object, with the correct number - // of strips for the currently connected ports and - // hook up the control event notification - void initialize_surface(); - // This sets up the notifications and sets the // controls to the correct values - void update_surface(); + void update_surfaces(); // connects global (not strip) signals from the Session to here // so the surface can be notified of changes from the other UIs. @@ -261,112 +219,277 @@ class MackieControlProtocol Sorted get_sorted_routes(); // bank switching - void switch_banks(int initial); - void prev_track(); - void next_track(); + void switch_banks (uint32_t first_remote_id, bool force = false); + void prev_track (); + void next_track (); - // delete all RouteSignal objects connecting Routes to Strips - void clear_route_signals(); - - typedef std::vector RouteSignals; - RouteSignals route_signals; - - // return which of the ports a particular route_table - // index belongs to - Mackie::MackiePort & port_for_id(uint32_t index); - - /** - Handle a button press for the control and return whether - the corresponding light should be on or off. - */ - bool handle_strip_button (Mackie::SurfacePort &, Mackie::Control &, Mackie::ButtonState, boost::shared_ptr); - - void add_port (MIDI::Port &, MIDI::Port &, int number); - - /** - Read session data and send to surface. Includes - automation from the currently active routes and - timecode displays. - */ - void poll_session_data(); - - // called from poll_automation to figure out which automations need to be sent - void update_automation(Mackie::RouteSignal &); - // also called from poll_automation to update timecode display void update_timecode_display(); std::string format_bbt_timecode (ARDOUR::framepos_t now_frame); std::string format_timecode_timecode (ARDOUR::framepos_t now_frame); - /** - notification that the port is about to start it's init sequence. - We must make sure that before this exits, the port is being polled - for new data. - */ - void handle_port_init(Mackie::SurfacePort *); - - /// notification from a MackiePort that it's now active - void handle_port_active(Mackie::SurfacePort *); - - /// notification from a MackiePort that it's now inactive - void handle_port_inactive(Mackie::SurfacePort *); - - boost::shared_ptr master_route(); - Mackie::Strip & master_strip(); - void do_request (MackieControlUIRequest*); int stop (); - + + void thread_init (); + void midi_connectivity_established (); + + bool route_is_locked_to_strip (boost::shared_ptr) const; + private: - void port_connected_or_disconnected (std::string, std::string, bool); - - boost::shared_ptr master_route_signal; - - static const char * default_port_name; - - /// The Midi port(s) connected to the units - typedef std::vector MackiePorts; - MackiePorts _ports; - - /// Sometimes the real port goes away, and we want to contain the breakage - Mackie::DummyPort _dummy_port; - - /// The initial remote_id of the currently switched in bank. - uint32_t _current_initial_bank; - - /// protects the port list - Glib::Mutex update_mutex; + struct ButtonHandlers { + Mackie::LedState (MackieControlProtocol::*press) (Mackie::Button&); + Mackie::LedState (MackieControlProtocol::*release) (Mackie::Button&); + + ButtonHandlers (Mackie::LedState (MackieControlProtocol::*p) (Mackie::Button&), + Mackie::LedState (MackieControlProtocol::*r) (Mackie::Button&)) + : press (p) + , release (r) {} + }; + typedef std::map ButtonMap; + typedef std::list PortSources; + + static MackieControlProtocol* _instance; + + Mackie::DeviceInfo _device_info; + Mackie::DeviceProfile _device_profile; + sigc::connection periodic_connection; + uint32_t _current_initial_bank; PBD::ScopedConnectionList audio_engine_connections; PBD::ScopedConnectionList session_connections; PBD::ScopedConnectionList port_connections; PBD::ScopedConnectionList route_connections; - - /// The representation of the physical controls on the surface. - Mackie::Surface * _surface; - + PBD::ScopedConnectionList gui_connections; bool _transport_previously_rolling; - // timer for two quick marker left presses - Mackie::Timer _frm_left_last; - - Mackie::JogWheel _jog_wheel; - + Mackie::Timer _frm_left_last; // last written timecode string - std::string _timecode_last; - + std::string _timecode_last; // Which timecode are we displaying? BBT or Timecode - ARDOUR::AnyTime::Type _timecode_type; - + ARDOUR::AnyTime::Type _timecode_type; // Bundle to represent our input ports boost::shared_ptr _input_bundle; // Bundle to represent our output ports boost::shared_ptr _output_bundle; + void* _gui; + bool _zoom_mode; + bool _scrub_mode; + bool _flip_mode; + ViewMode _view_mode; + int _current_selected_track; + int _modifier_state; + PortSources port_sources; + ButtonMap button_map; + int16_t _ipmidi_base; + bool needs_ipmidi_restart; + void create_surfaces (); + bool periodic(); void build_gui (); - void* _gui; + bool midi_input_handler (Glib::IOCondition ioc, MIDI::Port* port); + 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 ipmidi_restart (); + + /* BUTTON HANDLING */ + + typedef std::set DownButtonList; + typedef std::map DownButtonMap; + DownButtonMap _down_buttons; + DownButtonList _down_select_buttons; + + void pull_route_range (DownButtonList&, ARDOUR::RouteList&); + + /* implemented button handlers */ + Mackie::LedState frm_left_press(Mackie::Button &); + Mackie::LedState frm_left_release(Mackie::Button &); + Mackie::LedState frm_right_press(Mackie::Button &); + Mackie::LedState frm_right_release(Mackie::Button &); + Mackie::LedState stop_press(Mackie::Button &); + Mackie::LedState stop_release(Mackie::Button &); + Mackie::LedState play_press(Mackie::Button &); + Mackie::LedState play_release(Mackie::Button &); + Mackie::LedState record_press(Mackie::Button &); + Mackie::LedState record_release(Mackie::Button &); + Mackie::LedState loop_press(Mackie::Button &); + Mackie::LedState loop_release(Mackie::Button &); + Mackie::LedState punch_in_press(Mackie::Button &); + Mackie::LedState punch_in_release(Mackie::Button &); + Mackie::LedState punch_out_press(Mackie::Button &); + Mackie::LedState punch_out_release(Mackie::Button &); + Mackie::LedState home_press(Mackie::Button &); + Mackie::LedState home_release(Mackie::Button &); + Mackie::LedState end_press(Mackie::Button &); + Mackie::LedState end_release(Mackie::Button &); + Mackie::LedState rewind_press(Mackie::Button & button); + Mackie::LedState rewind_release(Mackie::Button & button); + Mackie::LedState ffwd_press(Mackie::Button & button); + Mackie::LedState ffwd_release(Mackie::Button & button); + Mackie::LedState cursor_up_press (Mackie::Button &); + Mackie::LedState cursor_up_release (Mackie::Button &); + Mackie::LedState cursor_down_press (Mackie::Button &); + Mackie::LedState cursor_down_release (Mackie::Button &); + Mackie::LedState cursor_left_press (Mackie::Button &); + Mackie::LedState cursor_left_release (Mackie::Button &); + Mackie::LedState cursor_right_press (Mackie::Button &); + Mackie::LedState cursor_right_release (Mackie::Button &); + Mackie::LedState left_press(Mackie::Button &); + Mackie::LedState left_release(Mackie::Button &); + Mackie::LedState right_press(Mackie::Button &); + Mackie::LedState right_release(Mackie::Button &); + Mackie::LedState channel_left_press(Mackie::Button &); + Mackie::LedState channel_left_release(Mackie::Button &); + Mackie::LedState channel_right_press(Mackie::Button &); + Mackie::LedState channel_right_release(Mackie::Button &); + Mackie::LedState clicking_press(Mackie::Button &); + Mackie::LedState clicking_release(Mackie::Button &); + Mackie::LedState global_solo_press(Mackie::Button &); + Mackie::LedState global_solo_release(Mackie::Button &); + Mackie::LedState marker_press(Mackie::Button &); + Mackie::LedState marker_release(Mackie::Button &); + Mackie::LedState save_press(Mackie::Button &); + Mackie::LedState save_release(Mackie::Button &); + Mackie::LedState timecode_beats_press(Mackie::Button &); + Mackie::LedState timecode_beats_release(Mackie::Button &); + Mackie::LedState zoom_press(Mackie::Button &); + Mackie::LedState zoom_release(Mackie::Button &); + Mackie::LedState scrub_press(Mackie::Button &); + Mackie::LedState scrub_release(Mackie::Button &); + Mackie::LedState undo_press (Mackie::Button &); + Mackie::LedState undo_release (Mackie::Button &); + Mackie::LedState redo_press (Mackie::Button &); + Mackie::LedState redo_release (Mackie::Button &); + Mackie::LedState shift_press (Mackie::Button &); + Mackie::LedState shift_release (Mackie::Button &); + Mackie::LedState option_press (Mackie::Button &); + Mackie::LedState option_release (Mackie::Button &); + Mackie::LedState control_press (Mackie::Button &); + Mackie::LedState control_release (Mackie::Button &); + Mackie::LedState cmd_alt_press (Mackie::Button &); + Mackie::LedState cmd_alt_release (Mackie::Button &); + + Mackie::LedState io_press (Mackie::Button &); + Mackie::LedState io_release (Mackie::Button &); + Mackie::LedState sends_press (Mackie::Button &); + Mackie::LedState sends_release (Mackie::Button &); + Mackie::LedState pan_press (Mackie::Button &); + Mackie::LedState pan_release (Mackie::Button &); + Mackie::LedState plugin_press (Mackie::Button &); + Mackie::LedState plugin_release (Mackie::Button &); + Mackie::LedState eq_press (Mackie::Button &); + Mackie::LedState eq_release (Mackie::Button &); + Mackie::LedState dyn_press (Mackie::Button &); + Mackie::LedState dyn_release (Mackie::Button &); + Mackie::LedState flip_press (Mackie::Button &); + Mackie::LedState flip_release (Mackie::Button &); + Mackie::LedState edit_press (Mackie::Button &); + Mackie::LedState edit_release (Mackie::Button &); + Mackie::LedState name_value_press (Mackie::Button &); + Mackie::LedState name_value_release (Mackie::Button &); + Mackie::LedState F1_press (Mackie::Button &); + Mackie::LedState F1_release (Mackie::Button &); + Mackie::LedState F2_press (Mackie::Button &); + Mackie::LedState F2_release (Mackie::Button &); + Mackie::LedState F3_press (Mackie::Button &); + Mackie::LedState F3_release (Mackie::Button &); + Mackie::LedState F4_press (Mackie::Button &); + Mackie::LedState F4_release (Mackie::Button &); + Mackie::LedState F5_press (Mackie::Button &); + Mackie::LedState F5_release (Mackie::Button &); + Mackie::LedState F6_press (Mackie::Button &); + Mackie::LedState F6_release (Mackie::Button &); + Mackie::LedState F7_press (Mackie::Button &); + Mackie::LedState F7_release (Mackie::Button &); + Mackie::LedState F8_press (Mackie::Button &); + Mackie::LedState F8_release (Mackie::Button &); + Mackie::LedState F9_press (Mackie::Button &); + Mackie::LedState F9_release (Mackie::Button &); + Mackie::LedState F10_press (Mackie::Button &); + Mackie::LedState F10_release (Mackie::Button &); + Mackie::LedState F11_press (Mackie::Button &); + Mackie::LedState F11_release (Mackie::Button &); + Mackie::LedState F12_press (Mackie::Button &); + Mackie::LedState F12_release (Mackie::Button &); + Mackie::LedState F13_press (Mackie::Button &); + Mackie::LedState F13_release (Mackie::Button &); + Mackie::LedState F14_press (Mackie::Button &); + Mackie::LedState F14_release (Mackie::Button &); + Mackie::LedState F15_press (Mackie::Button &); + Mackie::LedState F15_release (Mackie::Button &); + Mackie::LedState F16_press (Mackie::Button &); + Mackie::LedState F16_release (Mackie::Button &); + Mackie::LedState on_press (Mackie::Button &); + Mackie::LedState on_release (Mackie::Button &); + Mackie::LedState rec_ready_press (Mackie::Button &); + Mackie::LedState rec_ready_release (Mackie::Button &); + Mackie::LedState touch_press (Mackie::Button &); + Mackie::LedState touch_release (Mackie::Button &); + Mackie::LedState enter_press (Mackie::Button &); + Mackie::LedState enter_release (Mackie::Button &); + Mackie::LedState cancel_press (Mackie::Button &); + Mackie::LedState cancel_release (Mackie::Button &); + Mackie::LedState mixer_press (Mackie::Button &); + Mackie::LedState mixer_release (Mackie::Button &); + Mackie::LedState user_a_press (Mackie::Button &); + Mackie::LedState user_a_release (Mackie::Button &); + Mackie::LedState user_b_press (Mackie::Button &); + Mackie::LedState user_b_release (Mackie::Button &); + Mackie::LedState fader_touch_press (Mackie::Button &); + Mackie::LedState fader_touch_release (Mackie::Button &); + + Mackie::LedState snapshot_press (Mackie::Button&); + Mackie::LedState snapshot_release (Mackie::Button&); + Mackie::LedState read_press (Mackie::Button&); + Mackie::LedState read_release (Mackie::Button&); + Mackie::LedState write_press (Mackie::Button&); + Mackie::LedState write_release (Mackie::Button&); + Mackie::LedState fdrgroup_press (Mackie::Button&); + Mackie::LedState fdrgroup_release (Mackie::Button&); + Mackie::LedState clearsolo_press (Mackie::Button&); + Mackie::LedState clearsolo_release (Mackie::Button&); + Mackie::LedState track_press (Mackie::Button&); + Mackie::LedState track_release (Mackie::Button&); + Mackie::LedState send_press (Mackie::Button&); + Mackie::LedState send_release (Mackie::Button&); + Mackie::LedState miditracks_press (Mackie::Button&); + Mackie::LedState miditracks_release (Mackie::Button&); + Mackie::LedState inputs_press (Mackie::Button&); + Mackie::LedState inputs_release (Mackie::Button&); + Mackie::LedState audiotracks_press (Mackie::Button&); + Mackie::LedState audiotracks_release (Mackie::Button&); + Mackie::LedState audioinstruments_press (Mackie::Button&); + Mackie::LedState audioinstruments_release (Mackie::Button&); + Mackie::LedState aux_press (Mackie::Button&); + Mackie::LedState aux_release (Mackie::Button&); + Mackie::LedState busses_press (Mackie::Button&); + Mackie::LedState busses_release (Mackie::Button&); + Mackie::LedState outputs_press (Mackie::Button&); + Mackie::LedState outputs_release (Mackie::Button&); + Mackie::LedState user_press (Mackie::Button&); + Mackie::LedState user_release (Mackie::Button&); + Mackie::LedState trim_press (Mackie::Button&); + Mackie::LedState trim_release (Mackie::Button&); + Mackie::LedState latch_press (Mackie::Button&); + Mackie::LedState latch_release (Mackie::Button&); + Mackie::LedState grp_press (Mackie::Button&); + Mackie::LedState grp_release (Mackie::Button&); + Mackie::LedState nudge_press (Mackie::Button&); + Mackie::LedState nudge_release (Mackie::Button&); + Mackie::LedState drop_press (Mackie::Button&); + Mackie::LedState drop_release (Mackie::Button&); + Mackie::LedState replace_press (Mackie::Button&); + Mackie::LedState replace_release (Mackie::Button&); + Mackie::LedState click_press (Mackie::Button&); + Mackie::LedState click_release (Mackie::Button&); + Mackie::LedState view_press (Mackie::Button&); + Mackie::LedState view_release (Mackie::Button&); }; + + #endif // ardour_mackie_control_protocol_h diff --git a/libs/surfaces/mackie/mackie_control_protocol_poll.cc b/libs/surfaces/mackie/mackie_control_protocol_poll.cc index 06aafc5965..66c80c9a8b 100644 --- a/libs/surfaces/mackie/mackie_control_protocol_poll.cc +++ b/libs/surfaces/mackie/mackie_control_protocol_poll.cc @@ -25,57 +25,3 @@ using namespace std; using namespace Mackie; using namespace PBD; -const char * MackieControlProtocol::default_port_name = "mcu"; - -bool MackieControlProtocol::probe() -{ - return true; -} - -void MackieControlProtocol::handle_port_inactive( SurfacePort * port ) -{ - // port gone away. So stop polling it ASAP - { - // delete the port instance - Glib::Mutex::Lock lock( update_mutex ); - MackiePorts::iterator it = find( _ports.begin(), _ports.end(), port ); - if ( it != _ports.end() ) - { - delete *it; - _ports.erase( it ); - } - } - - // TODO all the rebuilding of surfaces and so on -} - -void MackieControlProtocol::handle_port_active (SurfacePort *) -{ - // no need to re-add port because it was already added - // during the init phase. So just update the local surface - // representation and send the representation to - // all existing ports - - // TODO update bank size - - // TODO rebuild surface, to have new units - - // finally update session state to the surface - // TODO but this is also done in set_active, and - // in fact update_surface won't execute unless -#ifdef DEBUG - cout << "update_surface in handle_port_active" << endl; -#endif - // _active == true - update_surface(); -} - -void MackieControlProtocol::handle_port_init (Mackie::SurfacePort *) -{ -#ifdef DEBUG - cout << "MackieControlProtocol::handle_port_init" << endl; -#endif -#ifdef DEBUG - cout << "MackieControlProtocol::handle_port_init finish" << endl; -#endif -} diff --git a/libs/surfaces/mackie/mackie_jog_wheel.cc b/libs/surfaces/mackie/mackie_jog_wheel.cc deleted file mode 100644 index 95ab97c5f7..0000000000 --- a/libs/surfaces/mackie/mackie_jog_wheel.cc +++ /dev/null @@ -1,196 +0,0 @@ -#include - -#include "ardour/session.h" - -#include "mackie_jog_wheel.h" - -#include "mackie_control_protocol.h" -#include "surface_port.h" -#include "controls.h" -#include "surface.h" - -#include - -using namespace Mackie; -using std::isnan; - -JogWheel::JogWheel( MackieControlProtocol & mcp ) -: _mcp( mcp ) -, _transport_speed( 4.0 ) -, _transport_direction( 0 ) -, _shuttle_speed( 0.0 ) -{ -} - -JogWheel::State JogWheel::jog_wheel_state() const -{ - if ( !_jog_wheel_states.empty() ) - return _jog_wheel_states.top(); - else - return scroll; -} - -void JogWheel::zoom_event (SurfacePort &, Control &, const ControlState &) -{ -} - -void JogWheel::scrub_event (SurfacePort &, Control &, const ControlState &) -{ -} - -void JogWheel::speed_event (SurfacePort &, Control &, const ControlState &) -{ -} - -void JogWheel::scroll_event (SurfacePort &, Control &, const ControlState &) -{ -} - -void JogWheel::jog_event (SurfacePort &, Control &, const ControlState & state) -{ - // TODO use current snap-to setting? - switch ( jog_wheel_state() ) - { - case scroll: - _mcp.ScrollTimeline( state.delta * state.sign ); - break; - - case zoom: - // Chunky Zoom. - // TODO implement something similar to ScrollTimeline which - // ends up in Editor::control_scroll for smoother zooming. - if ( state.sign > 0 ) - for ( unsigned int i = 0; i < state.ticks; ++i ) _mcp.ZoomIn(); - else - for ( unsigned int i = 0; i < state.ticks; ++i ) _mcp.ZoomOut(); - break; - - case speed: - // locally, _transport_speed is an positive value - _transport_speed += _mcp.surface().scaled_delta( state, _mcp.get_session().transport_speed() ); - - // make sure no weirdness gets to the session - if ( _transport_speed < 0 || isnan( _transport_speed ) ) - { - _transport_speed = 0.0; - } - - // translate _transport_speed speed to a signed transport velocity - _mcp.get_session().request_transport_speed_nonzero (transport_speed() * transport_direction()); - break; - - case scrub: - { - if ( state.sign != 0 ) - { - add_scrub_interval( _scrub_timer.restart() ); - // x clicks per second => speed == 1.0 - float speed = _mcp.surface().scrub_scaling_factor() / average_scrub_interval() * state.ticks; - _mcp.get_session().request_transport_speed_nonzero (speed * state.sign); - } - else - { - // we have a stop event - check_scrubbing(); - } - break; - } - - case shuttle: - _shuttle_speed = _mcp.get_session().transport_speed(); - _shuttle_speed += _mcp.surface().scaled_delta( state, _mcp.get_session().transport_speed() ); - _mcp.get_session().request_transport_speed_nonzero (_shuttle_speed); - break; - - case select: - std::cout << "JogWheel select not implemented" << std::endl; - break; - } -} - -void JogWheel::check_scrubbing() -{ - // if the last elapsed is greater than the average + std deviation, then stop - if ( !_scrub_intervals.empty() && _scrub_timer.elapsed() > average_scrub_interval() + std_dev_scrub_interval() ) - { - _mcp.get_session().request_transport_speed( 0.0 ); - _scrub_intervals.clear(); - } -} - -void JogWheel::push( State state ) -{ - _jog_wheel_states.push( state ); -} - -void JogWheel::pop() -{ - if ( _jog_wheel_states.size() > 0 ) - { - _jog_wheel_states.pop(); - } -} - -void JogWheel::zoom_state_toggle() -{ - if ( jog_wheel_state() == zoom ) - pop(); - else - push( zoom ); -} - -JogWheel::State JogWheel::scrub_state_cycle() -{ - State top = jog_wheel_state(); - if ( top == scrub ) - { - // stop scrubbing and go to shuttle - pop(); - push( shuttle ); - _shuttle_speed = 0.0; - } - else if ( top == shuttle ) - { - // default to scroll, or the last selected - pop(); - } - else - { - // start with scrub - push( scrub ); - } - - return jog_wheel_state(); -} - -void JogWheel::add_scrub_interval( unsigned long elapsed ) -{ - if ( _scrub_intervals.size() > 5 ) - { - _scrub_intervals.pop_front(); - } - _scrub_intervals.push_back( elapsed ); -} - -float JogWheel::average_scrub_interval() -{ - float sum = 0.0; - for ( std::deque::iterator it = _scrub_intervals.begin(); it != _scrub_intervals.end(); ++it ) - { - sum += *it; - } - return sum / _scrub_intervals.size(); -} - -float JogWheel::std_dev_scrub_interval() -{ - float average = average_scrub_interval(); - - // calculate standard deviation - float sum = 0.0; - for ( std::deque::iterator it = _scrub_intervals.begin(); it != _scrub_intervals.end(); ++it ) - { - sum += pow( *it - average, 2 ); - } - return sqrt( sum / _scrub_intervals.size() -1 ); -} diff --git a/libs/surfaces/mackie/mackie_jog_wheel.h b/libs/surfaces/mackie/mackie_jog_wheel.h deleted file mode 100644 index 83a4364393..0000000000 --- a/libs/surfaces/mackie/mackie_jog_wheel.h +++ /dev/null @@ -1,102 +0,0 @@ -#ifndef mackie_jog_wheel -#define mackie_jog_wheel - -#include "timer.h" - -#include -#include -#include - -class MackieControlProtocol; - -namespace Mackie -{ - -class SurfacePort; -class Control; -class ControlState; - -/** - A jog wheel can be used to control many things. This - handles all of the states and state transitions. - - Mainly it exists to avoid putting a bunch of messy - stuff in MackieControlProtocol. - - But it doesn't really know who it is, with stacks, queues and various - boolean state variables. -*/ -class JogWheel -{ -public: - enum State { scroll, zoom, speed, scrub, shuttle, select }; - - JogWheel( MackieControlProtocol & mcp ); - - /// As the wheel turns... - void jog_event( SurfacePort & port, Control & control, const ControlState & state ); - - // These are for incoming button presses that change the internal state - // but they're not actually used at the moment. - void zoom_event( SurfacePort & port, Control & control, const ControlState & state ); - void scrub_event( SurfacePort & port, Control & control, const ControlState & state ); - void speed_event( SurfacePort & port, Control & control, const ControlState & state ); - void scroll_event( SurfacePort & port, Control & control, const ControlState & state ); - - /// Return the current jog wheel mode, which defaults to Scroll - State jog_wheel_state() const; - - /// The current transport speed for ffwd and rew. Can be - /// set by wheel when they're pressed. - float transport_speed() const { return _transport_speed; } - - /// one of -1,0,1 - int transport_direction() const { return _transport_direction; } - void transport_direction( int rhs ) { _transport_direction = rhs; } - - void push( State state ); - void pop(); - - /// Turn zoom mode on and off - void zoom_state_toggle(); - - /** - Cycle scrub -> shuttle -> previous - */ - State scrub_state_cycle(); - - /// Check to see when the last scrub event was - /// And stop scrubbing if it was too long ago. - /// Intended to be called from a periodic timer of - /// some kind. - void check_scrubbing(); - -protected: - void add_scrub_interval( unsigned long elapsed ); - float average_scrub_interval(); - float std_dev_scrub_interval(); - -private: - MackieControlProtocol & _mcp; - - /// transport speed for ffwd and rew, controller by jog - float _transport_speed; - int _transport_direction; - - /// Speed for shuttle - float _shuttle_speed; - - /// a stack for keeping track of states - std::stack _jog_wheel_states; - - /// So we know how fast to set the transport speed while scrubbing - Timer _scrub_timer; - - /// to keep track of what the current scrub rate is - /// so we can calculate a moving average - std::deque _scrub_intervals; -}; - -} - -#endif diff --git a/libs/surfaces/mackie/mackie_midi_builder.cc b/libs/surfaces/mackie/mackie_midi_builder.cc deleted file mode 100644 index ff1046859f..0000000000 --- a/libs/surfaces/mackie/mackie_midi_builder.cc +++ /dev/null @@ -1,272 +0,0 @@ -/* - Copyright (C) 2006,2007 John Anderson - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ -#include "mackie_midi_builder.h" - -#include -#include -#include -#include - -#include "controls.h" -#include "midi_byte_array.h" -#include "mackie_port.h" - -using namespace Mackie; -using namespace std; - -MIDI::byte MackieMidiBuilder::calculate_pot_value( midi_pot_mode mode, const ControlState & state ) -{ - // TODO do an exact calc for 0.50? To allow manually re-centering the port. - - // center on or off - MIDI::byte retval = ( state.pos > 0.45 && state.pos < 0.55 ? 1 : 0 ) << 6; - - // mode - retval |= ( mode << 4 ); - - // value, but only if off hasn't explicitly been set - if ( state.led_state != off ) - retval += ( int(state.pos * 10.0) + 1 ) & 0x0f; // 0b00001111 - - return retval; -} - -MidiByteArray MackieMidiBuilder::build_led_ring( const Pot & pot, const ControlState & state, midi_pot_mode mode ) -{ - return build_led_ring( pot.led_ring(), state, mode ); -} - -MidiByteArray MackieMidiBuilder::build_led_ring( const LedRing & led_ring, const ControlState & state, midi_pot_mode mode ) -{ - // The other way of doing this: - // 0x30 + pot/ring number (0-7) - //, 0x30 + led_ring.ordinal() - 1 - return MidiByteArray ( 3 - // the control type - , midi_pot_id - // the id - , 0x20 + led_ring.raw_id() - // the value - , calculate_pot_value( mode, state ) - ); -} - -MidiByteArray MackieMidiBuilder::build_led( const Button & button, LedState ls ) -{ - return build_led( button.led(), ls ); -} - -MidiByteArray MackieMidiBuilder::build_led( const Led & led, LedState ls ) -{ - MIDI::byte state = 0; - switch ( ls.state() ) - { - case LedState::on: state = 0x7f; break; - case LedState::off: state = 0x00; break; - case LedState::none: state = 0x00; break; // actually, this should never happen. - case LedState::flashing: state = 0x01; break; - } - - return MidiByteArray ( 3 - , midi_button_id - , led.raw_id() - , state - ); -} - -MidiByteArray MackieMidiBuilder::build_fader( const Fader & fader, float pos ) -{ - int posi = int( 0x3fff * pos ); - - return MidiByteArray ( 3 - , midi_fader_id | fader.raw_id() - // lower-order bits - , posi & 0x7f - // higher-order bits - , ( posi >> 7 ) - ); -} - -MidiByteArray MackieMidiBuilder::zero_strip( SurfacePort & port, const Strip & strip ) -{ - Group::Controls::const_iterator it = strip.controls().begin(); - MidiByteArray retval; - for (; it != strip.controls().end(); ++it ) - { - Control & control = **it; - if ( control.accepts_feedback() ) - retval << zero_control( control ); - } - - // These must have sysex headers - - /* XXX: not sure about this check to only display stuff for strips of index < 8 */ - if (strip.index() < 8) { - retval << strip_display_blank( port, strip, 0 ); - retval << strip_display_blank( port, strip, 1 ); - } - - return retval; -} - -MidiByteArray MackieMidiBuilder::zero_control( const Control & control ) -{ - switch( control.type() ) - { - case Control::type_button: - return build_led( (Button&)control, off ); - - case Control::type_led: - return build_led( (Led&)control, off ); - - case Control::type_fader: - return build_fader( (Fader&)control, 0.0 ); - - case Control::type_pot: - return build_led_ring( dynamic_cast( control ), off ); - - case Control::type_led_ring: - return build_led_ring( dynamic_cast( control ), off ); - - default: - ostringstream os; - os << "Unknown control type " << control << " in Strip::zero_control"; - throw MackieControlException( os.str() ); - } -} - -char translate_seven_segment( char achar ) -{ - achar = toupper( achar ); - if ( achar >= 0x40 && achar <= 0x60 ) - return achar - 0x40; - else if ( achar >= 0x21 && achar <= 0x3f ) - return achar; - else - return 0x00; -} - -MidiByteArray MackieMidiBuilder::two_char_display( const std::string & msg, const std::string & dots ) -{ - if ( msg.length() != 2 ) throw MackieControlException( "MackieMidiBuilder::two_char_display: msg must be exactly 2 characters" ); - if ( dots.length() != 2 ) throw MackieControlException( "MackieMidiBuilder::two_char_display: dots must be exactly 2 characters" ); - - MidiByteArray bytes( 5, 0xb0, 0x4a, 0x00, 0x4b, 0x00 ); - - // chars are understood by the surface in right-to-left order - // could also exchange the 0x4a and 0x4b, above - bytes[4] = translate_seven_segment( msg[0] ) + ( dots[0] == '.' ? 0x40 : 0x00 ); - bytes[2] = translate_seven_segment( msg[1] ) + ( dots[1] == '.' ? 0x40 : 0x00 ); - - return bytes; -} - -MidiByteArray MackieMidiBuilder::two_char_display (unsigned int value, const std::string & /*dots*/) -{ - ostringstream os; - os << setfill('0') << setw(2) << value % 100; - return two_char_display( os.str() ); -} - -MidiByteArray MackieMidiBuilder::strip_display_blank( SurfacePort & port, const Strip & strip, unsigned int line_number ) -{ - // 6 spaces, not 7 because strip_display adds a space where appropriate - return strip_display( port, strip, line_number, " " ); -} - -MidiByteArray MackieMidiBuilder::strip_display( SurfacePort & port, const Strip & strip, unsigned int line_number, const std::string & line ) -{ - assert (line_number <= 1); - assert (strip.index() < 8); - -#ifdef DEBUG - cout << "MackieMidiBuilder::strip_display index: " << strip.index() << ", line " << line_number << ": " << line << endl; -#endif - - MidiByteArray retval; - - // sysex header - retval << port.sysex_hdr(); - - // code for display - retval << 0x12; - // offset (0 to 0x37 first line, 0x38 to 0x6f for second line ) - retval << ( strip.index() * 7 + ( line_number * 0x38 ) ); - - // ascii data to display - retval << line; - // pad with " " out to 6 chars - for ( int i = line.length(); i < 6; ++i ) retval << ' '; - - // column spacer, unless it's the right-hand column - if ( strip.index() < 7 ) retval << ' '; - - // sysex trailer - retval << MIDI::eox; - -#ifdef DEBUG - cout << "MackieMidiBuilder::strip_display midi: " << retval << endl; -#endif - return retval; -} - -MidiByteArray MackieMidiBuilder::all_strips_display (SurfacePort & /*port*/, std::vector & /*lines1*/, std::vector & /*lines2*/) -{ - MidiByteArray retval; - retval << 0x12 << 0; - // NOTE remember max 112 bytes per message, including sysex headers - retval << "Not working yet"; - return retval; -} - -MidiByteArray MackieMidiBuilder::timecode_display( SurfacePort & port, const std::string & timecode, const std::string & last_timecode ) -{ - // if there's no change, send nothing, not even sysex header - if ( timecode == last_timecode ) return MidiByteArray(); - - // length sanity checking - string local_timecode = timecode; - // truncate to 10 characters - if ( local_timecode.length() > 10 ) local_timecode = local_timecode.substr( 0, 10 ); - // pad to 10 characters - while ( local_timecode.length() < 10 ) local_timecode += " "; - - // find the suffix of local_timecode that differs from last_timecode - std::pair pp = mismatch( last_timecode.begin(), last_timecode.end(), local_timecode.begin() ); - - MidiByteArray retval; - - // sysex header - retval << port.sysex_hdr(); - - // code for timecode display - retval << 0x10; - - // translate characters. These are sent in reverse order of display - // hence the reverse iterators - string::reverse_iterator rend = reverse_iterator( pp.second ); - for ( string::reverse_iterator it = local_timecode.rbegin(); it != rend; ++it ) - { - retval << translate_seven_segment( *it ); - } - - // sysex trailer - retval << MIDI::eox; - - return retval; -} diff --git a/libs/surfaces/mackie/mackie_midi_builder.h b/libs/surfaces/mackie/mackie_midi_builder.h deleted file mode 100644 index b5fc9f73c9..0000000000 --- a/libs/surfaces/mackie/mackie_midi_builder.h +++ /dev/null @@ -1,104 +0,0 @@ -/* - Copyright (C) 2006,2007 John Anderson - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ -#ifndef mackie_midi_builder_h -#define mackie_midi_builder_h - -#include "midi_byte_array.h" -#include "types.h" -#include "controls.h" - -namespace Mackie -{ - -class SurfacePort; - -/** - This knows how to build midi messages given a control and - a state. -*/ -class MackieMidiBuilder -{ -public: - /** - The first byte of a midi message from the surface - will contain one of these, sometimes bitmasked - with the control id - */ - enum midi_types { - midi_fader_id = Control::type_fader - , midi_button_id = Control::type_button - , midi_pot_id = Control::type_pot - }; - - /** - The LED rings have these modes. - */ - enum midi_pot_mode { - midi_pot_mode_dot = 0 - , midi_pot_mode_boost_cut = 1 - , midi_pot_mode_wrap = 2 - , midi_pot_mode_spread = 3 - }; - - MidiByteArray build_led_ring( const Pot & pot, const ControlState &, midi_pot_mode mode = midi_pot_mode_dot ); - MidiByteArray build_led_ring( const LedRing & led_ring, const ControlState &, midi_pot_mode mode = midi_pot_mode_dot ); - - MidiByteArray build_led( const Led & led, LedState ls ); - MidiByteArray build_led( const Button & button, LedState ls ); - - MidiByteArray build_fader( const Fader & fader, float pos ); - - /// return bytes that will reset all controls to their zero positions - /// And blank the display for the strip. Pass SurfacePort so we know which sysex header to use. - MidiByteArray zero_strip( SurfacePort &, const Strip & strip ); - - // provide bytes to zero the given control - MidiByteArray zero_control( const Control & control ); - - // display the first 2 chars of the msg in the 2 char display - // . is appended to the previous character, so A.B. would - // be two characters - MidiByteArray two_char_display( const std::string & msg, const std::string & dots = " " ); - MidiByteArray two_char_display( unsigned int value, const std::string & dots = " " ); - - /** - Timecode display. Only the difference between timecode and last_timecode will - be encoded, to save midi bandwidth. If they're the same, an empty array will - be returned - */ - MidiByteArray timecode_display( SurfacePort &, const std::string & timecode, const std::string & last_timecode = "" ); - - /** - for displaying characters on the strip LCD - pass SurfacePort so we know which sysex header to use - */ - MidiByteArray strip_display( SurfacePort &, const Strip & strip, unsigned int line_number, const std::string & line ); - - /// blank the strip LCD, ie write all spaces. Pass SurfacePort so we know which sysex header to use. - MidiByteArray strip_display_blank( SurfacePort &, const Strip & strip, unsigned int line_number ); - - /// for generating all strip names. Pass SurfacePort so we know which sysex header to use. - MidiByteArray all_strips_display( SurfacePort &, std::vector & lines1, std::vector & lines2 ); - -protected: - static MIDI::byte calculate_pot_value( midi_pot_mode mode, const ControlState & ); -}; - -} - -#endif diff --git a/libs/surfaces/mackie/mackie_port.cc b/libs/surfaces/mackie/mackie_port.cc deleted file mode 100644 index bda1c2765d..0000000000 --- a/libs/surfaces/mackie/mackie_port.cc +++ /dev/null @@ -1,467 +0,0 @@ -/* - Copyright (C) 2006,2007 John Anderson - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ -#include "mackie_port.h" - -#include "mackie_control_exception.h" -#include "mackie_control_protocol.h" -#include "mackie_midi_builder.h" -#include "controls.h" -#include "surface.h" - -#include - -#include - -#include "midi++/types.h" -#include "midi++/port.h" - -#include "ardour/debug.h" -#include "ardour/rc_configuration.h" - -#include "i18n.h" - -#include - -using namespace std; -using namespace Mackie; -using namespace ARDOUR; -using namespace PBD; - -// The MCU sysex header -MidiByteArray mackie_sysex_hdr ( 5, MIDI::sysex, 0x0, 0x0, 0x66, 0x10 ); - -// The MCU extender sysex header -MidiByteArray mackie_sysex_hdr_xt ( 5, MIDI::sysex, 0x0, 0x0, 0x66, 0x11 ); - -MackiePort::MackiePort (MackieControlProtocol & mcp, MIDI::Port & input_port, MIDI::Port & output_port, int number, port_type_t port_type) - : SurfacePort (input_port, output_port, number) - , _mcp( mcp ) - , _port_type( port_type ) - , _emulation( none ) - , _initialising( true ) -{ - DEBUG_TRACE (DEBUG::MackieControl, "MackiePort::MackiePort\n"); -} - -MackiePort::~MackiePort() -{ - DEBUG_TRACE (DEBUG::MackieControl, "MackiePort::~MackiePort\n"); - close(); - DEBUG_TRACE (DEBUG::MackieControl, "~MackiePort finished\n"); -} - -int MackiePort::strips() const -{ - if ( _port_type == mcu ) - { - switch ( _emulation ) - { - // BCF2000 only has 8 faders, so reserve one for master - case bcf2000: return 7; - case mackie: return 8; - case none: - default: - throw MackieControlException( "MackiePort::strips: don't know what emulation we're using" ); - } - } - else - { - // must be an extender, ie no master fader - return 8; - } -} - -// should really be in MackiePort -void MackiePort::open() -{ - DEBUG_TRACE (DEBUG::MackieControl, string_compose ("MackiePort::open %1\n", *this)); - - input_port().parser()->sysex.connect_same_thread (sysex_connection, boost::bind (&MackiePort::handle_midi_sysex, this, _1, _2, _3)); - - // make sure the device is connected - init(); -} - -void MackiePort::close() -{ - DEBUG_TRACE (DEBUG::MackieControl, "MackiePort::close\n"); - - // disconnect signals - any_connection.disconnect(); - sysex_connection.disconnect(); - - // TODO emit a "closing" signal? -} - -const MidiByteArray & MackiePort::sysex_hdr() const -{ - switch ( _port_type ) - { - case mcu: return mackie_sysex_hdr; - case ext: return mackie_sysex_hdr_xt; - } - cout << "MackiePort::sysex_hdr _port_type not known" << endl; - return mackie_sysex_hdr; -} - -MidiByteArray calculate_challenge_response( MidiByteArray::iterator begin, MidiByteArray::iterator end ) -{ - MidiByteArray l; - back_insert_iterator back ( l ); - copy( begin, end, back ); - - MidiByteArray retval; - - // this is how to calculate the response to the challenge. - // from the Logic docs. - retval << ( 0x7f & ( l[0] + ( l[1] ^ 0xa ) - l[3] ) ); - retval << ( 0x7f & ( ( l[2] >> l[3] ) ^ ( l[0] + l[3] ) ) ); - retval << ( 0x7f & ( (l[3] - ( l[2] << 2 )) ^ ( l[0] | l[1] ) ) ); - retval << ( 0x7f & ( l[1] - l[2] + ( 0xf0 ^ ( l[3] << 4 ) ) ) ); - - return retval; -} - -// not used right now -MidiByteArray MackiePort::host_connection_query( MidiByteArray & bytes ) -{ - // handle host connection query - DEBUG_TRACE (DEBUG::MackieControl, string_compose ("host connection query: %1\n", bytes)); - - if ( bytes.size() != 18 ) - { - finalise_init( false ); - ostringstream os; - os << "expecting 18 bytes, read " << bytes << " from " << input_port().name(); - throw MackieControlException( os.str() ); - } - - // build and send host connection reply - MidiByteArray response; - response << 0x02; - copy( bytes.begin() + 6, bytes.begin() + 6 + 7, back_inserter( response ) ); - response << calculate_challenge_response( bytes.begin() + 6 + 7, bytes.begin() + 6 + 7 + 4 ); - return response; -} - -// not used right now -MidiByteArray MackiePort::host_connection_confirmation( const MidiByteArray & bytes ) -{ - DEBUG_TRACE (DEBUG::MackieControl, string_compose ("host_connection_confirmation: %1\n", bytes)); - - // decode host connection confirmation - if ( bytes.size() != 14 ) - { - finalise_init( false ); - ostringstream os; - os << "expecting 14 bytes, read " << bytes << " from " << input_port().name(); - throw MackieControlException( os.str() ); - } - - // send version request - return MidiByteArray( 2, 0x13, 0x00 ); -} - -void MackiePort::probe_emulation (const MidiByteArray &) -{ -#if 0 - cout << "MackiePort::probe_emulation: " << bytes.size() << ", " << bytes << endl; - - MidiByteArray version_string; - for ( int i = 6; i < 11; ++i ) version_string << bytes[i]; - cout << "version_string: " << version_string << endl; -#endif - - // TODO investigate using serial number. Also, possibly size of bytes might - // give an indication. Also, apparently MCU sends non-documented messages - // sometimes. - if (!_initialising) - { - //cout << "MackiePort::probe_emulation out of sequence." << endl; - return; - } - - finalise_init( true ); -} - -void MackiePort::init() -{ - DEBUG_TRACE (DEBUG::MackieControl, "MackiePort::init\n"); - - init_mutex.lock(); - _initialising = true; - - DEBUG_TRACE (DEBUG::MackieControl, "MackiePort::init lock acquired\n"); - - // emit pre-init signal - init_event(); - - // kick off initialisation. See docs in header file for init() - - // bypass the init sequence because sometimes the first - // message doesn't get to the unit, and there's no way - // to do a timed lock in Glib. - //write_sysex ( MidiByteArray ( 2, 0x13, 0x00 ) ); - - finalise_init( true ); -} - -void MackiePort::finalise_init( bool yn ) -{ - DEBUG_TRACE (DEBUG::MackieControl, "MackiePort::finalise_init\n"); - - bool emulation_ok = false; - - // probing doesn't work very well, so just use a config variable - // to set the emulation mode - // TODO This might have to be specified on a per-port basis - // in the config file - // if an mcu and a bcf are needed to work as one surface - if ( _emulation == none ) - { - // TODO same as code in mackie_control_protocol.cc - if ( ARDOUR::Config->get_mackie_emulation() == "bcf" ) - { - _emulation = bcf2000; - emulation_ok = true; - } - else if ( ARDOUR::Config->get_mackie_emulation() == "mcu" ) - { - _emulation = mackie; - emulation_ok = true; - } - else - { - cout << "unknown mackie emulation: " << ARDOUR::Config->get_mackie_emulation() << endl; - emulation_ok = false; - } - } - - yn = yn && emulation_ok; - - SurfacePort::active( yn ); - - if (yn) { - active_event(); - - // start handling messages from controls - connect_any(); - } - - _initialising = false; - init_cond.signal(); - init_mutex.unlock(); - - DEBUG_TRACE (DEBUG::MackieControl, "MackiePort::finalise_init lock released\n"); -} - -void MackiePort::connect_any() -{ - if (!any_connection.connected()) { - input_port().parser()->any.connect_same_thread (any_connection, boost::bind (&MackiePort::handle_midi_any, this, _1, _2, _3)); - } -} - -bool MackiePort::wait_for_init() -{ - Glib::Mutex::Lock lock( init_mutex ); - while (_initialising) { - DEBUG_TRACE (DEBUG::MackieControl, "MackiePort::wait_for_active waiting\n"); - init_cond.wait( init_mutex ); - DEBUG_TRACE (DEBUG::MackieControl, "MackiePort::wait_for_active released\n"); - } - DEBUG_TRACE (DEBUG::MackieControl, "MackiePort::wait_for_active returning\n"); - return SurfacePort::active(); -} - -void MackiePort::handle_midi_sysex (MIDI::Parser &, MIDI::byte * raw_bytes, size_t count ) -{ - MidiByteArray bytes( count, raw_bytes ); - DEBUG_TRACE (DEBUG::MackieControl, string_compose ("handle_midi_sysex: %1\n", bytes)); - - switch( bytes[5] ) - { - case 0x01: - // not used right now - write_sysex (host_connection_query (bytes)); - break; - case 0x03: - // not used right now - write_sysex (host_connection_confirmation (bytes)); - break; - case 0x04: - inactive_event (); - cout << "host connection error" << bytes << endl; - break; - case 0x14: - probe_emulation (bytes); - break; - default: - cout << "unknown sysex: " << bytes << endl; - } -} - -Control & MackiePort::lookup_control( MIDI::byte * bytes, size_t count ) -{ - // Don't instantiate a MidiByteArray here unless it's needed for exceptions. - // Reason being that this method is called for every single incoming - // midi event, and it needs to be as efficient as possible. - - Control * control = 0; - MIDI::byte midi_type = bytes[0] & 0xf0; //0b11110000 - - switch (midi_type) { - // fader - case MackieMidiBuilder::midi_fader_id: - { - int midi_id = bytes[0] & 0x0f; - control = _mcp.surface().faders[midi_id]; - if ( control == 0 ) - { - MidiByteArray mba( count, bytes ); - ostringstream os; - os << "Control for fader" << bytes << " id " << midi_id << " is null"; - throw MackieControlException( os.str() ); - } - break; - } - - // button - case MackieMidiBuilder::midi_button_id: - control = _mcp.surface().buttons[bytes[1]]; - if ( control == 0 ) - { - MidiByteArray mba( count, bytes ); - ostringstream os; - os << "Control for button " << mba << " is null"; - throw MackieControlException( os.str() ); - } - break; - - // pot (jog wheel, external control) - case MackieMidiBuilder::midi_pot_id: - control = _mcp.surface().pots[bytes[1]]; - if ( control == 0 ) - { - MidiByteArray mba( count, bytes ); - ostringstream os; - os << "Control for rotary " << mba << " is null"; - throw MackieControlException( os.str() ); - } - break; - - default: - MidiByteArray mba( count, bytes ); - ostringstream os; - os << "Cannot find control for " << mba; - throw MackieControlException( os.str() ); - } - return *control; -} - -// converts midi messages into control_event signals -// it might be worth combining this with lookup_control -// because they have similar logic flows. -void MackiePort::handle_midi_any (MIDI::Parser &, MIDI::byte * raw_bytes, size_t count ) -{ - MidiByteArray bytes( count, raw_bytes ); - DEBUG_TRACE (DEBUG::MackieControl, string_compose ("MackiePort::handle_midi_any %1\n", bytes)); - - try - { - // ignore sysex messages - if ( raw_bytes[0] == MIDI::sysex ) return; - - // sanity checking - if (count != 3) { - ostringstream os; - MidiByteArray mba( count, raw_bytes ); - os << "MackiePort::handle_midi_any needs 3 bytes, but received " << mba; - throw MackieControlException( os.str() ); - } - - Control & control = lookup_control( raw_bytes, count ); - control.set_in_use (true); - - // This handles incoming bytes. Outgoing bytes - // are sent by the signal handlers. - switch (control.type()) { - // fader - case Control::type_fader: - { - // only the top-order 10 bits out of 14 are used - int midi_pos = ( ( raw_bytes[2] << 7 ) + raw_bytes[1] ) >> 4; - - // in_use is set by the MackieControlProtocol::handle_strip_button - - // relies on implicit ControlState constructor - control_event( *this, control, float(midi_pos) / float(0x3ff) ); - } - break; - - // button - case Control::type_button: - { - ControlState control_state( raw_bytes[2] == 0x7f ? press : release ); - control.set_in_use (control_state.button_state == press); - control_event( *this, control, control_state ); - - break; - } - - // pot (jog wheel, external control) - case Control::type_pot: - { - ControlState state; - - // bytes[2] & 0b01000000 (0x40) give sign - state.sign = ( raw_bytes[2] & 0x40 ) == 0 ? 1 : -1; - // bytes[2] & 0b00111111 (0x3f) gives delta - state.ticks = ( raw_bytes[2] & 0x3f); - if (state.ticks == 0) { - /* euphonix and perhaps other devices send zero - when they mean 1, we think. - */ - state.ticks = 1; - } - state.delta = float( state.ticks ) / float( 0x3f ); - - /* Pots only emit events when they move, not when they - stop moving. So to get a stop event, we need to use a timeout. - */ - - control.set_in_use (true); - add_in_use_timeout (control, &control); - - // emit the control event - control_event( *this, control, state ); - break; - } - default: - cerr << "Do not understand control type " << control; - } - } - - catch( MackieControlException & e ) { - MidiByteArray bytes( count, raw_bytes ); - cout << bytes << ' ' << e.what() << endl; - } - - DEBUG_TRACE (DEBUG::MackieControl, string_compose ("finished MackiePort::handle_midi_any %1\n", bytes)); -} - diff --git a/libs/surfaces/mackie/mackie_port.h b/libs/surfaces/mackie/mackie_port.h deleted file mode 100644 index d12de3a099..0000000000 --- a/libs/surfaces/mackie/mackie_port.h +++ /dev/null @@ -1,124 +0,0 @@ -/* - Copyright (C) 2006,2007 John Anderson - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ -#ifndef mackie_port_h -#define mackie_port_h - -#include -#include - -#include "pbd/signals.h" - -#include "surface_port.h" -#include "midi_byte_array.h" -#include "types.h" - -namespace MIDI { - class Port; - class Parser; -} - -class MackieControlProtocol; - -namespace Mackie -{ - -class MackiePort : public SurfacePort -{ -public: - enum port_type_t { mcu, ext }; - enum emulation_t { none, mackie, bcf2000 }; - - MackiePort (MackieControlProtocol & mcp, MIDI::Port & input_port, MIDI::Port & output_port, int number, port_type_t = mcu); - ~MackiePort(); - - virtual void open(); - virtual void close(); - - /// MCU and extenders have different sysex headers - virtual const MidiByteArray & sysex_hdr() const; - - /// Handle device initialisation - void handle_midi_sysex( MIDI::Parser &, MIDI::byte *, size_t count ); - - /// Handle all control messags - void handle_midi_any( MIDI::Parser &, MIDI::byte *, size_t count ); - - Control & lookup_control( MIDI::byte *, size_t count ); - - /// return the number of strips associated with this port - virtual int strips() const; - - /// Block until the port has finished initialising, and then return - /// whether the intialisation succeeded - bool wait_for_init(); - - emulation_t emulation() const { return _emulation; } - - /// Connect the any signal from the parser to handle_midi_any - /// unless it's already connected - void connect_any(); - -protected: - /** - The initialisation sequence is fairly complex. First a lock is acquired - so that a condition can be used to signal the end of the init process. - Then a sysex is sent to the device. The response to the sysex - is handled by a switch in handle_midi_sysex which calls one of the - other methods. - - However, windows DAWs ignore the documented init sequence and so we - do too. Thanks to Essox for helping with this. - - So we use the version firmware to figure out what device is on - the other end of the cable. - */ - void init(); - - /** - Once the device is initialised, finalise_init(true) is called, which - releases the lock and signals the condition, and starts handling incoming - messages. finalise_init(false) will also release the lock but doesn't - start handling messages. - */ - void finalise_init( bool yn ); - - MidiByteArray host_connection_query( MidiByteArray & bytes ); - MidiByteArray host_connection_confirmation( const MidiByteArray & bytes ); - - /** - Will set _emulation to what it thinks is correct, based - on responses from the device. Or get/set parameters. Or - environment variables. Or existence of a file. - */ - void probe_emulation( const MidiByteArray & bytes ); - -private: - MackieControlProtocol & _mcp; - port_type_t _port_type; - PBD::ScopedConnection any_connection; - PBD::ScopedConnection sysex_connection; - emulation_t _emulation; - - bool _initialising; - Glib::Cond init_cond; - Glib::Mutex init_mutex; -}; - -} - -#endif diff --git a/libs/surfaces/mackie/mackie_surface.cc b/libs/surfaces/mackie/mackie_surface.cc deleted file mode 100644 index dbba726062..0000000000 --- a/libs/surfaces/mackie/mackie_surface.cc +++ /dev/null @@ -1,18 +0,0 @@ -#include "mackie_surface.h" -#include "surface_port.h" -#include "mackie_midi_builder.h" - -#include -#include - -using namespace Mackie; - -void MackieSurface::display_timecode( SurfacePort & port, MackieMidiBuilder & builder, const std::string & timecode, const std::string & timecode_last ) -{ - port.write( builder.timecode_display( port, timecode, timecode_last ) ); -} - -float MackieSurface::scaled_delta( const ControlState & state, float current_speed ) -{ - return state.sign * ( std::pow( float(state.ticks + 1), 2 ) + current_speed ) / 100.0; -} diff --git a/libs/surfaces/mackie/mackie_surface.h b/libs/surfaces/mackie/mackie_surface.h deleted file mode 100644 index b3bfb3b6d4..0000000000 --- a/libs/surfaces/mackie/mackie_surface.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef mackie_surface_mackie_h -#define mackie_surface_mackie_h -/* - Generated by scripts/generate-surface.rb -*/ - -#include "surface.h" - -namespace Mackie -{ - -class MackieButtonHandler; -class MackieSurface : public Surface -{ -public: - MackieSurface( uint32_t max_strips ) : Surface( max_strips ) - { - } - - virtual void handle_button( MackieButtonHandler & mbh, ButtonState bs, Button & button ); - virtual void init_controls(); - - virtual bool has_timecode_display() const { return true; } - virtual void display_timecode( SurfacePort &, MackieMidiBuilder &, const std::string & timecode, const std::string & timecode_last ); - - virtual float scrub_scaling_factor() { return 100.0; } - virtual float scaled_delta( const ControlState & state, float current_speed ); -}; - -} - -#endif diff --git a/libs/surfaces/mackie/mackie_surface_generated.cc b/libs/surfaces/mackie/mackie_surface_generated.cc deleted file mode 100644 index f991d2a972..0000000000 --- a/libs/surfaces/mackie/mackie_surface_generated.cc +++ /dev/null @@ -1,1507 +0,0 @@ -/* - Generated by scripts/generate-surface.rb -*/ - -#include "mackie_surface.h" - -#include "controls.h" -#include "mackie_button_handler.h" - -using namespace Mackie; - -void Mackie::MackieSurface::init_controls() -{ - // intialise groups and strips - Group * group = 0; - - // make sure there are enough strips - strips.resize( 8 ); - - group = new Group ( "user" ); - groups["user"] = group; - - group = new Group ( "assignment" ); - groups["assignment"] = group; - - group = new Group ( "none" ); - groups["none"] = group; - - group = new MasterStrip ( "master", 0 ); - groups["master"] = group; - strips[0] = dynamic_cast( group ); - - group = new Strip ( "strip_1", 0 ); - groups["strip_1"] = group; - strips[0] = dynamic_cast( group ); - - group = new Group ( "cursor" ); - groups["cursor"] = group; - - group = new Strip ( "strip_2", 1 ); - groups["strip_2"] = group; - strips[1] = dynamic_cast( group ); - - group = new Group ( "functions" ); - groups["functions"] = group; - - group = new Group ( "automation" ); - groups["automation"] = group; - - group = new Strip ( "strip_3", 2 ); - groups["strip_3"] = group; - strips[2] = dynamic_cast( group ); - - group = new Group ( "display" ); - groups["display"] = group; - - group = new Strip ( "strip_4", 3 ); - groups["strip_4"] = group; - strips[3] = dynamic_cast( group ); - - group = new Strip ( "strip_5", 4 ); - groups["strip_5"] = group; - strips[4] = dynamic_cast( group ); - - group = new Strip ( "strip_6", 5 ); - groups["strip_6"] = group; - strips[5] = dynamic_cast( group ); - - group = new Group ( "transport" ); - groups["transport"] = group; - - group = new Strip ( "strip_7", 6 ); - groups["strip_7"] = group; - strips[6] = dynamic_cast( group ); - - group = new Group ( "modifiers" ); - groups["modifiers"] = group; - - group = new Group ( "bank" ); - groups["bank"] = group; - - group = new Strip ( "strip_8", 7 ); - groups["strip_8"] = group; - strips[7] = dynamic_cast( group ); - - - // initialise controls - Fader * fader = 0; - Pot * pot = 0; - Button * button = 0; - Led * led = 0; - - group = groups["strip_1"]; - fader = new Fader ( 0, 1, "gain", *group ); - faders[0x00] = fader; - controls.push_back( fader ); - group->add( *fader ); - - group = groups["strip_2"]; - fader = new Fader ( 1, 2, "gain", *group ); - faders[0x01] = fader; - controls.push_back( fader ); - group->add( *fader ); - - group = groups["strip_3"]; - fader = new Fader ( 2, 3, "gain", *group ); - faders[0x02] = fader; - controls.push_back( fader ); - group->add( *fader ); - - group = groups["strip_4"]; - fader = new Fader ( 3, 4, "gain", *group ); - faders[0x03] = fader; - controls.push_back( fader ); - group->add( *fader ); - - group = groups["strip_5"]; - fader = new Fader ( 4, 5, "gain", *group ); - faders[0x04] = fader; - controls.push_back( fader ); - group->add( *fader ); - - group = groups["strip_6"]; - fader = new Fader ( 5, 6, "gain", *group ); - faders[0x05] = fader; - controls.push_back( fader ); - group->add( *fader ); - - group = groups["strip_7"]; - fader = new Fader ( 6, 7, "gain", *group ); - faders[0x06] = fader; - controls.push_back( fader ); - group->add( *fader ); - - group = groups["strip_8"]; - fader = new Fader ( 7, 8, "gain", *group ); - faders[0x07] = fader; - controls.push_back( fader ); - group->add( *fader ); - - group = groups["master"]; - fader = new Fader ( 8, 1, "gain", *group ); - faders[0x08] = fader; - controls.push_back( fader ); - group->add( *fader ); - - group = groups["strip_1"]; - pot = new Pot ( 16, 1, "vpot", *group ); - pots[0x10] = pot; - controls.push_back( pot ); - group->add( *pot ); - - group = groups["strip_2"]; - pot = new Pot ( 17, 2, "vpot", *group ); - pots[0x11] = pot; - controls.push_back( pot ); - group->add( *pot ); - - group = groups["strip_3"]; - pot = new Pot ( 18, 3, "vpot", *group ); - pots[0x12] = pot; - controls.push_back( pot ); - group->add( *pot ); - - group = groups["strip_4"]; - pot = new Pot ( 19, 4, "vpot", *group ); - pots[0x13] = pot; - controls.push_back( pot ); - group->add( *pot ); - - group = groups["strip_5"]; - pot = new Pot ( 20, 5, "vpot", *group ); - pots[0x14] = pot; - controls.push_back( pot ); - group->add( *pot ); - - group = groups["strip_6"]; - pot = new Pot ( 21, 6, "vpot", *group ); - pots[0x15] = pot; - controls.push_back( pot ); - group->add( *pot ); - - group = groups["strip_7"]; - pot = new Pot ( 22, 7, "vpot", *group ); - pots[0x16] = pot; - controls.push_back( pot ); - group->add( *pot ); - - group = groups["strip_8"]; - pot = new Pot ( 23, 8, "vpot", *group ); - pots[0x17] = pot; - controls.push_back( pot ); - group->add( *pot ); - - group = groups["none"]; - pot = new Jog ( 60, 1, "jog", *group ); - pots[0x3c] = pot; - controls.push_back( pot ); - controls_by_name["jog"] = pot; - group->add( *pot ); - - group = groups["none"]; - pot = new Pot ( 46, 1, "external", *group ); - pots[0x2e] = pot; - controls.push_back( pot ); - controls_by_name["external"] = pot; - group->add( *pot ); - - group = groups["strip_1"]; - button = new Button ( 0, 1, "recenable", *group ); - buttons[0x00] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_2"]; - button = new Button ( 1, 2, "recenable", *group ); - buttons[0x01] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_3"]; - button = new Button ( 2, 3, "recenable", *group ); - buttons[0x02] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_4"]; - button = new Button ( 3, 4, "recenable", *group ); - buttons[0x03] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_5"]; - button = new Button ( 4, 5, "recenable", *group ); - buttons[0x04] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_6"]; - button = new Button ( 5, 6, "recenable", *group ); - buttons[0x05] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_7"]; - button = new Button ( 6, 7, "recenable", *group ); - buttons[0x06] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_8"]; - button = new Button ( 7, 8, "recenable", *group ); - buttons[0x07] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_1"]; - button = new Button ( 8, 1, "solo", *group ); - buttons[0x08] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_2"]; - button = new Button ( 9, 2, "solo", *group ); - buttons[0x09] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_3"]; - button = new Button ( 10, 3, "solo", *group ); - buttons[0x0a] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_4"]; - button = new Button ( 11, 4, "solo", *group ); - buttons[0x0b] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_5"]; - button = new Button ( 12, 5, "solo", *group ); - buttons[0x0c] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_6"]; - button = new Button ( 13, 6, "solo", *group ); - buttons[0x0d] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_7"]; - button = new Button ( 14, 7, "solo", *group ); - buttons[0x0e] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_8"]; - button = new Button ( 15, 8, "solo", *group ); - buttons[0x0f] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_1"]; - button = new Button ( 16, 1, "mute", *group ); - buttons[0x10] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_2"]; - button = new Button ( 17, 2, "mute", *group ); - buttons[0x11] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_3"]; - button = new Button ( 18, 3, "mute", *group ); - buttons[0x12] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_4"]; - button = new Button ( 19, 4, "mute", *group ); - buttons[0x13] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_5"]; - button = new Button ( 20, 5, "mute", *group ); - buttons[0x14] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_6"]; - button = new Button ( 21, 6, "mute", *group ); - buttons[0x15] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_7"]; - button = new Button ( 22, 7, "mute", *group ); - buttons[0x16] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_8"]; - button = new Button ( 23, 8, "mute", *group ); - buttons[0x17] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_1"]; - button = new Button ( 24, 1, "select", *group ); - buttons[0x18] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_2"]; - button = new Button ( 25, 2, "select", *group ); - buttons[0x19] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_3"]; - button = new Button ( 26, 3, "select", *group ); - buttons[0x1a] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_4"]; - button = new Button ( 27, 4, "select", *group ); - buttons[0x1b] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_5"]; - button = new Button ( 28, 5, "select", *group ); - buttons[0x1c] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_6"]; - button = new Button ( 29, 6, "select", *group ); - buttons[0x1d] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_7"]; - button = new Button ( 30, 7, "select", *group ); - buttons[0x1e] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_8"]; - button = new Button ( 31, 8, "select", *group ); - buttons[0x1f] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_1"]; - button = new Button ( 32, 1, "vselect", *group ); - buttons[0x20] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_2"]; - button = new Button ( 33, 2, "vselect", *group ); - buttons[0x21] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_3"]; - button = new Button ( 34, 3, "vselect", *group ); - buttons[0x22] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_4"]; - button = new Button ( 35, 4, "vselect", *group ); - buttons[0x23] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_5"]; - button = new Button ( 36, 5, "vselect", *group ); - buttons[0x24] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_6"]; - button = new Button ( 37, 6, "vselect", *group ); - buttons[0x25] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_7"]; - button = new Button ( 38, 7, "vselect", *group ); - buttons[0x26] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_8"]; - button = new Button ( 39, 8, "vselect", *group ); - buttons[0x27] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["assignment"]; - button = new Button ( 40, 1, "io", *group ); - buttons[0x28] = button; - controls.push_back( button ); - controls_by_name["io"] = button; - group->add( *button ); - - group = groups["assignment"]; - button = new Button ( 41, 1, "sends", *group ); - buttons[0x29] = button; - controls.push_back( button ); - controls_by_name["sends"] = button; - group->add( *button ); - - group = groups["assignment"]; - button = new Button ( 42, 1, "pan", *group ); - buttons[0x2a] = button; - controls.push_back( button ); - controls_by_name["pan"] = button; - group->add( *button ); - - group = groups["assignment"]; - button = new Button ( 43, 1, "plugin", *group ); - buttons[0x2b] = button; - controls.push_back( button ); - controls_by_name["plugin"] = button; - group->add( *button ); - - group = groups["assignment"]; - button = new Button ( 44, 1, "eq", *group ); - buttons[0x2c] = button; - controls.push_back( button ); - controls_by_name["eq"] = button; - group->add( *button ); - - group = groups["assignment"]; - button = new Button ( 45, 1, "dyn", *group ); - buttons[0x2d] = button; - controls.push_back( button ); - controls_by_name["dyn"] = button; - group->add( *button ); - - group = groups["bank"]; - button = new Button ( 46, 1, "left", *group ); - buttons[0x2e] = button; - controls.push_back( button ); - controls_by_name["left"] = button; - group->add( *button ); - - group = groups["bank"]; - button = new Button ( 47, 1, "right", *group ); - buttons[0x2f] = button; - controls.push_back( button ); - controls_by_name["right"] = button; - group->add( *button ); - - group = groups["bank"]; - button = new Button ( 48, 1, "channel_left", *group ); - buttons[0x30] = button; - controls.push_back( button ); - controls_by_name["channel_left"] = button; - group->add( *button ); - - group = groups["bank"]; - button = new Button ( 49, 1, "channel_right", *group ); - buttons[0x31] = button; - controls.push_back( button ); - controls_by_name["channel_right"] = button; - group->add( *button ); - - group = groups["none"]; - button = new Button ( 50, 1, "flip", *group ); - buttons[0x32] = button; - controls.push_back( button ); - controls_by_name["flip"] = button; - group->add( *button ); - - group = groups["none"]; - button = new Button ( 51, 1, "edit", *group ); - buttons[0x33] = button; - controls.push_back( button ); - controls_by_name["edit"] = button; - group->add( *button ); - - group = groups["display"]; - button = new Button ( 52, 1, "name_value", *group ); - buttons[0x34] = button; - controls.push_back( button ); - controls_by_name["name_value"] = button; - group->add( *button ); - - group = groups["display"]; - button = new Button ( 53, 1, "timecode_beats", *group ); - buttons[0x35] = button; - controls.push_back( button ); - controls_by_name["timecode_beats"] = button; - group->add( *button ); - - group = groups["none"]; - button = new Button ( 54, 1, "F1", *group ); - buttons[0x36] = button; - controls.push_back( button ); - controls_by_name["F1"] = button; - group->add( *button ); - - group = groups["none"]; - button = new Button ( 55, 1, "F2", *group ); - buttons[0x37] = button; - controls.push_back( button ); - controls_by_name["F2"] = button; - group->add( *button ); - - group = groups["none"]; - button = new Button ( 56, 1, "F3", *group ); - buttons[0x38] = button; - controls.push_back( button ); - controls_by_name["F3"] = button; - group->add( *button ); - - group = groups["none"]; - button = new Button ( 57, 1, "F4", *group ); - buttons[0x39] = button; - controls.push_back( button ); - controls_by_name["F4"] = button; - group->add( *button ); - - group = groups["none"]; - button = new Button ( 58, 1, "F5", *group ); - buttons[0x3a] = button; - controls.push_back( button ); - controls_by_name["F5"] = button; - group->add( *button ); - - group = groups["none"]; - button = new Button ( 59, 1, "F6", *group ); - buttons[0x3b] = button; - controls.push_back( button ); - controls_by_name["F6"] = button; - group->add( *button ); - - group = groups["none"]; - button = new Button ( 60, 1, "F7", *group ); - buttons[0x3c] = button; - controls.push_back( button ); - controls_by_name["F7"] = button; - group->add( *button ); - - group = groups["none"]; - button = new Button ( 61, 1, "F8", *group ); - buttons[0x3d] = button; - controls.push_back( button ); - controls_by_name["F8"] = button; - group->add( *button ); - - group = groups["none"]; - button = new Button ( 62, 1, "F9", *group ); - buttons[0x3e] = button; - controls.push_back( button ); - controls_by_name["F9"] = button; - group->add( *button ); - - group = groups["none"]; - button = new Button ( 63, 1, "F10", *group ); - buttons[0x3f] = button; - controls.push_back( button ); - controls_by_name["F10"] = button; - group->add( *button ); - - group = groups["none"]; - button = new Button ( 64, 1, "F11", *group ); - buttons[0x40] = button; - controls.push_back( button ); - controls_by_name["F11"] = button; - group->add( *button ); - - group = groups["none"]; - button = new Button ( 65, 1, "F12", *group ); - buttons[0x41] = button; - controls.push_back( button ); - controls_by_name["F12"] = button; - group->add( *button ); - - group = groups["none"]; - button = new Button ( 66, 1, "F13", *group ); - buttons[0x42] = button; - controls.push_back( button ); - controls_by_name["F13"] = button; - group->add( *button ); - - group = groups["none"]; - button = new Button ( 67, 1, "F14", *group ); - buttons[0x43] = button; - controls.push_back( button ); - controls_by_name["F14"] = button; - group->add( *button ); - - group = groups["none"]; - button = new Button ( 68, 1, "F15", *group ); - buttons[0x44] = button; - controls.push_back( button ); - controls_by_name["F15"] = button; - group->add( *button ); - - group = groups["none"]; - button = new Button ( 69, 1, "F16", *group ); - buttons[0x45] = button; - controls.push_back( button ); - controls_by_name["F16"] = button; - group->add( *button ); - - group = groups["modifiers"]; - button = new Button ( 70, 1, "shift", *group ); - buttons[0x46] = button; - controls.push_back( button ); - controls_by_name["shift"] = button; - group->add( *button ); - - group = groups["modifiers"]; - button = new Button ( 71, 1, "option", *group ); - buttons[0x47] = button; - controls.push_back( button ); - controls_by_name["option"] = button; - group->add( *button ); - - group = groups["modifiers"]; - button = new Button ( 72, 1, "control", *group ); - buttons[0x48] = button; - controls.push_back( button ); - controls_by_name["control"] = button; - group->add( *button ); - - group = groups["modifiers"]; - button = new Button ( 73, 1, "cmd_alt", *group ); - buttons[0x49] = button; - controls.push_back( button ); - controls_by_name["cmd_alt"] = button; - group->add( *button ); - - group = groups["automation"]; - button = new Button ( 74, 1, "on", *group ); - buttons[0x4a] = button; - controls.push_back( button ); - controls_by_name["on"] = button; - group->add( *button ); - - group = groups["automation"]; - button = new Button ( 75, 1, "rec_ready", *group ); - buttons[0x4b] = button; - controls.push_back( button ); - controls_by_name["rec_ready"] = button; - group->add( *button ); - - group = groups["functions"]; - button = new Button ( 76, 1, "undo", *group ); - buttons[0x4c] = button; - controls.push_back( button ); - controls_by_name["undo"] = button; - group->add( *button ); - - group = groups["automation"]; - button = new Button ( 77, 1, "snapshot", *group ); - buttons[0x4d] = button; - controls.push_back( button ); - controls_by_name["snapshot"] = button; - group->add( *button ); - - group = groups["automation"]; - button = new Button ( 78, 1, "touch", *group ); - buttons[0x4e] = button; - controls.push_back( button ); - controls_by_name["touch"] = button; - group->add( *button ); - - group = groups["functions"]; - button = new Button ( 79, 1, "redo", *group ); - buttons[0x4f] = button; - controls.push_back( button ); - controls_by_name["redo"] = button; - group->add( *button ); - - group = groups["functions"]; - button = new Button ( 80, 1, "marker", *group ); - buttons[0x50] = button; - controls.push_back( button ); - controls_by_name["marker"] = button; - group->add( *button ); - - group = groups["functions"]; - button = new Button ( 81, 1, "enter", *group ); - buttons[0x51] = button; - controls.push_back( button ); - controls_by_name["enter"] = button; - group->add( *button ); - - group = groups["functions"]; - button = new Button ( 82, 1, "cancel", *group ); - buttons[0x52] = button; - controls.push_back( button ); - controls_by_name["cancel"] = button; - group->add( *button ); - - group = groups["functions"]; - button = new Button ( 83, 1, "mixer", *group ); - buttons[0x53] = button; - controls.push_back( button ); - controls_by_name["mixer"] = button; - group->add( *button ); - - group = groups["transport"]; - button = new Button ( 84, 1, "frm_left", *group ); - buttons[0x54] = button; - controls.push_back( button ); - controls_by_name["frm_left"] = button; - group->add( *button ); - - group = groups["transport"]; - button = new Button ( 85, 1, "frm_right", *group ); - buttons[0x55] = button; - controls.push_back( button ); - controls_by_name["frm_right"] = button; - group->add( *button ); - - group = groups["transport"]; - button = new Button ( 86, 1, "loop", *group ); - buttons[0x56] = button; - controls.push_back( button ); - controls_by_name["loop"] = button; - group->add( *button ); - - group = groups["transport"]; - button = new Button ( 87, 1, "punch_in", *group ); - buttons[0x57] = button; - controls.push_back( button ); - controls_by_name["punch_in"] = button; - group->add( *button ); - - group = groups["transport"]; - button = new Button ( 88, 1, "punch_out", *group ); - buttons[0x58] = button; - controls.push_back( button ); - controls_by_name["punch_out"] = button; - group->add( *button ); - - group = groups["transport"]; - button = new Button ( 89, 1, "home", *group ); - buttons[0x59] = button; - controls.push_back( button ); - controls_by_name["home"] = button; - group->add( *button ); - - group = groups["transport"]; - button = new Button ( 90, 1, "end", *group ); - buttons[0x5a] = button; - controls.push_back( button ); - controls_by_name["end"] = button; - group->add( *button ); - - group = groups["transport"]; - button = new Button ( 91, 1, "rewind", *group ); - buttons[0x5b] = button; - controls.push_back( button ); - controls_by_name["rewind"] = button; - group->add( *button ); - - group = groups["transport"]; - button = new Button ( 92, 1, "ffwd", *group ); - buttons[0x5c] = button; - controls.push_back( button ); - controls_by_name["ffwd"] = button; - group->add( *button ); - - group = groups["transport"]; - button = new Button ( 93, 1, "stop", *group ); - buttons[0x5d] = button; - controls.push_back( button ); - controls_by_name["stop"] = button; - group->add( *button ); - - group = groups["transport"]; - button = new Button ( 94, 1, "play", *group ); - buttons[0x5e] = button; - controls.push_back( button ); - controls_by_name["play"] = button; - group->add( *button ); - - group = groups["transport"]; - button = new Button ( 95, 1, "record", *group ); - buttons[0x5f] = button; - controls.push_back( button ); - controls_by_name["record"] = button; - group->add( *button ); - - group = groups["cursor"]; - button = new Button ( 96, 1, "cursor_up", *group ); - buttons[0x60] = button; - controls.push_back( button ); - controls_by_name["cursor_up"] = button; - group->add( *button ); - - group = groups["cursor"]; - button = new Button ( 97, 1, "cursor_down", *group ); - buttons[0x61] = button; - controls.push_back( button ); - controls_by_name["cursor_down"] = button; - group->add( *button ); - - group = groups["cursor"]; - button = new Button ( 98, 1, "cursor_left", *group ); - buttons[0x62] = button; - controls.push_back( button ); - controls_by_name["cursor_left"] = button; - group->add( *button ); - - group = groups["cursor"]; - button = new Button ( 99, 1, "cursor_right", *group ); - buttons[0x63] = button; - controls.push_back( button ); - controls_by_name["cursor_right"] = button; - group->add( *button ); - - group = groups["none"]; - button = new Button ( 100, 1, "zoom", *group ); - buttons[0x64] = button; - controls.push_back( button ); - controls_by_name["zoom"] = button; - group->add( *button ); - - group = groups["none"]; - button = new Button ( 101, 1, "scrub", *group ); - buttons[0x65] = button; - controls.push_back( button ); - controls_by_name["scrub"] = button; - group->add( *button ); - - group = groups["user"]; - button = new Button ( 102, 1, "user_a", *group ); - buttons[0x66] = button; - controls.push_back( button ); - controls_by_name["user_a"] = button; - group->add( *button ); - - group = groups["user"]; - button = new Button ( 103, 1, "user_b", *group ); - buttons[0x67] = button; - controls.push_back( button ); - controls_by_name["user_b"] = button; - group->add( *button ); - - group = groups["strip_1"]; - button = new Button ( 104, 1, "fader_touch", *group ); - buttons[0x68] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_2"]; - button = new Button ( 105, 2, "fader_touch", *group ); - buttons[0x69] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_3"]; - button = new Button ( 106, 3, "fader_touch", *group ); - buttons[0x6a] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_4"]; - button = new Button ( 107, 4, "fader_touch", *group ); - buttons[0x6b] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_5"]; - button = new Button ( 108, 5, "fader_touch", *group ); - buttons[0x6c] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_6"]; - button = new Button ( 109, 6, "fader_touch", *group ); - buttons[0x6d] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_7"]; - button = new Button ( 110, 7, "fader_touch", *group ); - buttons[0x6e] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["strip_8"]; - button = new Button ( 111, 8, "fader_touch", *group ); - buttons[0x6f] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["master"]; - button = new Button ( 112, 1, "fader_touch", *group ); - buttons[0x70] = button; - controls.push_back( button ); - group->add( *button ); - - group = groups["none"]; - led = new Led ( 113, 1, "timecode", *group ); - leds[0x71] = led; - controls.push_back( led ); - controls_by_name["timecode"] = led; - group->add( *led ); - - group = groups["none"]; - led = new Led ( 114, 1, "beats", *group ); - leds[0x72] = led; - controls.push_back( led ); - controls_by_name["beats"] = led; - group->add( *led ); - - group = groups["none"]; - led = new Led ( 115, 1, "solo", *group ); - leds[0x73] = led; - controls.push_back( led ); - controls_by_name["solo"] = led; - group->add( *led ); - - group = groups["none"]; - led = new Led ( 118, 1, "relay_click", *group ); - leds[0x76] = led; - controls.push_back( led ); - controls_by_name["relay_click"] = led; - group->add( *led ); - -} - -void Mackie::MackieSurface::handle_button( MackieButtonHandler & mbh, ButtonState bs, Button & button ) -{ - if ( bs != press && bs != release ) - { - mbh.update_led( button, none ); - return; - } - - LedState ls; - switch ( button.id() ) - { - - case 0x9028: // io - switch ( bs ) { - case press: ls = mbh.io_press( button ); break; - case release: ls = mbh.io_release( button ); break; - case neither: break; - } - break; - - case 0x9029: // sends - switch ( bs ) { - case press: ls = mbh.sends_press( button ); break; - case release: ls = mbh.sends_release( button ); break; - case neither: break; - } - break; - - case 0x902a: // pan - switch ( bs ) { - case press: ls = mbh.pan_press( button ); break; - case release: ls = mbh.pan_release( button ); break; - case neither: break; - } - break; - - case 0x902b: // plugin - switch ( bs ) { - case press: ls = mbh.plugin_press( button ); break; - case release: ls = mbh.plugin_release( button ); break; - case neither: break; - } - break; - - case 0x902c: // eq - switch ( bs ) { - case press: ls = mbh.eq_press( button ); break; - case release: ls = mbh.eq_release( button ); break; - case neither: break; - } - break; - - case 0x902d: // dyn - switch ( bs ) { - case press: ls = mbh.dyn_press( button ); break; - case release: ls = mbh.dyn_release( button ); break; - case neither: break; - } - break; - - case 0x902e: // left - switch ( bs ) { - case press: ls = mbh.left_press( button ); break; - case release: ls = mbh.left_release( button ); break; - case neither: break; - } - break; - - case 0x902f: // right - switch ( bs ) { - case press: ls = mbh.right_press( button ); break; - case release: ls = mbh.right_release( button ); break; - case neither: break; - } - break; - - case 0x9030: // channel_left - switch ( bs ) { - case press: ls = mbh.channel_left_press( button ); break; - case release: ls = mbh.channel_left_release( button ); break; - case neither: break; - } - break; - - case 0x9031: // channel_right - switch ( bs ) { - case press: ls = mbh.channel_right_press( button ); break; - case release: ls = mbh.channel_right_release( button ); break; - case neither: break; - } - break; - - case 0x9032: // flip - switch ( bs ) { - case press: ls = mbh.flip_press( button ); break; - case release: ls = mbh.flip_release( button ); break; - case neither: break; - } - break; - - case 0x9033: // edit - switch ( bs ) { - case press: ls = mbh.edit_press( button ); break; - case release: ls = mbh.edit_release( button ); break; - case neither: break; - } - break; - - case 0x9034: // name_value - switch ( bs ) { - case press: ls = mbh.name_value_press( button ); break; - case release: ls = mbh.name_value_release( button ); break; - case neither: break; - } - break; - - case 0x9035: // timecode_beats - switch ( bs ) { - case press: ls = mbh.timecode_beats_press( button ); break; - case release: ls = mbh.timecode_beats_release( button ); break; - case neither: break; - } - break; - - case 0x9036: // F1 - switch ( bs ) { - case press: ls = mbh.F1_press( button ); break; - case release: ls = mbh.F1_release( button ); break; - case neither: break; - } - break; - - case 0x9037: // F2 - switch ( bs ) { - case press: ls = mbh.F2_press( button ); break; - case release: ls = mbh.F2_release( button ); break; - case neither: break; - } - break; - - case 0x9038: // F3 - switch ( bs ) { - case press: ls = mbh.F3_press( button ); break; - case release: ls = mbh.F3_release( button ); break; - case neither: break; - } - break; - - case 0x9039: // F4 - switch ( bs ) { - case press: ls = mbh.F4_press( button ); break; - case release: ls = mbh.F4_release( button ); break; - case neither: break; - } - break; - - case 0x903a: // F5 - switch ( bs ) { - case press: ls = mbh.F5_press( button ); break; - case release: ls = mbh.F5_release( button ); break; - case neither: break; - } - break; - - case 0x903b: // F6 - switch ( bs ) { - case press: ls = mbh.F6_press( button ); break; - case release: ls = mbh.F6_release( button ); break; - case neither: break; - } - break; - - case 0x903c: // F7 - switch ( bs ) { - case press: ls = mbh.F7_press( button ); break; - case release: ls = mbh.F7_release( button ); break; - case neither: break; - } - break; - - case 0x903d: // F8 - switch ( bs ) { - case press: ls = mbh.F8_press( button ); break; - case release: ls = mbh.F8_release( button ); break; - case neither: break; - } - break; - - case 0x903e: // F9 - switch ( bs ) { - case press: ls = mbh.F9_press( button ); break; - case release: ls = mbh.F9_release( button ); break; - case neither: break; - } - break; - - case 0x903f: // F10 - switch ( bs ) { - case press: ls = mbh.F10_press( button ); break; - case release: ls = mbh.F10_release( button ); break; - case neither: break; - } - break; - - case 0x9040: // F11 - switch ( bs ) { - case press: ls = mbh.F11_press( button ); break; - case release: ls = mbh.F11_release( button ); break; - case neither: break; - } - break; - - case 0x9041: // F12 - switch ( bs ) { - case press: ls = mbh.F12_press( button ); break; - case release: ls = mbh.F12_release( button ); break; - case neither: break; - } - break; - - case 0x9042: // F13 - switch ( bs ) { - case press: ls = mbh.F13_press( button ); break; - case release: ls = mbh.F13_release( button ); break; - case neither: break; - } - break; - - case 0x9043: // F14 - switch ( bs ) { - case press: ls = mbh.F14_press( button ); break; - case release: ls = mbh.F14_release( button ); break; - case neither: break; - } - break; - - case 0x9044: // F15 - switch ( bs ) { - case press: ls = mbh.F15_press( button ); break; - case release: ls = mbh.F15_release( button ); break; - case neither: break; - } - break; - - case 0x9045: // F16 - switch ( bs ) { - case press: ls = mbh.F16_press( button ); break; - case release: ls = mbh.F16_release( button ); break; - case neither: break; - } - break; - - case 0x9046: // shift - switch ( bs ) { - case press: ls = mbh.shift_press( button ); break; - case release: ls = mbh.shift_release( button ); break; - case neither: break; - } - break; - - case 0x9047: // option - switch ( bs ) { - case press: ls = mbh.option_press( button ); break; - case release: ls = mbh.option_release( button ); break; - case neither: break; - } - break; - - case 0x9048: // control - switch ( bs ) { - case press: ls = mbh.control_press( button ); break; - case release: ls = mbh.control_release( button ); break; - case neither: break; - } - break; - - case 0x9049: // cmd_alt - switch ( bs ) { - case press: ls = mbh.cmd_alt_press( button ); break; - case release: ls = mbh.cmd_alt_release( button ); break; - case neither: break; - } - break; - - case 0x904a: // on - switch ( bs ) { - case press: ls = mbh.on_press( button ); break; - case release: ls = mbh.on_release( button ); break; - case neither: break; - } - break; - - case 0x904b: // rec_ready - switch ( bs ) { - case press: ls = mbh.rec_ready_press( button ); break; - case release: ls = mbh.rec_ready_release( button ); break; - case neither: break; - } - break; - - case 0x904c: // undo - switch ( bs ) { - case press: ls = mbh.undo_press( button ); break; - case release: ls = mbh.undo_release( button ); break; - case neither: break; - } - break; - - case 0x904d: // snapshot - switch ( bs ) { - case press: ls = mbh.snapshot_press( button ); break; - case release: ls = mbh.snapshot_release( button ); break; - case neither: break; - } - break; - - case 0x904e: // touch - switch ( bs ) { - case press: ls = mbh.touch_press( button ); break; - case release: ls = mbh.touch_release( button ); break; - case neither: break; - } - break; - - case 0x904f: // redo - switch ( bs ) { - case press: ls = mbh.redo_press( button ); break; - case release: ls = mbh.redo_release( button ); break; - case neither: break; - } - break; - - case 0x9050: // marker - switch ( bs ) { - case press: ls = mbh.marker_press( button ); break; - case release: ls = mbh.marker_release( button ); break; - case neither: break; - } - break; - - case 0x9051: // enter - switch ( bs ) { - case press: ls = mbh.enter_press( button ); break; - case release: ls = mbh.enter_release( button ); break; - case neither: break; - } - break; - - case 0x9052: // cancel - switch ( bs ) { - case press: ls = mbh.cancel_press( button ); break; - case release: ls = mbh.cancel_release( button ); break; - case neither: break; - } - break; - - case 0x9053: // mixer - switch ( bs ) { - case press: ls = mbh.mixer_press( button ); break; - case release: ls = mbh.mixer_release( button ); break; - case neither: break; - } - break; - - case 0x9054: // frm_left - switch ( bs ) { - case press: ls = mbh.frm_left_press( button ); break; - case release: ls = mbh.frm_left_release( button ); break; - case neither: break; - } - break; - - case 0x9055: // frm_right - switch ( bs ) { - case press: ls = mbh.frm_right_press( button ); break; - case release: ls = mbh.frm_right_release( button ); break; - case neither: break; - } - break; - - case 0x9056: // loop - switch ( bs ) { - case press: ls = mbh.loop_press( button ); break; - case release: ls = mbh.loop_release( button ); break; - case neither: break; - } - break; - - case 0x9057: // punch_in - switch ( bs ) { - case press: ls = mbh.punch_in_press( button ); break; - case release: ls = mbh.punch_in_release( button ); break; - case neither: break; - } - break; - - case 0x9058: // punch_out - switch ( bs ) { - case press: ls = mbh.punch_out_press( button ); break; - case release: ls = mbh.punch_out_release( button ); break; - case neither: break; - } - break; - - case 0x9059: // home - switch ( bs ) { - case press: ls = mbh.home_press( button ); break; - case release: ls = mbh.home_release( button ); break; - case neither: break; - } - break; - - case 0x905a: // end - switch ( bs ) { - case press: ls = mbh.end_press( button ); break; - case release: ls = mbh.end_release( button ); break; - case neither: break; - } - break; - - case 0x905b: // rewind - switch ( bs ) { - case press: ls = mbh.rewind_press( button ); break; - case release: ls = mbh.rewind_release( button ); break; - case neither: break; - } - break; - - case 0x905c: // ffwd - switch ( bs ) { - case press: ls = mbh.ffwd_press( button ); break; - case release: ls = mbh.ffwd_release( button ); break; - case neither: break; - } - break; - - case 0x905d: // stop - switch ( bs ) { - case press: ls = mbh.stop_press( button ); break; - case release: ls = mbh.stop_release( button ); break; - case neither: break; - } - break; - - case 0x905e: // play - switch ( bs ) { - case press: ls = mbh.play_press( button ); break; - case release: ls = mbh.play_release( button ); break; - case neither: break; - } - break; - - case 0x905f: // record - switch ( bs ) { - case press: ls = mbh.record_press( button ); break; - case release: ls = mbh.record_release( button ); break; - case neither: break; - } - break; - - case 0x9060: // cursor_up - switch ( bs ) { - case press: ls = mbh.cursor_up_press( button ); break; - case release: ls = mbh.cursor_up_release( button ); break; - case neither: break; - } - break; - - case 0x9061: // cursor_down - switch ( bs ) { - case press: ls = mbh.cursor_down_press( button ); break; - case release: ls = mbh.cursor_down_release( button ); break; - case neither: break; - } - break; - - case 0x9062: // cursor_left - switch ( bs ) { - case press: ls = mbh.cursor_left_press( button ); break; - case release: ls = mbh.cursor_left_release( button ); break; - case neither: break; - } - break; - - case 0x9063: // cursor_right - switch ( bs ) { - case press: ls = mbh.cursor_right_press( button ); break; - case release: ls = mbh.cursor_right_release( button ); break; - case neither: break; - } - break; - - case 0x9064: // zoom - switch ( bs ) { - case press: ls = mbh.zoom_press( button ); break; - case release: ls = mbh.zoom_release( button ); break; - case neither: break; - } - break; - - case 0x9065: // scrub - switch ( bs ) { - case press: ls = mbh.scrub_press( button ); break; - case release: ls = mbh.scrub_release( button ); break; - case neither: break; - } - break; - - case 0x9066: // user_a - switch ( bs ) { - case press: ls = mbh.user_a_press( button ); break; - case release: ls = mbh.user_a_release( button ); break; - case neither: break; - } - break; - - case 0x9067: // user_b - switch ( bs ) { - case press: ls = mbh.user_b_press( button ); break; - case release: ls = mbh.user_b_release( button ); break; - case neither: break; - } - break; - - } - mbh.update_led( button, ls ); -} diff --git a/libs/surfaces/mackie/mcp_buttons.cc b/libs/surfaces/mackie/mcp_buttons.cc new file mode 100644 index 0000000000..6ded9a9d20 --- /dev/null +++ b/libs/surfaces/mackie/mcp_buttons.cc @@ -0,0 +1,1224 @@ +/* + Copyright (C) 2006,2007 John Anderson + Copyright (C) 2012 Paul Davis + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include + +#include "pbd/memento_command.h" + +#include "ardour/debug.h" +#include "ardour/session.h" +#include "ardour/route.h" +#include "ardour/location.h" +#include "ardour/rc_configuration.h" + +#include "mackie_control_protocol.h" +#include "surface.h" + +#include "i18n.h" + +/* handlers for all buttons, broken into a separate file to avoid clutter in + * mackie_control_protocol.cc + */ + +using namespace Mackie; +using namespace ARDOUR; +using namespace PBD; +using std::string; + +LedState +MackieControlProtocol::shift_press (Button &) +{ + _modifier_state |= MODIFIER_SHIFT; + return on; +} +LedState +MackieControlProtocol::shift_release (Button &) +{ + _modifier_state &= ~MODIFIER_SHIFT; + return on; +} +LedState +MackieControlProtocol::option_press (Button &) +{ + _modifier_state |= MODIFIER_OPTION; + return on; +} +LedState +MackieControlProtocol::option_release (Button &) +{ + _modifier_state &= ~MODIFIER_OPTION; + return on; +} +LedState +MackieControlProtocol::control_press (Button &) +{ + _modifier_state |= MODIFIER_CONTROL; + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("CONTROL Press: modifier state now set to %1\n", _modifier_state)); + return on; +} +LedState +MackieControlProtocol::control_release (Button &) +{ + _modifier_state &= ~MODIFIER_CONTROL; + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("CONTROL Release: modifier state now set to %1\n", _modifier_state)); + return on; +} +LedState +MackieControlProtocol::cmd_alt_press (Button &) +{ + _modifier_state |= MODIFIER_CMDALT; + return on; +} +LedState +MackieControlProtocol::cmd_alt_release (Button &) +{ + _modifier_state &= ~MODIFIER_CMDALT; + return on; +} + +LedState +MackieControlProtocol::left_press (Button &) +{ + Sorted sorted = get_sorted_routes(); + uint32_t strip_cnt = n_strips (); + uint32_t route_cnt = sorted.size(); + + 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; + } + return off; +} + +LedState +MackieControlProtocol::left_release (Button &) +{ + return off; +} + +LedState +MackieControlProtocol::right_press (Button &) +{ + Sorted sorted = get_sorted_routes(); + uint32_t strip_cnt = n_strips(); + 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())); + + 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; + } + + return off; +} + +LedState +MackieControlProtocol::right_release (Button &) +{ + if (_zoom_mode) { + + } + + return off; +} + +LedState +MackieControlProtocol::cursor_left_press (Button& ) +{ + if (_zoom_mode) { + + if (_modifier_state & MODIFIER_OPTION) { + /* reset selected tracks to default vertical zoom */ + } else { + ZoomOut (); /* EMIT SIGNAL */ + } + } else { + float page_fraction; + if (_modifier_state == MODIFIER_CONTROL) { + page_fraction = 1.0; + } else if (_modifier_state == MODIFIER_OPTION) { + page_fraction = 0.1; + } else if (_modifier_state == MODIFIER_SHIFT) { + page_fraction = 2.0; + } else { + page_fraction = 0.25; + } + + ScrollTimeline (-page_fraction); + } + + return off; +} + +LedState +MackieControlProtocol::cursor_left_release (Button&) +{ + return off; +} + +LedState +MackieControlProtocol::cursor_right_press (Button& ) +{ + if (_zoom_mode) { + + if (_modifier_state & MODIFIER_OPTION) { + /* reset selected tracks to default vertical zoom */ + } else { + ZoomIn (); /* EMIT SIGNAL */ + } + } else { + float page_fraction; + if (_modifier_state == MODIFIER_CONTROL) { + page_fraction = 1.0; + } else if (_modifier_state == MODIFIER_OPTION) { + page_fraction = 0.1; + } else if (_modifier_state == MODIFIER_SHIFT) { + page_fraction = 2.0; + } else { + page_fraction = 0.25; + } + + ScrollTimeline (page_fraction); + } + + return off; +} + +LedState +MackieControlProtocol::cursor_right_release (Button&) +{ + return off; +} + +LedState +MackieControlProtocol::cursor_up_press (Button&) +{ + if (_zoom_mode) { + + if (_modifier_state & MODIFIER_CONTROL) { + VerticalZoomInSelected (); /* EMIT SIGNAL */ + } else { + VerticalZoomInAll (); /* EMIT SIGNAL */ + } + } else { + StepTracksUp (); /* EMIT SIGNAL */ + } + return off; +} + +LedState +MackieControlProtocol::cursor_up_release (Button&) +{ + return off; +} + +LedState +MackieControlProtocol::cursor_down_press (Button&) +{ + if (_zoom_mode) { + if (_modifier_state & MODIFIER_OPTION) { + VerticalZoomOutSelected (); /* EMIT SIGNAL */ + } else { + VerticalZoomOutAll (); /* EMIT SIGNAL */ + } + } else { + StepTracksDown (); /* EMIT SIGNAL */ + } + return off; +} + +LedState +MackieControlProtocol::cursor_down_release (Button&) +{ + return off; +} + +LedState +MackieControlProtocol::channel_left_press (Button &) +{ + Sorted sorted = get_sorted_routes(); + if (sorted.size() > n_strips()) { + prev_track(); + return on; + } else { + return flashing; + } +} + +LedState +MackieControlProtocol::channel_left_release (Button &) +{ + return off; +} + +LedState +MackieControlProtocol::channel_right_press (Button &) +{ + Sorted sorted = get_sorted_routes(); + if (sorted.size() > n_strips()) { + next_track(); + return on; + } else { + return flashing; + } +} + +LedState +MackieControlProtocol::channel_right_release (Button &) +{ + return off; +} + +Mackie::LedState +MackieControlProtocol::zoom_press (Mackie::Button &) +{ + _zoom_mode = !_zoom_mode; + return (_zoom_mode ? on : off); +} + +Mackie::LedState +MackieControlProtocol::zoom_release (Mackie::Button &) +{ + return (_zoom_mode ? on : off); +} + +Mackie::LedState +MackieControlProtocol::scrub_press (Mackie::Button &) +{ + if (!surfaces.empty()) { + surfaces.front()->next_jog_mode (); + } + return none; +} + +Mackie::LedState +MackieControlProtocol::scrub_release (Mackie::Button &) +{ + return none; +} + +LedState +MackieControlProtocol::undo_press (Button&) +{ + if (_modifier_state & MODIFIER_SHIFT) { + Redo(); /* EMIT SIGNAL */ + } else { + Undo(); /* EMIT SIGNAL */ + } + return off; +} + +LedState +MackieControlProtocol::undo_release (Button&) +{ + return off; +} + +LedState +MackieControlProtocol::redo_press (Button&) +{ + Redo(); /* EMIT SIGNAL */ + return off; +} + +LedState +MackieControlProtocol::redo_release (Button&) +{ + return off; +} + +LedState +MackieControlProtocol::drop_press (Button &) +{ + session->remove_last_capture(); + return on; +} + +LedState +MackieControlProtocol::drop_release (Button &) +{ + return off; +} + +LedState +MackieControlProtocol::save_press (Button &) +{ + session->save_state (""); + return on; +} + +LedState +MackieControlProtocol::save_release (Button &) +{ + return off; +} + +LedState +MackieControlProtocol::timecode_beats_press (Button &) +{ + switch (_timecode_type) { + case ARDOUR::AnyTime::BBT: + _timecode_type = ARDOUR::AnyTime::Timecode; + break; + case ARDOUR::AnyTime::Timecode: + _timecode_type = ARDOUR::AnyTime::BBT; + break; + default: + return off; + } + + update_timecode_beats_led(); + + return on; +} + +LedState +MackieControlProtocol::timecode_beats_release (Button &) +{ + return off; +} + +///////////////////////////////////// +// Functions +///////////////////////////////////// +LedState +MackieControlProtocol::marker_press (Button &) +{ + string markername; + + session->locations()->next_available_name (markername,"mcu"); + add_marker (markername); + + return on; +} + +LedState +MackieControlProtocol::marker_release (Button &) +{ + return off; +} + +///////////////////////////////////// +// Transport Buttons +///////////////////////////////////// + +LedState +MackieControlProtocol::frm_left_press (Button &) +{ + // can use first_mark_before/after as well + unsigned long elapsed = _frm_left_last.restart(); + + Location * loc = session->locations()->first_location_before (session->transport_frame()); + + // allow a quick double to go past a previous mark + if (session->transport_rolling() && elapsed < 500 && loc != 0) { + Location * loc_two_back = session->locations()->first_location_before (loc->start()); + if (loc_two_back != 0) + { + loc = loc_two_back; + } + } + + // move to the location, if it's valid + if (loc != 0) { + session->request_locate (loc->start(), session->transport_rolling()); + } else { + session->request_locate (session->locations()->session_range_location()->start(), session->transport_rolling()); + } + + return on; +} + +LedState +MackieControlProtocol::frm_left_release (Button &) +{ + return off; +} + +LedState +MackieControlProtocol::frm_right_press (Button &) +{ + // can use first_mark_before/after as well + Location * loc = session->locations()->first_location_after (session->transport_frame()); + + if (loc != 0) { + session->request_locate (loc->start(), session->transport_rolling()); + } else { + session->request_locate (session->locations()->session_range_location()->end(), session->transport_rolling()); + } + + return on; +} + +LedState +MackieControlProtocol::frm_right_release (Button &) +{ + return off; +} + +LedState +MackieControlProtocol::stop_press (Button &) +{ + transport_stop (); + return on; +} + +LedState +MackieControlProtocol::stop_release (Button &) +{ + return session->transport_stopped(); +} + +LedState +MackieControlProtocol::play_press (Button &) +{ + /* if we're already rolling at normal speed, and we're pressed + again, jump back to where we started last time + */ + + transport_play (session->transport_rolling() == 1.0); + return none; +} + +LedState +MackieControlProtocol::play_release (Button &) +{ + return none; +} + +LedState +MackieControlProtocol::record_press (Button &) +{ + rec_enable_toggle (); + return none; +} + +LedState +MackieControlProtocol::record_release (Button &) +{ + return none; +} + +LedState +MackieControlProtocol::rewind_press (Button &) +{ + if (_modifier_state == MODIFIER_CONTROL) { + goto_start (); + } else { + rewind (); + } + return none; +} + +LedState +MackieControlProtocol::rewind_release (Button &) +{ + return none; +} + +LedState +MackieControlProtocol::ffwd_press (Button &) +{ + if (_modifier_state == MODIFIER_CONTROL) { + goto_end(); + } else { + ffwd (); + } + return none; +} + +LedState +MackieControlProtocol::ffwd_release (Button &) +{ + return none; +} + +LedState +MackieControlProtocol::loop_press (Button &) +{ + if (_modifier_state & MODIFIER_CONTROL) { + set_view_mode (Loop); + return on; + } else { + session->request_play_loop (!session->get_play_loop()); + return none; + } +} + +LedState +MackieControlProtocol::loop_release (Button &) +{ + return none; +} + +LedState +MackieControlProtocol::punch_in_press (Button &) +{ + bool const state = !session->config.get_punch_in(); + session->config.set_punch_in (state); + return state; +} + +LedState +MackieControlProtocol::punch_in_release (Button &) +{ + return session->config.get_punch_in(); +} + +LedState +MackieControlProtocol::punch_out_press (Button &) +{ + bool const state = !session->config.get_punch_out(); + session->config.set_punch_out (state); + return state; +} + +LedState +MackieControlProtocol::punch_out_release (Button &) +{ + return session->config.get_punch_out(); +} + +LedState +MackieControlProtocol::home_press (Button &) +{ + session->goto_start(); + return on; +} + +LedState +MackieControlProtocol::home_release (Button &) +{ + return off; +} + +LedState +MackieControlProtocol::end_press (Button &) +{ + session->goto_end(); + return on; +} + +LedState +MackieControlProtocol::end_release (Button &) +{ + return off; +} + +LedState +MackieControlProtocol::clicking_press (Button &) +{ + bool state = !Config->get_clicking(); + Config->set_clicking (state); + return state; +} + +LedState +MackieControlProtocol::clicking_release (Button &) +{ + return Config->get_clicking(); +} + +LedState MackieControlProtocol::global_solo_press (Button &) +{ + bool state = !session->soloing(); + session->set_solo (session->get_routes(), state); + return state; +} + +LedState MackieControlProtocol::global_solo_release (Button &) +{ + return session->soloing(); +} + +LedState +MackieControlProtocol::enter_press (Button &) +{ + Enter(); /* EMIT SIGNAL */ + return off; +} + +LedState +MackieControlProtocol::enter_release (Button &) +{ + return off; +} + +LedState +MackieControlProtocol::F1_press (Button &) +{ + return off; +} +LedState +MackieControlProtocol::F1_release (Button &) +{ + return off; +} +LedState +MackieControlProtocol::F2_press (Button &) +{ + return off; +} +LedState +MackieControlProtocol::F2_release (Button &) +{ + return off; +} +LedState +MackieControlProtocol::F3_press (Button &) +{ + return off; +} +LedState +MackieControlProtocol::F3_release (Button &) +{ + return off; +} +LedState +MackieControlProtocol::F4_press (Button &) +{ + return off; +} +LedState +MackieControlProtocol::F4_release (Button &) +{ + return off; +} +LedState +MackieControlProtocol::F5_press (Button &) +{ + return off; +} +LedState +MackieControlProtocol::F5_release (Button &) +{ + return off; +} +LedState +MackieControlProtocol::F6_press (Button &) +{ + return off; +} +LedState +MackieControlProtocol::F6_release (Button &) +{ + return off; +} +LedState +MackieControlProtocol::F7_press (Button &) +{ + return off; +} +LedState +MackieControlProtocol::F7_release (Button &) +{ + return off; +} +LedState +MackieControlProtocol::F8_press (Button &) +{ + CloseDialog (); /* EMIT SIGNAL */ + return off; +} +LedState +MackieControlProtocol::F8_release (Button &) +{ + return off; +} + +/* UNIMPLEMENTED */ + +LedState +MackieControlProtocol::io_press (Button &) +{ + return off; +} +LedState +MackieControlProtocol::io_release (Button &) +{ + return off; +} +LedState +MackieControlProtocol::sends_press (Button &) +{ + set_view_mode (Sends); + return on; +} +LedState +MackieControlProtocol::sends_release (Button &) +{ + return none; +} +LedState +MackieControlProtocol::pan_press (Button &) +{ + return off; +} +LedState +MackieControlProtocol::pan_release (Button &) +{ + return off; +} +LedState +MackieControlProtocol::plugin_press (Button &) +{ + return off; +} +LedState +MackieControlProtocol::plugin_release (Button &) +{ + return off; +} +LedState +MackieControlProtocol::eq_press (Button &) +{ + set_view_mode (EQ); + return on; +} +LedState +MackieControlProtocol::eq_release (Button &) +{ + return none; +} +LedState +MackieControlProtocol::dyn_press (Button &) +{ + set_view_mode (Dynamics); + return on; +} +LedState +MackieControlProtocol::dyn_release (Button &) +{ + return none; +} +LedState +MackieControlProtocol::flip_press (Button &) +{ + set_flip_mode (!_flip_mode); + return (_flip_mode ? on : off); +} +LedState +MackieControlProtocol::flip_release (Button &) +{ + return none; +} +LedState +MackieControlProtocol::edit_press (Button &) +{ + return off; +} +LedState +MackieControlProtocol::edit_release (Button &) +{ + return off; +} +LedState +MackieControlProtocol::name_value_press (Button &) +{ + return off; +} +LedState +MackieControlProtocol::name_value_release (Button &) +{ + return off; +} +LedState +MackieControlProtocol::F9_press (Button &) +{ + return off; +} +LedState +MackieControlProtocol::F9_release (Button &) +{ + return off; +} +LedState +MackieControlProtocol::F10_press (Button &) +{ + return off; +} +LedState +MackieControlProtocol::F10_release (Button &) +{ + return off; +} +LedState +MackieControlProtocol::F11_press (Button &) +{ + return off; +} +LedState +MackieControlProtocol::F11_release (Button &) +{ + return off; +} +LedState +MackieControlProtocol::F12_press (Button &) +{ + return off; +} +LedState +MackieControlProtocol::F12_release (Button &) +{ + return off; +} +LedState +MackieControlProtocol::F13_press (Button &) +{ + return off; +} +LedState +MackieControlProtocol::F13_release (Button &) +{ + return off; +} +LedState +MackieControlProtocol::F14_press (Button &) +{ + return off; +} +LedState +MackieControlProtocol::F14_release (Button &) +{ + return off; +} +LedState +MackieControlProtocol::F15_press (Button &) +{ + return off; +} +LedState +MackieControlProtocol::F15_release (Button &) +{ + return off; +} +LedState +MackieControlProtocol::F16_press (Button &) +{ + return off; +} +LedState +MackieControlProtocol::F16_release (Button &) +{ + return off; +} +LedState +MackieControlProtocol::on_press (Button &) +{ + return off; +} +LedState +MackieControlProtocol::on_release (Button &) +{ + return off; +} +LedState +MackieControlProtocol::rec_ready_press (Button &) +{ + return off; +} +LedState +MackieControlProtocol::rec_ready_release (Button &) +{ + return off; +} +LedState +MackieControlProtocol::touch_press (Button &) +{ + return off; +} +LedState +MackieControlProtocol::touch_release (Button &) +{ + return off; +} +LedState +MackieControlProtocol::cancel_press (Button &) +{ + return off; +} +LedState +MackieControlProtocol::cancel_release (Button &) +{ + return off; +} +LedState +MackieControlProtocol::mixer_press (Button &) +{ + return off; +} +LedState +MackieControlProtocol::mixer_release (Button &) +{ + return off; +} +LedState +MackieControlProtocol::user_a_press (Button &) +{ + transport_play (session->transport_speed() == 1.0); + return off; +} +LedState +MackieControlProtocol::user_a_release (Button &) +{ + return off; +} +LedState +MackieControlProtocol::user_b_press (Button &) +{ + transport_stop(); + return off; +} +LedState +MackieControlProtocol::user_b_release (Button &) +{ + return off; +} + +Mackie::LedState +MackieControlProtocol::snapshot_press (Mackie::Button&) +{ + return none; +} +Mackie::LedState +MackieControlProtocol::snapshot_release (Mackie::Button&) +{ + return none; +} +Mackie::LedState +MackieControlProtocol::read_press (Mackie::Button&) +{ + return none; +} +Mackie::LedState +MackieControlProtocol::read_release (Mackie::Button&) +{ + return none; +} +Mackie::LedState +MackieControlProtocol::write_press (Mackie::Button&) +{ + return none; +} +Mackie::LedState +MackieControlProtocol::write_release (Mackie::Button&) +{ + return none; +} +Mackie::LedState +MackieControlProtocol::fdrgroup_press (Mackie::Button&) +{ + return none; +} +Mackie::LedState +MackieControlProtocol::fdrgroup_release (Mackie::Button&) +{ + return none; +} +Mackie::LedState +MackieControlProtocol::clearsolo_press (Mackie::Button&) +{ + return none; +} +Mackie::LedState +MackieControlProtocol::clearsolo_release (Mackie::Button&) +{ + return none; +} +Mackie::LedState +MackieControlProtocol::track_press (Mackie::Button&) +{ + return none; +} +Mackie::LedState +MackieControlProtocol::track_release (Mackie::Button&) +{ + return none; +} +Mackie::LedState +MackieControlProtocol::send_press (Mackie::Button&) +{ + return none; +} +Mackie::LedState +MackieControlProtocol::send_release (Mackie::Button&) +{ + return none; +} +Mackie::LedState +MackieControlProtocol::miditracks_press (Mackie::Button&) +{ + return none; +} +Mackie::LedState +MackieControlProtocol::miditracks_release (Mackie::Button&) +{ + return none; +} +Mackie::LedState +MackieControlProtocol::inputs_press (Mackie::Button&) +{ + return none; +} +Mackie::LedState +MackieControlProtocol::inputs_release (Mackie::Button&) +{ + return none; +} +Mackie::LedState +MackieControlProtocol::audiotracks_press (Mackie::Button&) +{ + return none; +} +Mackie::LedState +MackieControlProtocol::audiotracks_release (Mackie::Button&) +{ + return none; +} +Mackie::LedState +MackieControlProtocol::audioinstruments_press (Mackie::Button&) +{ + return none; +} +Mackie::LedState +MackieControlProtocol::audioinstruments_release (Mackie::Button&) +{ + return none; +} +Mackie::LedState +MackieControlProtocol::aux_press (Mackie::Button&) +{ + return none; +} +Mackie::LedState +MackieControlProtocol::aux_release (Mackie::Button&) +{ + return none; +} +Mackie::LedState +MackieControlProtocol::busses_press (Mackie::Button&) +{ + return none; +} +Mackie::LedState +MackieControlProtocol::busses_release (Mackie::Button&) +{ + return none; +} +Mackie::LedState +MackieControlProtocol::outputs_press (Mackie::Button&) +{ + return none; +} +Mackie::LedState +MackieControlProtocol::outputs_release (Mackie::Button&) +{ + return none; +} +Mackie::LedState +MackieControlProtocol::user_press (Mackie::Button&) +{ + return none; +} +Mackie::LedState +MackieControlProtocol::user_release (Mackie::Button&) +{ + return none; +} +Mackie::LedState +MackieControlProtocol::trim_press (Mackie::Button&) +{ + return none; +} +Mackie::LedState +MackieControlProtocol::trim_release (Mackie::Button&) +{ + return none; +} +Mackie::LedState +MackieControlProtocol::latch_press (Mackie::Button&) +{ + return none; +} +Mackie::LedState +MackieControlProtocol::latch_release (Mackie::Button&) +{ + return none; +} +Mackie::LedState +MackieControlProtocol::grp_press (Mackie::Button&) +{ + return none; +} +Mackie::LedState +MackieControlProtocol::grp_release (Mackie::Button&) +{ + return none; +} +Mackie::LedState +MackieControlProtocol::nudge_press (Mackie::Button&) +{ + return none; +} +Mackie::LedState +MackieControlProtocol::nudge_release (Mackie::Button&) +{ + return none; +} +Mackie::LedState +MackieControlProtocol::replace_press (Mackie::Button&) +{ + return none; +} +Mackie::LedState +MackieControlProtocol::replace_release (Mackie::Button&) +{ + return none; +} +Mackie::LedState +MackieControlProtocol::click_press (Mackie::Button&) +{ + return none; +} +Mackie::LedState +MackieControlProtocol::click_release (Mackie::Button&) +{ + return none; +} +Mackie::LedState +MackieControlProtocol::view_press (Mackie::Button&) +{ + return none; +} +Mackie::LedState +MackieControlProtocol::view_release (Mackie::Button&) +{ + return none; +} diff --git a/libs/surfaces/mackie/meter.cc b/libs/surfaces/mackie/meter.cc new file mode 100644 index 0000000000..a4824d3612 --- /dev/null +++ b/libs/surfaces/mackie/meter.cc @@ -0,0 +1,98 @@ +/* + Copyright (C) 2006,2007 John Anderson + Copyright (C) 2012 Paul Davis + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include + +#include "pbd/compose.h" +#include "ardour/debug.h" + +#include "meter.h" +#include "surface.h" +#include "surface_port.h" +#include "control_group.h" + +using namespace Mackie; +using namespace PBD; + +Control* +Meter::factory (Surface& surface, int id, const char* name, Group& group) +{ + Meter* m = new Meter (id, name, group); + surface.meters[id] = m; + surface.controls.push_back (m); + group.add (*m); + return m; +} + +MidiByteArray +Meter::update_message (float dB) +{ + float def = 0.0f; /* Meter deflection %age */ + + // DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Meter ID %1 dB %2\n", id(), dB)); + + if (dB < -70.0f) { + def = 0.0f; + } else if (dB < -60.0f) { + def = (dB + 70.0f) * 0.25f; + } else if (dB < -50.0f) { + def = (dB + 60.0f) * 0.5f + 2.5f; + } else if (dB < -40.0f) { + def = (dB + 50.0f) * 0.75f + 7.5f; + } else if (dB < -30.0f) { + def = (dB + 40.0f) * 1.5f + 15.0f; + } else if (dB < -20.0f) { + def = (dB + 30.0f) * 2.0f + 30.0f; + } else if (dB < 6.0f) { + def = (dB + 20.0f) * 2.5f + 50.0f; + } else { + def = 115.0f; + } + + /* 115 is the deflection %age that would be + when dB=6.0. this is an arbitrary + endpoint for our scaling. + */ + + MidiByteArray msg; + + if (def > 100.0f) { + if (!overload_on) { + overload_on = true; + msg << MidiByteArray (2, 0xd0, (id() << 4) | 0xe); + } + } else { + if (overload_on) { + overload_on = false; + msg << MidiByteArray (2, 0xd0, (id() << 4) | 0xf); + } + } + + /* we can use up to 13 segments */ + + int segment = lrintf ((def/115.0) * 13.0); + + if (last_segment_value_sent != segment) { + last_segment_value_sent = segment; + msg << MidiByteArray (2, 0xD0, (id()<<4) | segment); + } + + return msg; +} + diff --git a/libs/surfaces/mackie/meter.h b/libs/surfaces/mackie/meter.h new file mode 100644 index 0000000000..1898bf8199 --- /dev/null +++ b/libs/surfaces/mackie/meter.h @@ -0,0 +1,52 @@ +/* + Copyright (C) 2006,2007 John Anderson + Copyright (C) 2012 Paul Davis + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __ardour_mackie_control_protocol_meter_h__ +#define __ardour_mackie_control_protocol_meter_h__ + +#include "controls.h" +#include "midi_byte_array.h" + +namespace Mackie { + +class SurfacePort; + +class Meter : public Control +{ +public: + Meter (int id, std::string name, Group & group) + : Control (id, name, group) + , last_segment_value_sent (-1) + , overload_on (false) {} + + MidiByteArray update_message (float dB); + + MidiByteArray zero() { return update_message (-99999999.0); } + + static Control* factory (Surface&, int id, const char*, Group&); + + int last_segment_value_sent; + + private: + bool overload_on; +}; + +} + +#endif /* __ardour_mackie_control_protocol_meter_h__ */ diff --git a/libs/surfaces/mackie/pot.cc b/libs/surfaces/mackie/pot.cc new file mode 100644 index 0000000000..838753bf31 --- /dev/null +++ b/libs/surfaces/mackie/pot.cc @@ -0,0 +1,66 @@ +/* + Copyright (C) 2006,2007 John Anderson + Copyright (C) 2012 Paul Davis + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#include + +#include "pot.h" +#include "surface.h" +#include "control_group.h" + +using namespace Mackie; + +int const Pot::External = 0x2e; /* specific ID for "vpot" representing external control */ +int const Pot::ID = 0x10; /* base value for v-pot IDs */ + +Control* +Pot::factory (Surface& surface, int id, const char* name, Group& group) +{ + Pot* p = new Pot (id, name, group); + surface.pots[id] = p; + surface.controls.push_back (p); + group.add (*p); + return p; +} + +MidiByteArray +Pot::set (float val, bool onoff, Mode mode) +{ + // TODO do an exact calc for 0.50? To allow manually re-centering the port. + + // center on if val is "very close" to 0.50 + MIDI::byte msg = (val > 0.48 && val < 0.58 ? 1 : 0) << 6; + + // Pot/LED mode + msg |= (mode << 4); + + // val, but only if off hasn't explicitly been set + if (onoff) { + if (mode == spread) { + msg |= (lrintf (val * 6) + 1) & 0x0f; // 0b00001111 + } else { + msg |= (lrintf (val * 10.0) + 1) & 0x0f; // 0b00001111 + } + } + + /* outbound LED message requires 0x20 to be added to the LED's id + */ + + return MidiByteArray (3, 0xb0, 0x20 + id(), msg); + +} + diff --git a/libs/surfaces/mackie/pot.h b/libs/surfaces/mackie/pot.h new file mode 100644 index 0000000000..3bbf0f33f4 --- /dev/null +++ b/libs/surfaces/mackie/pot.h @@ -0,0 +1,52 @@ +/* + Copyright (C) 2006,2007 John Anderson + Copyright (C) 2012 Paul Davis + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __ardour_mackie_control_protocol_pot_h__ +#define __ardour_mackie_control_protocol_pot_h__ + +#include "controls.h" + +namespace Mackie { + +class Pot : public Control +{ +public: + static int const External; + static int const ID; + + enum Mode { + dot = 0, + boost_cut = 1, + wrap = 2, + spread = 3, + }; + + Pot (int id, std::string name, Group & group) + : Control (id, name, group) {} + + MidiByteArray set (float, bool, Mode); + MidiByteArray zero() { return set (0.0, false, Pot::spread); } + + static Control* factory (Surface&, int id, const char*, Group&); + +}; + +} + +#endif /* __ardour_mackie_control_protocol_pot_h__ */ diff --git a/libs/surfaces/mackie/route_signal.cc b/libs/surfaces/mackie/route_signal.cc deleted file mode 100644 index 4446420cd4..0000000000 --- a/libs/surfaces/mackie/route_signal.cc +++ /dev/null @@ -1,103 +0,0 @@ -/* - Copyright (C) 2006,2007 John Anderson - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ -#include "route_signal.h" - -#include "ardour/route.h" -#include "ardour/track.h" -#include "ardour/midi_ui.h" -#include "ardour/pannable.h" -#include "ardour/session_object.h" // for Properties::name - -#include "mackie_control_protocol.h" - -#include - -using namespace ARDOUR; -using namespace Mackie; -using namespace std; - -#define midi_ui_context() MidiControlUI::instance() /* a UICallback-derived object that specifies the event loop for signal handling */ -#define ui_bind(f, ...) boost::protect (boost::bind (f, __VA_ARGS__)) - -void RouteSignal::connect() -{ - if (_strip.has_solo()) { - _route->solo_control()->Changed.connect(connections, MISSING_INVALIDATOR, ui_bind (&MackieControlProtocol::notify_solo_changed, &_mcp, this), midi_ui_context()); - } - - if (_strip.has_mute()) { - _route->mute_control()->Changed.connect(connections, MISSING_INVALIDATOR, ui_bind (&MackieControlProtocol::notify_mute_changed, &_mcp, this), midi_ui_context()); - } - - if (_strip.has_gain()) { - _route->gain_control()->Changed.connect(connections, MISSING_INVALIDATOR, ui_bind (&MackieControlProtocol::notify_gain_changed, &_mcp, this, false), midi_ui_context()); - } - - _route->PropertyChanged.connect (connections, MISSING_INVALIDATOR, ui_bind (&MackieControlProtocol::notify_property_changed, &_mcp, _1, this), midi_ui_context()); - - if (_route->pannable()) { - _route->pannable()->pan_azimuth_control->Changed.connect(connections, MISSING_INVALIDATOR, ui_bind (&MackieControlProtocol::notify_panner_changed, &_mcp, this, false), midi_ui_context()); - _route->pannable()->pan_width_control->Changed.connect(connections, MISSING_INVALIDATOR, ui_bind (&MackieControlProtocol::notify_panner_changed, &_mcp, this, false), midi_ui_context()); - } - - boost::shared_ptr trk = boost::dynamic_pointer_cast(_route); - if (trk) { - trk->rec_enable_control()->Changed .connect(connections, MISSING_INVALIDATOR, ui_bind (&MackieControlProtocol::notify_record_enable_changed, &_mcp, this), midi_ui_context()); - } - - // TODO this works when a currently-banked route is made inactive, but not - // when a route is activated which should be currently banked. - _route->active_changed.connect (connections, MISSING_INVALIDATOR, ui_bind (&MackieControlProtocol::notify_active_changed, &_mcp, this), midi_ui_context()); - - _route->DropReferences.connect (connections, MISSING_INVALIDATOR, ui_bind (&MackieControlProtocol::refresh_current_bank, &_mcp), midi_ui_context()); - - // TODO - // SelectedChanged - // RemoteControlIDChanged. Better handled at Session level. -} - -void RouteSignal::disconnect() -{ - connections.drop_connections (); -} - -void RouteSignal::notify_all() -{ -#ifdef DEBUG - cout << "RouteSignal::notify_all for " << _strip << endl; -#endif - if ( _strip.has_solo() ) - _mcp.notify_solo_changed( this ); - - if ( _strip.has_mute() ) - _mcp.notify_mute_changed( this ); - - if ( _strip.has_gain() ) - _mcp.notify_gain_changed( this ); - - _mcp.notify_property_changed (PBD::PropertyChange (ARDOUR::Properties::name), this ); - - if ( _strip.has_vpot() ) - _mcp.notify_panner_changed( this ); - - if ( _strip.has_recenable() ) - _mcp.notify_record_enable_changed( this ); -#ifdef DEBUG - cout << "RouteSignal::notify_all finish" << endl; -#endif -} diff --git a/libs/surfaces/mackie/route_signal.h b/libs/surfaces/mackie/route_signal.h deleted file mode 100644 index 59bfc66e7b..0000000000 --- a/libs/surfaces/mackie/route_signal.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - Copyright (C) 2006,2007 John Anderson - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ -#ifndef route_signal_h -#define route_signal_h - -#include -#include - -#include "pbd/signals.h" - -#include "midi_byte_array.h" - -class MackieControlProtocol; - -namespace ARDOUR { - class Route; -} - -namespace Mackie -{ - -class Strip; -class SurfacePort; - -/** - This class is intended to easily create and destroy the set of - connections from a route to a control surface strip. Instantiating - it will connect the signals, and destructing it will disconnect - the signals. -*/ -class RouteSignal -{ -public: - RouteSignal(boost::shared_ptr route, MackieControlProtocol & mcp, Strip & strip, SurfacePort & port ) - : _route( route ), _mcp( mcp ), _strip( strip ), _port( port ), _last_gain_written(0.0) - { - connect(); - } - - ~RouteSignal() - { - disconnect(); - } - - void connect(); - void disconnect(); - - // call all signal handlers manually - void notify_all(); - - boost::shared_ptr route() const { return _route; } - Strip & strip() { return _strip; } - SurfacePort & port() { return _port; } - - float last_gain_written() const { return _last_gain_written; } - void last_gain_written( float other ) { _last_gain_written = other; } - - const MidiByteArray & last_pan_written() const { return _last_pan_written; } - void last_pan_written( const MidiByteArray & other ) { _last_pan_written = other; } - -private: - boost::shared_ptr _route; - MackieControlProtocol & _mcp; - Strip & _strip; - SurfacePort & _port; - - PBD::ScopedConnectionList connections; - - // Last written values for the gain and pan, to avoid overloading - // the midi connection to the surface - float _last_gain_written; - MidiByteArray _last_pan_written; -}; - -} - -#endif diff --git a/libs/surfaces/mackie/scripts/bank.rb b/libs/surfaces/mackie/scripts/bank.rb deleted file mode 100644 index e1482545ee..0000000000 --- a/libs/surfaces/mackie/scripts/bank.rb +++ /dev/null @@ -1,32 +0,0 @@ -#! /usr/bin/ruby - -class Bank - attr_accessor :routes, :strips, :current - - def initialize( routes = 17, strips = 8, current = 0 ) - @routes = routes - @strips = strips - @current = current - end - - def left - new_initial = current - routes - if new_initial < 0 - new_initial = 0 - end - current = new_initial - self - end - - def right - delta = routes - ( strips + current ) - 1 - puts "delta: #{delta}" - if delta > strips - delta = strips - end - @current += delta - self - end -end - -b=Bank.new diff --git a/libs/surfaces/mackie/scripts/bcf-controls.csv b/libs/surfaces/mackie/scripts/bcf-controls.csv deleted file mode 100644 index 4dd3bfdd28..0000000000 --- a/libs/surfaces/mackie/scripts/bcf-controls.csv +++ /dev/null @@ -1,95 +0,0 @@ -type,count,group,name,switch,led,id -# faders -fader,7,strip,gain,1,0,0x00 -fader,1,master,gain,1,0,0x07 - -# pots -pot,7,strip,vpot,1,1,0x10 -pot,1,,jog,1,0,0x17 -pot,1,,external,1,0,0x2e - -# strip buttons -button,7,strip,recenable,1,1,0x18 -button,7,strip,solo,1,1,0x20 -button,7,strip,mute,1,1,0x10 -button,7,strip,select,1,1,0x0 -button,7,strip,vselect,1,0,0x08 - -# overlay buttons -button,1,assignment,io,1,1,0x28 -button,1,assignment,sends,1,1,0x5a -button,1,assignment,pan,1,1,0x59 -button,1,assignment,plugin,1,1,0x57 -button,1,functions,drop,1,1,0x58 # was eq -button,1,assignment,zoom,1,1,0x2d -button,1,bank,left,1,0,0x2e -button,1,bank,right,1,0,0x2f -button,1,bank,channel_left,1,0,0x30 -button,1,bank,channel_right,1,0,0x31 -button,1,,scrub,1,1,0x32 -button,1,,edit,1,1,0x56 - -button,1,display,name_value,1,0,0x34 -button,1,display,smpte_beats,1,0,0x35 -button,1,,F1,1,0,0x36 -button,1,,F2,1,0,0x37 -button,1,,F3,1,0,0x38 -button,1,,F4,1,0,0x39 -button,1,,F5,1,0,0x3a -button,1,,F6,1,0,0x3b -button,1,,F7,1,0,0x3c -button,1,,F8,1,0,0x3d -button,1,,F9,1,0,0x3e -button,1,,F10,1,0,0x3f -button,1,,F11,1,0,0x40 -button,1,,F12,1,0,0x41 -button,1,,F13,1,0,0x42 -button,1,,F14,1,0,0x43 -button,1,,F15,1,0,0x44 -button,1,,F16,1,0,0x45 -# turn on/off all solos -button,1,,global_solo,1,0,0x27 -button,1,modifiers,option,1,0,0x50 -button,1,modifiers,cmd_alt,1,0,0x49 -button,1,automation,on,1,1,0x4a -button,1,automation,rec_ready,1,1,0x4b -button,1,functions,undo,1,1,0x4c -button,1,automation,snapshot,1,1,0x5f -button,1,functions,redo,1,1,0x4f -button,1,functions,marker,1,1,0x47 -button,1,functions,enter,1,1,0x51 -button,1,functions,cancel,1,0,0x52 -button,1,functions,mixer,1,0,0x53 -button,1,functions,save,1,0,0x4d - -# transport buttons -button,1,transport,frm_left,1,1,0x5b -button,1,transport,frm_right,1,1,0x5c -button,1,transport,loop,1,1,0x46 -button,1,transport,punch_in,1,1,0x48 -button,1,transport,punch_out,1,1,0x4e -button,1,transport,home,1,1,0x2a -button,1,transport,end,1,1,0x29 -button,1,transport,"rewind",1,1,0x2c -button,1,transport,"ffwd",1,1,0x2b -button,1,transport,"stop",1,1,0x5d -button,1,transport,"play",1,1,0x5e -button,1,transport,"record",1,1,0x1f -button,1,cursor,"cursor_up",1,0,0x60 -button,1,cursor,"cursor_down",1,0,0x61 -button,1,cursor,"cursor_left",1,0,0x62 -button,1,cursor,"cursor_right",1,0,0x63 -button,1,,"dyn",1,1,0x64 -button,1,,"flip",1,1,0x65 -button,1,user,"user_a",1,0,0x66 -button,1,user,"user_b",1,0,0x67 - -button,7,strip,"fader_touch",1,0,0x68 -button,1,master,"fader_touch",1,0,0x6f -button,1,master,mute,1,0,0x17 -button,1,,clicking,1,1,0x33 - -button,1,,"smpte",0,1,0x71 -button,1,,"beats",0,1,0x72 -button,1,,"solo",0,1,0x73 -button,1,,"relay_click",0,1,0x76 diff --git a/libs/surfaces/mackie/scripts/controls.rb b/libs/surfaces/mackie/scripts/controls.rb deleted file mode 100644 index 666c34d4af..0000000000 --- a/libs/surfaces/mackie/scripts/controls.rb +++ /dev/null @@ -1,216 +0,0 @@ -#! /usr/bin/ruby -# Copyright (C) 2006,2007 John Anderson - -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. - -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -this_dir = File.dirname(__FILE__) - -require 'faster_csv' -require "#{this_dir}/mackie.rb" - -class Control - attr_accessor :id, :led, :group, :name, :ordinal, :switch - - def initialize( obj, group ) - @id = obj.id - @name = obj.name - @ordinal = obj.ordinal - @switch = obj.switch - @group = group - end - - def ordinal_name - end -end - -class Fader < Control - def self.midi_zero_byte - 0xe0 - end - - def self.mask_for_id( bytes ) - bytes[0] & 0b00001111 - end -end - -class Button < Control - def self.midi_zero_byte - 0x90 - end - - def self.mask_for_id( bytes ) - bytes[1] - end -end - -class Led < Control -end - -class LedRing < Led -end - -class Pot < Control - def self.midi_zero_byte - 0xb0 - end - - def self.mask_for_id( bytes ) - bytes[1] & 0b00011111 - end - - def led=( rhs ) - @led = LedRing.new( rhs, group ) - end -end - -class Group < Array - attr_accessor :name, :controls - - def initialize( name ) - @name = name - end - - def add_control( control ) - @controls ||= Array.new - @controls << control - end -end - -class Strip < Group - - attr_accessor :ordinal - def initialize( name, ordinal ) - super( name ) - @ordinal = ordinal - end - - def name - @name == 'master' ? @name : "#{@name}_#{@ordinal}" - end - - def is_master - name == 'master' - end - -end - -types = { 0xe0 => Fader, 0x90 => Button, 0xb0 => Pot } - -# number of controls, name, switch, led, id -# anything that doesn't have the correct number -# of columns will be ignored -# actually, 'switch' means it generates data -# whereas 'led' means it receives data - -class Row - attr_accessor :count, :name, :switch, :led, :start_id, :type, :group - attr_accessor :id, :ordinal_name, :ordinal_group, :ordinal - - def initialize( hash ) - @count = hash['count'].to_i - @name = hash['name'] - @switch = hash['switch'].to_b - @led = hash['led'].to_b - @start_id = hash['id'].hex - @type = hash['type'] - @group = hash['group'] - - @hash = hash - end - - def each_ordinal( &block ) - for i in 0...count - @ordinal = i + 1 - @ordinal_name = count > 1 ? "#{name}_#{ordinal}" : name - @ordinal_group = count > 1 ? "#{group}_#{ordinal}" : group - @id = start_id + i - - @hash['ordinal_name'] = @ordinal_name - @hash['ordinal_group'] = @ordinal_group - - yield( self ) - end - self - end - - def to_hash - @hash - end -end - -class Surface - attr_reader :groups, :controls_by_id, :types, :midis, :controls, :name - - def initialize( name = 'none' ) - @name = name - @types = Hash.new - @groups = Hash.new - @controls = Array.new - @controls_by_id = Hash.new - @midis = Hash.new - end - - def add_or_create_group( name, ordinal = nil ) - if name.nil? - @groups['none'] = Group.new('none') - else - group = name =~ /strip/ || name == 'master' ? Strip.new( name, ordinal ) : Group.new( name ) - @groups[group.name] ||= group - end - end - - def parse( control_data ) - FasterCSV.parse( control_data, :headers => true ) do |csv_row| - next if csv_row.entries.size < 5 || csv_row[0] =~ /^\s*#/ || csv_row['id'].nil? - row = Row.new( csv_row ) - - row.each_ordinal do |row| - group = add_or_create_group( row.group, row.ordinal ) - if row.switch - # for controls - control = eval "#{row.type.capitalize}.new( row, group )" - - # for controls with leds - control.led = Led.new( row, group ) if row.led - else - # for LED-only entries - if row.led - control = Led.new( row, group ) - control.led = control - end - end - - # add the new control to the various lookups - # but first print a warning if the id is duplicated - if @controls_by_id.has_key?( row.id ) && control.group.class != Strip - duplicated = @controls_by_id[row.id] - puts "duplicate id #{control.id}:#{control.name} of #{duplicated.id}:#{duplicated.name}" - end - - @controls_by_id[row.id] = control - @controls << control - group << control - - # add incoming midi bytes - if row.switch - types[control.class.midi_zero_byte] = control.class - midis[control.class.midi_zero_byte] ||= Hash.new - midis[control.class.midi_zero_byte][row.id] = control - end - end - end - self - end -end diff --git a/libs/surfaces/mackie/scripts/dump.rb b/libs/surfaces/mackie/scripts/dump.rb deleted file mode 100755 index f1e341fb34..0000000000 --- a/libs/surfaces/mackie/scripts/dump.rb +++ /dev/null @@ -1,11 +0,0 @@ -#! /usr/bin/ruby - -while !File.exist? ARGV[0] - sleep 0.010 -end - -file = File.open ARGV[0], 'r' - -while bytes = file.sysread( 3 ) - puts "%02x %02x %02x" % [ bytes[0], bytes[1], bytes[2] ] -end diff --git a/libs/surfaces/mackie/scripts/generate-button-handlers-cc.erb b/libs/surfaces/mackie/scripts/generate-button-handlers-cc.erb deleted file mode 100644 index 62bc65d0c3..0000000000 --- a/libs/surfaces/mackie/scripts/generate-button-handlers-cc.erb +++ /dev/null @@ -1,59 +0,0 @@ -<%# - Copyright (C) 2006,2007 John Anderson - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. --%> -<%- -require 'controls.rb' - -sf = Surface.new -sf.parse( File.open "mackie-controls.csv" ) -buttons = sf.controls.find_all{|x| x.class == Button && x.group.class != Strip} --%> -/* - Generated by scripts/generate-button-handlers.erb -*/ -#include "mackie_button_handler.h" -#include "controls.h" - -#include - -using namespace std; -using namespace Mackie; - -LedState MackieButtonHandler::default_button_press( Button & button ) -{ - cout << "press: " << button << endl; - return on; -} -LedState MackieButtonHandler::default_button_release( Button & button ) -{ - cout << "release: " << button << endl; - return off; -} - -<%- -buttons.each do |button| -%> -LedState MackieButtonHandler::<%=button.name%>_press( Button & button ) -{ - return default_button_press( button ); -} - -LedState MackieButtonHandler::<%=button.name%>_release( Button & button ) -{ - return default_button_release( button ); -} -<% end %> diff --git a/libs/surfaces/mackie/scripts/generate-surface.rb b/libs/surfaces/mackie/scripts/generate-surface.rb deleted file mode 100755 index c6a028804a..0000000000 --- a/libs/surfaces/mackie/scripts/generate-surface.rb +++ /dev/null @@ -1,26 +0,0 @@ -#! /usr/bin/ruby - -require 'erb' - -require File.dirname(__FILE__) + '/controls.rb' - -cc_template = '' -File.open( File.dirname(__FILE__) + "/surface-cc-template.erb", "r" ) { |f| cc_template = f.read } - -h_template = '' -File.open( File.dirname(__FILE__) + "/surface-h-template.erb", "r" ) { |f| h_template = f.read } - -sf = Surface.new( ARGV[0] ) -control_data = '' -File.open( File.dirname(__FILE__) + "/#{sf.name.downcase}-controls.csv", "r") { |f| control_data = f.read } -sf.parse control_data - -@result = "" -erb = ERB.new( cc_template , 0, "%<>-", "@result" ) -erb.result -File.open( "#{sf.name.downcase}_surface.cc", "w" ) { |f| f.write @result } - -erb = ERB.new( h_template , 0, "%<>-", "@result" ) -erb.result -File.open( "#{sf.name.downcase}_surface.h", "w" ) { |f| f.write @result } - diff --git a/libs/surfaces/mackie/scripts/host.rb b/libs/surfaces/mackie/scripts/host.rb deleted file mode 100755 index e9ccf695f4..0000000000 --- a/libs/surfaces/mackie/scripts/host.rb +++ /dev/null @@ -1,106 +0,0 @@ -#! /usr/bin/ruby -# Copyright (C) 2006,2007 John Anderson - -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. - -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -require 'controls.rb' -require 'mackie.rb' - -if ARGV.size != 2 - puts "#$0 /dev/snd/midiXXXX control-file.csv" - exit 1 -end - -while !File.exist? ARGV[0] - sleep 0.010 -end - -#mapping_csv = ARGV[1] || "mackie-controls.csv" -mapping_csv = ARGV[1] -puts "mapping_csv: #{mapping_csv}" -puts "" - -file = File.open ARGV[0], 'r+' -mck = Mackie.new( file ) - -# faders to minimum. bcf2000 doesn't respond -mck.write_sysex "\x61" - -# all leds off. bcf2000 doesn't respond -mck.write_sysex "\x62" - -# get version. comes back as ASCII bytes -version = mck.sysex "\x13\x00" -puts "version: #{version.map{|x| x.chr}}" - -# write a welcome message. bcf2000 responds with exact -# string but doesn't display anything -# 0 offset, -#~ file.write hdr + "\x12\x3fLCDE\xf7" -#~ file.flush -#~ answer = read_sysex file -#~ puts "answer: #{answer[hdr.length..-1].map{|x| x.chr}}" - -# write to BBT display -#~ file.write hdr + "\x10LCDE\xf7" -#~ file.flush -#~ bbt = [] -#~ while ( nc = file.read( 1 ) )[0] != 0xf7 - #~ bbt << nc[0] -#~ end -#~ puts "bbt: #{bbt[hdr.length..-1].map{|x| x.chr}}" - -# write 7-segment display -#~ file.write hdr + "\x11LCDE\xf7" -#~ file.flush - -# go offline. bcf2000 doesn't respond -#~ file.write( hdr + "\x0f\x7f\xf7" ) -#~ file.flush - -sf = Surface.new -control_data = "" -File.open( mapping_csv ) { |f| control_data = f.read } -sf.parse( control_data ) - -# send all faders to 0, but bounce them first -# otherwise the bcf gets confused -sf.midis[0xe0].values.find_all{|x| x.class == Fader}.each do |x| - bytes = Array.new - bytes[0] = 0xe0 + x.ordinal - 1 - bytes[1] = 0x1 - bytes[2] = 0x1 - file.write bytes.pack( 'C*' ) - bytes[0] = 0xe0 + x.ordinal - 1 - bytes[1] = 0x0 - bytes[2] = 0x0 - file.write bytes.pack( 'C*' ) -end -file.flush - -# respond to control movements -while bytes = mck.file.read( 3 ) - print "received: %02.x %02.x %02.x" % [ bytes[0], bytes[1], bytes[2] ] - midi_type = bytes[0] & 0b11110000 - - control_id = sf.types[midi_type].mask_for_id( bytes ) - control = sf.midis[midi_type][control_id] - - print " Control Type: %-7s, " % sf.types[midi_type] - print "id: %4x" % control_id - print ", control: %15s" % ( control ? control.name : "nil control" ) - print ", %15s" % ( control ? control.group.name : "nil group" ) - print "\n" -end diff --git a/libs/surfaces/mackie/scripts/mackie-controls.csv b/libs/surfaces/mackie/scripts/mackie-controls.csv deleted file mode 100644 index 5dbb6297f8..0000000000 --- a/libs/surfaces/mackie/scripts/mackie-controls.csv +++ /dev/null @@ -1,93 +0,0 @@ -type,count,group,name,switch,led,id -# faders -fader,8,strip,gain,1,0,0x00 -fader,1,master,gain,1,0,0x08 - -# pots -pot,8,strip,vpot,1,1,0x10 -pot,1,,jog,1,0,0x3c -pot,1,,external,1,0,0x2e - -# strip buttons -button,8,strip,recenable,1,1,0x0 -button,8,strip,solo,1,1,0x08 -button,8,strip,mute,1,1,0x10 -button,8,strip,select,1,1,0x18 -button,8,strip,vselect,1,0,0x20 - -# overlay buttons -button,1,assignment,io,1,1,0x28 -button,1,assignment,sends,1,1,0x29 -button,1,assignment,pan,1,1,0x2a -button,1,assignment,plugin,1,1,0x2b -button,1,assignment,eq,1,1,0x2c -button,1,assignment,dyn,1,1,0x2d -button,1,bank,left,1,0,0x2e -button,1,bank,right,1,0,0x2f -button,1,bank,channel_left,1,0,0x30 -button,1,bank,channel_right,1,0,0x31 -button,1,,flip,1,1,0x32 -button,1,,edit,1,1,0x33 - -button,1,display,name_value,1,0,0x34 -button,1,display,smpte_beats,1,0,0x35 -button,1,,F1,1,0,0x36 -button,1,,F2,1,0,0x37 -button,1,,F3,1,0,0x38 -button,1,,F4,1,0,0x39 -button,1,,F5,1,0,0x3a -button,1,,F6,1,0,0x3b -button,1,,F7,1,0,0x3c -button,1,,F8,1,0,0x3d -button,1,,F9,1,0,0x3e -button,1,,F10,1,0,0x3f -button,1,,F11,1,0,0x40 -button,1,,F12,1,0,0x41 -button,1,,F13,1,0,0x42 -button,1,,F14,1,0,0x43 -button,1,,F15,1,0,0x44 -button,1,,F16,1,0,0x45 -button,1,modifiers,shift,1,0,0x46 -button,1,modifiers,option,1,0,0x47 -button,1,modifiers,control,1,0,0x48 -button,1,modifiers,cmd_alt,1,0,0x49 -button,1,automation,on,1,1,0x4a -button,1,automation,rec_ready,1,1,0x4b -button,1,functions,undo,1,1,0x4c -button,1,automation,snapshot,1,1,0x4d -button,1,automation,touch,1,1,0x4e -button,1,functions,redo,1,1,0x4f -button,1,functions,marker,1,1,0x50 -button,1,functions,enter,1,1,0x51 -button,1,functions,cancel,1,0,0x52 -button,1,functions,mixer,1,0,0x53 -button,1,transport,frm_left,1,1,0x54 -button,1,transport,frm_right,1,1,0x55 -button,1,transport,loop,1,1,0x56 -button,1,transport,punch_in,1,1,0x57 -button,1,transport,punch_out,1,1,0x58 -button,1,transport,home,1,1,0x59 -button,1,transport,end,1,1,0x5a - -# transport buttons -button,1,transport,"rewind",1,1,0x5b -button,1,transport,"ffwd",1,1,0x5c -button,1,transport,"stop",1,1,0x5d -button,1,transport,"play",1,1,0x5e -button,1,transport,"record",1,1,0x5f -button,1,cursor,"cursor_up",1,0,0x60 -button,1,cursor,"cursor_down",1,0,0x61 -button,1,cursor,"cursor_left",1,0,0x62 -button,1,cursor,"cursor_right",1,0,0x63 -button,1,,"zoom",1,1,0x64 -button,1,,"scrub",1,1,0x65 -button,1,user,"user_a",1,0,0x66 -button,1,user,"user_b",1,0,0x67 - -button,8,strip,"fader_touch",1,0,0x68 -button,1,master,"fader_touch",1,0,0x70 - -button,1,,"smpte",0,1,0x71 -button,1,,"beats",0,1,0x72 -button,1,,"solo",0,1,0x73 -button,1,,"relay_click",0,1,0x76 diff --git a/libs/surfaces/mackie/scripts/mackie-dump.midi b/libs/surfaces/mackie/scripts/mackie-dump.midi deleted file mode 100644 index 57b36002a52034d1f58d75618168977d7984bfba..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 495 zcmaKlK?=km5Cm87kV8PWpOAOtBIMv9kSFaI@|1bvc54z`9F}Hux~3g%&AqHQ>IdK= z2mXI(YZ&E1#oU1l+tOh)fcx`nV93 mute.attribute('id').value, - 'event' => "0xb0", - 'channel' => channel.to_s, - 'additional' => "0x41" - ) - -end - -pp controls.elements diff --git a/libs/surfaces/mackie/scripts/signals.rb b/libs/surfaces/mackie/scripts/signals.rb deleted file mode 100644 index 6128bfb06b..0000000000 --- a/libs/surfaces/mackie/scripts/signals.rb +++ /dev/null @@ -1,137 +0,0 @@ -#~ /usr/bin/ruby -# Copyright (C) 2006,2007 John Anderson - -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. - -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -require 'erb' - -signals = %w{ -solo_changed -mute_changed -record_enable_changed -gain_changed -name_changed -panner_changed -} - -@signal_calls = { 'panner_changed' => 'panner()[0]->Changed' } - -def connection_call( x ) - if @signal_calls.include? x - @signal_calls[x] - else - x - end -end - -signals.each do |x| - puts < - -class MackieControlProtocol; - -namespace ARDOUR { - class Route; -} - -namespace Mackie -{ - -class Strip; - -/** - This class is intended to easily create and destroy the set of - connections from a route to a control surface strip. Instanting - it will connect the signals, and destructing it will disconnect - the signals. -*/ -class RouteSignal -{ -public: - RouteSignal( ARDOUR::Route & route, MackieControlProtocol & mcp, Strip & strip ) - : _route( route ), _mcp( mcp ), _strip( strip ) - { - connect(); - } - - ~RouteSignal() - { - disconnect(); - } - -private: - ARDOUR::Route & _route; - MackieControlProtocol & _mcp; - Strip & _strip; - -<% signals.each do |x| -%> - sigc::connection _<%= x %>_connection; -<% end -%> -}; - -} - -#endif -EOF - -erb = ERB.new( class_def, 0, ">-" ) -erb.run - -impl_def = < - _<%=x%>_connection = _route.<%=connection_call(x)%>.connect( sigc::bind ( mem_fun ( _mcp, &MackieControlProtocol::notify_<%=x%> ), &_route ) ); -<% end -%> -} - -void RouteSignal::disconnect() -{ -<% signals.each do |x| -%> - _<%= x %>_connection.disconnect(); -<% end -%> -} -EOF - -erb = ERB.new( impl_def, 0, ">-" ) -erb.run diff --git a/libs/surfaces/mackie/scripts/simple_host.rb b/libs/surfaces/mackie/scripts/simple_host.rb deleted file mode 100644 index a369ac8904..0000000000 --- a/libs/surfaces/mackie/scripts/simple_host.rb +++ /dev/null @@ -1,128 +0,0 @@ -#! /usr/bin/ruby -# Copyright (C) 2006,2007 John Anderson - -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. - -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -require 'mackie' - -buttons = {} -pots = {} - -while !File.exist? ARGV[0] - sleep 0.010 -end - -file = File.open( ARGV[0], 'r+' ) -#mck = Mackie.new( file ) -device = false - -# respond to control movements -while bytes = file.read( 3 ) - puts "received: %02.x %02.x %02.x" % [ bytes[0], bytes[1], bytes[2] ] - output = nil - case bytes[0] & 0b11110000 - when 0xe0 - # fader moved, so respond if move is OK - output = bytes - when 0x90 - # button pressed - case bytes[1] - when 0x68..0x6f - # do nothing - touch detection - puts "touch detect: %02.x" % bytes[2] - else - # treat all buttons as toggles - button_id = bytes[1] - - # only toggle on release. Not working. All buttons send press - # and then release signals - if bytes[2] == 0 - if buttons.include?( button_id ) - # toggle button state - puts "button id #{buttons[button_id]} to #{!buttons[button_id]}" - buttons[button_id] = !buttons[button_id] - else - # create a new button as on - puts "adding button id #{button_id}" - buttons[button_id] = true - end - bytes[2] = buttons[button_id] ? 0x7f : 0 - output = bytes - end - end - when 0xb0 - # pots, jog wheel, external - case bytes[1] - when 0x10..0x17 - #pot turned - pot_id = bytes[1] & 0b00000111 - direction = bytes[2] & 0b01000000 - delta = bytes[2] & 0b00111111 - sign = direction == 0 ? 1 : -1 - - if pots.include? pot_id - current_led_pos = pots[pot_id] - else - current_led_pos = pots[pot_id] = 6 - end - new_led_pos = current_led_pos + sign - new_led_pos = case - when new_led_pos <= 0 - 0 - when new_led_pos >= 11 - 11 - else - new_led_pos - end - - pots[pot_id] = new_led_pos - - puts "pot #{pot_id} turned #{sign} #{direction == 0 ? 'clockwise' : 'widdershins'}: %02.x to #{new_led_pos}" % delta - - output = bytes - output[1] += 0x20 - output[2] = 0b01000000 - #~ modes: - #~ 0 - single dot - #~ 1 - boost/cut - #~ 2 - wrap - #~ 3 - spread - mode = pot_id < 4 ? pot_id : 0 - output[2] |= ( mode << 4 ) - output[2] += ( new_led_pos ) & 0b00001111 - when 0x2e - # external controller - when 0x3c - # jog wheel - end - else - puts "don't know what this means" - end - - # output bytes - if device && output - #sleep 0.1 - puts "sending: %02.x %02.x %02.x" % [ output[0], output[1], output[2] ] - begin - res = file.write output - puts "res: #{res}" - file.flush - rescue => e - puts "oops #{e}" - file.close - file = File.open ARGV[0], 'r+' - end - end -end diff --git a/libs/surfaces/mackie/scripts/surface-cc-template.erb b/libs/surfaces/mackie/scripts/surface-cc-template.erb deleted file mode 100644 index a82a144b67..0000000000 --- a/libs/surfaces/mackie/scripts/surface-cc-template.erb +++ /dev/null @@ -1,107 +0,0 @@ -<%# - Copyright (C) 2006,2007 John Anderson - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. --%> -/* - Generated by scripts/generate-surface.rb -*/ - -#include "<%= sf.name.downcase %>_surface.h" - -#include "controls.h" -#include "mackie_button_handler.h" - -using namespace Mackie; - -void Mackie::<%= sf.name %>Surface::init_controls() -{ - // intialise groups and strips - Group * group = 0; - - // make sure there are enough strips - strips.resize( <%= sf.groups.values.find_all{|x| x.name =~ /strip/}.size %> ); - -% sf.groups.values.each do |group| - <%- if group.class == Strip -%> - <%- if group.name == 'master' -%> - group = new MasterStrip ( "<%=group.name%>", 0 ); - <%- else -%> - group = new <%= group.class.name %> ( "<%=group.name%>", <%=group.ordinal - 1%> ); - <%- end -%> - <%- else -%> - group = new <%= group.class.name %> ( "<%=group.name%>" ); - <%- end -%> - groups["<%=group.name%>"] = group; - <%- if group.class == Strip -%> - strips[<%=group.ordinal - 1%>] = dynamic_cast( group ); - <%- end -%> - -% end - - // initialise controls - Fader * fader = 0; - Pot * pot = 0; - Button * button = 0; - Led * led = 0; - -% sf.controls.each do |control| - <%- - variable_name = control.class.name.downcase - class_name = - if control.name == 'jog' - 'Jog' - else - control.class.name - end - -%> - group = groups["<%=control.group.name%>"]; - <%= variable_name %> = new <%= class_name %> ( <%= control.id %>, <%= control.ordinal %>, "<%=control.name%>", *group ); - <%= variable_name %>s[0x<%=control.id.to_hex %>] = <%= variable_name %>; - controls.push_back( <%= variable_name %> ); - <%- if control.group.class != Strip -%> - controls_by_name["<%= control.name %>"] = <%= variable_name %>; - <%- end -%> - group->add( *<%= variable_name %> ); - -% end -} - -void Mackie::<%= sf.name %>Surface::handle_button( MackieButtonHandler & mbh, ButtonState bs, Button & button ) -{ - if ( bs != press && bs != release ) - { - mbh.update_led( button, none ); - return; - } - - LedState ls; - switch ( button.id() ) - { -<%- -buttons = sf.controls.find_all{|x| x.class == Button && x.group.class != Strip} -buttons.each do |button| -%> - case 0x<%= ( button.class.midi_zero_byte << 8 | button.id ).to_hex %>: // <%= button.name %> - switch ( bs ) { - case press: ls = mbh.<%= button.name %>_press( button ); break; - case release: ls = mbh.<%= button.name %>_release( button ); break; - case neither: break; - } - break; -<% end %> - } - mbh.update_led( button, ls ); -} diff --git a/libs/surfaces/mackie/scripts/surface-h-template.erb b/libs/surfaces/mackie/scripts/surface-h-template.erb deleted file mode 100644 index 2f54f66c47..0000000000 --- a/libs/surfaces/mackie/scripts/surface-h-template.erb +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef mackie_surface_<%= sf.name.downcase %>_h -#define mackie_surface_<%= sf.name.downcase %>_h -/* - Generated by scripts/generate-surface.rb -*/ - -#include "surface.h" - -namespace Mackie -{ - -class MackieButtonHandler; - -class <%= sf.name %>Surface : public Surface -{ -public: - <%= sf.name %>Surface( uint32_t max_strips ) : Surface( max_strips ) - { - } - - virtual void handle_button( MackieButtonHandler & mbh, ButtonState bs, Button & button ); - virtual void init_controls(); -}; - -} - -#endif diff --git a/libs/surfaces/mackie/scripts/test_controls.rb b/libs/surfaces/mackie/scripts/test_controls.rb deleted file mode 100755 index 5fd34914d8..0000000000 --- a/libs/surfaces/mackie/scripts/test_controls.rb +++ /dev/null @@ -1,9 +0,0 @@ -#! /usr/bin/ruby - -require 'controls.rb' -require 'pp' - -sf = Surface.new -sf.parse( ARGV[0] ) -sf.types.each{|k,v| puts "%02.x #{v}" % k} - diff --git a/libs/surfaces/mackie/scripts/transform.rb b/libs/surfaces/mackie/scripts/transform.rb deleted file mode 100644 index e0221e188b..0000000000 --- a/libs/surfaces/mackie/scripts/transform.rb +++ /dev/null @@ -1,26 +0,0 @@ -class ElementHandler - - def apply( anElement ) - anElement.each {|e| handle(e)} if anElement - end - - def handle( aNode ) - if aNode.kind_of? REXML::Text - handleTextNode(aNode) - elsif aNode.kind_of? REXML::Element - handle_element aNode - else - return #ignore comments and processing instructions - end - end - - def handle_element( anElement ) - handler_method = "handle_" + anElement.name.tr("-","_") - if self.respond_to? handler_method - self.send(handler_method, anElement) - else - default_handler(anElement) - end - end - -end diff --git a/libs/surfaces/mackie/scripts/write.rb b/libs/surfaces/mackie/scripts/write.rb deleted file mode 100644 index 20b72477e8..0000000000 --- a/libs/surfaces/mackie/scripts/write.rb +++ /dev/null @@ -1,10 +0,0 @@ -#! /usr/bin/ruby - -require 'mackie.rb' - -@file = File.open '/dev/snd/midiC2D0', 'r+' - -@led_8_on = [ 0x90, 0x18, 0x7f ] -@hci = [ 0, 0xf7 ] -@version_req = [ 0x13, 0, 0xf7 ] - diff --git a/libs/surfaces/mackie/strip.cc b/libs/surfaces/mackie/strip.cc new file mode 100644 index 0000000000..af859ab41d --- /dev/null +++ b/libs/surfaces/mackie/strip.cc @@ -0,0 +1,1061 @@ +/* + Copyright (C) 2006,2007 John Anderson + Copyright (C) 2012 Paul Davis + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include "strip.h" + +#include + +#include "midi++/port.h" + +#include "pbd/compose.h" +#include "pbd/convert.h" + +#include "ardour/amp.h" +#include "ardour/bundle.h" +#include "ardour/debug.h" +#include "ardour/midi_ui.h" +#include "ardour/meter.h" +#include "ardour/pannable.h" +#include "ardour/panner.h" +#include "ardour/panner_shell.h" +#include "ardour/rc_configuration.h" +#include "ardour/route.h" +#include "ardour/session.h" +#include "ardour/send.h" +#include "ardour/track.h" +#include "ardour/user_bundle.h" + +#include "mackie_control_protocol.h" +#include "surface_port.h" +#include "surface.h" +#include "button.h" +#include "led.h" +#include "pot.h" +#include "fader.h" +#include "jog.h" +#include "meter.h" + +using namespace Mackie; +using namespace std; +using namespace ARDOUR; +using namespace PBD; + +#define ui_context() MackieControlProtocol::instance() /* a UICallback-derived object that specifies the event loop for signal handling */ + +Strip::Strip (Surface& s, const std::string& name, int index, const map& strip_buttons) + : Group (name) + , _solo (0) + , _recenable (0) + , _mute (0) + , _select (0) + , _vselect (0) + , _fader_touch (0) + , _vpot (0) + , _fader (0) + , _index (index) + , _surface (&s) + , _controls_locked (false) + , _reset_display_at (0) + , _last_gain_position_written (-1.0) + , _last_pan_azi_position_written (-1.0) + , _last_pan_width_position_written (-1.0) +{ + _fader = dynamic_cast (Fader::factory (*_surface, index, "fader", *this)); + _vpot = dynamic_cast (Pot::factory (*_surface, Pot::ID + index, "vpot", *this)); + _meter = dynamic_cast (Meter::factory (*_surface, index, "meter", *this)); + + for (map::const_iterator b = strip_buttons.begin(); b != strip_buttons.end(); ++b) { + Button* bb = dynamic_cast (Button::factory (*_surface, b->first, b->second.base_id + index, b->second.name, *this)); + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("surface %1 strip %2 new button BID %3 id %4 from base %5\n", + _surface->number(), index, Button::id_to_name (bb->bid()), + bb->id(), b->second.base_id)); + } +} + +Strip::~Strip () +{ + /* surface is responsible for deleting all controls */ +} + +void +Strip::add (Control & control) +{ + Button* button; + + Group::add (control); + + /* fader, vpot, meter were all set explicitly */ + + if ((button = dynamic_cast(&control)) != 0) { + switch (button->bid()) { + case Button::RecEnable: + _recenable = button; + break; + case Button::Mute: + _mute = button; + break; + case Button::Solo: + _solo = button; + break; + case Button::Select: + _select = button; + break; + case Button::VSelect: + _vselect = button; + break; + case Button::FaderTouch: + _fader_touch = button; + default: + break; + } + } +} + +void +Strip::set_route (boost::shared_ptr r, bool with_messages) +{ + if (_controls_locked) { + return; + } + + route_connections.drop_connections (); + + _solo->set_control (boost::shared_ptr()); + _mute->set_control (boost::shared_ptr()); + _select->set_control (boost::shared_ptr()); + _recenable->set_control (boost::shared_ptr()); + _fader->set_control (boost::shared_ptr()); + _vpot->set_control (boost::shared_ptr()); + + _route = r; + + control_by_parameter.clear (); + reset_saved_values (); + + if (!r) { + zero (); + return; + } + + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 strip %2 now mapping route %3\n", + _surface->number(), _index, _route->name())); + + _solo->set_control (_route->solo_control()); + _mute->set_control (_route->mute_control()); + + set_vpot_parameter (PanAzimuthAutomation); + + _route->solo_control()->Changed.connect(route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_solo_changed, this), ui_context()); + _route->mute_control()->Changed.connect(route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_mute_changed, this), ui_context()); + + boost::shared_ptr pannable = _route->pannable(); + + if (pannable && pannable->panner()) { + pannable->pan_azimuth_control->Changed.connect(route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_panner_azi_changed, this, false), ui_context()); + pannable->pan_width_control->Changed.connect(route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_panner_width_changed, this, false), ui_context()); + } + _route->gain_control()->Changed.connect(route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_gain_changed, this, false), ui_context()); + _route->PropertyChanged.connect (route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_property_changed, this, _1), ui_context()); + + boost::shared_ptr trk = boost::dynamic_pointer_cast(_route); + + if (trk) { + _recenable->set_control (trk->rec_enable_control()); + trk->rec_enable_control()->Changed .connect(route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_record_enable_changed, this), ui_context()); + + + } + + // TODO this works when a currently-banked route is made inactive, but not + // when a route is activated which should be currently banked. + + _route->active_changed.connect (route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_active_changed, this), ui_context()); + _route->DropReferences.connect (route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_route_deleted, this), ui_context()); + + /* Update */ + + notify_all (); + + /* setup legal VPot modes for this route */ + + build_input_list (_route->input()->n_ports()); + build_output_list (_route->output()->n_ports()); + + possible_pot_parameters.clear(); + + if (pannable) { + boost::shared_ptr panner = pannable->panner(); + if (panner) { + set automatable = panner->what_can_be_automated (); + set::iterator a; + + if ((a = automatable.find (PanAzimuthAutomation)) != automatable.end()) { + possible_pot_parameters.push_back (PanAzimuthAutomation); + } + + if ((a = automatable.find (PanWidthAutomation)) != automatable.end()) { + possible_pot_parameters.push_back (PanWidthAutomation); + } + } + } +} + +void +Strip::notify_all() +{ + if (!_route) { + zero (); + return; + } + + notify_solo_changed (); + notify_mute_changed (); + notify_gain_changed (); + notify_property_changed (PBD::PropertyChange (ARDOUR::Properties::name)); + notify_panner_azi_changed (); + notify_panner_width_changed (); + notify_record_enable_changed (); +} + +void +Strip::notify_solo_changed () +{ + if (_route && _solo) { + _surface->write (_solo->set_state (_route->soloed() ? on : off)); + } +} + +void +Strip::notify_mute_changed () +{ + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Strip %1 mute changed\n", _index)); + if (_route && _mute) { + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("\troute muted ? %1\n", _route->muted())); + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("mute message: %1\n", _mute->set_state (_route->muted() ? on : off))); + + _surface->write (_mute->set_state (_route->muted() ? on : off)); + } +} + +void +Strip::notify_record_enable_changed () +{ + if (_route && _recenable) { + _surface->write (_recenable->set_state (_route->record_enabled() ? on : off)); + } +} + +void +Strip::notify_active_changed () +{ + _surface->mcp().refresh_current_bank(); +} + +void +Strip::notify_route_deleted () +{ + _surface->mcp().refresh_current_bank(); +} + +void +Strip::notify_gain_changed (bool force_update) +{ + if (_route) { + + Control* control; + + if (_surface->mcp().flip_mode()) { + control = _vpot; + } else { + control = _fader; + } + + + boost::shared_ptr ac = _route->gain_control(); + + float gain_coefficient = ac->get_value(); + float normalized_position = ac->internal_to_interface (gain_coefficient); + + if (force_update || normalized_position != _last_gain_position_written) { + + if (_surface->mcp().flip_mode()) { + if (!control->in_use()) { + _surface->write (_vpot->set (normalized_position, true, Pot::wrap)); + } + do_parameter_display (GainAutomation, gain_coefficient); + } else { + if (!control->in_use()) { + _surface->write (_fader->set_position (normalized_position)); + } + do_parameter_display (GainAutomation, gain_coefficient); + } + + queue_display_reset (2000); + _last_gain_position_written = normalized_position; + } + } +} + +void +Strip::notify_property_changed (const PropertyChange& what_changed) +{ + if (!what_changed.contains (ARDOUR::Properties::name)) { + return; + } + + if (_route) { + string line1; + string fullname = _route->name(); + + if (fullname.length() <= 6) { + line1 = fullname; + } else { + line1 = PBD::short_version (fullname, 6); + } + + _surface->write (display (0, line1)); + } +} + +void +Strip::notify_panner_azi_changed (bool force_update) +{ + if (_route) { + + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("pan change for strip %1\n", _index)); + + boost::shared_ptr pannable = _route->pannable(); + + if (!pannable || !pannable->panner()) { + _surface->write (_vpot->zero()); + return; + } + + Control* control = control_by_parameter[PanAzimuthAutomation]; + + if (!control) { + return; + } + + double pos = pannable->pan_azimuth_control->internal_to_interface (pannable->pan_azimuth_control->get_value()); + + if (force_update || pos != _last_pan_azi_position_written) { + + if (control == _fader) { + if (!_fader->in_use()) { + _surface->write (_fader->set_position (pos)); + } + } else if (control == _vpot) { + _surface->write (_vpot->set (pos, true, Pot::dot)); + } + + do_parameter_display (PanAzimuthAutomation, pos); + queue_display_reset (2000); + _last_pan_azi_position_written = pos; + } + } +} + +void +Strip::notify_panner_width_changed (bool force_update) +{ + if (_route) { + + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("pan width change for strip %1\n", _index)); + + boost::shared_ptr pannable = _route->pannable(); + + if (!pannable || !pannable->panner()) { + _surface->write (_vpot->zero()); + return; + } + + + Control* control = control_by_parameter[PanWidthAutomation]; + + if (!control) { + return; + } + + double pos = pannable->pan_width_control->internal_to_interface (pannable->pan_width_control->get_value()); + + if (force_update || pos != _last_pan_azi_position_written) { + + if (_surface->mcp().flip_mode()) { + + if (control == _fader) { + if (!control->in_use()) { + _surface->write (_fader->set_position (pos)); + } + } + + } else if (control == _vpot) { + _surface->write (_vpot->set (pos, true, Pot::spread)); + } + + do_parameter_display (PanWidthAutomation, pos); + queue_display_reset (2000); + _last_pan_azi_position_written = pos; + } + } +} + +void +Strip::select_event (Button& button, ButtonState bs) +{ + DEBUG_TRACE (DEBUG::MackieControl, "select button\n"); + + if (bs == press) { + + int ms = _surface->mcp().modifier_state(); + + if (ms & MackieControlProtocol::MODIFIER_CMDALT) { + _controls_locked = !_controls_locked; + _surface->write (display (1,_controls_locked ? "Locked" : "Unlock")); + queue_display_reset (1000); + return; + } + + if (ms & MackieControlProtocol::MODIFIER_SHIFT) { + /* reset to default */ + boost::shared_ptr ac = _fader->control (); + if (ac) { + ac->set_value (ac->normal()); + } + return; + } + + DEBUG_TRACE (DEBUG::MackieControl, "add select button on press\n"); + _surface->mcp().add_down_select_button (_surface->number(), _index); + _surface->mcp().select_range (); + + } else { + DEBUG_TRACE (DEBUG::MackieControl, "remove select button on release\n"); + _surface->mcp().remove_down_select_button (_surface->number(), _index); + } +} + +void +Strip::vselect_event (Button& button, ButtonState bs) +{ + if (bs == press) { + + int ms = _surface->mcp().modifier_state(); + + if (ms & MackieControlProtocol::MODIFIER_SHIFT) { + boost::shared_ptr ac = button.control (); + + if (ac) { + + /* reset to default/normal value */ + ac->set_value (ac->normal()); + } + + } else { + DEBUG_TRACE (DEBUG::MackieControl, "switching to next pot mode\n"); + next_pot_mode (); + } + + } +} + +void +Strip::fader_touch_event (Button& button, ButtonState bs) +{ + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("fader touch, press ? %1\n", (bs == press))); + + if (bs == press) { + + boost::shared_ptr ac = _fader->control (); + + if (_surface->mcp().modifier_state() == MackieControlProtocol::MODIFIER_SHIFT) { + if (ac) { + ac->set_value (ac->normal()); + } + } else { + + _fader->set_in_use (true); + _fader->start_touch (_surface->mcp().transport_frame()); + + if (ac) { + do_parameter_display ((AutomationType) ac->parameter().type(), ac->internal_to_interface (ac->get_value())); + queue_display_reset (2000); + } + } + + } else { + + _fader->set_in_use (false); + _fader->stop_touch (_surface->mcp().transport_frame(), true); + + } +} + + +void +Strip::handle_button (Button& button, ButtonState bs) +{ + boost::shared_ptr control; + + if (bs == press) { + button.set_in_use (true); + } else { + button.set_in_use (false); + } + + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip %1 handling button %2 press ? %3\n", _index, button.bid(), (bs == press))); + + switch (button.bid()) { + case Button::Select: + select_event (button, bs); + break; + + case Button::VSelect: + vselect_event (button, bs); + break; + + case Button::FaderTouch: + fader_touch_event (button, bs); + break; + + default: + if ((control = button.control ())) { + if (bs == press) { + DEBUG_TRACE (DEBUG::MackieControl, "add button on press\n"); + _surface->mcp().add_down_button ((AutomationType) control->parameter().type(), _surface->number(), _index); + + float new_value; + int ms = _surface->mcp().modifier_state(); + + if (ms & MackieControlProtocol::MODIFIER_SHIFT) { + /* reset to default/normal value */ + new_value = control->normal(); + } else { + new_value = control->get_value() ? 0.0 : 1.0; + } + + /* get all controls that either have their + * button down or are within a range of + * several down buttons + */ + + MackieControlProtocol::ControlList controls = _surface->mcp().down_controls ((AutomationType) control->parameter().type()); + + + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("there are %1 buttons down for control type %2, new value = %3\n", + controls.size(), control->parameter().type(), new_value)); + + /* apply change */ + + for (MackieControlProtocol::ControlList::iterator c = controls.begin(); c != controls.end(); ++c) { + (*c)->set_value (new_value); + } + + } else { + DEBUG_TRACE (DEBUG::MackieControl, "remove button on release\n"); + _surface->mcp().remove_down_button ((AutomationType) control->parameter().type(), _surface->number(), _index); + } + } + break; + } +} + +void +Strip::do_parameter_display (AutomationType type, float val) +{ + switch (type) { + case GainAutomation: + if (val == 0.0) { + _surface->write (display (1, " -inf ")); + } else { + char buf[16]; + float dB = accurate_coefficient_to_dB (val); + snprintf (buf, sizeof (buf), "%6.1f", dB); + _surface->write (display (1, buf)); + } + break; + + case PanAzimuthAutomation: + if (_route) { + boost::shared_ptr p = _route->pannable(); + if (p && p->panner()) { + string str = p->panner()->value_as_string (p->pan_azimuth_control); + _surface->write (display (1, str)); + } + } + break; + + case PanWidthAutomation: + if (_route) { + char buf[16]; + snprintf (buf, sizeof (buf), "%5ld%%", lrintf (val * 100.0)); + _surface->write (display (1, buf)); + } + break; + + default: + break; + } +} + +void +Strip::handle_fader (Fader& fader, float position) +{ + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("fader to %1\n", position)); + + fader.set_value (position); + fader.start_touch (_surface->mcp().transport_frame()); + queue_display_reset (2000); + + // must echo bytes back to slider now, because + // the notifier only works if the fader is not being + // touched. Which it is if we're getting input. + + _surface->write (fader.set_position (position)); +} + +void +Strip::handle_pot (Pot& pot, float delta) +{ + /* Pots only emit events when they move, not when they + stop moving. So to get a stop event, we need to use a timeout. + */ + + boost::shared_ptr ac = pot.control(); + double p = pot.get_value (); + p += delta; + p = max (ac->lower(), p); + p = min (ac->upper(), p); + pot.set_value (p); +} + +void +Strip::periodic (uint64_t usecs) +{ + if (!_route) { + return; + } + + update_automation (); + update_meter (); + + if (_reset_display_at && _reset_display_at < usecs) { + reset_display (); + } +} + +void +Strip::update_automation () +{ + ARDOUR::AutoState gain_state = _route->gain_control()->automation_state(); + + if (gain_state == Touch || gain_state == Play) { + notify_gain_changed (false); + } + + if (_route->panner()) { + ARDOUR::AutoState panner_state = _route->panner()->automation_state(); + if (panner_state == Touch || panner_state == Play) { + notify_panner_azi_changed (false); + notify_panner_width_changed (false); + } + } +} + +void +Strip::update_meter () +{ + if (_meter) { + float dB = const_cast (_route->peak_meter()).peak_power (0); + _surface->write (_meter->update_message (dB)); + } +} + +void +Strip::zero () +{ + for (Group::Controls::const_iterator it = _controls.begin(); it != _controls.end(); ++it) { + _surface->write ((*it)->zero ()); + } + + _surface->write (blank_display (0)); + _surface->write (blank_display (1)); +} + +MidiByteArray +Strip::blank_display (uint32_t line_number) +{ + return display (line_number, string()); +} + +MidiByteArray +Strip::display (uint32_t line_number, const std::string& line) +{ + assert (line_number <= 1); + + MidiByteArray retval; + + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip_display index: %1, line %2 = %3\n", _index, line_number, line)); + + // sysex header + retval << _surface->sysex_hdr(); + + // code for display + retval << 0x12; + // offset (0 to 0x37 first line, 0x38 to 0x6f for second line) + retval << (_index * 7 + (line_number * 0x38)); + + // ascii data to display + retval << line; + // pad with " " out to 6 chars + for (int i = line.length(); i < 6; ++i) { + retval << ' '; + } + + // column spacer, unless it's the right-hand column + if (_index < 7) { + retval << ' '; + } + + // sysex trailer + retval << MIDI::eox; + + return retval; +} + +void +Strip::lock_controls () +{ + _controls_locked = true; +} + +void +Strip::unlock_controls () +{ + _controls_locked = false; +} + +void +Strip::gui_selection_changed (ARDOUR::RouteNotificationListPtr rl) +{ + for (ARDOUR::RouteNotificationList::iterator i = rl->begin(); i != rl->end(); ++i) { + if ((*i) == _route) { + _surface->write (_select->set_state (on)); + return; + } + } + + _surface->write (_select->set_state (off)); +} + +string +Strip::vpot_mode_string () const +{ + boost::shared_ptr ac = _vpot->control(); + + if (!ac) { + return string(); + } + + switch (ac->parameter().type()) { + case GainAutomation: + return "Fader"; + case PanAzimuthAutomation: + return "Pan"; + case PanWidthAutomation: + return "Width"; + case PanElevationAutomation: + return "Elev"; + case PanFrontBackAutomation: + return "F/Rear"; + case PanLFEAutomation: + return "LFE"; + } + + return "???"; +} + +void +Strip::flip_mode_changed (bool notify) +{ + if (!_route) { + return; + } + + reset_saved_values (); + + boost::shared_ptr fader_controllable = _fader->control (); + boost::shared_ptr vpot_controllable = _vpot->control (); + + _fader->set_control (vpot_controllable); + _vpot->set_control (fader_controllable); + + control_by_parameter[fader_controllable->parameter()] = _vpot; + control_by_parameter[vpot_controllable->parameter()] = _fader; + + _surface->write (display (1, vpot_mode_string ())); + + if (notify) { + notify_all (); + } +} + +void +Strip::queue_display_reset (uint32_t msecs) +{ + struct timeval now; + struct timeval delta; + struct timeval when; + gettimeofday (&now, 0); + + delta.tv_sec = msecs/1000; + delta.tv_usec = (msecs - ((msecs/1000) * 1000)) * 1000; + + timeradd (&now, &delta, &when); + + _reset_display_at = (when.tv_sec * 1000000) + when.tv_usec; +} + +void +Strip::clear_display_reset () +{ + _reset_display_at = 0; +} + +void +Strip::reset_display () +{ + if (_route) { + _surface->write (display (1, vpot_mode_string())); + } else { + _surface->write (blank_display (1)); + } + + clear_display_reset (); +} + +struct RouteCompareByName { + bool operator() (boost::shared_ptr a, boost::shared_ptr b) { + return a->name().compare (b->name()) < 0; + } +}; + +void +Strip::maybe_add_to_bundle_map (BundleMap& bm, boost::shared_ptr b, bool for_input, const ChanCount& channels) +{ + if (b->ports_are_outputs() == !for_input || b->nchannels() != channels) { + return; + } + + bm[b->name()] = b; +} + +void +Strip::build_input_list (const ChanCount& channels) +{ + boost::shared_ptr b = _surface->mcp().get_session().bundles (); + + input_bundles.clear (); + + /* give user bundles first chance at being in the menu */ + + for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) { + if (boost::dynamic_pointer_cast (*i)) { + maybe_add_to_bundle_map (input_bundles, *i, true, channels); + } + } + + for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) { + if (boost::dynamic_pointer_cast (*i) == 0) { + maybe_add_to_bundle_map (input_bundles, *i, true, channels); + } + } + + boost::shared_ptr routes = _surface->mcp().get_session().get_routes (); + RouteList copy = *routes; + copy.sort (RouteCompareByName ()); + + for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) { + maybe_add_to_bundle_map (input_bundles, (*i)->output()->bundle(), true, channels); + } + +} + +void +Strip::build_output_list (const ChanCount& channels) +{ + boost::shared_ptr b = _surface->mcp().get_session().bundles (); + + output_bundles.clear (); + + /* give user bundles first chance at being in the menu */ + + for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) { + if (boost::dynamic_pointer_cast (*i)) { + maybe_add_to_bundle_map (output_bundles, *i, false, channels); + } + } + + for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) { + if (boost::dynamic_pointer_cast (*i) == 0) { + maybe_add_to_bundle_map (output_bundles, *i, false, channels); + } + } + + boost::shared_ptr routes = _surface->mcp().get_session().get_routes (); + RouteList copy = *routes; + copy.sort (RouteCompareByName ()); + + for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) { + maybe_add_to_bundle_map (output_bundles, (*i)->input()->bundle(), false, channels); + } +} + +void +Strip::next_pot_mode () +{ + vector::iterator i; + + if (_surface->mcp().flip_mode()) { + /* do not change vpot mode while in flipped mode */ + DEBUG_TRACE (DEBUG::MackieControl, "not stepping pot mode - in flip mode\n"); + _surface->write (display (1, "Flip")); + queue_display_reset (1000); + return; + } + + + boost::shared_ptr ac = _vpot->control(); + + if (!ac) { + return; + } + + if (possible_pot_parameters.empty() || (possible_pot_parameters.size() == 1 && possible_pot_parameters.front() == ac->parameter())) { + return; + } + + for (i = possible_pot_parameters.begin(); i != possible_pot_parameters.end(); ++i) { + if ((*i) == ac->parameter()) { + break; + } + } + + /* move to the next mode in the list, or back to the start (which will + also happen if the current mode is not in the current pot mode list) + */ + + if (i != possible_pot_parameters.end()) { + ++i; + } + + if (i == possible_pot_parameters.end()) { + i = possible_pot_parameters.begin(); + } + + set_vpot_parameter (*i); +} + +void +Strip::set_vpot_parameter (Evoral::Parameter p) +{ + boost::shared_ptr pannable; + + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("switch to vpot mode %1\n", p)); + + reset_saved_values (); + + switch (p.type()) { + case PanAzimuthAutomation: + pannable = _route->pannable (); + if (pannable) { + if (_surface->mcp().flip_mode()) { + /* gain to vpot, pan azi to fader */ + _vpot->set_control (_route->gain_control()); + control_by_parameter[GainAutomation] = _vpot; + if (pannable) { + _fader->set_control (pannable->pan_azimuth_control); + control_by_parameter[PanAzimuthAutomation] = _fader; + } else { + _fader->set_control (boost::shared_ptr()); + control_by_parameter[PanAzimuthAutomation] = 0; + } + } else { + /* gain to fader, pan azi to vpot */ + _fader->set_control (_route->gain_control()); + control_by_parameter[GainAutomation] = _fader; + if (pannable) { + _vpot->set_control (pannable->pan_azimuth_control); + control_by_parameter[PanAzimuthAutomation] = _vpot; + } else { + _vpot->set_control (boost::shared_ptr()); + control_by_parameter[PanAzimuthAutomation] = 0; + } + } + } + break; + case PanWidthAutomation: + pannable = _route->pannable (); + if (pannable) { + if (_surface->mcp().flip_mode()) { + /* gain to vpot, pan width to fader */ + _vpot->set_control (_route->gain_control()); + control_by_parameter[GainAutomation] = _vpot; + if (pannable) { + _fader->set_control (pannable->pan_width_control); + control_by_parameter[PanWidthAutomation] = _fader; + } else { + _fader->set_control (boost::shared_ptr()); + control_by_parameter[PanWidthAutomation] = 0; + } + } else { + /* gain to fader, pan width to vpot */ + _fader->set_control (_route->gain_control()); + control_by_parameter[GainAutomation] = _fader; + if (pannable) { + _vpot->set_control (pannable->pan_width_control); + control_by_parameter[PanWidthAutomation] = _vpot; + } else { + _vpot->set_control (boost::shared_ptr()); + control_by_parameter[PanWidthAutomation] = 0; + } + } + } + break; + case PanElevationAutomation: + break; + case PanFrontBackAutomation: + break; + case PanLFEAutomation: + break; + } + + _surface->write (display (1, vpot_mode_string())); +} + +void +Strip::reset_saved_values () +{ + _last_pan_azi_position_written = -1.0; + _last_pan_width_position_written = -1.0; + _last_gain_position_written = -1.0; + +} diff --git a/libs/surfaces/mackie/strip.h b/libs/surfaces/mackie/strip.h new file mode 100644 index 0000000000..d7e80c95e5 --- /dev/null +++ b/libs/surfaces/mackie/strip.h @@ -0,0 +1,151 @@ +#ifndef __ardour_mackie_control_protocol_strip_h__ +#define __ardour_mackie_control_protocol_strip_h__ + +#include +#include + +#include "evoral/Parameter.hpp" + +#include "pbd/property_basics.h" +#include "pbd/signals.h" + +#include "ardour/types.h" +#include "control_protocol/types.h" + +#include "control_group.h" +#include "types.h" +#include "midi_byte_array.h" +#include "device_info.h" + +namespace ARDOUR { + class Route; + class Bundle; + class ChannelCount; +} + +namespace Mackie { + +class Control; +class Surface; +class Button; +class Pot; +class Fader; +class Meter; +class SurfacePort; + +struct StripControlDefinition { + const char* name; + uint32_t base_id; + Control* (*factory)(Surface&, int index, const char* name, Group&); +}; + +struct GlobalControlDefinition { + const char* name; + uint32_t id; + Control* (*factory)(Surface&, int index, const char* name, Group&); + const char* group_name; +}; + +/** + This is the set of controls that make up a strip. +*/ +class Strip : public Group +{ +public: + Strip (Surface&, const std::string & name, int index, const std::map&); + ~Strip(); + + boost::shared_ptr route() const { return _route; } + + void add (Control & control); + int index() const { return _index; } // zero based + + void set_route (boost::shared_ptr, bool with_messages = true); + + // call all signal handlers manually + void notify_all(); + + void handle_button (Button&, ButtonState bs); + void handle_fader (Fader&, float position); + void handle_pot (Pot&, float delta); + + void periodic (uint64_t now_usecs); + + MidiByteArray display (uint32_t line_number, const std::string&); + MidiByteArray blank_display (uint32_t line_number); + + void zero (); + + void flip_mode_changed (bool notify=false); + + void lock_controls (); + void unlock_controls (); + bool locked() const { return _controls_locked; } + + void gui_selection_changed (ARDOUR::RouteNotificationListPtr); + +private: + Button* _solo; + Button* _recenable; + Button* _mute; + Button* _select; + Button* _vselect; + Button* _fader_touch; + Pot* _vpot; + Fader* _fader; + Meter* _meter; + int _index; + Surface* _surface; + bool _controls_locked; + uint64_t _reset_display_at; + boost::shared_ptr _route; + PBD::ScopedConnectionList route_connections; + + float _last_gain_position_written; + float _last_pan_azi_position_written; + float _last_pan_width_position_written; + + void notify_solo_changed (); + void notify_mute_changed (); + void notify_record_enable_changed (); + void notify_gain_changed (bool force_update = true); + void notify_property_changed (const PBD::PropertyChange&); + void notify_panner_azi_changed (bool force_update = true); + void notify_panner_width_changed (bool force_update = true); + void notify_active_changed (); + void notify_route_deleted (); + + void update_automation (); + void update_meter (); + + std::string vpot_mode_string () const; + + void queue_display_reset (uint32_t msecs); + void clear_display_reset (); + void reset_display (); + void do_parameter_display (ARDOUR::AutomationType, float val); + + typedef std::map > BundleMap; + BundleMap input_bundles; + BundleMap output_bundles; + + void build_input_list (const ARDOUR::ChanCount&); + void build_output_list (const ARDOUR::ChanCount&); + void maybe_add_to_bundle_map (BundleMap& bm, boost::shared_ptr, bool for_input, const ARDOUR::ChanCount&); + + void select_event (Button&, ButtonState); + void vselect_event (Button&, ButtonState); + void fader_touch_event (Button&, ButtonState); + + std::vector possible_pot_parameters; + void next_pot_mode (); + void set_vpot_parameter (Evoral::Parameter); + + void reset_saved_values (); + + std::map control_by_parameter; +}; + +} + +#endif /* __ardour_mackie_control_protocol_strip_h__ */ diff --git a/libs/surfaces/mackie/surface.cc b/libs/surfaces/mackie/surface.cc index 5b79cac4a6..fecc4d5a33 100644 --- a/libs/surfaces/mackie/surface.cc +++ b/libs/surfaces/mackie/surface.cc @@ -1,73 +1,836 @@ -#include "surface.h" - #include #include #include +#include +#include + +#include "midi++/port.h" +#include "midi++/manager.h" + +#include "ardour/automation_control.h" +#include "ardour/debug.h" +#include "ardour/route.h" +#include "ardour/panner.h" +#include "ardour/panner_shell.h" +#include "ardour/rc_configuration.h" +#include "ardour/session.h" +#include "ardour/utils.h" + +#include "control_group.h" +#include "surface_port.h" +#include "surface.h" +#include "strip.h" +#include "mackie_control_protocol.h" +#include "jog_wheel.h" + +#include "strip.h" +#include "button.h" +#include "led.h" +#include "pot.h" +#include "fader.h" +#include "jog.h" +#include "meter.h" + +#include "i18n.h" using namespace std; +using namespace PBD; using namespace Mackie; +using ARDOUR::Route; +using ARDOUR::Panner; +using ARDOUR::Pannable; +using ARDOUR::AutomationControl; -Surface::Surface( uint32_t max_strips, uint32_t unit_strips ) -: _max_strips( max_strips ), _unit_strips( unit_strips ) +#define ui_context() MackieControlProtocol::instance() /* a UICallback-derived object that specifies the event loop for signal handling */ + +// The MCU sysex header.4th byte Will be overwritten +// when we get an incoming sysex that identifies +// the device type +static MidiByteArray mackie_sysex_hdr (5, MIDI::sysex, 0x0, 0x0, 0x66, 0x14); + +// The MCU extender sysex header.4th byte Will be overwritten +// when we get an incoming sysex that identifies +// the device type +static MidiByteArray mackie_sysex_hdr_xt (5, MIDI::sysex, 0x0, 0x0, 0x66, 0x15); + +static MidiByteArray empty_midi_byte_array; + +Surface::Surface (MackieControlProtocol& mcp, const std::string& device_name, uint32_t number, surface_type_t stype) + : _mcp (mcp) + , _stype (stype) + , _number (number) + , _name (device_name) + , _active (false) + , _connected (false) + , _jog_wheel (0) { + DEBUG_TRACE (DEBUG::MackieControl, "Surface::init\n"); + + _port = new SurfacePort (*this); + + /* only the first Surface object has global controls */ + + if (_number == 0) { + if (_mcp.device_info().has_global_controls()) { + init_controls (); + } + + if (_mcp.device_info().has_master_fader()) { + setup_master (); + } + } + + uint32_t n = _mcp.device_info().strip_cnt(); + + if (n) { + init_strips (n); + } + + connect_to_signals (); + + DEBUG_TRACE (DEBUG::MackieControl, "Surface::init finish\n"); } -void Surface::init() +Surface::~Surface () { -#ifdef DEBUG - cout << "Surface::init" << endl; -#endif - init_controls(); - init_strips( _max_strips, _unit_strips ); -#ifdef DEBUG - cout << "Surface::init finish" << endl; -#endif -} + DEBUG_TRACE (DEBUG::MackieControl, "Surface: destructor\n"); + + zero_all (); -Surface::~Surface() -{ // delete groups - for( Groups::iterator it = groups.begin(); it != groups.end(); ++it ) - { + for (Groups::iterator it = groups.begin(); it != groups.end(); ++it) { delete it->second; } // delete controls - for( Controls::iterator it = controls.begin(); it != controls.end(); ++it ) - { + for (Controls::iterator it = controls.begin(); it != controls.end(); ++it) { delete *it; } + + delete _jog_wheel; + delete _port; +} + +const MidiByteArray& +Surface::sysex_hdr() const +{ + switch (_stype) { + case mcu: return mackie_sysex_hdr; + case ext: return mackie_sysex_hdr_xt; + } + cout << "SurfacePort::sysex_hdr _port_type not known" << endl; + return mackie_sysex_hdr; +} + +static GlobalControlDefinition mackie_global_controls[] = { + { "external", Pot::External, Pot::factory, "none" }, + { "fader_touch", Led::FaderTouch, Led::factory, "master" }, + { "timecode", Led::Timecode, Led::factory, "none" }, + { "beats", Led::Beats, Led::factory, "none" }, + { "solo", Led::RudeSolo, Led::factory, "none" }, + { "relay_click", Led::RelayClick, Led::factory, "none" }, + { "", 0, Led::factory, "" } +}; + +void +Surface::init_controls() +{ + Group* group; + + groups["assignment"] = new Group ("assignment"); + groups["automation"] = new Group ("automation"); + groups["bank"] = new Group ("bank"); + groups["cursor"] = new Group ("cursor"); + groups["display"] = new Group ("display"); + groups["functions"] = new Group ("functions"); + groups["modifiers"] = new Group ("modifiers"); + groups["none"] = new Group ("none"); + groups["transport"] = new Group ("transport"); + groups["user"] = new Group ("user"); + groups["master"] = new Group ("master"); + groups["view"] = new Group ("view"); + + if (_mcp.device_info().has_jog_wheel()) { + _jog_wheel = new Mackie::JogWheel (_mcp); + } + + for (uint32_t n = 0; mackie_global_controls[n].name[0]; ++n) { + group = groups[mackie_global_controls[n].group_name]; + Control* control = mackie_global_controls[n].factory (*this, mackie_global_controls[n].id, mackie_global_controls[n].name, *group); + controls_by_device_independent_id[mackie_global_controls[n].id] = control; + } + + /* add global buttons */ + + const map& global_buttons (_mcp.device_info().global_buttons()); + + for (map::const_iterator b = global_buttons.begin(); b != global_buttons.end(); ++b){ + group = groups[b->second.group]; + controls_by_device_independent_id[b->first] = Button::factory (*this, b->first, b->second.id, b->second.label, *group); + } } -// Mackie-specific, because of multiple devices on separate ports -// add the strips from 9..max_strips -// unit_strips is the number of strips for additional units. -void Surface::init_strips( uint32_t max_strips, uint32_t unit_strips ) +void +Surface::init_strips (uint32_t n) { - if ( strips.size() < max_strips ) - { - uint32_t const old_size = strips.size(); - strips.resize (max_strips); - for (uint32_t i = old_size; i < max_strips; ++i) { - // because I can't find itoa - ostringstream os; - os << "strip_" << i + 1; - string name = os.str(); - - // shallow copy existing strip - // which works because the controls - // have the same ids across units - // TODO this needs to be a deep copy because - // controls hold state now - in_use - Strip * strip = new Strip( *strips[i % unit_strips] ); - - // update the relevant values - strip->index( i ); - strip->name( name ); - - // add to data structures - groups[name] = strip; - strips[i] = strip; + const map& strip_buttons (_mcp.device_info().strip_buttons()); + + for (uint32_t i = 0; i < n; ++i) { + + char name[32]; + + snprintf (name, sizeof (name), "strip_%d", (8* _number) + i); + + Strip* strip = new Strip (*this, name, i, strip_buttons); + + groups[name] = strip; + strips.push_back (strip); + } +} + +void +Surface::setup_master () +{ + _master_fader = dynamic_cast (Fader::factory (*this, 8, "master", *groups["master"])); + + boost::shared_ptr m; + + if ((m = _mcp.get_session().monitor_out()) == 0) { + m = _mcp.get_session().master_out(); + } + + if (!m) { + return; + } + + _master_fader->set_control (m->gain_control()); + m->gain_control()->Changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&Surface::master_gain_changed, this), ui_context()); +} + +void +Surface::master_gain_changed () +{ + boost::shared_ptr ac = _master_fader->control(); + float pos = ac->internal_to_interface (ac->get_value()); + _port->write (_master_fader->set_position (pos)); +} + +float +Surface::scaled_delta (float delta, float current_speed) +{ + /* XXX needs work before use */ + const float sign = delta < 0.0 ? -1.0 : 1.0; + + return ((sign * std::pow (delta + 1.0, 2.0)) + current_speed) / 100.0; +} + +void +Surface::display_bank_start (uint32_t current_bank) +{ + if (current_bank == 0) { + // send Ar. to 2-char display on the master + show_two_char_display ("Ar", ".."); + } else { + // write the current first remote_id to the 2-char display + show_two_char_display (current_bank); + } +} + +void +Surface::blank_jog_ring () +{ + Control* control = controls_by_device_independent_id[Jog::ID]; + + if (control) { + Pot* pot = dynamic_cast (control); + if (pot) { + _port->write (pot->set (0.0, false, Pot::spread)); } } } + +float +Surface::scrub_scaling_factor () const +{ + return 100.0; +} + +void +Surface::connect_to_signals () +{ + if (!_connected) { + + + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 connecting to signals on port %2\n", + number(), _port->input_port().name())); + + MIDI::Parser* p = _port->input_port().parser(); + + /* Incoming sysex */ + p->sysex.connect_same_thread (*this, boost::bind (&Surface::handle_midi_sysex, this, _1, _2, _3)); + /* V-Pot messages are Controller */ + p->controller.connect_same_thread (*this, boost::bind (&Surface::handle_midi_controller_message, this, _1, _2)); + /* Button messages are NoteOn */ + p->note_on.connect_same_thread (*this, boost::bind (&Surface::handle_midi_note_on_message, this, _1, _2)); + /* Button messages are NoteOn. libmidi++ sends note-on w/velocity = 0 as note-off so catch them too */ + p->note_off.connect_same_thread (*this, boost::bind (&Surface::handle_midi_note_on_message, this, _1, _2)); + /* Fader messages are Pitchbend */ + p->channel_pitchbend[0].connect_same_thread (*this, boost::bind (&Surface::handle_midi_pitchbend_message, this, _1, _2, 0U)); + p->channel_pitchbend[1].connect_same_thread (*this, boost::bind (&Surface::handle_midi_pitchbend_message, this, _1, _2, 1U)); + p->channel_pitchbend[2].connect_same_thread (*this, boost::bind (&Surface::handle_midi_pitchbend_message, this, _1, _2, 2U)); + p->channel_pitchbend[3].connect_same_thread (*this, boost::bind (&Surface::handle_midi_pitchbend_message, this, _1, _2, 3U)); + p->channel_pitchbend[4].connect_same_thread (*this, boost::bind (&Surface::handle_midi_pitchbend_message, this, _1, _2, 4U)); + p->channel_pitchbend[5].connect_same_thread (*this, boost::bind (&Surface::handle_midi_pitchbend_message, this, _1, _2, 5U)); + p->channel_pitchbend[6].connect_same_thread (*this, boost::bind (&Surface::handle_midi_pitchbend_message, this, _1, _2, 6U)); + p->channel_pitchbend[7].connect_same_thread (*this, boost::bind (&Surface::handle_midi_pitchbend_message, this, _1, _2, 7U)); + + _connected = true; + } +} + +void +Surface::handle_midi_pitchbend_message (MIDI::Parser&, MIDI::pitchbend_t pb, uint32_t fader_id) +{ + /* Pitchbend messages are fader messages. Nothing in the data we get + * from the MIDI::Parser conveys the fader ID, which was given by the + * channel ID in the status byte. + * + * Instead, we have used bind() to supply the fader-within-strip ID + * 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)); + + Fader* fader = faders[fader_id]; + + if (fader) { + Strip* strip = dynamic_cast (&fader->group()); + float pos = (pb >> 4)/1023.0; // only the top 10 bytes are used + if (strip) { + strip->handle_fader (*fader, pos); + } else { + /* master fader */ + fader->set_value (pos); // alter master gain + _port->write (fader->set_position (pos)); // write back value (required for servo) + } + } else { + DEBUG_TRACE (DEBUG::MackieControl, "fader not found\n"); + } +} + +void +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)); + + Button* button = buttons[ev->note_number]; + + if (button) { + Strip* strip = dynamic_cast (&button->group()); + + if (strip) { + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip %1 button %2 pressed ? %3\n", + strip->index(), button->name(), (ev->velocity > 64))); + strip->handle_button (*button, ev->velocity > 64 ? press : release); + } else { + /* global button */ + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("global button %1\n", button->id())); + _mcp.handle_button_event (*this, *button, ev->velocity > 64 ? press : release); + } + } else { + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("no button found for %1\n", ev->note_number)); + } +} + +void +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)); + + Pot* pot = pots[ev->controller_number]; + + // bit 6 gives the sign + float sign = (ev->value & 0x40) == 0 ? 1.0 : -1.0; + // bits 0..5 give the velocity. we interpret this as "ticks + // moved before this message was sent" + float ticks = (ev->value & 0x3f); + if (ticks == 0) { + /* euphonix and perhaps other devices send zero + when they mean 1, we think. + */ + ticks = 1; + } + float delta = sign * (ticks / (float) 0x3f); + + if (!pot) { + if (ev->controller_number == Jog::ID && _jog_wheel) { + + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Jog wheel moved %1\n", ticks)); + _jog_wheel->jog_event (delta); + return; + } + + return; + } + + Strip* strip = dynamic_cast (&pot->group()); + if (strip) { + strip->handle_pot (*pot, delta); + } +} + +void +Surface::handle_midi_sysex (MIDI::Parser &, MIDI::byte * raw_bytes, size_t count) +{ + MidiByteArray bytes (count, raw_bytes); + + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("handle_midi_sysex: %1\n", bytes)); + + /* always save the device type ID so that our outgoing sysex messages + * are correct + */ + + if (_stype == mcu) { + mackie_sysex_hdr[3] = bytes[4]; + } else { + mackie_sysex_hdr_xt[3] = bytes[4]; + } + + switch (bytes[5]) { + case 0x01: + /* MCP: Device Ready + LCP: Connection Challenge + */ + if (bytes[4] == 0x10 || bytes[4] == 0x11) { + 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 (); + } + } + break; + + case 0x03: /* LCP Connection Confirmation */ + if (bytes[4] == 0x10 || bytes[4] == 0x11) { + write_sysex (host_connection_confirmation (bytes)); + _active = true; + } + break; + + case 0x04: /* LCP: Confirmation Denied */ + _active = false; + break; + default: + error << "MCP: unknown sysex: " << bytes << endmsg; + } +} + +static MidiByteArray +calculate_challenge_response (MidiByteArray::iterator begin, MidiByteArray::iterator end) +{ + MidiByteArray l; + back_insert_iterator back (l); + copy (begin, end, back); + + MidiByteArray retval; + + // this is how to calculate the response to the challenge. + // from the Logic docs. + retval << (0x7f & (l[0] + (l[1] ^ 0xa) - l[3])); + retval << (0x7f & ( (l[2] >> l[3]) ^ (l[0] + l[3]))); + retval << (0x7f & ((l[3] - (l[2] << 2)) ^ (l[0] | l[1]))); + retval << (0x7f & (l[1] - l[2] + (0xf0 ^ (l[3] << 4)))); + + return retval; +} + +// not used right now +MidiByteArray +Surface::host_connection_query (MidiByteArray & bytes) +{ + MidiByteArray response; + + if (bytes[4] != 0x10 && bytes[4] != 0x11) { + /* not a Logic Control device - no response required */ + return response; + } + + // handle host connection query + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("host connection query: %1\n", bytes)); + + if (bytes.size() != 18) { + cerr << "expecting 18 bytes, read " << bytes << " from " << _port->input_port().name() << endl; + return response; + } + + // build and send host connection reply + response << 0x02; + copy (bytes.begin() + 6, bytes.begin() + 6 + 7, back_inserter (response)); + response << calculate_challenge_response (bytes.begin() + 6 + 7, bytes.begin() + 6 + 7 + 4); + return response; +} + +// not used right now +MidiByteArray +Surface::host_connection_confirmation (const MidiByteArray & bytes) +{ + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("host_connection_confirmation: %1\n", bytes)); + + // decode host connection confirmation + if (bytes.size() != 14) { + ostringstream os; + os << "expecting 14 bytes, read " << bytes << " from " << _port->input_port().name(); + throw MackieControlException (os.str()); + } + + // send version request + return MidiByteArray (2, 0x13, 0x00); +} + +void +Surface::handle_port_inactive (SurfacePort * port) +{ + _active = false; +} + +void +Surface::write_sysex (const MidiByteArray & mba) +{ + if (mba.empty()) { + return; + } + + MidiByteArray buf; + buf << sysex_hdr() << mba << MIDI::eox; + _port->write (buf); +} + +void +Surface::write_sysex (MIDI::byte msg) +{ + MidiByteArray buf; + buf << sysex_hdr() << msg << MIDI::eox; + _port->write (buf); +} + +uint32_t +Surface::n_strips (bool with_locked_strips) const +{ + if (with_locked_strips) { + return strips.size(); + } + + uint32_t n = 0; + + for (Strips::const_iterator it = strips.begin(); it != strips.end(); ++it) { + if (!(*it)->locked()) { + ++n; + } + } + return n; +} + +Strip* +Surface::nth_strip (uint32_t n) const +{ + if (n > n_strips()) { + return 0; + } + return strips[n]; +} + +void +Surface::zero_all () +{ + // TODO turn off Timecode displays + + // zero all strips + for (Strips::iterator it = strips.begin(); it != strips.end(); ++it) { + (*it)->zero(); + } + + zero_controls (); +} + +void +Surface::zero_controls () +{ + if (_stype != mcu || !_mcp.device_info().has_global_controls()) { + return; + } + + // turn off global buttons and leds + // global buttons are only ever on mcu_port, so we don't have + // to figure out which port. + + for (Controls::iterator it = controls.begin(); it != controls.end(); ++it) { + Control & control = **it; + if (!control.group().is_strip()) { + _port->write (control.zero()); + } + } + + if (_number == 0 && _mcp.device_info().has_two_character_display()) { + // any hardware-specific stuff + // clear 2-char display + show_two_char_display (" "); + } + + // and the led ring for the master strip + blank_jog_ring (); +} + +void +Surface::periodic (uint64_t now_usecs) +{ + for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) { + (*s)->periodic (now_usecs); + } +} + +void +Surface::write (const MidiByteArray& data) +{ + if (_active) { + _port->write (data); + } +} + +void +Surface::map_routes (const vector >& routes) +{ + vector >::const_iterator r; + Strips::iterator s = strips.begin(); + + for (r = routes.begin(); r != routes.end() && s != strips.end(); ++s) { + + /* don't try to assign routes to a locked strip. it won't + use it anyway, but if we do, then we get out of sync + with the proposed mapping. + */ + + if (!(*s)->locked()) { + (*s)->set_route (*r); + ++r; + } + } + + for (; s != strips.end(); ++s) { + (*s)->set_route (boost::shared_ptr()); + } + + +} + +static char +translate_seven_segment (char achar) +{ + achar = toupper (achar); + + if (achar >= 0x40 && achar <= 0x60) { + return achar - 0x40; + } else if (achar >= 0x21 && achar <= 0x3f) { + return achar; + } else { + return 0x00; + } +} + +void +Surface::show_two_char_display (const std::string & msg, const std::string & dots) +{ + if (_stype != mcu || !_mcp.device_info().has_two_character_display() || msg.length() != 2 || dots.length() != 2) { + return; + } + + MidiByteArray right (3, 0xb0, 0x4b, 0x00); + MidiByteArray left (3, 0xb0, 0x4a, 0x00); + + right[2] = translate_seven_segment (msg[0]) + (dots[0] == '.' ? 0x40 : 0x00); + left[2] = translate_seven_segment (msg[1]) + (dots[1] == '.' ? 0x40 : 0x00); + + _port->write (right); + _port->write (left); +} + +void +Surface::show_two_char_display (unsigned int value, const std::string & /*dots*/) +{ + ostringstream os; + os << setfill('0') << setw(2) << value % 100; + show_two_char_display (os.str()); +} + +void +Surface::display_timecode (const std::string & timecode, const std::string & timecode_last) +{ + if (_active && _mcp.device_info().has_timecode_display()) { + _port->write (timecode_display (timecode, timecode_last)); + } +} + +MidiByteArray +Surface::timecode_display (const std::string & timecode, const std::string & last_timecode) +{ + // if there's no change, send nothing, not even sysex header + if (timecode == last_timecode) return MidiByteArray(); + + // length sanity checking + string local_timecode = timecode; + + // truncate to 10 characters + if (local_timecode.length() > 10) { + local_timecode = local_timecode.substr (0, 10); + } + + // pad to 10 characters + while (local_timecode.length() < 10) { + local_timecode += " "; + } + + // find the suffix of local_timecode that differs from last_timecode + std::pair pp = mismatch (last_timecode.begin(), last_timecode.end(), local_timecode.begin()); + + MidiByteArray retval; + + // sysex header + retval << sysex_hdr(); + + // code for timecode display + retval << 0x10; + + // translate characters. These are sent in reverse order of display + // hence the reverse iterators + string::reverse_iterator rend = reverse_iterator (pp.second); + for (string::reverse_iterator it = local_timecode.rbegin(); it != rend; ++it) { + retval << translate_seven_segment (*it); + } + + // sysex trailer + retval << MIDI::eox; + + return retval; +} + +void +Surface::update_flip_mode_display () +{ + for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) { + (*s)->flip_mode_changed (true); + } +} + +void +Surface::update_view_mode_display () +{ + string text; + int id = -1; + + if (!_active) { + return; + } + + switch (_mcp.view_mode()) { + case MackieControlProtocol::Mixer: + show_two_char_display ("Mx"); + id = Button::Pan; + break; + case MackieControlProtocol::Dynamics: + show_two_char_display ("Dy"); + id = Button::Dyn; + break; + case MackieControlProtocol::EQ: + show_two_char_display ("EQ"); + id = Button::Eq; + break; + case MackieControlProtocol::Loop: + show_two_char_display ("LP"); + id = Button::Loop; + break; + case MackieControlProtocol::AudioTracks: + show_two_char_display ("AT"); + break; + case MackieControlProtocol::MidiTracks: + show_two_char_display ("MT"); + break; + case MackieControlProtocol::Sends: + show_two_char_display ("Sn"); + id = Button::Sends; + break; + case MackieControlProtocol::Plugins: + show_two_char_display ("Pl"); + id = Button::Plugin; + break; + default: + break; + } + + if (id >= 0) { + + /* we are attempting to turn a global button/LED on */ + + map::iterator x = controls_by_device_independent_id.find (id); + + if (x != controls_by_device_independent_id.end()) { + Button* button = dynamic_cast (x->second); + if (button) { + _port->write (button->set_state (on)); + } + } + } + + if (!text.empty()) { + for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) { + _port->write ((*s)->display (1, text)); + } + } +} + +void +Surface::gui_selection_changed (ARDOUR::RouteNotificationListPtr routes) +{ + for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) { + (*s)->gui_selection_changed (routes); + } +} + +void +Surface::say_hello () +{ + /* wakeup for Mackie Control */ + MidiByteArray wakeup (7, MIDI::sysex, 0x00, 0x00, 0x66, 0x14, 0x00, MIDI::eox); + _port->write (wakeup); + wakeup[4] = 0x15; /* wakup Mackie XT */ + _port->write (wakeup); + wakeup[4] = 0x10; /* wakupe Logic Control */ + _port->write (wakeup); + wakeup[4] = 0x11; /* wakeup Logic Control XT */ + _port->write (wakeup); +} + +void +Surface::next_jog_mode () +{ +} + +void +Surface::set_jog_mode (JogWheel::Mode m) +{ +} + +bool +Surface::route_is_locked_to_strip (boost::shared_ptr r) const +{ + for (Strips::const_iterator s = strips.begin(); s != strips.end(); ++s) { + if ((*s)->route() == r && (*s)->locked()) { + return true; + } + } + return false; +} diff --git a/libs/surfaces/mackie/surface.h b/libs/surfaces/mackie/surface.h index 05983cbfa2..9f3ac0b8c6 100644 --- a/libs/surfaces/mackie/surface.h +++ b/libs/surfaces/mackie/surface.h @@ -1,9 +1,26 @@ #ifndef mackie_surface_h #define mackie_surface_h +#include + +#include "midi++/types.h" + +#include "control_protocol/types.h" + #include "controls.h" #include "types.h" -#include +#include "jog_wheel.h" + +namespace MIDI { + class Parser; +} + +namespace ARDOUR { + class Route; +} + +class MidiByteArray; +class MackieControlProtocol; namespace Mackie { @@ -11,100 +28,98 @@ namespace Mackie class MackieButtonHandler; class SurfacePort; class MackieMidiBuilder; +class Button; +class Meter; +class Fader; +class Jog; +class Pot; +class Led; -/** - This represents an entire control surface, made up of Groups, - Strips and Controls. There are several collections for - ease of addressing in different ways, but only one collection - has definitive ownership. - - It handles mapping button ids to press_ and release_ calls. - - There are various emulations of the Mackie around, so specific - emulations will inherit from this to change button mapping, or - have 7 fader channels instead of 8, or whatever. - - Currently there are BcfSurface and MackieSurface. - - TODO maybe make Group inherit from Control, for ease of ownership. -*/ -class Surface +class Surface : public PBD::ScopedConnectionList { public: - /** - A Surface can be made up of multiple units. eg one Mackie MCU plus - one or more Mackie MCU extenders. - - \param max_strips is the number of strips for the entire surface. - \param unit_strips is the number of strips per unit. - */ - Surface( uint32_t max_strips, uint32_t unit_strips = 8 ); + Surface (MackieControlProtocol&, const std::string& name, uint32_t number, surface_type_t stype); virtual ~Surface(); - /// Calls the virtual initialisation methods. This *must* be called after - /// construction, because c++ is too dumb to call virtual methods from - /// inside a constructor - void init(); + surface_type_t type() const { return _stype; } + uint32_t number() const { return _number; } + const std::string& name() { return _name; } + + void say_hello (); + + bool active() const { return _active; } + void drop_routes (); typedef std::vector Controls; - - /// This collection has ownership of all the controls Controls controls; - /** - These are alternative addressing schemes - They use maps because the indices aren't always - 0-based. - - Indexed by raw_id not by id. @see Control for the distinction. - */ std::map faders; std::map pots; - std::map buttons; + std::map buttons; // index is device-DEPENDENT std::map leds; + std::map meters; + std::map controls_by_device_independent_id; + + Mackie::JogWheel* jog_wheel() const { return _jog_wheel; } + Fader* master_fader() const { return _master_fader; } - /// no strip controls in here because they usually - /// have the same names. - std::map controls_by_name; - - /// The collection of all numbered strips. No master - /// strip in here. + /// The collection of all numbered strips. typedef std::vector Strips; Strips strips; + uint32_t n_strips (bool with_locked_strips = true) const; + Strip* nth_strip (uint32_t n) const; + + bool route_is_locked_to_strip (boost::shared_ptr) const; + /// This collection owns the groups typedef std::map Groups; Groups groups; - uint32_t max_strips() const - { - return _max_strips; - } - - /// map button ids to calls to press_ and release_ in mbh - virtual void handle_button( MackieButtonHandler & mbh, ButtonState bs, Button & button ) = 0; + SurfacePort& port() const { return *_port; } + + void map_routes (const std::vector >& routes); + + const MidiByteArray& sysex_hdr() const; + + void periodic (uint64_t now_usecs); + + void handle_midi_pitchbend_message (MIDI::Parser&, MIDI::pitchbend_t, uint32_t channel_id); + void handle_midi_controller_message (MIDI::Parser&, MIDI::EventTwoBytes*); + void handle_midi_note_on_message (MIDI::Parser&, MIDI::EventTwoBytes*); + + /// Connect the any signal from the parser to handle_midi_any + /// unless it's already connected + void connect_to_signals (); + + /// notification from a MackiePort that it's now inactive + void handle_port_inactive(Mackie::SurfacePort *); + + /// write a sysex message + void write_sysex (const MidiByteArray& mba); + void write_sysex (MIDI::byte msg); + /// proxy write for port + void write (const MidiByteArray&); -public: /// display an indicator of the first switched-in Route. Do nothing by default. - virtual void display_bank_start( SurfacePort &, MackieMidiBuilder &, uint32_t /*current_bank*/ ) {}; + void display_bank_start (uint32_t /*current_bank*/); - /// called from MackieControlPRotocol::zero_all to turn things off - virtual void zero_all( SurfacePort &, MackieMidiBuilder & ) {}; + /// called from MackieControlProtocol::zero_all to turn things off + void zero_all (); + void zero_controls (); /// turn off leds around the jog wheel. This is for surfaces that use a pot /// pretending to be a jog wheel. - virtual void blank_jog_ring( SurfacePort &, MackieMidiBuilder & ) {}; + void blank_jog_ring (); + + void display_timecode (const std::string & /*timecode*/, const std::string & /*timecode_last*/); - virtual bool has_timecode_display() const = 0; - virtual void display_timecode( SurfacePort &, MackieMidiBuilder &, const std::string & /*timecode*/, const std::string & /*timecode_last*/) {}; - -public: /** This is used to calculate the clicks per second that define a transport speed of 1.0 for the jog wheel. 100.0 is 10 clicks per second, 50.5 is 5 clicks per second. */ - virtual float scrub_scaling_factor() = 0; + float scrub_scaling_factor() const; /** The scaling factor function for speed increase and decrease. At @@ -113,15 +128,52 @@ public: high definition control at low speeds and quick speed changes to/from higher speeds. */ - virtual float scaled_delta( const ControlState & state, float current_speed ) = 0; + float scaled_delta (float delta, float current_speed); -protected: - virtual void init_controls() = 0; - virtual void init_strips( uint32_t max_strips, uint32_t unit_strips ); + // display the first 2 chars of the msg in the 2 char display + // . is appended to the previous character, so A.B. would + // be two characters + void show_two_char_display (const std::string & msg, const std::string & dots = " "); + void show_two_char_display (unsigned int value, const std::string & dots = " "); + + /** + Timecode display. Only the difference between timecode and last_timecode will + be encoded, to save midi bandwidth. If they're the same, an empty array will + be returned + */ + MidiByteArray timecode_display (const std::string & timecode, const std::string & last_timecode = ""); -private: - uint32_t _max_strips; - uint32_t _unit_strips; + void update_view_mode_display (); + void update_flip_mode_display (); + + void gui_selection_changed (ARDOUR::RouteNotificationListPtr); + + MackieControlProtocol& mcp() const { return _mcp; } + + void next_jog_mode (); + void set_jog_mode (Mackie::JogWheel::Mode); + + protected: + + private: + MackieControlProtocol& _mcp; + SurfacePort* _port; + surface_type_t _stype; + uint32_t _number; + std::string _name; + bool _active; + bool _connected; + Mackie::JogWheel* _jog_wheel; + Fader* _master_fader; + + void handle_midi_sysex (MIDI::Parser&, MIDI::byte *, size_t count); + MidiByteArray host_connection_query (MidiByteArray& bytes); + MidiByteArray host_connection_confirmation (const MidiByteArray& bytes); + + void init_controls(); + void init_strips (uint32_t n); + void setup_master (); + void master_gain_changed (); }; } diff --git a/libs/surfaces/mackie/surface_port.cc b/libs/surfaces/mackie/surface_port.cc index 031009cd3d..bf5e7417ab 100644 --- a/libs/surfaces/mackie/surface_port.cc +++ b/libs/surfaces/mackie/surface_port.cc @@ -15,180 +15,131 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include "surface_port.h" - -#include "mackie_control_exception.h" -#include "controls.h" - -#include "midi++/types.h" -#include "midi++/port.h" -#include "midi++/manager.h" -#include -#include - -#include "i18n.h" #include - #include #include +#include +#include + +#include "midi++/types.h" +#include "midi++/port.h" +#include "midi++/jack_midi_port.h" +#include "midi++/ipmidi_port.h" +#include "midi++/manager.h" + +#include "ardour/debug.h" +#include "ardour/rc_configuration.h" +#include "ardour/session.h" +#include "ardour/audioengine.h" + +#include "controls.h" +#include "mackie_control_protocol.h" +#include "surface.h" +#include "surface_port.h" + + +#include "i18n.h" + using namespace std; using namespace Mackie; +using namespace PBD; -SurfacePort::SurfacePort() - : _input_port (0), _output_port (0), _number (0), _active (false) -{ -} - -/** @param input_port Input MIDI::Port; this object takes responsibility for removing it from - * the MIDI::Manager and destroying it. - * @param output_port Output MIDI::Port; responsibility similarly taken. +/** @param input_port Input MIDI::Port; this object takes responsibility for + * adding & removing it from the MIDI::Manager and destroying it. @param + * output_port Output MIDI::Port; responsibility similarly taken. */ -SurfacePort::SurfacePort (MIDI::Port & input_port, MIDI::Port & output_port, int number) - : _input_port (&input_port), _output_port (&output_port), _number (number), _active (false) +SurfacePort::SurfacePort (Surface& s) + : _surface (&s) + , _input_port (0) + , _output_port (0) { + if (_surface->mcp().device_info().uses_ipmidi()) { + _input_port = new MIDI::IPMIDIPort (_surface->mcp().ipmidi_base() +_surface->number()); + _output_port = _input_port; + } else { + jack_client_t* jack = MackieControlProtocol::instance()->get_session().engine().jack(); + + _input_port = new MIDI::JackMIDIPort (string_compose (_("%1 in"), _surface->name()), MIDI::Port::IsInput, jack); + _output_port =new MIDI::JackMIDIPort (string_compose (_("%1 out"), _surface->name()), MIDI::Port::IsOutput, jack); + + /* MackieControl has its own thread for handling input from the input + * port, and we don't want anything handling output from the output + * port. This stops the Generic MIDI UI event loop in ardour from + * attempting to handle these ports. + */ + + _input_port->set_centrally_parsed (false); + _output_port->set_centrally_parsed (false); + + MIDI::Manager * mm = MIDI::Manager::instance(); + + mm->add_port (_input_port); + mm->add_port (_output_port); + } } SurfacePort::~SurfacePort() { -#ifdef PORT_DEBUG - cout << "~SurfacePort::SurfacePort()" << endl; -#endif - // make sure another thread isn't reading or writing as we close the port - Glib::RecMutex::Lock lock( _rwlock ); - _active = false; - - MIDI::Manager* mm = MIDI::Manager::instance (); - - if (_input_port) { - mm->remove_port (_input_port); + if (_surface->mcp().device_info().uses_ipmidi()) { delete _input_port; - } + } else { - if (_output_port) { - mm->remove_port (_output_port); - delete _output_port; + MIDI::Manager* mm = MIDI::Manager::instance (); + + if (_input_port) { + mm->remove_port (_input_port); + delete _input_port; + } + + if (_output_port) { + _output_port->drain (10000); + mm->remove_port (_output_port); + delete _output_port; + } } - -#ifdef PORT_DEBUG - cout << "~SurfacePort::SurfacePort() finished" << endl; -#endif } // wrapper for one day when strerror_r is working properly -string fetch_errmsg( int error_number ) +string fetch_errmsg (int error_number) { - char * msg = strerror( error_number ); + char * msg = strerror (error_number); return msg; } -MidiByteArray SurfacePort::read() +int +SurfacePort::write (const MidiByteArray & mba) { - const int max_buf_size = 512; - MIDI::byte buf[max_buf_size]; - MidiByteArray retval; - - // check active. Mainly so that the destructor - // doesn't destroy the mutex while it's still locked - if ( !active() ) return retval; - - // return nothing read if the lock isn't acquired -#if 0 - Glib::RecMutex::Lock lock( _rwlock, Glib::TRY_LOCK ); - - if ( !lock.locked() ) - { - cout << "SurfacePort::read not locked" << endl; - return retval; + if (mba.empty()) { + return 0; } - - // check active again - destructor sequence - if ( !active() ) return retval; -#endif - - // read port and copy to return value - int nread = input_port().read( buf, sizeof (buf) ); - if (nread >= 0) { - retval.copy( nread, buf ); - if ((size_t) nread == sizeof (buf)) - { -#ifdef PORT_DEBUG - cout << "SurfacePort::read recursive" << endl; -#endif - retval << read(); - } - } - else - { - if ( errno != EAGAIN ) - { - ostringstream os; - os << "Surface: error reading from port: " << input_port().name(); - os << ": " << errno << fetch_errmsg( errno ); + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("port %1 write %2\n", output_port().name(), mba)); - cout << os.str() << endl; - inactive_event(); - throw MackieControlException( os.str() ); - } - } -#ifdef PORT_DEBUG - cout << "SurfacePort::read: " << retval << endl; -#endif - return retval; -} + int count = output_port().write (mba.bytes().get(), mba.size(), 0); -void SurfacePort::write( const MidiByteArray & mba ) -{ -#ifdef PORT_DEBUG - cout << "SurfacePort::write: " << mba << " to " << output_port().name() << endl; -#endif - - // check active before and after lock - to make sure - // that the destructor doesn't destroy the mutex while - // it's still in use - if ( !active() ) return; - Glib::RecMutex::Lock lock( _rwlock ); - if ( !active() ) return; + if (count != (int)mba.size()) { + + if (errno == 0) { - int count = output_port().write( mba.bytes().get(), mba.size(), 0); - if ( count != (int)mba.size() ) - { - if ( errno == 0 ) - { cout << "port overflow on " << output_port().name() << ". Did not write all of " << mba << endl; - } - else if ( errno != EAGAIN ) - { + + } else if (errno != EAGAIN) { ostringstream os; os << "Surface: couldn't write to port " << output_port().name(); - os << ", error: " << fetch_errmsg( errno ) << "(" << errno << ")"; - + os << ", error: " << fetch_errmsg (errno) << "(" << errno << ")"; cout << os.str() << endl; - inactive_event(); } + + return -1; } -#ifdef PORT_DEBUG - cout << "SurfacePort::wrote " << count << endl; -#endif + + return 0; } -void SurfacePort::write_sysex( const MidiByteArray & mba ) -{ - MidiByteArray buf; - buf << sysex_hdr() << mba << MIDI::eox; - write( buf ); -} - -void SurfacePort::write_sysex( MIDI::byte msg ) -{ - MidiByteArray buf; - buf << sysex_hdr() << msg << MIDI::eox; - write( buf ); -} - -ostream & Mackie::operator << ( ostream & os, const SurfacePort & port ) +ostream & +Mackie::operator << (ostream & os, const SurfacePort & port) { os << "{ "; os << "name: " << port.input_port().name() << " " << port.output_port().name(); @@ -197,42 +148,3 @@ ostream & Mackie::operator << ( ostream & os, const SurfacePort & port ) return os; } -/** Handle timeouts to reset in_use for controls that can't - * do this by themselves (e.g. pots, and faders without touch support). - * @param in_use_control the control whose in_use flag to reset. - * @param touch_control a touch control to emit an event for, or 0. - */ -bool -SurfacePort::control_in_use_timeout (Control* in_use_control, Control* touch_control) -{ - in_use_control->set_in_use (false); - - if (touch_control) { - // empty control_state - ControlState control_state; - control_event (*this, *touch_control, control_state); - } - - // only call this method once from the timer - return false; -} - -/** Add a timeout so that a control's in_use flag will be reset some time in the future. - * @param in_use_control the control whose in_use flag to reset. - * @param touch_control a touch control to emit an event for, or 0. - */ -void -SurfacePort::add_in_use_timeout (Control& in_use_control, Control* touch_control) -{ - in_use_control.in_use_connection.disconnect (); - - /* XXX should this use the GUI event loop (default) or the MIDI UI event loop? */ - - /* timeout after 250ms */ - in_use_control.in_use_connection = Glib::signal_timeout().connect ( - sigc::bind (sigc::mem_fun (*this, &SurfacePort::control_in_use_timeout), &in_use_control, touch_control), - 250 - ); - - in_use_control.in_use_touch_control = touch_control; -} diff --git a/libs/surfaces/mackie/surface_port.h b/libs/surfaces/mackie/surface_port.h index fb2d8d0019..70b1df4580 100644 --- a/libs/surfaces/mackie/surface_port.h +++ b/libs/surfaces/mackie/surface_port.h @@ -18,6 +18,7 @@ #ifndef surface_port_h #define surface_port_h +#include #include #include "pbd/signals.h" @@ -26,85 +27,43 @@ namespace MIDI { class Port; + class Parser; } +class MackieControlProtocol; + namespace Mackie { +class Surface; + /** Make a relationship between a midi port and a Mackie device. */ -class SurfacePort + +class SurfacePort { public: - SurfacePort (MIDI::Port & input_port, MIDI::Port & output_port, int number); + SurfacePort (Mackie::Surface&); virtual ~SurfacePort(); - // when this is successful, active() should return true - virtual void open() = 0; - - // subclasses should call this before doing their own close - virtual void close() = 0; - - /// read bytes from the port. They'll either end up in the - /// parser, or if that's not active they'll be returned - virtual MidiByteArray read(); - /// an easier way to output bytes via midi - virtual void write( const MidiByteArray & ); + int write (const MidiByteArray&); - /// write a sysex message - void write_sysex( const MidiByteArray & mba ); - void write_sysex( MIDI::byte msg ); + MIDI::Port& input_port() { return *_input_port; } + const MIDI::Port& input_port() const { return *_input_port; } + MIDI::Port& output_port() { return *_output_port; } + const MIDI::Port& output_port() const { return *_output_port; } - /// return the correct sysex header for this port - virtual const MidiByteArray & sysex_hdr() const = 0; - - MIDI::Port & input_port() { return *_input_port; } - const MIDI::Port & input_port() const { return *_input_port; } - MIDI::Port & output_port() { return *_output_port; } - const MIDI::Port & output_port() const { return *_output_port; } - - // all control notofications are sent from here - PBD::Signal3 control_event; - - // emitted just before the port goes into initialisation - // where it tries to establish that its device is connected - PBD::Signal0 init_event; - - // emitted when the port completes initialisation successfully - PBD::Signal0 active_event; - - // emitted when the port goes inactive (ie a read or write failed) - PBD::Signal0 inactive_event; - - // the port number - master is 0(extenders are 1((,4 - virtual int number() const { return _number; } - - // number of strips handled by this port. Usually 8. - virtual int strips() const = 0; - - virtual bool active() const { return _active; } - virtual void active( bool yn ) { _active = yn; } - - void add_in_use_timeout (Control &, Control *); - protected: - /// Only for use by DummyPort - SurfacePort(); - + private: - bool control_in_use_timeout (Control *, Control *); - - MIDI::Port * _input_port; - MIDI::Port * _output_port; - int _number; - bool _active; - - Glib::RecMutex _rwlock; + Mackie::Surface* _surface; + MIDI::Port* _input_port; + MIDI::Port* _output_port; }; -std::ostream & operator << ( std::ostream & , const SurfacePort & port ); +std::ostream& operator << (std::ostream& , const SurfacePort& port); } diff --git a/libs/surfaces/mackie/timer.h b/libs/surfaces/mackie/timer.h index 88875539fe..96d7210ff6 100644 --- a/libs/surfaces/mackie/timer.h +++ b/libs/surfaces/mackie/timer.h @@ -1,19 +1,19 @@ /* -Copyright (C) 1998, 1999, 2000, 2007 John Anderson - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU Library General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU Library General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + Copyright (C) 1998, 1999, 2000, 2007 John Anderson + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef timer_h diff --git a/libs/surfaces/mackie/types.h b/libs/surfaces/mackie/types.h index be5c7e8b79..d906af6d32 100644 --- a/libs/surfaces/mackie/types.h +++ b/libs/surfaces/mackie/types.h @@ -23,6 +23,11 @@ namespace Mackie { +enum surface_type_t { + mcu, + ext, +}; + /** This started off as an enum, but it got really annoying typing ? on : off @@ -31,16 +36,18 @@ class LedState { public: enum state_t { none, off, flashing, on }; - LedState() : _state( none ) {} - LedState( bool yn ): _state( yn ? on : off ) {} - LedState( state_t state ): _state( state ) {} + LedState() : _state (none) {} + LedState (bool yn): _state (yn ? on : off) {} + LedState (state_t state): _state (state) {} - bool operator == ( const LedState & other ) const + LedState& operator= (state_t s) { _state = s; return *this; } + + bool operator == (const LedState & other) const { return state() == other.state(); } - bool operator != ( const LedState & other ) const + bool operator != (const LedState & other) const { return state() != other.state(); } @@ -66,13 +73,13 @@ struct ControlState { ControlState(): pos(0.0), sign(0), delta(0.0), ticks(0), led_state(off), button_state(neither) {} - ControlState( LedState ls ): pos(0.0), delta(0.0), led_state(ls), button_state(neither) {} + ControlState (LedState ls): pos(0.0), delta(0.0), led_state(ls), button_state(neither) {} // Note that this sets both pos and delta to the flt value - ControlState( LedState ls, float flt ): pos(flt), delta(flt), ticks(0), led_state(ls), button_state(neither) {} - ControlState( float flt ): pos(flt), delta(flt), ticks(0), led_state(none), button_state(neither) {} - ControlState( float flt, unsigned int tcks ): pos(flt), delta(flt), ticks(tcks), led_state(none), button_state(neither) {} - ControlState( ButtonState bs ): pos(0.0), delta(0.0), ticks(0), led_state(none), button_state(bs) {} + ControlState (LedState ls, float flt): pos(flt), delta(flt), ticks(0), led_state(ls), button_state(neither) {} + ControlState (float flt): pos(flt), delta(flt), ticks(0), led_state(none), button_state(neither) {} + ControlState (float flt, unsigned int tcks): pos(flt), delta(flt), ticks(tcks), led_state(none), button_state(neither) {} + ControlState (ButtonState bs): pos(0.0), delta(0.0), ticks(0), led_state(none), button_state(bs) {} /// For faders. Between 0 and 1. float pos; @@ -90,7 +97,7 @@ struct ControlState ButtonState button_state; }; -std::ostream & operator << ( std::ostream &, const ControlState & ); +std::ostream & operator << (std::ostream &, const ControlState &); class Control; class Fader; @@ -99,7 +106,6 @@ class Strip; class Group; class Pot; class Led; -class LedRing; } diff --git a/libs/surfaces/mackie/wscript b/libs/surfaces/mackie/wscript index ba8b8e5b4b..fe276c4e82 100644 --- a/libs/surfaces/mackie/wscript +++ b/libs/surfaces/mackie/wscript @@ -21,29 +21,30 @@ def configure(conf): def build(bld): obj = bld(features = 'cxx cxxshlib') obj.source = ''' - bcf_surface.cc - bcf_surface_generated.cc + button.cc controls.cc - dummy_port.cc + device_info.cc + device_profile.cc + fader.cc gui.cc interface.cc - mackie_button_handler.cc + jog.cc + jog_wheel.cc + led.cc mackie_control_protocol.cc - mackie_control_protocol_poll.cc - mackie_jog_wheel.cc - mackie_midi_builder.cc - mackie_port.cc - mackie_surface.cc - mackie_surface_generated.cc + mcp_buttons.cc + meter.cc midi_byte_array.cc - route_signal.cc + pot.cc + strip.cc surface.cc surface_port.cc types.cc ''' obj.export_includes = ['./mackie'] obj.cxxflags = '-DPACKAGE="ardour_mackie"' - obj.includes = ['.', './mackie'] + # need ../libs because some GTK2 header files require stuff there + obj.includes = ['.', '../libs'] obj.name = 'libardour_mcp' obj.target = 'ardour_mcp' obj.uselib = 'GTKMM' diff --git a/libs/surfaces/osc/osc.cc b/libs/surfaces/osc/osc.cc index a76943156c..bde72169b5 100644 --- a/libs/surfaces/osc/osc.cc +++ b/libs/surfaces/osc/osc.cc @@ -56,8 +56,6 @@ using namespace Glib; #include "pbd/abstract_ui.cc" // instantiate template -#define ui_bind(f, ...) boost::protect (boost::bind (f, __VA_ARGS__)) - OSC* OSC::_instance = 0; #ifdef DEBUG @@ -73,7 +71,7 @@ static void error_callback(int, const char *, const char *) #endif OSC::OSC (Session& s, uint32_t port) - : ControlProtocol (s, "OSC", this) + : ControlProtocol (s, "OSC") , AbstractUI ("osc") , _port(port) { @@ -90,7 +88,7 @@ OSC::OSC (Session& s, uint32_t port) // "Application Hooks" session_loaded (s); - session->Exported.connect (*this, MISSING_INVALIDATOR, ui_bind (&OSC::session_exported, this, _1, _2), this); + session->Exported.connect (*this, MISSING_INVALIDATOR, boost::bind (&OSC::session_exported, this, _1, _2), this); } OSC::~OSC() diff --git a/libs/surfaces/osc/osc_route_observer.cc b/libs/surfaces/osc/osc_route_observer.cc index bfef2fb67f..576b92d1bb 100644 --- a/libs/surfaces/osc/osc_route_observer.cc +++ b/libs/surfaces/osc/osc_route_observer.cc @@ -28,21 +28,18 @@ #include "i18n.h" -#define ui_bind(f, ...) boost::protect (boost::bind (f, __VA_ARGS__)) - using namespace std; using namespace sigc; using namespace PBD; using namespace ARDOUR; using namespace boost; - OSCRouteObserver::OSCRouteObserver (boost::shared_ptr r, lo_address a) : _route (r) { addr = lo_address_new (lo_address_get_hostname(a) , lo_address_get_port(a)); - _route->PropertyChanged.connect (name_changed_connection, MISSING_INVALIDATOR, ui_bind (&OSCRouteObserver::name_changed, this, boost::lambda::_1), OSC::instance()); + _route->PropertyChanged.connect (name_changed_connection, MISSING_INVALIDATOR, boost::bind (&OSCRouteObserver::name_changed, this, boost::lambda::_1), OSC::instance()); if (dynamic_pointer_cast(_route) || dynamic_pointer_cast(_route)) { diff --git a/libs/surfaces/powermate/powermate.cc b/libs/surfaces/powermate/powermate.cc index 44db8741ac..0475603a51 100644 --- a/libs/surfaces/powermate/powermate.cc +++ b/libs/surfaces/powermate/powermate.cc @@ -91,7 +91,7 @@ int find_powermate(int mode) } PowermateControlProtocol::PowermateControlProtocol (Session& s) - : ControlProtocol (s, "powermate", 0 /* XXX need an event loop here */) + : ControlProtocol (s, "powermate") { } diff --git a/libs/taglib/taglib/mpeg/id3v1/id3v1tag.cpp b/libs/taglib/taglib/mpeg/id3v1/id3v1tag.cpp index 490f8ddaf4..f698865b15 100644 --- a/libs/taglib/taglib/mpeg/id3v1/id3v1tag.cpp +++ b/libs/taglib/taglib/mpeg/id3v1/id3v1tag.cpp @@ -207,8 +207,9 @@ void ID3v1::Tag::read() // some initial sanity checking if(data.size() == 128 && data.startsWith("TAG")) parse(data); - else + else { debug("ID3v1 tag is not valid or could not be read at the specified offset."); + } } } diff --git a/libs/taglib/taglib/mpeg/id3v2/id3v2tag.cpp b/libs/taglib/taglib/mpeg/id3v2/id3v2tag.cpp index beb496c8a0..1e9eb3f83f 100644 --- a/libs/taglib/taglib/mpeg/id3v2/id3v2tag.cpp +++ b/libs/taglib/taglib/mpeg/id3v2/id3v2tag.cpp @@ -431,8 +431,9 @@ void ID3v2::Tag::parse(const ByteVector &origData) // portion of the frame data. if(data.at(frameDataPosition) == 0) { - if(d->header.footerPresent()) + if(d->header.footerPresent()) { debug("Padding *and* a footer found. This is not allowed by the spec."); + } d->paddingSize = frameDataLength - frameDataPosition; return; diff --git a/libs/taglib/taglib/ogg/flac/oggflacfile.cpp b/libs/taglib/taglib/ogg/flac/oggflacfile.cpp index 3070f3ab14..9281fbaf1d 100644 --- a/libs/taglib/taglib/ogg/flac/oggflacfile.cpp +++ b/libs/taglib/taglib/ogg/flac/oggflacfile.cpp @@ -256,8 +256,9 @@ void Ogg::FLAC::File::scan() d->hasXiphComment = true; d->commentPacket = ipacket; } - else if(blockType > 5) + else if(blockType > 5) { debug("Ogg::FLAC::File::scan() -- Unknown metadata block"); + } } diff --git a/libs/taglib/taglib/ogg/oggpage.cpp b/libs/taglib/taglib/ogg/oggpage.cpp index 0b82828467..da231a143b 100644 --- a/libs/taglib/taglib/ogg/oggpage.cpp +++ b/libs/taglib/taglib/ogg/oggpage.cpp @@ -151,8 +151,9 @@ ByteVectorList Ogg::Page::packets() const for(; it != packetSizes.end(); ++it) l.append(d->file->readBlock(*it)); } - else + else { debug("Ogg::Page::packets() -- attempting to read packets from an invalid page."); + } return l; } @@ -173,8 +174,9 @@ ByteVector Ogg::Page::render() const d->file->seek(d->packetOffset); data.append(d->file->readBlock(d->dataSize)); } - else + else { debug("Ogg::Page::render() -- this page is empty!"); + } } else { ByteVectorList::ConstIterator it = d->packets.begin(); diff --git a/libs/taglib/taglib/ogg/speex/speexproperties.cpp b/libs/taglib/taglib/ogg/speex/speexproperties.cpp index 29deb019c1..9790325175 100644 --- a/libs/taglib/taglib/ogg/speex/speexproperties.cpp +++ b/libs/taglib/taglib/ogg/speex/speexproperties.cpp @@ -161,10 +161,12 @@ void Speex::Properties::read() if(start >= 0 && end >= 0 && d->sampleRate > 0) d->length = (int) ((end - start) / (long long) d->sampleRate); - else + else { debug("Speex::Properties::read() -- Either the PCM values for the start or " "end of this file was incorrect or the sample rate is zero."); + } } - else + else { debug("Speex::Properties::read() -- Could not find valid first and last Ogg pages."); + } } diff --git a/libs/taglib/taglib/ogg/vorbis/vorbisproperties.cpp b/libs/taglib/taglib/ogg/vorbis/vorbisproperties.cpp index c82a3191f3..492a0fdccb 100644 --- a/libs/taglib/taglib/ogg/vorbis/vorbisproperties.cpp +++ b/libs/taglib/taglib/ogg/vorbis/vorbisproperties.cpp @@ -174,10 +174,12 @@ void Vorbis::Properties::read() if(start >= 0 && end >= 0 && d->sampleRate > 0) d->length = (end - start) / (long long) d->sampleRate; - else + else { debug("Vorbis::Properties::read() -- Either the PCM values for the start or " "end of this file was incorrect or the sample rate is zero."); + } } - else + else { debug("Vorbis::Properties::read() -- Could not find valid first and last Ogg pages."); + } } diff --git a/libs/taglib/taglib/toolkit/tfile.cpp b/libs/taglib/taglib/toolkit/tfile.cpp index 7090a64199..f63a06612a 100644 --- a/libs/taglib/taglib/toolkit/tfile.cpp +++ b/libs/taglib/taglib/toolkit/tfile.cpp @@ -114,8 +114,9 @@ File::FilePrivate::FilePrivate(FileName fileName) : else file = fopen(name, "rb"); - if(!file) + if(!file) { debug("Could not open file " + String((const char *) name)); + } } //////////////////////////////////////////////////////////////////////////////// diff --git a/libs/taglib/taglib/toolkit/tstring.cpp b/libs/taglib/taglib/toolkit/tstring.cpp index ffb706f70a..6611e7cdca 100644 --- a/libs/taglib/taglib/toolkit/tstring.cpp +++ b/libs/taglib/taglib/toolkit/tstring.cpp @@ -232,8 +232,9 @@ std::string String::to8Bit(bool unicode) const &target, targetBuffer + outputBufferSize, Unicode::lenientConversion); - if(result != Unicode::conversionOK) + if(result != Unicode::conversionOK) { debug("String::to8Bit() - Unicode conversion error."); + } int newSize = target - targetBuffer; s.resize(newSize); @@ -728,8 +729,9 @@ void String::prepare(Type t) &target, targetBuffer + bufferSize, Unicode::lenientConversion); - if(result != Unicode::conversionOK) + if(result != Unicode::conversionOK) { debug("String::prepare() - Unicode conversion error."); + } int newSize = target != targetBuffer ? target - targetBuffer - 1 : 0; diff --git a/mcp/mcpro.device b/mcp/mcpro.device new file mode 100644 index 0000000000..016c02fa4a --- /dev/null +++ b/mcp/mcpro.device @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/mcp/mcproxt.device b/mcp/mcproxt.device new file mode 100644 index 0000000000..168d974505 --- /dev/null +++ b/mcp/mcproxt.device @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/mcp/nucleus.device b/mcp/nucleus.device new file mode 100644 index 0000000000..1456d4e8a7 --- /dev/null +++ b/mcp/nucleus.device @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/mcp/paul-nucleus.profile b/mcp/paul-nucleus.profile new file mode 100644 index 0000000000..fec778b9dc --- /dev/null +++ b/mcp/paul-nucleus.profile @@ -0,0 +1,19 @@ + + + + +