From 60fbeedb5f819b42fdca1f8175319aa2a9bca4c1 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Thu, 18 May 2006 02:19:27 +0000 Subject: [PATCH] a) basic prototype of OSC control b) various changes to ControlProtocol model/implementation c) more attempts to get autoscroll to work nicely (unfinished) d) move editor item types into their own header git-svn-id: svn://localhost/trunk/ardour2@506 d708f5d6-7413-0410-9779-e7cbd77b26cf --- SConstruct | 15 + gtk2_ardour/draginfo.h | 3 + gtk2_ardour/editor.cc | 46 +- gtk2_ardour/editor.h | 53 +-- gtk2_ardour/editor_mouse.cc | 54 +-- libs/ardour/ardour/basic_ui.h | 14 +- libs/ardour/ardour/control_protocol.h | 41 ++ libs/ardour/basic_ui.cc | 2 +- libs/ardour/control_protocol.cc | 269 ++++++++++++ libs/surfaces/osc/SConscript | 52 +++ libs/surfaces/osc/i18n.h | 16 + libs/surfaces/osc/interface.cc | 44 ++ libs/surfaces/osc/osc_server.cc | 392 ++++++++++++++++++ libs/surfaces/osc/osc_server.h | 102 +++++ .../tranzport/tranzport_control_protocol.cc | 136 ++---- .../tranzport/tranzport_control_protocol.h | 1 - 16 files changed, 1041 insertions(+), 199 deletions(-) create mode 100644 libs/surfaces/osc/SConscript create mode 100644 libs/surfaces/osc/i18n.h create mode 100644 libs/surfaces/osc/interface.cc create mode 100644 libs/surfaces/osc/osc_server.cc create mode 100644 libs/surfaces/osc/osc_server.h diff --git a/SConstruct b/SConstruct index 807643df6e..f8093e4f6f 100644 --- a/SConstruct +++ b/SConstruct @@ -418,6 +418,19 @@ else: libraries['usb'] = conf.Finish () +# +# Check for liblo + +libraries['lo'] = LibraryInfo () + +conf = Configure (libraries['lo']) +if conf.CheckLib ('lo', 'lo_server_new'): + have_liblo = True +else: + have_liblo = False + +libraries['lo'] = conf.Finish () + # # Check for dmalloc @@ -558,6 +571,8 @@ if env['SURFACES']: surface_subdirs += [ 'libs/surfaces/generic_midi' ] if have_libusb: surface_subdirs += [ 'libs/surfaces/tranzport' ] + if have_liblo: + surface_subdirs += [ 'libs/surfaces/osc' ] opts.Save('scache.conf', env) Help(opts.GenerateHelpText(env)) diff --git a/gtk2_ardour/draginfo.h b/gtk2_ardour/draginfo.h index 3db8bab1e8..a86f7362cc 100644 --- a/gtk2_ardour/draginfo.h +++ b/gtk2_ardour/draginfo.h @@ -5,6 +5,8 @@ #include #include "canvas.h" +#include "editor_items.h" + #include namespace ARDOUR { @@ -16,6 +18,7 @@ class TimeAxisView; struct DragInfo { ArdourCanvas::Item* item; + ItemType item_type; void* data; jack_nframes_t last_frame_position; int32_t pointer_frame_offset; diff --git a/gtk2_ardour/editor.cc b/gtk2_ardour/editor.cc index 2c6e981bd7..5df36c91b1 100644 --- a/gtk2_ardour/editor.cc +++ b/gtk2_ardour/editor.cc @@ -2844,6 +2844,10 @@ Editor::autoscroll_canvas () { jack_nframes_t new_frame; bool keep_calling = true; + jack_nframes_t limit = max_frames - current_page_frames(); + GdkEventMotion ev; + + autoscroll_distance = current_page_frames() * 3 / 4; if (autoscroll_direction < 0) { if (leftmost_frame < autoscroll_distance) { @@ -2851,42 +2855,62 @@ Editor::autoscroll_canvas () } else { new_frame = leftmost_frame - autoscroll_distance; } + ev.x = drag_info.current_pointer_x - autoscroll_distance; } else { - if (leftmost_frame > max_frames - autoscroll_distance) { - new_frame = max_frames; + if (leftmost_frame > limit - autoscroll_distance) { + new_frame = limit; } else { new_frame = leftmost_frame + autoscroll_distance; } + ev.x = drag_info.current_pointer_x + autoscroll_distance; } if (new_frame != leftmost_frame) { + cerr << "move to " << new_frame << " which is " << autoscroll_distance << " away" << endl; reposition_x_origin (new_frame); } - if (new_frame == 0 || new_frame == max_frames) { + /* now fake a motion event to get the object that is being dragged to move too */ + + ev.type = GDK_MOTION_NOTIFY; + ev.x = frame_to_unit (ev.x); + ev.y = frame_to_unit (drag_info.current_pointer_y); + motion_handler (drag_info.item, (GdkEvent*) &ev, drag_info.item_type, true); + + if (new_frame == 0 || new_frame == limit) { /* we are done */ - return FALSE; + return false; } + return false; + autoscroll_cnt++; if (autoscroll_cnt == 1) { /* connect the timeout so that we get called repeatedly */ - - autoscroll_timeout_tag = gtk_timeout_add (40, _autoscroll_canvas, this); - } else if (autoscroll_cnt > 10 && autoscroll_cnt < 20) { + autoscroll_timeout_tag = gtk_timeout_add (20, _autoscroll_canvas, this); + keep_calling = false; + + } else if (autoscroll_cnt == 50) { /* 0.5 seconds */ /* after about a while, speed up a bit by changing the timeout interval */ - autoscroll_timeout_tag = gtk_timeout_add (20, _autoscroll_canvas, this); + autoscroll_distance = (jack_nframes_t) floor (current_page_frames()/50.0f); + cerr << "change distance to " << autoscroll_distance << endl; - } else if (autoscroll_cnt >= 30) { + } else if (autoscroll_cnt == 75) { /* 1.0 seconds */ + + autoscroll_distance = (jack_nframes_t) floor (current_page_frames()/20.0f); + cerr << "change distance to " << autoscroll_distance << endl; + + } else if (autoscroll_cnt == 100) { /* 1.5 seconds */ /* after about another while, speed up by increasing the shift per callback */ - autoscroll_distance = (jack_nframes_t) floor (0.5 * current_page_frames()); + autoscroll_distance = (jack_nframes_t) floor (current_page_frames()/10.0f); + cerr << "change distance to " << autoscroll_distance << endl; } @@ -2903,7 +2927,7 @@ Editor::start_canvas_autoscroll (int dir) stop_canvas_autoscroll (); autoscroll_direction = dir; - autoscroll_distance = (jack_nframes_t) floor ((canvas_width * frames_per_unit)/50.0); + autoscroll_distance = (jack_nframes_t) floor (current_page_frames()/100.0); autoscroll_cnt = 0; /* do it right now, which will start the repeated callbacks */ diff --git a/gtk2_ardour/editor.h b/gtk2_ardour/editor.h index 8770c6c93f..91f2bc9810 100644 --- a/gtk2_ardour/editor.h +++ b/gtk2_ardour/editor.h @@ -53,6 +53,7 @@ #include "public_editor.h" #include "editing.h" #include "enums.h" +#include "editor_items.h" #include "region_selection.h" #include "canvas.h" #include "draginfo.h" @@ -347,56 +348,6 @@ class Editor : public PublicEditor PlaylistSelector* _playlist_selector; - enum ItemType { - RegionItem, - StreamItem, - PlayheadCursorItem, - EditCursorItem, - MarkerItem, - MarkerBarItem, - RangeMarkerBarItem, - TransportMarkerBarItem, - SelectionItem, - GainControlPointItem, - GainLineItem, - GainAutomationControlPointItem, - GainAutomationLineItem, - PanAutomationControlPointItem, - PanAutomationLineItem, - RedirectAutomationControlPointItem, - RedirectAutomationLineItem, - MeterMarkerItem, - TempoMarkerItem, - MeterBarItem, - TempoBarItem, - AudioRegionViewNameHighlight, - AudioRegionViewName, - StartSelectionTrimItem, - EndSelectionTrimItem, - AutomationTrackItem, - FadeInItem, - FadeInHandleItem, - FadeOutItem, - FadeOutHandleItem, - - /* */ - MarkerViewItem, - MarkerTimeAxisItem, - MarkerViewHandleStartItem, - MarkerViewHandleEndItem, - ImageFrameItem, - ImageFrameTimeAxisItem, - ImageFrameHandleStartItem, - ImageFrameHandleEndItem, - /* */ - - CrossfadeViewItem, - - /* don't remove this */ - - NoItem - }; - void set_frames_per_unit (double); void frames_per_unit_modified (); @@ -861,7 +812,7 @@ class Editor : public PublicEditor bool typed_event (ArdourCanvas::Item*, GdkEvent*, ItemType); bool button_press_handler (ArdourCanvas::Item*, GdkEvent*, ItemType); bool button_release_handler (ArdourCanvas::Item*, GdkEvent*, ItemType); - bool motion_handler (ArdourCanvas::Item*, GdkEvent*, ItemType); + bool motion_handler (ArdourCanvas::Item*, GdkEvent*, ItemType, bool from_autoscroll = false); bool enter_handler (ArdourCanvas::Item*, GdkEvent*, ItemType); bool leave_handler (ArdourCanvas::Item*, GdkEvent*, ItemType); diff --git a/gtk2_ardour/editor_mouse.cc b/gtk2_ardour/editor_mouse.cc index 79053a2e52..21e99ae202 100644 --- a/gtk2_ardour/editor_mouse.cc +++ b/gtk2_ardour/editor_mouse.cc @@ -1107,26 +1107,23 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT void Editor::maybe_autoscroll (GdkEvent* event) { - jack_nframes_t one_page = (jack_nframes_t) rint (canvas_width * frames_per_unit); - jack_nframes_t rightmost_frame = leftmost_frame + one_page; + jack_nframes_t rightmost_frame = leftmost_frame + current_page_frames(); jack_nframes_t frame = drag_info.current_pointer_frame; - if (autoscroll_timeout_tag < 0) { - if (frame > rightmost_frame) { - if (rightmost_frame < max_frames) { - start_canvas_autoscroll (1); - } - } else if (frame < leftmost_frame) { - if (leftmost_frame > 0) { - start_canvas_autoscroll (-1); - } - } - } else { - if (frame >= leftmost_frame && frame < rightmost_frame) { - stop_canvas_autoscroll (); + cerr << "maybe autoscroll @ " << frame << " left = " << leftmost_frame << " right = " << rightmost_frame << endl; + + if (frame > rightmost_frame) { + if (rightmost_frame < max_frames) { + autoscroll_direction = 1; + autoscroll_canvas (); } - } + } else if (frame < leftmost_frame) { + if (leftmost_frame > 0) { + autoscroll_direction = -1; + autoscroll_canvas (); + } + } } bool @@ -1469,7 +1466,7 @@ Editor::left_automation_track () } bool -Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type) +Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll) { gint x, y; @@ -1495,10 +1492,11 @@ Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item return true; } + drag_info.item_type = item_type; drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x, &drag_info.current_pointer_y); - if (drag_info.item) { + if (!from_autoscroll && drag_info.item) { /* item != 0 is the best test i can think of for dragging. */ @@ -1544,12 +1542,14 @@ Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item /* */ if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK || (event->motion.state & Gdk::BUTTON2_MASK))) { - maybe_autoscroll (event); - (this->*(drag_info.motion_callback)) (item, event); - goto handled; - } - goto not_handled; - + if (!from_autoscroll) { + maybe_autoscroll (event); + } + (this->*(drag_info.motion_callback)) (item, event); + goto handled; + } + goto not_handled; + default: break; } @@ -1561,7 +1561,9 @@ Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item case MouseTimeFX: if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK || (event->motion.state & GDK_BUTTON2_MASK))) { - maybe_autoscroll (event); + if (!from_autoscroll) { + maybe_autoscroll (event); + } (this->*(drag_info.motion_callback)) (item, event); goto handled; } @@ -2770,6 +2772,8 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) vector height_list(512) ; vector::iterator j; + cerr << "region motion to " << drag_info.current_pointer_frame << endl; + /* Which trackview is this ? */ TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y); diff --git a/libs/ardour/ardour/basic_ui.h b/libs/ardour/ardour/basic_ui.h index ddfe0c5a01..a2395f8f59 100644 --- a/libs/ardour/ardour/basic_ui.h +++ b/libs/ardour/ardour/basic_ui.h @@ -10,25 +10,31 @@ class BasicUI { BasicUI (ARDOUR::Session&); virtual ~BasicUI (); + void add_marker (); + + /* transport control */ + void loop_toggle (); void goto_start (); void goto_end (); - void add_marker (); void rewind (); void ffwd (); void transport_stop (); void transport_play (); - void rec_enable_toggle (); + void set_transport_speed (float speed); + float get_transport_speed (float speed); + void save_state (); void prev_marker (); void next_marker (); - void move_at (float speed); void undo (); void redo (); - void toggle_all_rec_enables (); void toggle_punch_in (); void toggle_punch_out (); + void rec_enable_toggle (); + void toggle_all_rec_enables (); + protected: ARDOUR::Session& session; }; diff --git a/libs/ardour/ardour/control_protocol.h b/libs/ardour/ardour/control_protocol.h index 70c7d2dc0d..9a9c14021c 100644 --- a/libs/ardour/ardour/control_protocol.h +++ b/libs/ardour/ardour/control_protocol.h @@ -2,6 +2,7 @@ #define ardour_control_protocols_h #include +#include #include #include @@ -35,9 +36,49 @@ class ControlProtocol : public sigc::trackable, public BasicUI { static sigc::signal Enter; static sigc::signal ScrollTimeline; + /* the model here is as follows: + + we imagine most control surfaces being able to control + from 1 to N tracks at a time, with a session that may + contain 1 to M tracks, where M may be smaller, larger or + equal to N. + + the control surface has a fixed set of physical controllers + which can potentially be mapped onto different tracks/busses + via some mechanism. + + therefore, the control protocol object maintains + a table that reflects the current mapping between + the controls and route object. + */ + + void set_route_table_size (uint32_t size); + void set_route_table (uint32_t table_index, ARDOUR::Route*); + + void route_set_rec_enable (uint32_t table_index, bool yn); + bool route_get_rec_enable (uint32_t table_index); + + float route_get_gain (uint32_t table_index); + void route_set_gain (uint32_t table_index, float); + float route_get_effective_gain (uint32_t table_index); + + float route_get_peak_input_power (uint32_t table_index, uint32_t which_input); + + bool route_get_muted (uint32_t table_index); + void route_set_muted (uint32_t table_index, bool); + + bool route_get_soloed (uint32_t table_index); + void route_set_soloed (uint32_t table_index, bool); + + std::string route_get_name (uint32_t table_index); + protected: + std::vector route_table; std::string _name; bool _active; + + void next_track (uint32_t initial_id); + void prev_track (uint32_t initial_id); }; extern "C" { diff --git a/libs/ardour/basic_ui.cc b/libs/ardour/basic_ui.cc index cfed836eab..02e45ac84a 100644 --- a/libs/ardour/basic_ui.cc +++ b/libs/ardour/basic_ui.cc @@ -157,7 +157,7 @@ BasicUI::next_marker () } void -BasicUI::move_at (float speed) +BasicUI::set_transport_speed (float speed) { session.request_transport_speed (speed); } diff --git a/libs/ardour/control_protocol.cc b/libs/ardour/control_protocol.cc index b7004a0ffa..222ecbf279 100644 --- a/libs/ardour/control_protocol.cc +++ b/libs/ardour/control_protocol.cc @@ -19,6 +19,9 @@ */ #include +#include +#include +#include using namespace ARDOUR; using namespace std; @@ -40,3 +43,269 @@ ControlProtocol::~ControlProtocol () { } +void +ControlProtocol::next_track (uint32_t initial_id) +{ + uint32_t limit = session.nroutes(); + Route* cr = route_table[0]; + uint32_t id; + + if (cr) { + id = cr->remote_control_id (); + } else { + id = 0; + } + + if (id == limit) { + id = 0; + } else { + id++; + } + + while (id < limit) { + if ((cr = session.route_by_remote_id (id)) != 0) { + break; + } + id++; + } + + if (id == limit) { + id = 0; + while (id != initial_id) { + if ((cr = session.route_by_remote_id (id)) != 0) { + break; + } + id++; + } + } + + route_table[0] = cr; +} + +void +ControlProtocol::prev_track (uint32_t initial_id) +{ + uint32_t limit = session.nroutes() - 1; + Route* cr = route_table[0]; + uint32_t id; + + if (cr) { + id = cr->remote_control_id (); + } else { + id = 0; + } + + if (id == 0) { + id = session.nroutes() - 1; + } else { + id--; + } + + while (id >= 0) { + if ((cr = session.route_by_remote_id (id)) != 0) { + break; + } + id--; + } + + if (id < 0) { + id = limit; + while (id > initial_id) { + if ((cr = session.route_by_remote_id (id)) != 0) { + break; + } + id--; + } + } + + route_table[0] = cr; +} + + +void +ControlProtocol::set_route_table_size (uint32_t size) +{ + while (route_table.size() < size) { + route_table.push_back (0); + } +} + +void +ControlProtocol::set_route_table (uint32_t table_index, ARDOUR::Route*) +{ +} + +void +ControlProtocol::route_set_rec_enable (uint32_t table_index, bool yn) +{ + if (table_index > route_table.size()) { + return; + } + + Route* r = route_table[table_index]; + + AudioTrack* at = dynamic_cast(r); + + if (at) { + at->set_record_enable (yn, this); + } +} + +bool +ControlProtocol::route_get_rec_enable (uint32_t table_index) +{ + if (table_index > route_table.size()) { + return false; + } + + Route* r = route_table[table_index]; + + AudioTrack* at = dynamic_cast(r); + + if (at) { + at->record_enabled (); + } +} + + +float +ControlProtocol::route_get_gain (uint32_t table_index) +{ + if (table_index > route_table.size()) { + return 0.0f; + } + + Route* r = route_table[table_index]; + + if (r == 0) { + return 0.0f; + } + + return r->gain (); +} + +void +ControlProtocol::route_set_gain (uint32_t table_index, float gain) +{ + if (table_index > route_table.size()) { + return; + } + + Route* r = route_table[table_index]; + + if (r != 0) { + r->set_gain (gain, this); + } +} + +float +ControlProtocol::route_get_effective_gain (uint32_t table_index) +{ + if (table_index > route_table.size()) { + return 0.0f; + } + + Route* r = route_table[table_index]; + + if (r == 0) { + return 0.0f; + } + + return r->effective_gain (); +} + + +float +ControlProtocol::route_get_peak_input_power (uint32_t table_index, uint32_t which_input) +{ + if (table_index > route_table.size()) { + return 0.0f; + } + + Route* r = route_table[table_index]; + + if (r == 0) { + return 0.0f; + } + + return r->peak_input_power (which_input); +} + + +bool +ControlProtocol::route_get_muted (uint32_t table_index) +{ + if (table_index > route_table.size()) { + return false; + } + + Route* r = route_table[table_index]; + + if (r == 0) { + return false; + } + + return r->muted (); +} + +void +ControlProtocol::route_set_muted (uint32_t table_index, bool yn) +{ + if (table_index > route_table.size()) { + return; + } + + Route* r = route_table[table_index]; + + if (r != 0) { + r->set_mute (yn, this); + } +} + + +bool +ControlProtocol::route_get_soloed (uint32_t table_index) +{ + if (table_index > route_table.size()) { + return false; + } + + Route* r = route_table[table_index]; + + if (r == 0) { + return false; + } + + return r->soloed (); +} + +void +ControlProtocol::route_set_soloed (uint32_t table_index, bool yn) +{ + if (table_index > route_table.size()) { + return; + } + + Route* r = route_table[table_index]; + + if (r != 0) { + r->set_solo (yn, this); + } +} + +string +ControlProtocol:: route_get_name (uint32_t table_index) +{ + if (table_index > route_table.size()) { + return ""; + } + + Route* r = route_table[table_index]; + + if (r == 0) { + return ""; + } + + return r->name(); +} + diff --git a/libs/surfaces/osc/SConscript b/libs/surfaces/osc/SConscript new file mode 100644 index 0000000000..c01fe88edf --- /dev/null +++ b/libs/surfaces/osc/SConscript @@ -0,0 +1,52 @@ +# -*- python -*- + +import os +import os.path +import glob + +Import('env final_prefix install_prefix final_config_prefix libraries i18n') + +osc = env.Copy() + +# +# this defines the version number of libardour_osc +# + +domain = 'ardour_osc' + +osc.Append(DOMAIN = domain, MAJOR = 1, MINOR = 0, MICRO = 0) +osc.Append(CXXFLAGS = "-DPACKAGE=\\\"" + domain + "\\\"") +osc.Append(CXXFLAGS="-DLIBSIGC_DISABLE_DEPRECATED") +osc.Append(PACKAGE = domain) +osc.Append(POTFILE = domain + '.pot') + +osc_files=Split(""" +interface.cc +osc_server.cc +""") + +osc.Append(CCFLAGS="-D_REENTRANT -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE") +osc.Append(CXXFLAGS="-DDATA_DIR=\\\""+final_prefix+"/share\\\"") +osc.Append(CXXFLAGS="-DCONFIG_DIR=\\\""+final_config_prefix+"\\\"") +osc.Append(CXXFLAGS="-DLOCALEDIR=\\\""+final_prefix+"/share/locale\\\"") + +osc.Merge ([ + libraries['ardour'], + libraries['sigc2'], + libraries['pbd3'], + libraries['lo'] +]) + +libardour_osc = osc.SharedLibrary('ardour_osc', osc_files) + +Default(libardour_osc) + +if env['NLS']: + i18n (osc, osc_files, env) + +env.Alias('install', env.Install(os.path.join(install_prefix, 'lib/ardour2/surfaces'), libardour_osc)) + +env.Alias('tarball', env.Distribute (env['DISTTREE'], + [ 'SConscript', 'i18n.h', 'gettext.h' ] + + osc_files + + glob.glob('po/*.po') + glob.glob('*.h'))) diff --git a/libs/surfaces/osc/i18n.h b/libs/surfaces/osc/i18n.h new file mode 100644 index 0000000000..3ace250419 --- /dev/null +++ b/libs/surfaces/osc/i18n.h @@ -0,0 +1,16 @@ +#ifndef __i18n_h__ +#define __i18n_h__ + +#include +#include "gettext.h" + +#include +#include + +std::vector internationalize (const char **); + +#define _(Text) dgettext (PACKAGE,Text) +#define N_(Text) gettext_noop (Text) +#define X_(Text) Text + +#endif // __i18n_h__ diff --git a/libs/surfaces/osc/interface.cc b/libs/surfaces/osc/interface.cc new file mode 100644 index 0000000000..3ec5a6571f --- /dev/null +++ b/libs/surfaces/osc/interface.cc @@ -0,0 +1,44 @@ +#include + +#include "osc_server.h" + +using namespace ARDOUR; + +ControlProtocol* +new_osc_protocol (ControlProtocolDescriptor* descriptor, Session* s) +{ + ControlOSC* osc = new ControlOSC (*s, 3891); + + if (osc->set_active (true)) { + delete osc; + return 0; + } + + return osc; + +} + +void +delete_osc_protocol (ControlProtocolDescriptor* descriptor, ControlProtocol* cp) +{ + delete cp; +} + +static ControlProtocolDescriptor osc_descriptor = { + name : "OSC", + id : "uri://ardour.org/surfaces/osc:0", + ptr : 0, + module : 0, + initialize : new_osc_protocol, + destroy : delete_osc_protocol + +}; + + +extern "C" { +ControlProtocolDescriptor* +protocol_descriptor () { + return &osc_descriptor; +} +} + diff --git a/libs/surfaces/osc/osc_server.cc b/libs/surfaces/osc/osc_server.cc new file mode 100644 index 0000000000..5d0a2daa4d --- /dev/null +++ b/libs/surfaces/osc/osc_server.cc @@ -0,0 +1,392 @@ +/* +** Copyright (C) 2004 Jesse Chappell +** +** 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +*/ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include + +#include "osc_server.h" + +#include "i18n.h" + +using namespace sigc; +using namespace std; + +#include + +static void error_callback(int num, const char *m, const char *path) +{ +#ifdef DEBUG + fprintf(stderr, "liblo server error %d in path %s: %s\n", num, path, m); +#endif +} + +ControlOSC::ControlOSC (ARDOUR::Session& s, uint32_t port) + : ControlProtocol(s, X_("OSC")), + AbstractUI (X_("OSC"), false), + _port(port) +{ + _shutdown = false; + _osc_server = 0; + _osc_unix_server = 0; + _osc_thread = 0; +} + +int +ControlOSC::set_active (bool yn) +{ + if (yn) { + + char tmpstr[255]; + + for (int j=0; j < 20; ++j) { + snprintf(tmpstr, sizeof(tmpstr), "%d", _port); + + if ((_osc_server = lo_server_new (tmpstr, error_callback))) { + break; + } +#ifdef DEBUG + cerr << "can't get osc at port: " << _port << endl; +#endif + _port++; + continue; + } + +#ifdef ARDOUR_OSC_UNIX_SERVER + + // APPEARS sluggish for now + + // attempt to create unix socket server too + + snprintf(tmpstr, sizeof(tmpstr), "/tmp/sooperlooper_XXXXXX"); + int fd = mkstemp(tmpstr); + + if (fd >= 0 ) { + unlink (tmpstr); + close (fd); + + _osc_unix_server = lo_server_new (tmpstr, error_callback); + + if (_osc_unix_server) { + _osc_unix_socket_path = tmpstr; + } + } +#endif + + register_callbacks(); + + on_session_load (); + + // lo_server_thread_add_method(_sthread, NULL, NULL, ControlOSC::_dummy_handler, this); + + if (!init_osc_thread()) { + return -1; + } + + } else { + + /* need to stop the OSC UDP server */ + + if (!_osc_unix_socket_path.empty()) { + // unlink it + unlink(_osc_unix_socket_path.c_str()); + } + + // stop server thread + terminate_osc_thread(); + } + + return 0; +} + +bool +ControlOSC::caller_is_ui_thread () +{ + return false; +} + +ControlOSC::~ControlOSC() +{ + set_active (false); +} + +void +ControlOSC::register_callbacks() +{ + lo_server srvs[2]; + lo_server serv; + + srvs[0] = _osc_server; + srvs[1] = _osc_unix_server; + + for (size_t i = 0; i < 2; ++i) { + + if (!srvs[i]) { + continue; + } + + serv = srvs[i]; + +#define REGISTER_CALLBACK(serv,path,types, function) lo_server_add_method (serv, path, types, ControlOSC::_ ## function, this) + + REGISTER_CALLBACK (serv, "/session/add_marker", "", add_marker); + REGISTER_CALLBACK (serv, "/session/loop_toggle", "", loop_toggle); + REGISTER_CALLBACK (serv, "/session/goto_start", "", goto_start); + REGISTER_CALLBACK (serv, "/session/goto_end", "", goto_end); + REGISTER_CALLBACK (serv, "/session/rewind", "", rewind); + REGISTER_CALLBACK (serv, "/session/ffwd", "", ffwd); + REGISTER_CALLBACK (serv, "/session/transport_stop", "", transport_stop); + REGISTER_CALLBACK (serv, "/session/transport_play", "", transport_play); + REGISTER_CALLBACK (serv, "/session/set_transport_speed", "f", set_transport_speed); + REGISTER_CALLBACK (serv, "/session/save_state", "", save_state); + REGISTER_CALLBACK (serv, "/session/prev_marker", "", prev_marker); + REGISTER_CALLBACK (serv, "/session/next_marker", "", next_marker); + REGISTER_CALLBACK (serv, "/session/undo", "", undo); + REGISTER_CALLBACK (serv, "/session/redo", "", redo); + REGISTER_CALLBACK (serv, "/session/toggle_punch_in", "", toggle_punch_in); + REGISTER_CALLBACK (serv, "/session/toggle_punch_out", "", toggle_punch_out); + REGISTER_CALLBACK (serv, "/session/rec_enable_toggle", "", rec_enable_toggle); + REGISTER_CALLBACK (serv, "/session/toggle_all_rec_enables", "", toggle_all_rec_enables); + +#if 0 + + lo_server_add_method(serv, "/session/set", "ss", ControlOSC::global_set_handler, this); + lo_server_add_method(serv, "/session/get", "ss", ControlOSC::global_get_handler, this); + + // un/register_update args= s:ctrl s:returl s:retpath + lo_server_add_method(serv, "/register_update", "sss", ControlOSC::global_register_update_handler, this); + lo_server_add_method(serv, "/unregister_update", "sss", ControlOSC::global_unregister_update_handler, this); + lo_server_add_method(serv, "/register_auto_update", "siss", ControlOSC::global_register_auto_update_handler, this); + lo_server_add_method(serv, "/unregister_auto_update", "sss", ControlOSC::_global_unregister_auto_update_handler, this); +#endif + } +} + +bool +ControlOSC::init_osc_thread () +{ + // create new thread to run server + if (pipe (_request_pipe)) { + cerr << "Cannot create osc request signal pipe" << strerror (errno) << endl; + return false; + } + + if (fcntl (_request_pipe[0], F_SETFL, O_NONBLOCK)) { + cerr << "osc: cannot set O_NONBLOCK on signal read pipe " << strerror (errno) << endl; + return false; + } + + if (fcntl (_request_pipe[1], F_SETFL, O_NONBLOCK)) { + cerr << "osc: cannot set O_NONBLOCK on signal write pipe " << strerror (errno) << endl; + return false; + } + + pthread_create (&_osc_thread, NULL, &ControlOSC::_osc_receiver, this); + if (!_osc_thread) { + return false; + } + + //pthread_detach (_osc_thread); + return true; +} + +void +ControlOSC::terminate_osc_thread () +{ + void* status; + + _shutdown = true; + + poke_osc_thread (); + + pthread_join (_osc_thread, &status); +} + +void +ControlOSC::poke_osc_thread () +{ + char c; + + if (write (_request_pipe[1], &c, 1) != 1) { + cerr << "cannot send signal to osc thread! " << strerror (errno) << endl; + } +} + +void +ControlOSC::on_session_load () +{ +} + +void +ControlOSC::on_session_unload () +{ + // will be called from main event loop +} + + +std::string +ControlOSC::get_server_url() +{ + string url; + char * urlstr; + + if (_osc_server) { + urlstr = lo_server_get_url (_osc_server); + url = urlstr; + free (urlstr); + } + + return url; +} + +std::string +ControlOSC::get_unix_server_url() +{ + string url; + char * urlstr; + + if (_osc_unix_server) { + urlstr = lo_server_get_url (_osc_unix_server); + url = urlstr; + free (urlstr); + } + + return url; +} + + +/* server thread */ + +void * +ControlOSC::_osc_receiver(void * arg) +{ + static_cast (arg)->osc_receiver(); + return 0; +} + +void +ControlOSC::osc_receiver() +{ + struct pollfd pfd[3]; + int fds[3]; + lo_server srvs[3]; + int nfds = 0; + int timeout = -1; + int ret; + + fds[0] = _request_pipe[0]; + nfds++; + + if (_osc_server && lo_server_get_socket_fd(_osc_server) >= 0) { + fds[nfds] = lo_server_get_socket_fd(_osc_server); + srvs[nfds] = _osc_server; + nfds++; + } + + if (_osc_unix_server && lo_server_get_socket_fd(_osc_unix_server) >= 0) { + fds[nfds] = lo_server_get_socket_fd(_osc_unix_server); + srvs[nfds] = _osc_unix_server; + nfds++; + } + + + while (!_shutdown) { + + for (int i=0; i < nfds; ++i) { + pfd[i].fd = fds[i]; + pfd[i].events = POLLIN|POLLPRI|POLLHUP|POLLERR; + pfd[i].revents = 0; + } + + again: + //cerr << "poll on " << nfds << " for " << timeout << endl; + if ((ret = poll (pfd, nfds, timeout)) < 0) { + if (errno == EINTR) { + /* gdb at work, perhaps */ + cerr << "EINTR hit " << endl; + goto again; + } + + cerr << "OSC thread poll failed: " << strerror (errno) << endl; + + break; + } + + //cerr << "poll returned " << ret << " pfd[0].revents = " << pfd[0].revents << " pfd[1].revents = " << pfd[1].revents << endl; + + if (_shutdown) { + break; + } + + if ((pfd[0].revents & ~POLLIN)) { + cerr << "OSC: error polling extra port" << endl; + break; + } + + for (int i=1; i < nfds; ++i) { + if (pfd[i].revents & POLLIN) + { + // this invokes callbacks + //cerr << "invoking recv on " << pfd[i].fd << endl; + lo_server_recv(srvs[i]); + } + } + + } + + //cerr << "SL engine shutdown" << endl; + + if (_osc_server) { + int fd = lo_server_get_socket_fd(_osc_server); + if (fd >=0) { + // hack around + close(fd); + } + lo_server_free (_osc_server); + _osc_server = 0; + } + + if (_osc_unix_server) { + cerr << "freeing unix server" << endl; + lo_server_free (_osc_unix_server); + _osc_unix_server = 0; + } + + close(_request_pipe[0]); + close(_request_pipe[1]); +} + +void +ControlOSC::do_request (OSCRequest* req) +{ +} + +/* path callbacks */ + + diff --git a/libs/surfaces/osc/osc_server.h b/libs/surfaces/osc/osc_server.h new file mode 100644 index 0000000000..d3d4516d4d --- /dev/null +++ b/libs/surfaces/osc/osc_server.h @@ -0,0 +1,102 @@ +#ifndef ardour_osc_control_protocol_h +#define ardour_osc_control_protocol_h + +#include + +#include +#include + +#include + +#include +#include +#include + +#include + +struct OSCRequest : public BaseUI::BaseRequestObject { + /* nothing yet */ +}; + +class ControlOSC : public ARDOUR::ControlProtocol, public AbstractUI +{ + public: + ControlOSC (ARDOUR::Session&, uint32_t port); + virtual ~ControlOSC(); + + int set_active (bool yn); + + bool caller_is_ui_thread(); + + private: + uint32_t _port; + volatile bool _ok; + volatile bool _shutdown; + lo_server _osc_server; + lo_server _osc_unix_server; + std::string _osc_unix_socket_path; + pthread_t _osc_thread; + int _request_pipe[2]; + + static void * _osc_receiver(void * arg); + void osc_receiver(); + + bool init_osc_thread (); + void terminate_osc_thread (); + void poke_osc_thread (); + + void register_callbacks (); + + void on_session_load (); + void on_session_unload (); + + std::string get_server_url (); + std::string get_unix_server_url (); + + void do_request (OSCRequest* req); + +#define PATH_CALLBACK(name) \ + static int _ ## name (const char *path, const char *types, lo_arg **argv, int argc, void *data, void *user_data) { \ + return static_cast(user_data)->cb_ ## name (path, types, argv, argc, data); \ + } \ + int cb_ ## name (const char *path, const char *types, lo_arg **argv, int argc, void *data) { \ + name (); \ + return 0; \ + } + + + PATH_CALLBACK(add_marker); + PATH_CALLBACK(loop_toggle); + PATH_CALLBACK(goto_start); + PATH_CALLBACK(goto_end); + PATH_CALLBACK(rewind); + PATH_CALLBACK(ffwd); + PATH_CALLBACK(transport_stop); + PATH_CALLBACK(transport_play); + PATH_CALLBACK(save_state); + PATH_CALLBACK(prev_marker); + PATH_CALLBACK(next_marker); + PATH_CALLBACK(undo); + PATH_CALLBACK(redo); + PATH_CALLBACK(toggle_punch_in); + PATH_CALLBACK(toggle_punch_out); + PATH_CALLBACK(rec_enable_toggle); + PATH_CALLBACK(toggle_all_rec_enables); + +#define PATH_CALLBACK1(name,type) \ + static int _ ## name (const char *path, const char *types, lo_arg **argv, int argc, void *data, void *user_data) { \ + return static_cast(user_data)->cb_ ## name (path, types, argv, argc, data); \ + } \ + int cb_ ## name (const char *path, const char *types, lo_arg **argv, int argc, void *data) { \ + if (argc > 0) { \ + name (argv[0]->type); \ + }\ + return 0; \ + } + + + PATH_CALLBACK1(set_transport_speed,f); +}; + + +#endif // ardour_osc_control_protocol_h diff --git a/libs/surfaces/tranzport/tranzport_control_protocol.cc b/libs/surfaces/tranzport/tranzport_control_protocol.cc index 30ca270593..7857744b17 100644 --- a/libs/surfaces/tranzport/tranzport_control_protocol.cc +++ b/libs/surfaces/tranzport/tranzport_control_protocol.cc @@ -70,12 +70,15 @@ TranzportControlProtocol::TranzportControlProtocol (Session& s) AbstractUI (X_("Tranzport"), false) { + /* tranzport controls one track at a time */ + + set_route_table_size (1); + timeout = 60000; buttonmask = 0; _datawheel = 0; _device_status = STATUS_OFFLINE; udev = 0; - current_route = 0; current_track_id = 0; last_where = max_frames; wheel_mode = WheelTimeline; @@ -139,11 +142,11 @@ TranzportControlProtocol::set_active (bool yn) void TranzportControlProtocol::show_track_gain () { - if (current_route) { - gain_t g = current_route->gain(); + if (route_table[0]) { + gain_t g = route_get_gain (0); if (g != last_track_gain) { char buf[16]; - snprintf (buf, sizeof (buf), "%6.1fdB", coefficient_to_dB (current_route->effective_gain())); + snprintf (buf, sizeof (buf), "%6.1fdB", coefficient_to_dB (route_get_effective_gain (0))); print (0, 9, buf); last_track_gain = g; } @@ -234,11 +237,11 @@ log_meter (float db) void TranzportControlProtocol::show_meter () { - if (current_route == 0) { + if (route_table[0] == 0) { return; } - float level = current_route->peak_input_power (0); + float level = route_get_peak_input_power (0, 0); float fraction = log_meter (level); /* we draw using a choice of a sort of double colon-like character ("::") or a single, left-aligned ":". @@ -579,10 +582,6 @@ TranzportControlProtocol::monitor_work () val = usb_interrupt_read (udev, READ_ENDPOINT, (char*) buf, 8, 10); pthread_testcancel(); - /* any requests to handle? */ - - handle_ui_requests (); - if (val == 8) { process (buf); } @@ -673,19 +672,19 @@ TranzportControlProtocol::update_state () /* per track */ - if (current_route) { - AudioTrack* at = dynamic_cast (current_route); + if (route_table[0]) { + AudioTrack* at = dynamic_cast (route_table[0]); if (at && at->record_enabled()) { pending_lights[LightTrackrec] = true; } else { pending_lights[LightTrackrec] = false; } - if (current_route->muted()) { + if (route_get_muted (0)) { pending_lights[LightTrackmute] = true; } else { pending_lights[LightTrackmute] = false; } - if (current_route->soloed()) { + if (route_get_soloed (0)) { pending_lights[LightTracksolo] = true; } else { pending_lights[LightTracksolo] = false; @@ -954,10 +953,10 @@ TranzportControlProtocol::process (uint8_t* buf) void TranzportControlProtocol::show_current_track () { - if (current_route == 0) { + if (route_table[0] == 0) { print (0, 0, "--------"); } else { - print (0, 0, current_route->name().substr (0, 8).c_str()); + print (0, 0, route_get_name (0).substr (0, 8).c_str()); } } @@ -1009,10 +1008,7 @@ TranzportControlProtocol::button_event_trackrec_press (bool shifted) if (shifted) { toggle_all_rec_enables (); } else { - if (current_route) { - AudioTrack* at = dynamic_cast(current_route); - at->set_record_enable (!at->record_enabled(), this); - } + route_set_rec_enable (0, !route_get_rec_enable (0)); } } @@ -1024,9 +1020,7 @@ TranzportControlProtocol::button_event_trackrec_release (bool shifted) void TranzportControlProtocol::button_event_trackmute_press (bool shifted) { - if (current_route) { - current_route->set_mute (!current_route->muted(), this); - } + route_set_muted (0, !route_get_muted (0)); } void @@ -1045,9 +1039,7 @@ TranzportControlProtocol::button_event_tracksolo_press (bool shifted) if (shifted) { session.set_all_solo (!session.soloing()); } else { - if (current_route) { - current_route->set_solo (!current_route->soloed(), this); - } + route_set_soloed (0, !route_get_soloed (0)); } } @@ -1267,7 +1259,7 @@ TranzportControlProtocol::datawheel () /* parameter control */ - if (current_route) { + if (route_table[0]) { switch (wheel_shift_mode) { case WheelShiftGain: if (_datawheel < WheelDirectionThreshold) { @@ -1358,7 +1350,7 @@ TranzportControlProtocol::scrub () last_wheel_motion = now; last_wheel_dir = dir; - move_at (speed * dir); + set_transport_speed (speed * dir); } void @@ -1392,7 +1384,7 @@ TranzportControlProtocol::step_gain_up () gain_fraction = 2.0; } - current_route->set_gain (slider_position_to_gain (gain_fraction), this); + route_set_gain (0, slider_position_to_gain (gain_fraction)); } void @@ -1408,7 +1400,7 @@ TranzportControlProtocol::step_gain_down () gain_fraction = 0.0; } - current_route->set_gain (slider_position_to_gain (gain_fraction), this); + route_set_gain (0, slider_position_to_gain (gain_fraction)); } void @@ -1458,83 +1450,15 @@ TranzportControlProtocol::next_wheel_mode () void TranzportControlProtocol::next_track () { - uint32_t limit = session.nroutes(); - uint32_t start = current_track_id; - Route* cr = current_route; - - if (current_track_id == limit) { - current_track_id = 0; - } else { - current_track_id++; - } - - while (current_track_id < limit) { - if ((cr = session.route_by_remote_id (current_track_id)) != 0) { - break; - } - current_track_id++; - } - - if (current_track_id == limit) { - current_track_id = 0; - while (current_track_id != start) { - if ((cr = session.route_by_remote_id (current_track_id)) != 0) { - break; - } - current_track_id++; - } - } - - current_route = cr; - gain_fraction = gain_to_slider_position (current_route->effective_gain()); + ControlProtocol::next_track (current_track_id); + gain_fraction = gain_to_slider_position (route_get_effective_gain (0)); } void TranzportControlProtocol::prev_track () { - uint32_t limit = session.nroutes() - 1; - uint32_t start = current_track_id; - Route* cr = current_route; - - if (current_track_id == 0) { - current_track_id = session.nroutes() - 1; - } else { - current_track_id--; - } - - while (current_track_id >= 0) { - if ((cr = session.route_by_remote_id (current_track_id)) != 0) { - break; - } - current_track_id--; - } - - if (current_track_id < 0) { - current_track_id = limit; - while (current_track_id > start) { - if ((cr = session.route_by_remote_id (current_track_id)) != 0) { - break; - } - current_track_id--; - } - } - - current_route = cr; - gain_fraction = gain_to_slider_position (current_route->effective_gain()); -} - -void -TranzportControlProtocol::set_current_track (Route* r) -{ - TranzportRequest* req = get_request (SetCurrentTrack); - - if (req == 0) { - return; - } - - req->track = r; - - send_request (req); + ControlProtocol::prev_track (current_track_id); + gain_fraction = gain_to_slider_position (route_get_effective_gain (0)); } void @@ -1640,8 +1564,8 @@ void TranzportControlProtocol::do_request (TranzportRequest* req) { if (req->type == SetCurrentTrack) { - current_route = req->track; - } - - return; + route_table[0] = req->track; + } + + return; } diff --git a/libs/surfaces/tranzport/tranzport_control_protocol.h b/libs/surfaces/tranzport/tranzport_control_protocol.h index 69812cb5ec..b24a94577a 100644 --- a/libs/surfaces/tranzport/tranzport_control_protocol.h +++ b/libs/surfaces/tranzport/tranzport_control_protocol.h @@ -101,7 +101,6 @@ class TranzportControlProtocol : public ARDOUR::ControlProtocol, public Abstract uint8_t _device_status; usb_dev_handle* udev; - ARDOUR::Route* current_route; uint32_t current_track_id; WheelMode wheel_mode; WheelShiftMode wheel_shift_mode;