From: Carl Hetherington Date: Tue, 29 Apr 2014 11:42:49 +0000 (+0100) Subject: Merge branch 'cairocanvas' of git.ardour.org:ardour/ardour into cairocanvas X-Git-Tag: 4.0-rc1~1601^2~1292 X-Git-Url: https://git.carlh.net/gitweb/?a=commitdiff_plain;h=152935e736eaf06f85bc7f5cb27337a62d95edd4;hp=e36f74e071d4c14862d23da5ff0d49df0940d536;p=ardour.git Merge branch 'cairocanvas' of git.ardour.org:ardour/ardour into cairocanvas --- diff --git a/export/wscript b/export/wscript index 2dce68a01b..23a070f61d 100644 --- a/export/wscript +++ b/export/wscript @@ -11,7 +11,7 @@ def configure(conf): def build(bld): presets = bld.path.ant_glob ('*.preset') formats = bld.path.ant_glob ('*.format') - bld.install_files (os.path.join(bld.env['DATADIR'], 'ardour3', 'export'), + bld.install_files (os.path.join(bld.env['DATADIR'], 'export'), presets + formats) def options(opt): diff --git a/gtk2_ardour/ardev_common.sh.in b/gtk2_ardour/ardev_common.sh.in index 565ea0b865..d65a287701 100644 --- a/gtk2_ardour/ardev_common.sh.in +++ b/gtk2_ardour/ardev_common.sh.in @@ -17,7 +17,7 @@ export ARDOUR_DATA_PATH=$TOP:$TOP/build:$TOP/gtk2_ardour:$TOP/build/gtk2_ardour: export ARDOUR_MIDIMAPS_PATH=$TOP/midi_maps:. export ARDOUR_MCP_PATH=$TOP/mcp:. export ARDOUR_EXPORT_FORMATS_PATH=$TOP/export:. -export ARDOUR_BACKEND_PATH=$libs/backends/jack:$libs/backends/wavesaudio +export ARDOUR_BACKEND_PATH=$libs/backends/jack:$libs/backends/wavesaudio:$libs/backends/dummy # # even though we set the above variables, ardour requires that these diff --git a/gtk2_ardour/ardour.menus.in b/gtk2_ardour/ardour.menus.in index 6c5e08c8f8..6d08937cf0 100644 --- a/gtk2_ardour/ardour.menus.in +++ b/gtk2_ardour/ardour.menus.in @@ -288,6 +288,7 @@ + @@ -647,6 +648,7 @@ + diff --git a/gtk2_ardour/editor.h b/gtk2_ardour/editor.h index f3f6bfeee7..1398936979 100644 --- a/gtk2_ardour/editor.h +++ b/gtk2_ardour/editor.h @@ -319,6 +319,8 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD void nudge_forward_capture_offset (); void nudge_backward_capture_offset (); + void sequence_regions (); + /* playhead/screen stuff */ void set_stationary_playhead (bool yn); diff --git a/gtk2_ardour/editor_actions.cc b/gtk2_ardour/editor_actions.cc index 674952781d..48d56193a6 100644 --- a/gtk2_ardour/editor_actions.cc +++ b/gtk2_ardour/editor_actions.cc @@ -1897,6 +1897,8 @@ Editor::register_region_actions () reg_sens (_region_actions, "nudge-forward", _("Nudge Later"), sigc::bind (sigc::mem_fun (*this, &Editor::nudge_forward), false, false)); reg_sens (_region_actions, "nudge-backward", _("Nudge Earlier"), sigc::bind (sigc::mem_fun (*this, &Editor::nudge_backward), false, false)); + reg_sens (_region_actions, "sequence-regions", _("Sequence Regions"), sigc::mem_fun (*this, &Editor::sequence_regions)); + reg_sens ( _region_actions, "nudge-forward-by-capture-offset", diff --git a/gtk2_ardour/editor_ops.cc b/gtk2_ardour/editor_ops.cc index f7dc46659a..08d6297faa 100644 --- a/gtk2_ardour/editor_ops.cc +++ b/gtk2_ardour/editor_ops.cc @@ -512,6 +512,60 @@ Editor::nudge_backward_capture_offset () commit_reversible_command (); } +struct RegionSelectionPositionSorter { + bool operator() (RegionView* a, RegionView* b) { + return a->region()->position() < b->region()->position(); + } +}; + +void +Editor::sequence_regions () +{ + framepos_t r_end; + framepos_t r_end_prev; + + int iCount=0; + + if (!_session) { + return; + } + + RegionSelection rs = get_regions_from_selection_and_entered (); + rs.sort(RegionSelectionPositionSorter()); + + if (!rs.empty()) { + + begin_reversible_command (_("sequence regions")); + for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) { + boost::shared_ptr r ((*i)->region()); + + r->clear_changes(); + + if(r->locked()) + { + continue; + } + if(r->position_locked()) + { + continue; + } + if(iCount>0) + { + r_end_prev=r_end; + r->set_position(r_end_prev); + } + + _session->add_command (new StatefulDiffCommand (r)); + + r_end=r->position() + r->length(); + + iCount++; + } + commit_reversible_command (); + } +} + + /* DISPLAY MOTION */ void diff --git a/headless/wscript b/headless/wscript index 5001bea77c..be80f9c73b 100644 --- a/headless/wscript +++ b/headless/wscript @@ -57,7 +57,7 @@ def build(bld): 'CONFIG_DIR="' + os.path.normpath(bld.env['SYSCONFDIR']) + '"', 'LOCALEDIR="' + os.path.join(os.path.normpath(bld.env['DATADIR']), 'locale') + '"', ] - obj.install_path = os.path.join(bld.env['LIBDIR'], 'ardour3') + obj.install_path = bld.env['LIBDIR'] obj.uselib = 'UUID FLAC FONTCONFIG GLIBMM GTHREAD OGG CURL DL' obj.uselib += ' FFTW3F' obj.uselib += ' AUDIOUNITS OSX LO ' diff --git a/libs/ardour/ardour/async_midi_port.h b/libs/ardour/ardour/async_midi_port.h index c5babf6135..26946e3016 100644 --- a/libs/ardour/ardour/async_midi_port.h +++ b/libs/ardour/ardour/async_midi_port.h @@ -22,6 +22,8 @@ #include #include +#include + #include "pbd/xml++.h" #include "pbd/crossthread.h" #include "pbd/signals.h" @@ -64,6 +66,8 @@ class LIBARDOUR_API AsyncMIDIPort : public ARDOUR::MidiPort, public MIDI::Port { #endif } + void set_timer (boost::function&); + static void set_process_thread (pthread_t); static pthread_t get_process_thread () { return _process_thread; } static bool is_process_thread(); @@ -71,6 +75,8 @@ class LIBARDOUR_API AsyncMIDIPort : public ARDOUR::MidiPort, public MIDI::Port { private: bool _currently_in_cycle; MIDI::timestamp_t _last_write_timestamp; + bool have_timer; + boost::function timer; RingBuffer< Evoral::Event > output_fifo; Evoral::EventRingBuffer input_fifo; Glib::Threads::Mutex output_fifo_lock; diff --git a/libs/ardour/ardour/location.h b/libs/ardour/ardour/location.h index b0956eea36..6cea208f05 100644 --- a/libs/ardour/ardour/location.h +++ b/libs/ardour/ardour/location.h @@ -34,10 +34,13 @@ #include "pbd/statefuldestructible.h" #include "ardour/ardour.h" +#include "ardour/scene_change.h" #include "ardour/session_handle.h" namespace ARDOUR { +class SceneChange; + class LIBARDOUR_API Location : public SessionHandleRef, public PBD::StatefulDestructible { public: @@ -93,6 +96,9 @@ class LIBARDOUR_API Location : public SessionHandleRef, public PBD::StatefulDest Flags flags () const { return _flags; } + boost::shared_ptr scene_change() const { return _scene_change; } + void set_scene_change (boost::shared_ptr); + PBD::Signal1 name_changed; PBD::Signal1 end_changed; PBD::Signal1 start_changed; @@ -116,6 +122,8 @@ class LIBARDOUR_API Location : public SessionHandleRef, public PBD::StatefulDest void set_position_lock_style (PositionLockStyle ps); void recompute_frames_from_bbt (); + static PBD::Signal0 scene_changed; + private: std::string _name; framepos_t _start; @@ -125,6 +133,7 @@ class LIBARDOUR_API Location : public SessionHandleRef, public PBD::StatefulDest Flags _flags; bool _locked; PositionLockStyle _position_lock_style; + boost::shared_ptr _scene_change; void set_mark (bool yn); bool set_flag_internal (bool yn, Flags flag); @@ -161,6 +170,8 @@ class LIBARDOUR_API Locations : public SessionHandleRef, public PBD::StatefulDes int set_current (Location *, bool want_lock = true); Location *current () const { return current_location; } + Location* mark_at (framepos_t, framecnt_t slop = 0) const; + framepos_t first_mark_before (framepos_t, bool include_special_ranges = false); framepos_t first_mark_after (framepos_t, bool include_special_ranges = false); diff --git a/libs/ardour/ardour/midi_scene_change.h b/libs/ardour/ardour/midi_scene_change.h new file mode 100644 index 0000000000..86793c57fb --- /dev/null +++ b/libs/ardour/ardour/midi_scene_change.h @@ -0,0 +1,63 @@ +/* + Copyright (C) 2014 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 __libardour_midi_scene_change_h__ +#define __libardour_midi_scene_change_h__ + +#include "evoral/PatchChange.hpp" + +#include "ardour/scene_change.h" + +namespace ARDOUR +{ + +class MidiPort; + +class MIDISceneChange : public SceneChange +{ + public: + MIDISceneChange (framepos_t time, int channel, int bank = -1, int program = -1); + MIDISceneChange (const XMLNode&, int version); + ~MIDISceneChange (); + + void set_channel (int channel); + void set_program (int program); + void set_bank (int bank); + + int channel () const { return _channel; } + int program () const { return _program; } + int bank () const { return _bank; } + + size_t get_bank_msb_message (uint8_t* buf, size_t size) const; + size_t get_bank_lsb_message (uint8_t* buf, size_t size) const; + size_t get_program_message (uint8_t* buf, size_t size) const; + + XMLNode& get_state(); + int set_state (const XMLNode&, int version); + + private: + int _bank; + int _program; + uint8_t _channel; +}; + +} /* namespace */ + + +#endif /* __libardour_scene_change_h__ */ diff --git a/libs/ardour/ardour/midi_scene_changer.h b/libs/ardour/ardour/midi_scene_changer.h new file mode 100644 index 0000000000..2cc0464bec --- /dev/null +++ b/libs/ardour/ardour/midi_scene_changer.h @@ -0,0 +1,72 @@ +/* + Copyright (C) 2014 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 __libardour_midi_scene_changer_h__ +#define __libardour_midi_scene_changer_h__ + +#include "ardour/scene_changer.h" + +namespace ARDOUR +{ + +class MIDISceneChanger : public SceneChanger +{ + public: + MIDISceneChanger (Session&); + ~MIDISceneChanger (); + + void run (framepos_t start, framepos_t end); + void set_input_port (MIDI::Port*); + void set_output_port (boost::shared_ptr); + + uint8_t bank_at (framepos_t, uint8_t channel); + uint8_t program_at (framepos_t, uint8_t channel); + + void set_recording (bool); + void locate (framepos_t); + + private: + typedef std::multimap > Scenes; + + MIDI::Port* input_port; + boost::shared_ptr output_port; + Scenes scenes; + bool _recording; + framepos_t last_bank_message_time; + framepos_t last_program_message_time; + unsigned short current_bank; + int last_delivered_program; + int last_delivered_bank; + + void gather (); + bool recording () const; + void jump_to (int bank, int program); + void deliver (MidiBuffer&, framepos_t, boost::shared_ptr); + + void bank_change_input (MIDI::Parser&, unsigned short); + void program_change_input (MIDI::Parser&, MIDI::byte); + void locations_changed (Locations::Change); + + PBD::ScopedConnection incoming_bank_change_connection; + PBD::ScopedConnection incoming_program_change_connection; +}; + +} // namespace + +#endif /* __libardour_midi_scene_changer_h__ */ diff --git a/libs/ardour/ardour/midiport_manager.h b/libs/ardour/ardour/midiport_manager.h index b5b46e8510..5e87238c22 100644 --- a/libs/ardour/ardour/midiport_manager.h +++ b/libs/ardour/ardour/midiport_manager.h @@ -30,6 +30,7 @@ #include "midi++/port.h" #include "ardour/libardour_visibility.h" +#include "ardour/midi_port.h" #include "ardour/types.h" namespace ARDOUR { @@ -56,7 +57,12 @@ class LIBARDOUR_API MidiPortManager { MIDI::Port* midi_output_port () const { return _midi_output_port; } MIDI::Port* mmc_input_port () const { return _mmc_input_port; } MIDI::Port* mmc_output_port () const { return _mmc_output_port; } + MIDI::Port* scene_input_port () const { return _scene_input_port; } + MIDI::Port* scene_output_port () const { return _scene_output_port; } + boost::shared_ptr scene_in() const { return boost::dynamic_pointer_cast(_scene_in); } + boost::shared_ptr scene_out() const { return boost::dynamic_pointer_cast(_scene_out); } + /* Ports used for synchronization. These have their I/O handled inside the * process callback. */ @@ -77,13 +83,17 @@ class LIBARDOUR_API MidiPortManager { MIDI::Port* _midi_output_port; MIDI::Port* _mmc_input_port; MIDI::Port* _mmc_output_port; - /* these point to the same objects as the 4 members above, + MIDI::Port* _scene_input_port; + MIDI::Port* _scene_output_port; + /* these point to the same objects as the members above, but cast to their ARDOUR::Port base class */ boost::shared_ptr _midi_in; boost::shared_ptr _midi_out; boost::shared_ptr _mmc_in; boost::shared_ptr _mmc_out; + boost::shared_ptr _scene_in; + boost::shared_ptr _scene_out; /* synchronously handled ports: ARDOUR::MidiPort */ boost::shared_ptr _mtc_input_port; diff --git a/libs/ardour/ardour/rc_configuration_vars.h b/libs/ardour/ardour/rc_configuration_vars.h index d290d8ebc8..4401b1f74c 100644 --- a/libs/ardour/ardour/rc_configuration_vars.h +++ b/libs/ardour/ardour/rc_configuration_vars.h @@ -44,6 +44,7 @@ CONFIG_VARIABLE (int32_t, mmc_receive_device_id, "mmc-receive-device-id", 0x7f) CONFIG_VARIABLE (int32_t, mmc_send_device_id, "mmc-send-device-id", 0) CONFIG_VARIABLE (int32_t, initial_program_change, "initial-program-change", -1) CONFIG_VARIABLE (bool, first_midi_bank_is_zero, "display-first-midi-bank-as-zero", false) +CONFIG_VARIABLE (int32_t, inter_scene_gap_msecs, "inter-scene-gap-msecs", 1) /* Timecode and related */ diff --git a/libs/ardour/ardour/scene_change.h b/libs/ardour/ardour/scene_change.h new file mode 100644 index 0000000000..b81766b0ea --- /dev/null +++ b/libs/ardour/ardour/scene_change.h @@ -0,0 +1,49 @@ +/* + Copyright (C) 2014 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 __libardour_scene_change_h__ +#define __libardour_scene_change_h__ + +#include "pbd/stateful.h" + +#include "ardour/types.h" + +namespace ARDOUR +{ + +class SceneChange : public PBD::Stateful +{ + public: + SceneChange (framepos_t t) : _time (t) {}; + virtual ~SceneChange () {}; + + void set_time (framepos_t); + framepos_t time() const { return _time; } + + static boost::shared_ptr factory (const XMLNode&, int version); + static std::string xml_node_name; + + private: + framepos_t _time; +}; + +} /* namespace */ + + +#endif /* __libardour_scene_change_h__ */ diff --git a/libs/ardour/ardour/scene_changer.h b/libs/ardour/ardour/scene_changer.h new file mode 100644 index 0000000000..d5ba984e92 --- /dev/null +++ b/libs/ardour/ardour/scene_changer.h @@ -0,0 +1,56 @@ +/* + Copyright (C) 2014 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 __libardour_scene_changer_h__ +#define __libardour_scene_changer_h__ + +#include + +#include "pbd/signals.h" + +#include "ardour/location.h" +#include "ardour/midi_scene_change.h" +#include "ardour/session_handle.h" +#include "ardour/types.h" + +namespace MIDI +{ +class Parser; +class Port; +} + +namespace ARDOUR +{ + +class Session; +class AsyncMidiPort; + +class SceneChanger : public SessionHandleRef +{ + public: + SceneChanger (Session& s) : SessionHandleRef (s) {} + virtual ~SceneChanger () {}; + + virtual void run (framepos_t start, framepos_t end) = 0; +}; + +} /* namespace */ + + +#endif /* __libardour_scene_change_h__ */ diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h index 15af67ada7..40ada138a6 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -129,6 +129,7 @@ class Route; class RouteGroup; class SMFSource; class Send; +class SceneChanger; class SessionDirectory; class SessionMetadata; class SessionPlaylists; @@ -868,23 +869,31 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop */ static PBD::Signal2 VersionMismatch; + SceneChanger* scene_changer() const { return _scene_changer; } + boost::shared_ptr ltc_input_port() const; boost::shared_ptr ltc_output_port() const; boost::shared_ptr ltc_input_io() { return _ltc_input; } boost::shared_ptr ltc_output_io() { return _ltc_output; } - MIDI::Port* midi_input_port () const; - MIDI::Port* midi_output_port () const; - MIDI::Port* mmc_output_port () const; - MIDI::Port* mmc_input_port () const; + MIDI::Port* midi_input_port () const; + MIDI::Port* midi_output_port () const; + MIDI::Port* mmc_output_port () const; + MIDI::Port* mmc_input_port () const; - boost::shared_ptr midi_clock_output_port () const; - boost::shared_ptr midi_clock_input_port () const; - boost::shared_ptr mtc_output_port () const; - boost::shared_ptr mtc_input_port () const; + MIDI::Port* scene_input_port () const; + MIDI::Port* scene_output_port () const; - MIDI::MachineControl& mmc() { return *_mmc; } + boost::shared_ptr scene_in () const; + boost::shared_ptr scene_out () const; + + boost::shared_ptr midi_clock_output_port () const; + boost::shared_ptr midi_clock_input_port () const; + boost::shared_ptr mtc_output_port () const; + boost::shared_ptr mtc_input_port () const; + + MIDI::MachineControl& mmc() { return *_mmc; } protected: friend class AudioEngine; @@ -1607,16 +1616,19 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop void reconnect_ltc_input (); void reconnect_ltc_output (); - /* persistent, non-track related MIDI ports */ - MidiPortManager* _midi_ports; - MIDI::MachineControl* _mmc; - - void setup_ltc (); - void setup_click (); - void setup_click_state (const XMLNode*); - void setup_bundles (); - - static int get_session_info_from_path (XMLTree& state_tree, const std::string& xmlpath); + /* Scene Changing */ + SceneChanger* _scene_changer; + + /* persistent, non-track related MIDI ports */ + MidiPortManager* _midi_ports; + MIDI::MachineControl* _mmc; + + void setup_ltc (); + void setup_click (); + void setup_click_state (const XMLNode*); + void setup_bundles (); + + static int get_session_info_from_path (XMLTree& state_tree, const std::string& xmlpath); }; } // namespace ARDOUR diff --git a/libs/ardour/async_midi_port.cc b/libs/ardour/async_midi_port.cc index bd583328c3..21b59dec00 100644 --- a/libs/ardour/async_midi_port.cc +++ b/libs/ardour/async_midi_port.cc @@ -50,6 +50,7 @@ AsyncMIDIPort::AsyncMIDIPort (string const & name, PortFlags flags) , MIDI::Port (name, MIDI::Port::Flags (0)) , _currently_in_cycle (false) , _last_write_timestamp (0) + , have_timer (false) , output_fifo (512) , input_fifo (1024) #ifndef PLATFORM_WINDOWS @@ -62,6 +63,13 @@ AsyncMIDIPort::~AsyncMIDIPort () { } +void +AsyncMIDIPort::set_timer (boost::function& f) +{ + timer = f; + have_timer = true; +} + void AsyncMIDIPort::flush_output_fifo (MIDI::pframes_t nframes) { @@ -113,9 +121,18 @@ AsyncMIDIPort::cycle_start (MIDI::pframes_t nframes) if (ARDOUR::Port::receives_input()) { MidiBuffer& mb (get_midi_buffer (nframes)); - pframes_t when = AudioEngine::instance()->sample_time_at_cycle_start(); + framecnt_t when; + + if (have_timer) { + when = timer (); + } else { + when = AudioEngine::instance()->sample_time_at_cycle_start(); + } for (MidiBuffer::iterator b = mb.begin(); b != mb.end(); ++b) { + if (!have_timer) { + when += (*b).time(); + } input_fifo.write (when, (Evoral::EventType) 0, (*b).size(), (*b).buffer()); } diff --git a/libs/ardour/location.cc b/libs/ardour/location.cc index 2a27fc318a..90265af4e4 100644 --- a/libs/ardour/location.cc +++ b/libs/ardour/location.cc @@ -30,6 +30,7 @@ #include "pbd/enumwriter.h" #include "ardour/location.h" +#include "ardour/midi_scene_change.h" #include "ardour/session.h" #include "ardour/audiofilesource.h" #include "ardour/tempo.h" @@ -42,6 +43,8 @@ using namespace std; using namespace ARDOUR; using namespace PBD; +PBD::Signal0 Location::scene_changed; + Location::Location (Session& s) : SessionHandleRef (s) , _start (0) @@ -87,6 +90,8 @@ Location::Location (const Location& other) assert (_start >= 0); assert (_end >= 0); + + /* scene change is NOT COPIED */ } Location::Location (Session& s, const XMLNode& node) @@ -134,6 +139,8 @@ Location::operator= (const Location& other) _bbt_end = other._bbt_end; _flags = other._flags; _position_lock_style = other._position_lock_style; + + /* XXX need to copy scene change */ /* copy is not locked even if original was */ @@ -431,11 +438,15 @@ Location::get_state () node->add_property ("locked", (_locked ? "yes" : "no")); node->add_property ("position-lock-style", enum_2_string (_position_lock_style)); + if (_scene_change) { + node->add_child_nocopy (_scene_change->get_state()); + } + return *node; } int -Location::set_state (const XMLNode& node, int /*version*/) +Location::set_state (const XMLNode& node, int version) { const XMLProperty *prop; @@ -521,6 +532,16 @@ Location::set_state (const XMLNode& node, int /*version*/) _position_lock_style = PositionLockStyle (string_2_enum (prop->value(), _position_lock_style)); } + XMLNode* scene_child = find_named_node (node, SceneChange::xml_node_name); + + if (scene_child) { + _scene_change = SceneChange::factory (*scene_child, version); + + if (_scene_change) { + _scene_change->set_time (_start); + } + } + recompute_bbt_from_frames (); changed (this); /* EMIT SIGNAL */ @@ -581,6 +602,14 @@ Location::unlock () LockChanged (this); } +void +Location::set_scene_change (boost::shared_ptr sc) +{ + _scene_change = sc; + + scene_changed (); /* EMIT SIGNAL */ +} + /*---------------------------------------------------------------------- */ Locations::Locations (Session& s) @@ -675,6 +704,7 @@ Locations::clear () ++tmp; if (!(*i)->is_session_range()) { + delete *i; locations.erase (i); } @@ -700,6 +730,7 @@ Locations::clear_markers () ++tmp; if ((*i)->is_mark() && !(*i)->is_session_range()) { + delete *i; locations.erase (i); } @@ -723,6 +754,7 @@ Locations::clear_ranges () ++tmp; if (!(*i)->is_mark()) { + delete *i; locations.erase (i); } @@ -779,6 +811,7 @@ Locations::remove (Location *loc) for (i = locations.begin(); i != locations.end(); ++i) { if ((*i) == loc) { + delete *i; locations.erase (i); was_removed = true; if (current_location == loc) { @@ -972,6 +1005,44 @@ Locations::first_mark_before (framepos_t frame, bool include_special_ranges) return -1; } +Location* +Locations::mark_at (framepos_t pos, framecnt_t slop) const +{ + Glib::Threads::Mutex::Lock lm (lock); + Location* closest = 0; + frameoffset_t mindelta = max_framepos; + frameoffset_t delta; + + /* locations are not necessarily stored in linear time order so we have + * to iterate across all of them to find the one closest to a give point. + */ + + for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) { + + if ((*i)->is_mark()) { + if (pos > (*i)->start()) { + delta = pos - (*i)->start(); + } else { + delta = (*i)->start() - pos; + } + + if (slop == 0 && delta == 0) { + /* special case: no slop, and direct hit for position */ + return *i; + } + + if (delta <= slop) { + if (delta < mindelta) { + closest = *i; + mindelta = delta; + } + } + } + } + + return closest; +} + framepos_t Locations::first_mark_after (framepos_t frame, bool include_special_ranges) { @@ -1146,3 +1217,4 @@ Locations::find_all_between (framepos_t start, framepos_t end, LocationList& ll, } } } + diff --git a/libs/ardour/midi_scene_change.cc b/libs/ardour/midi_scene_change.cc new file mode 100644 index 0000000000..81a74911a9 --- /dev/null +++ b/libs/ardour/midi_scene_change.cc @@ -0,0 +1,144 @@ +/* + Copyright (C) 2014 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 "pbd/error.h" +#include "pbd/compose.h" + +#include "ardour/midi_port.h" +#include "ardour/midi_scene_change.h" + +#include "i18n.h" + +using namespace PBD; +using namespace ARDOUR; + +MIDISceneChange::MIDISceneChange (framepos_t time, int c, int b, int p) + : SceneChange (time) + , _bank (b) + , _program (p) + , _channel (c & 0xf) +{ + if (_bank > 16384) { + _bank = -1; + } + + if (_program > 128) { + _program = -1; + } +} + +MIDISceneChange::MIDISceneChange (const XMLNode& node, int version) + : SceneChange (0) + , _bank (-1) + , _program (-1) + , _channel (-1) +{ + set_state (node, version); +} + +MIDISceneChange::~MIDISceneChange () +{ +} + +size_t +MIDISceneChange::get_bank_msb_message (uint8_t* buf, size_t size) const +{ + if (size < 3 || _bank < 0) { + return 0; + } + + buf[0] = 0xB0 | (_channel & 0xf); + buf[1] = 0x0; + buf[2] = (_bank & 0xf700) >> 8; + + return 3; +} + +size_t +MIDISceneChange::get_bank_lsb_message (uint8_t* buf, size_t size) const +{ + if (size < 3 || _bank < 0) { + return 0; + } + + buf[0] = 0xB0 | (_channel & 0xf); + buf[1] = 0x20; + buf[2] = (_bank & 0xf7); + + return 3; +} + +size_t +MIDISceneChange::get_program_message (uint8_t* buf, size_t size) const +{ + if (size < 2 || _program < 0) { + return 0; + } + + buf[0] = 0xC0 | (_channel & 0xf); + buf[1] = _program & 0xf7; + + return 2; +} + +XMLNode& +MIDISceneChange::get_state () +{ + char buf[32]; + XMLNode* node = new XMLNode (SceneChange::xml_node_name); + + node->add_property (X_("type"), X_("MIDI")); + snprintf (buf, sizeof (buf), "%d", (int) _program); + node->add_property (X_("id"), id().to_s()); + snprintf (buf, sizeof (buf), "%d", (int) _program); + node->add_property (X_("program"), buf); + snprintf (buf, sizeof (buf), "%d", (int) _bank); + node->add_property (X_("bank"), buf); + snprintf (buf, sizeof (buf), "%d", (int) _channel); + node->add_property (X_("channel"), buf); + + return *node; +} + +int +MIDISceneChange::set_state (const XMLNode& node, int /* version-ignored */) +{ + if (!set_id (node)) { + return -1; + } + + const XMLProperty* prop; + + if ((prop = node.property (X_("program"))) == 0) { + return -1; + } + _program = atoi (prop->value()); + + if ((prop = node.property (X_("bank"))) == 0) { + return -1; + } + _bank = atoi (prop->value()); + + if ((prop = node.property (X_("channel"))) == 0) { + return -1; + } + _channel = atoi (prop->value()); + + return 0; +} diff --git a/libs/ardour/midi_scene_changer.cc b/libs/ardour/midi_scene_changer.cc new file mode 100644 index 0000000000..49e835ca80 --- /dev/null +++ b/libs/ardour/midi_scene_changer.cc @@ -0,0 +1,279 @@ +/* + Copyright (C) 2014 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 "evoral/MIDIEvent.hpp" +#include "midi++/parser.h" +#include "midi++/port.h" + +#include "ardour/event_type_map.h" +#include "ardour/midi_port.h" +#include "ardour/midi_scene_change.h" +#include "ardour/midi_scene_changer.h" +#include "ardour/session.h" + +#include "i18n.h" + +using namespace ARDOUR; + +MIDISceneChanger::MIDISceneChanger (Session& s) + : SceneChanger (s) + , _recording (true) + , last_bank_message_time (-1) + , last_program_message_time (-1) + , last_delivered_program (-1) + , last_delivered_bank (-1) + +{ + _session.locations()->changed.connect_same_thread (*this, boost::bind (&MIDISceneChanger::locations_changed, this, _1)); + Location::scene_changed.connect_same_thread (*this, boost::bind (&MIDISceneChanger::gather, this)); +} + +MIDISceneChanger::~MIDISceneChanger () +{ +} + +void +MIDISceneChanger::locations_changed (Locations::Change) +{ + gather (); +} + +/** Use the session's list of locations to collect all patch changes. + * + * This is called whenever the locations change in anyway. + */ +void +MIDISceneChanger::gather () +{ + const Locations::LocationList& locations (_session.locations()->list()); + boost::shared_ptr sc; + + scenes.clear (); + + for (Locations::LocationList::const_iterator l = locations.begin(); l != locations.end(); ++l) { + + if ((sc = (*l)->scene_change()) != 0) { + + boost::shared_ptr msc = boost::dynamic_pointer_cast (sc); + + if (msc) { + scenes.insert (std::make_pair (msc->time(), msc)); + } + } + } +} + +void +MIDISceneChanger::deliver (MidiBuffer& mbuf, framepos_t when, boost::shared_ptr msc) +{ + uint8_t buf[4]; + size_t cnt; + + if ((cnt = msc->get_bank_msb_message (buf, sizeof (buf))) > 0) { + mbuf.push_back (when, cnt, buf); + + if ((cnt = msc->get_bank_lsb_message (buf, sizeof (buf))) > 0) { + mbuf.push_back (when, cnt, buf); + } + + last_delivered_bank = msc->bank(); + } + + if ((cnt = msc->get_program_message (buf, sizeof (buf))) > 0) { + mbuf.push_back (when, cnt, buf); + + last_delivered_program = msc->program(); + } +} + + +void +MIDISceneChanger::run (framepos_t start, framepos_t end) +{ + if (!output_port || recording()) { + return; + } + + /* get lower bound of events to consider */ + + Scenes::const_iterator i = scenes.lower_bound (start); + MidiBuffer& mbuf (output_port->get_midi_buffer (end-start)); + + while (i != scenes.end()) { + + if (i->first >= end) { + break; + } + + deliver (mbuf, i->first - start, i->second); + + ++i; + } +} + +void +MIDISceneChanger::locate (framepos_t pos) +{ + Scenes::const_iterator i = scenes.upper_bound (pos); + + if (i == scenes.end()) { + return; + } + + if (i->second->program() != last_delivered_program || i->second->bank() != last_delivered_bank) { + // MidiBuffer& mbuf (output_port->get_midi_buffer (end-start)); + // deliver (mbuf, i->first, i->second); + } +} + +void +MIDISceneChanger::set_input_port (MIDI::Port* mp) +{ + input_port = mp; + + incoming_bank_change_connection.disconnect (); + incoming_program_change_connection.disconnect (); + + if (input_port) { + + /* midi port is asynchronous. MIDI parsing will be carried out + * by the MIDI UI thread which will emit the relevant signals + * and thus invoke our callbacks as necessary. + */ + + input_port->parser()->bank_change.connect_same_thread (incoming_bank_change_connection, boost::bind (&MIDISceneChanger::bank_change_input, this, _1, _2)); + input_port->parser()->program_change.connect_same_thread (incoming_program_change_connection, boost::bind (&MIDISceneChanger::program_change_input, this, _1, _2)); + } +} + +void +MIDISceneChanger::set_output_port (boost::shared_ptr mp) +{ + output_port = mp; +} + +void +MIDISceneChanger::set_recording (bool yn) +{ + _recording = yn; +} + +bool +MIDISceneChanger::recording() const +{ + return _session.transport_rolling() && _session.get_record_enabled(); +} + +void +MIDISceneChanger::bank_change_input (MIDI::Parser& parser, unsigned short bank) +{ + if (!recording()) { + return; + } + + last_bank_message_time = parser.get_timestamp (); + current_bank = bank; +} + +void +MIDISceneChanger::program_change_input (MIDI::Parser& parser, MIDI::byte program) +{ + framecnt_t time = parser.get_timestamp (); + frameoffset_t delta = time - last_program_message_time; + + last_program_message_time = time; + + if (!recording()) { + jump_to (current_bank, program); + return; + } + + Locations* locations (_session.locations ()); + Location* loc; + bool new_mark = false; + framecnt_t slop = (framecnt_t) floor ((Config->get_inter_scene_gap_msecs() / 1000.0) * _session.frame_rate()); + + /* check for marker at current location */ + + loc = locations->mark_at (time, slop); + + if (!loc) { + /* create a new marker at the desired position */ + + std::string new_name; + + if (!locations->next_available_name (new_name, _("Scene "))) { + std::cerr << "No new marker name available\n"; + return; + } + + loc = new Location (_session, time, time, new_name, Location::IsMark); + new_mark = true; + } + + uint8_t bank; + uint8_t channel = (program & 0xf0) >> 8; + + /* if we received a bank change message within the last 2 msec, use the + * current bank value, otherwise lookup the current bank number and use + * that. + */ + + if (time - last_bank_message_time < (2 * _session.frame_rate() / 1000.0)) { + bank = current_bank; + } else { + bank = -1; + } + + MIDISceneChange* msc =new MIDISceneChange (loc->start(), channel, bank, program & 0x7f); + + loc->set_scene_change (boost::shared_ptr (msc)); + + /* this will generate a "changed" signal to be emitted by locations, + and we will call ::gather() to update our list of MIDI events. + */ + + if (new_mark) { + locations->add (loc); + } +} + +void +MIDISceneChanger::jump_to (int bank, int program) +{ + const Locations::LocationList& locations (_session.locations()->list()); + boost::shared_ptr sc; + framepos_t where = max_framepos; + + for (Locations::LocationList::const_iterator l = locations.begin(); l != locations.end(); ++l) { + + if ((sc = (*l)->scene_change()) != 0) { + + boost::shared_ptr msc = boost::dynamic_pointer_cast (sc); + + if (msc->bank() == bank && msc->program() == program && (*l)->start() < where) { + where = (*l)->start(); + } + } + } + + if (where != max_framepos) { + _session.request_locate (where); + } +} diff --git a/libs/ardour/midi_ui.cc b/libs/ardour/midi_ui.cc index 97dfdce6bf..e00ec587ec 100644 --- a/libs/ardour/midi_ui.cc +++ b/libs/ardour/midi_ui.cc @@ -122,6 +122,10 @@ MidiControlUI::reset_ports () if ((p = dynamic_cast (_session.mmc_input_port()))) { ports.push_back (p); } + + if ((p = dynamic_cast (_session.scene_input_port()))) { + ports.push_back (p); + } if (ports.empty()) { return; diff --git a/libs/ardour/midiport_manager.cc b/libs/ardour/midiport_manager.cc index 6de0436586..b1699ca5ab 100644 --- a/libs/ardour/midiport_manager.cc +++ b/libs/ardour/midiport_manager.cc @@ -40,8 +40,14 @@ MidiPortManager::~MidiPortManager () if (_midi_in) { AudioEngine::instance()->unregister_port (_midi_in); } - if (_midi_in) { - AudioEngine::instance()->unregister_port (_midi_in); + if (_midi_out) { + AudioEngine::instance()->unregister_port (_midi_out); + } + if (_scene_in) { + AudioEngine::instance()->unregister_port (_scene_in); + } + if (_scene_out) { + AudioEngine::instance()->unregister_port (_scene_out); } if (_mtc_input_port) { AudioEngine::instance()->unregister_port (_mtc_input_port); @@ -73,7 +79,10 @@ MidiPortManager::create_ports () _mmc_in = AudioEngine::instance()->register_input_port (DataType::MIDI, X_("MMC in"), true); _mmc_out = AudioEngine::instance()->register_output_port (DataType::MIDI, X_("MMC out"), true); - + + _scene_in = AudioEngine::instance()->register_input_port (DataType::MIDI, X_("Scene in"), true); + _scene_out = AudioEngine::instance()->register_output_port (DataType::MIDI, X_("Scene out"), true); + /* XXX nasty type conversion needed because of the mixed inheritance * required to integrate MIDI::IPMidiPort and ARDOUR::AsyncMIDIPort. * @@ -88,6 +97,9 @@ MidiPortManager::create_ports () _mmc_input_port = boost::dynamic_pointer_cast(_mmc_in).get(); _mmc_output_port = boost::dynamic_pointer_cast(_mmc_out).get(); + _scene_input_port = boost::dynamic_pointer_cast(_scene_in).get(); + _scene_output_port = boost::dynamic_pointer_cast(_scene_out).get(); + /* Now register ports used for sync (MTC and MIDI Clock) */ @@ -129,6 +141,8 @@ MidiPortManager::set_midi_port_states (const XMLNodeList&nodes) ports.insert (make_pair (_midi_output_port->name(), _midi_out)); ports.insert (make_pair (_mmc_input_port->name(), _mmc_in)); ports.insert (make_pair (_mmc_output_port->name(), _mmc_out)); + ports.insert (make_pair (_scene_output_port->name(), _scene_out)); + ports.insert (make_pair (_scene_input_port->name(), _scene_in)); for (XMLNodeList::const_iterator n = nodes.begin(); n != nodes.end(); ++n) { if ((prop = (*n)->property (X_("name"))) == 0) { @@ -159,6 +173,8 @@ MidiPortManager::get_midi_port_states () const ports.insert (make_pair (_midi_output_port->name(), _midi_out)); ports.insert (make_pair (_mmc_input_port->name(), _mmc_in)); ports.insert (make_pair (_mmc_output_port->name(), _mmc_out)); + ports.insert (make_pair (_scene_output_port->name(), _scene_out)); + ports.insert (make_pair (_scene_input_port->name(), _scene_in)); for (PortMap::const_iterator p = ports.begin(); p != ports.end(); ++p) { s.push_back (&p->second->get_state()); diff --git a/libs/ardour/scene_change.cc b/libs/ardour/scene_change.cc new file mode 100644 index 0000000000..dec6a11f85 --- /dev/null +++ b/libs/ardour/scene_change.cc @@ -0,0 +1,45 @@ +/* + Copyright (C) 2014 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/midi_scene_change.h" + +#include "i18n.h" + +using namespace PBD; +using namespace ARDOUR; + +std::string SceneChange::xml_node_name = X_("SceneChange"); + +boost::shared_ptr +SceneChange::factory (const XMLNode& node, int version) +{ + const XMLProperty* prop = node.property (X_("type")); + + if (prop->value() == X_("MIDI")) { + return boost::shared_ptr (new MIDISceneChange (node, version)); + } + + return boost::shared_ptr(); +} + +void +SceneChange::set_time (framepos_t t) +{ + _time = t; +} diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index 2aef745961..05fa883a9d 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -68,6 +68,7 @@ #include "ardour/filename_extensions.h" #include "ardour/graph.h" #include "ardour/midiport_manager.h" +#include "ardour/scene_changer.h" #include "ardour/midi_track.h" #include "ardour/midi_ui.h" #include "ardour/operations.h" @@ -260,6 +261,7 @@ Session::Session (AudioEngine &eng, , _speakers (new Speakers) , _order_hint (0) , ignore_route_processor_changes (false) + , _scene_changer (0) , _midi_ports (0) , _mmc (0) { @@ -547,6 +549,8 @@ Session::destroy () /* not strictly necessary, but doing it here allows the shared_ptr debugging to work */ playlists.reset (); + delete _scene_changer; _scene_changer = 0; + delete _mmc; _mmc = 0; delete _midi_ports; _midi_ports = 0; delete _locations; _locations = 0; diff --git a/libs/ardour/session_midi.cc b/libs/ardour/session_midi.cc index ea6dfe81cf..93df1fc946 100644 --- a/libs/ardour/session_midi.cc +++ b/libs/ardour/session_midi.cc @@ -644,3 +644,27 @@ Session::mmc_input_port () const { return _midi_ports->mmc_input_port (); } + +MIDI::Port* +Session::scene_output_port () const +{ + return _midi_ports->scene_output_port (); +} + +MIDI::Port* +Session::scene_input_port () const +{ + return _midi_ports->scene_input_port (); +} + +boost::shared_ptr +Session::scene_in () const +{ + return _midi_ports->scene_in (); +} + +boost::shared_ptr +Session::scene_out () const +{ + return _midi_ports->scene_out (); +} diff --git a/libs/ardour/session_process.cc b/libs/ardour/session_process.cc index eb5c0de294..680f2861de 100644 --- a/libs/ardour/session_process.cc +++ b/libs/ardour/session_process.cc @@ -35,6 +35,7 @@ #include "ardour/graph.h" #include "ardour/port.h" #include "ardour/process_thread.h" +#include "ardour/scene_changer.h" #include "ardour/session.h" #include "ardour/slave.h" #include "ardour/ticker.h" @@ -86,6 +87,9 @@ Session::process (pframes_t nframes) if (!_silent && !_engine.freewheeling() && Config->get_send_midi_clock() && (transport_speed() == 1.0f || transport_speed() == 0.0f) && midi_clock->has_midi_port()) { midi_clock->tick (transport_at_start, nframes); } + + _scene_changer->run (transport_at_start, transport_at_start + nframes); + } catch (...) { /* don't bother with a message */ } diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc index 07f10e9bc1..6a06863e9e 100644 --- a/libs/ardour/session_state.cc +++ b/libs/ardour/session_state.cc @@ -77,6 +77,7 @@ #include "pbd/localtime_r.h" #include "ardour/amp.h" +#include "ardour/async_midi_port.h" #include "ardour/audio_diskstream.h" #include "ardour/audio_track.h" #include "ardour/audioengine.h" @@ -92,6 +93,7 @@ #include "ardour/midi_model.h" #include "ardour/midi_patch_manager.h" #include "ardour/midi_region.h" +#include "ardour/midi_scene_changer.h" #include "ardour/midi_source.h" #include "ardour/midi_track.h" #include "ardour/pannable.h" @@ -207,6 +209,16 @@ Session::post_engine_init () BootMessage (_("Using configuration")); _midi_ports = new MidiPortManager; + + MIDISceneChanger* msc; + + _scene_changer = msc = new MIDISceneChanger (*this); + msc->set_input_port (scene_input_port()); + msc->set_output_port (scene_out()); + + boost::function timer_func (boost::bind (&Session::audible_frame, this)); + boost::dynamic_pointer_cast(scene_in())->set_timer (timer_func); + setup_midi_machine_control (); if (_butler->start_thread()) { diff --git a/libs/ardour/wscript b/libs/ardour/wscript index c12aec4ef8..4a50064545 100644 --- a/libs/ardour/wscript +++ b/libs/ardour/wscript @@ -119,6 +119,8 @@ libardour_sources = [ 'midi_port.cc', 'midi_region.cc', 'midi_ring_buffer.cc', + 'midi_scene_change.cc', + 'midi_scene_changer.cc', 'midi_source.cc', 'midi_state_tracker.cc', 'midi_stretch.cc', @@ -164,6 +166,7 @@ libardour_sources = [ 'route_group.cc', 'route_group_member.cc', 'rb_effect.cc', + 'scene_change.cc', 'search_paths.cc', 'send.cc', 'session.cc', @@ -352,7 +355,7 @@ def build(bld): obj.use.extend(['librubberband', 'libltc_includes', 'libltc']) obj.vnum = LIBARDOUR_LIB_VERSION - obj.install_path = os.path.join(bld.env['LIBDIR'], 'ardour3') + obj.install_path = bld.env['LIBDIR'] obj.defines += [ 'PACKAGE="' + I18N_PACKAGE + '"', 'DATA_DIR="' + os.path.normpath(bld.env['DATADIR']) + '"', @@ -566,7 +569,7 @@ def create_ardour_test_program(bld, includes, name, target, sources): testobj.name = name testobj.target = target # not sure about install path - testobj.install_path = os.path.join(bld.env['LIBDIR'], 'ardour3') + testobj.install_path = bld.env['LIBDIR'] testobj.defines = [ 'PACKAGE="libardour3test"', 'DATA_DIR="' + os.path.normpath(bld.env['DATADIR']) + '"', diff --git a/libs/audiographer/wscript b/libs/audiographer/wscript index 0e4cd5d7f4..2fe2739741 100644 --- a/libs/audiographer/wscript +++ b/libs/audiographer/wscript @@ -82,7 +82,7 @@ def build(bld): audiographer.uselib = 'GLIB GLIBMM GTHREAD SAMPLERATE SNDFILE' audiographer.use = 'libpbd' audiographer.vnum = AUDIOGRAPHER_LIB_VERSION - audiographer.install_path = os.path.join(bld.env['LIBDIR'], 'ardour3') + audiographer.install_path = bld.env['LIBDIR'] if bld.env['BUILD_TESTS'] and bld.is_defined('HAVE_CPPUNIT'): diff --git a/libs/backends/dummy/dummy_audiobackend.cc b/libs/backends/dummy/dummy_audiobackend.cc new file mode 100644 index 0000000000..f0eeb8db12 --- /dev/null +++ b/libs/backends/dummy/dummy_audiobackend.cc @@ -0,0 +1,1271 @@ +/* + * Copyright (C) 2014 Robin Gareus + * Copyright (C) 2013 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 "dummy_audiobackend.h" +#include "pbd/error.h" +#include "i18n.h" + +using namespace ARDOUR; + +static std::string s_instance_name; +size_t DummyAudioBackend::_max_buffer_size = 8192; + +DummyAudioBackend::DummyAudioBackend (AudioEngine& e) + : AudioBackend (e) + , _running (false) + , _freewheeling (false) + , _samplerate (48000) + , _samples_per_period (1024) + , _dsp_load (0) + , _n_inputs (0) + , _n_outputs (0) + , _systemic_input_latency (0) + , _systemic_output_latency (0) + , _processed_samples (0) +{ + _instance_name = s_instance_name; +} + +DummyAudioBackend::~DummyAudioBackend () +{ +} + +/* AUDIOBACKEND API */ + +std::string +DummyAudioBackend::name () const +{ + return X_("Dummy"); +} + +bool +DummyAudioBackend::is_realtime () const +{ + return false; +} + +std::vector +DummyAudioBackend::enumerate_devices () const +{ + std::vector s; + s.push_back (DeviceStatus (_("Dummy"), true)); + return s; +} + +std::vector +DummyAudioBackend::available_sample_rates (const std::string&) const +{ + std::vector sr; + sr.push_back (8000.0); + sr.push_back (22050.0); + sr.push_back (24000.0); + sr.push_back (44100.0); + sr.push_back (48000.0); + sr.push_back (88200.0); + sr.push_back (96000.0); + sr.push_back (176400.0); + sr.push_back (192000.0); + return sr; +} + +std::vector +DummyAudioBackend::available_buffer_sizes (const std::string&) const +{ + std::vector bs; + bs.push_back (4); + bs.push_back (8); + bs.push_back (16); + bs.push_back (32); + bs.push_back (64); + bs.push_back (128); + bs.push_back (256); + bs.push_back (512); + bs.push_back (1024); + bs.push_back (2048); + bs.push_back (4096); + bs.push_back (8192); + return bs; +} + +uint32_t +DummyAudioBackend::available_input_channel_count (const std::string&) const +{ + return 128; +} + +uint32_t +DummyAudioBackend::available_output_channel_count (const std::string&) const +{ + return 128; +} + +bool +DummyAudioBackend::can_change_sample_rate_when_running () const +{ + return true; +} + +bool +DummyAudioBackend::can_change_buffer_size_when_running () const +{ + return true; +} + +int +DummyAudioBackend::set_device_name (const std::string&) +{ + return 0; +} + +int +DummyAudioBackend::set_sample_rate (float sr) +{ + if (sr <= 0) { return -1; } + _samplerate = sr; + engine.sample_rate_change (sr); + return 0; +} + +int +DummyAudioBackend::set_buffer_size (uint32_t bs) +{ + if (bs <= 0 || bs >= _max_buffer_size) { + return -1; + } + _samples_per_period = bs; + engine.buffer_size_change (bs); + return 0; +} + +int +DummyAudioBackend::set_interleaved (bool yn) +{ + if (!yn) { return 0; } + return -1; +} + +int +DummyAudioBackend::set_input_channels (uint32_t cc) +{ + _n_inputs = cc; + return 0; +} + +int +DummyAudioBackend::set_output_channels (uint32_t cc) +{ + _n_outputs = cc; + return 0; +} + +int +DummyAudioBackend::set_systemic_input_latency (uint32_t sl) +{ + _systemic_input_latency = sl; + return 0; +} + +int +DummyAudioBackend::set_systemic_output_latency (uint32_t sl) +{ + _systemic_output_latency = sl; + return 0; +} + +/* Retrieving parameters */ +std::string +DummyAudioBackend::device_name () const +{ + return _("Dummy Device"); +} + +float +DummyAudioBackend::sample_rate () const +{ + return _samplerate; +} + +uint32_t +DummyAudioBackend::buffer_size () const +{ + return _samples_per_period; +} + +bool +DummyAudioBackend::interleaved () const +{ + return false; +} + +uint32_t +DummyAudioBackend::input_channels () const +{ + return _n_inputs; +} + +uint32_t +DummyAudioBackend::output_channels () const +{ + return _n_outputs; +} + +uint32_t +DummyAudioBackend::systemic_input_latency () const +{ + return _systemic_input_latency; +} + +uint32_t +DummyAudioBackend::systemic_output_latency () const +{ + return _systemic_output_latency; +} + +/* MIDI */ +std::vector +DummyAudioBackend::enumerate_midi_options () const +{ + std::vector m; + m.push_back (_("None")); + return m; +} + +int +DummyAudioBackend::set_midi_option (const std::string&) +{ + return -1; +} + +std::string +DummyAudioBackend::midi_option () const +{ + return ""; +} + +/* State Control */ + +static void * pthread_process (void *arg) +{ + DummyAudioBackend *d = static_cast(arg); + d->main_process_thread (); + pthread_exit (0); + return 0; +} + +int +DummyAudioBackend::_start (bool /*for_latency_measurement*/) +{ + if (_running) { + PBD::error << _("DummyAudioBackend: already active.") << endmsg; + return -1; + } + + if (_ports.size()) { + PBD::warning << _("DummyAudioBackend: recovering from unclean shutdown, port registry is not empty.") << endmsg; + _ports.clear(); + } + + if (register_system_ports()) { + PBD::error << _("DummyAudioBackend: failed to register system ports.") << endmsg; + return -1; + } + + if (engine.reestablish_ports ()) { + PBD::error << _("DummyAudioBackend: Could not re-establish ports.") << endmsg; + stop (); + return -1; + } + + engine.reconnect_ports (); + + if (pthread_create (&_main_thread, NULL, pthread_process, this)) { + PBD::error << _("DummyAudioBackend: cannot start.") << endmsg; + } + + int timeout = 5000; + while (!_running && --timeout > 0) { usleep (1000); } + + if (timeout == 0 || !_running) { + PBD::error << _("DummyAudioBackend: failed to start process thread.") << endmsg; + return -1; + } + + return 0; +} + +int +DummyAudioBackend::stop () +{ + void *status; + if (!_running) { + return -1; + } + + _running = false; + if (pthread_join (_main_thread, &status)) { + PBD::error << _("DummyAudioBackend: failed to terminate.") << endmsg; + return -1; + } + unregister_system_ports(); + return 0; +} + +int +DummyAudioBackend::freewheel (bool onoff) +{ + if (onoff == _freewheeling) { + return 0; + } + _freewheeling = onoff; + engine.freewheel_callback (onoff); + return 0; +} + +float +DummyAudioBackend::dsp_load () const +{ + return 100.f * _dsp_load; +} + +size_t +DummyAudioBackend::raw_buffer_size (DataType t) +{ + switch (t) { + case DataType::AUDIO: + return _max_buffer_size * sizeof(Sample); + case DataType::MIDI: + return _max_buffer_size; // XXX not really limited + } + return 0; +} + +/* Process time */ +pframes_t +DummyAudioBackend::sample_time () +{ + return _processed_samples; +} + +pframes_t +DummyAudioBackend::sample_time_at_cycle_start () +{ + return _processed_samples; +} + +pframes_t +DummyAudioBackend::samples_since_cycle_start () +{ + return 0; +} + + +void * +DummyAudioBackend::dummy_process_thread (void *arg) +{ + ThreadData* td = reinterpret_cast (arg); + boost::function f = td->f; + delete td; + f (); + return 0; +} + +int +DummyAudioBackend::create_process_thread (boost::function func) +{ + pthread_t thread_id; + pthread_attr_t attr; + size_t stacksize = 100000; + + pthread_attr_init (&attr); + pthread_attr_setstacksize (&attr, stacksize); + ThreadData* td = new ThreadData (this, func, stacksize); + + if (pthread_create (&thread_id, &attr, dummy_process_thread, td)) { + PBD::error << _("AudioEngine: cannot create process thread.") << endmsg; + return -1; + } + + _threads.push_back (thread_id); + return 0; +} + +int +DummyAudioBackend::join_process_threads () +{ + int rv = 0; + + for (std::vector::const_iterator i = _threads.begin (); i != _threads.end (); ++i) + { + void *status; + if (pthread_join (*i, &status)) { + PBD::error << _("AudioEngine: cannot terminate process thread.") << endmsg; + rv -= 1; + } + } + _threads.clear (); + return rv; +} + +bool +DummyAudioBackend::in_process_thread () +{ + for (std::vector::const_iterator i = _threads.begin (); i != _threads.end (); ++i) + { +#ifdef COMPILER_MINGW + if (*i == GetCurrentThread ()) { + return true; + } +#else // pthreads + if (pthread_equal (*i, pthread_self ()) != 0) { + return true; + } +#endif + } + return false; +} + +uint32_t +DummyAudioBackend::process_thread_count () +{ + return _threads.size (); +} + +void +DummyAudioBackend::update_latencies () +{ +} + +/* PORTENGINE API */ + +void* +DummyAudioBackend::private_handle () const +{ + return NULL; +} + +const std::string& +DummyAudioBackend::my_name () const +{ + return _instance_name; +} + +bool +DummyAudioBackend::available () const +{ + return true; +} + +uint32_t +DummyAudioBackend::port_name_size () const +{ + return 256; +} + +int +DummyAudioBackend::set_port_name (PortEngine::PortHandle port, const std::string& name) +{ + if (!valid_port (port)) { + PBD::error << _("DummyBackend::set_port_name: Invalid Port(s)") << endmsg; + return -1; + } + return static_cast(port)->set_name (_instance_name + ":" + name); +} + +std::string +DummyAudioBackend::get_port_name (PortEngine::PortHandle port) const +{ + if (!valid_port (port)) { + PBD::error << _("DummyBackend::get_port_name: Invalid Port(s)") << endmsg; + return std::string (); + } + return static_cast(port)->name (); +} + +PortEngine::PortHandle +DummyAudioBackend::get_port_by_name (const std::string& name) const +{ + PortHandle port = (PortHandle) find_port (name); + return port; +} + +int +DummyAudioBackend::get_ports ( + const std::string& port_name_pattern, + DataType type, PortFlags flags, + std::vector& port_names) const +{ + int rv = 0; + regex_t port_regex; + bool use_regexp = false; + if (port_name_pattern.size () > 0) { + if (!regcomp (&port_regex, port_name_pattern.c_str (), REG_EXTENDED|REG_NOSUB)) { + use_regexp = true; + } + } + for (size_t i = 0; i < _ports.size (); ++i) { + DummyPort* port = _ports[i]; + if ((port->type () == type) && (port->flags () & flags)) { + if (!use_regexp || !regexec (&port_regex, port->name ().c_str (), 0, NULL, 0)) { + port_names.push_back (port->name ()); + ++rv; + } + } + } + if (use_regexp) { + regfree (&port_regex); + } + return rv; +} + +DataType +DummyAudioBackend::port_data_type (PortEngine::PortHandle port) const +{ + if (!valid_port (port)) { + return DataType::NIL; + } + return static_cast(port)->type (); +} + +PortEngine::PortHandle +DummyAudioBackend::register_port ( + const std::string& name, + ARDOUR::DataType type, + ARDOUR::PortFlags flags) +{ + if (name.size () == 0) { return 0; } + if (flags & IsPhysical) { return 0; } + return add_port (_instance_name + ":" + name, type, flags); +} + +PortEngine::PortHandle +DummyAudioBackend::add_port ( + const std::string& name, + ARDOUR::DataType type, + ARDOUR::PortFlags flags) +{ + assert(name.size ()); + if (find_port (name)) { + PBD::error << _("DummyBackend::register_port: Port already exists:") + << " (" << name << ")" << endmsg; + return 0; + } + DummyPort* port = NULL; + switch (type) { + case DataType::AUDIO: + port = new DummyAudioPort (name, flags); + break; + case DataType::MIDI: + port = new DummyMidiPort (name, flags); + break; + default: + PBD::error << _("DummyBackend::register_port: Invalid Data Type.") << endmsg; + return 0; + } + + _ports.push_back (port); + + return port; +} + +void +DummyAudioBackend::unregister_port (PortEngine::PortHandle port_handle) +{ + if (!valid_port (port_handle)) { + PBD::error << _("DummyBackend::unregister_port: Invalid Port.") << endmsg; + } + DummyPort* port = static_cast(port_handle); + std::vector::iterator i = std::find (_ports.begin (), _ports.end (), static_cast(port_handle)); + if (i == _ports.end ()) { + PBD::error << _("DummyBackend::unregister_port: Failed to find port") << endmsg; + return; + } + disconnect_all(port_handle); + _ports.erase (i); + delete port; +} + +int +DummyAudioBackend::register_system_ports() +{ + LatencyRange lr; + + const int a_ins = _n_inputs > 0 ? _n_inputs : 8; + const int a_out = _n_outputs > 0 ? _n_outputs : 8; + const int m_ins = 2; // TODO + const int m_out = 2; + + /* audio ports */ + lr.min = lr.max = _samples_per_period + _systemic_input_latency; + for (int i = 1; i <= a_ins; ++i) { + char tmp[64]; + snprintf(tmp, sizeof(tmp), "system:capture_%d", i); + PortHandle p = add_port(std::string(tmp), DataType::AUDIO, static_cast(IsOutput | IsPhysical | IsTerminal)); + if (!p) return -1; + set_latency_range (p, false, lr); + } + + lr.min = lr.max = _samples_per_period + _systemic_output_latency; + for (int i = 1; i <= a_out; ++i) { + char tmp[64]; + snprintf(tmp, sizeof(tmp), "system:playback_%d", i); + PortHandle p = add_port(std::string(tmp), DataType::AUDIO, static_cast(IsInput | IsPhysical | IsTerminal)); + if (!p) return -1; + set_latency_range (p, false, lr); + } + + /* midi ports */ + lr.min = lr.max = _samples_per_period + _systemic_input_latency; + for (int i = 1; i <= m_ins; ++i) { + char tmp[64]; + snprintf(tmp, sizeof(tmp), "system:midi_capture_%d", i); + PortHandle p = add_port(std::string(tmp), DataType::MIDI, static_cast(IsOutput | IsPhysical | IsTerminal)); + if (!p) return -1; + set_latency_range (p, false, lr); + } + + lr.min = lr.max = _samples_per_period + _systemic_output_latency; + for (int i = 1; i <= m_out; ++i) { + char tmp[64]; + snprintf(tmp, sizeof(tmp), "system:midi_playback_%d", i); + PortHandle p = add_port(std::string(tmp), DataType::MIDI, static_cast(IsInput | IsPhysical | IsTerminal)); + if (!p) return -1; + set_latency_range (p, false, lr); + } + + return 0; +} + +void +DummyAudioBackend::unregister_system_ports() +{ + size_t i = 0; + while (i < _ports.size ()) { + DummyPort* port = _ports[i]; + if (port->is_physical () && port->is_terminal ()) { + port->disconnect_all (); + _ports.erase (_ports.begin() + i); + } else { + ++i; + } + } +} + +int +DummyAudioBackend::connect (const std::string& src, const std::string& dst) +{ + DummyPort* src_port = find_port (src); + DummyPort* dst_port = find_port (dst); + + if (!src_port) { + PBD::error << _("DummyBackend::connect: Invalid Source port:") + << " (" << src <<")" << endmsg; + return -1; + } + if (!dst_port) { + PBD::error << _("DummyBackend::connect: Invalid Destination port:") + << " (" << dst <<")" << endmsg; + return -1; + } + return src_port->connect (dst_port); +} + +int +DummyAudioBackend::disconnect (const std::string& src, const std::string& dst) +{ + DummyPort* src_port = find_port (src); + DummyPort* dst_port = find_port (dst); + + if (!src_port || !dst_port) { + PBD::error << _("DummyBackend::disconnect: Invalid Port(s)") << endmsg; + return -1; + } + return src_port->disconnect (dst_port); +} + +int +DummyAudioBackend::connect (PortEngine::PortHandle src, const std::string& dst) +{ + DummyPort* dst_port = find_port (dst); + if (!valid_port (src)) { + PBD::error << _("DummyBackend::connect: Invalid Source Port Handle") << endmsg; + return -1; + } + if (!dst_port) { + PBD::error << _("DummyBackend::connect: Invalid Destination Port") + << " (" << dst << ")" << endmsg; + return -1; + } + return static_cast(src)->connect (dst_port); +} + +int +DummyAudioBackend::disconnect (PortEngine::PortHandle src, const std::string& dst) +{ + DummyPort* dst_port = find_port (dst); + if (!valid_port (src) || !dst_port) { + PBD::error << _("DummyBackend::disconnect: Invalid Port(s)") << endmsg; + return -1; + } + return static_cast(src)->disconnect (dst_port); +} + +int +DummyAudioBackend::disconnect_all (PortEngine::PortHandle port) +{ + if (!valid_port (port)) { + PBD::error << _("DummyBackend::disconnect_all: Invalid Port") << endmsg; + return -1; + } + static_cast(port)->disconnect_all (); + return 0; +} + +bool +DummyAudioBackend::connected (PortEngine::PortHandle port, bool /* process_callback_safe*/) +{ + if (!valid_port (port)) { + PBD::error << _("DummyBackend::disconnect_all: Invalid Port") << endmsg; + return false; + } + return static_cast(port)->is_connected (); +} + +bool +DummyAudioBackend::connected_to (PortEngine::PortHandle src, const std::string& dst, bool /*process_callback_safe*/) +{ + DummyPort* dst_port = find_port (dst); + if (!valid_port (src) || !dst_port) { + PBD::error << _("DummyBackend::connected_to: Invalid Port") << endmsg; + return false; + } + return static_cast(src)->is_connected (dst_port); +} + +bool +DummyAudioBackend::physically_connected (PortEngine::PortHandle port, bool /*process_callback_safe*/) +{ + if (!valid_port (port)) { + PBD::error << _("DummyBackend::physically_connected: Invalid Port") << endmsg; + return false; + } + return static_cast(port)->is_physically_connected (); +} + +int +DummyAudioBackend::get_connections (PortEngine::PortHandle port, std::vector& names, bool /*process_callback_safe*/) +{ + if (!valid_port (port)) { + PBD::error << _("DummyBackend::get_connections: Invalid Port") << endmsg; + return -1; + } + + assert (0 == names.size ()); + + const std::vector& connected_ports = static_cast(port)->get_connections (); + + for (std::vector::const_iterator i = connected_ports.begin (); i != connected_ports.end (); ++i) { + names.push_back ((*i)->name ()); + } + + return (int)names.size (); +} + +/* MIDI */ +int +DummyAudioBackend::midi_event_get ( + pframes_t& timestamp, + size_t& size, uint8_t** buf, void* port_buffer, + uint32_t event_index) +{ + assert (buf && port_buffer); + DummyMidiBuffer& source = * static_cast(port_buffer); + if (event_index >= source.size ()) { + return -1; + } + DummyMidiEvent * const event = source[event_index].get (); + + timestamp = event->timestamp (); + size = event->size (); + *buf = event->data (); + return 0; +} + +int +DummyAudioBackend::midi_event_put ( + void* port_buffer, + pframes_t timestamp, + const uint8_t* buffer, size_t size) +{ + assert (buffer && port_buffer); + DummyMidiBuffer& dst = * static_cast(port_buffer); + if (dst.size () && (pframes_t)dst.back ()->timestamp () > timestamp) { + fprintf (stderr, "DummyMidiBuffer: it's too late for this event.\n"); + return -1; + } + dst.push_back (boost::shared_ptr(new DummyMidiEvent (timestamp, buffer, size))); + return 0; +} + +uint32_t +DummyAudioBackend::get_midi_event_count (void* port_buffer) +{ + assert (port_buffer && _running); + return static_cast(port_buffer)->size (); +} + +void +DummyAudioBackend::midi_clear (void* port_buffer) +{ + assert (port_buffer && _running); + DummyMidiBuffer * buf = static_cast(port_buffer); + assert (buf); + buf->clear (); +} + +/* Monitoring */ + +bool +DummyAudioBackend::can_monitor_input () const +{ + return false; +} + +int +DummyAudioBackend::request_input_monitoring (PortEngine::PortHandle, bool) +{ + return -1; +} + +int +DummyAudioBackend::ensure_input_monitoring (PortEngine::PortHandle, bool) +{ + return -1; +} + +bool +DummyAudioBackend::monitoring_input (PortEngine::PortHandle) +{ + return false; +} + +/* Latency management */ + +void +DummyAudioBackend::set_latency_range (PortEngine::PortHandle port, bool for_playback, LatencyRange latency_range) +{ + if (!valid_port (port)) { + PBD::error << _("DummyPort::set_latency_range (): invalid port.") << endmsg; + } + static_cast(port)->set_latency_range (latency_range, for_playback); +} + +LatencyRange +DummyAudioBackend::get_latency_range (PortEngine::PortHandle port, bool for_playback) +{ + if (!valid_port (port)) { + PBD::error << _("DummyPort::get_latency_range (): invalid port.") << endmsg; + LatencyRange r; + r.min = 0; + r.max = 0; + return r; + } + return static_cast(port)->latency_range (for_playback); +} + +/* Discovering physical ports */ + +bool +DummyAudioBackend::port_is_physical (PortEngine::PortHandle port) const +{ + if (!valid_port (port)) { + PBD::error << _("DummyPort::port_is_physical (): invalid port.") << endmsg; + return false; + } + return static_cast(port)->is_physical (); +} + +void +DummyAudioBackend::get_physical_outputs (DataType type, std::vector& port_names) +{ + for (size_t i = 0; i < _ports.size (); ++i) { + DummyPort* port = _ports[i]; + if ((port->type () == type) && port->is_output () && port->is_physical ()) { + port_names.push_back (port->name ()); + } + } +} + +void +DummyAudioBackend::get_physical_inputs (DataType type, std::vector& port_names) +{ + for (size_t i = 0; i < _ports.size (); ++i) { + DummyPort* port = _ports[i]; + if ((port->type () == type) && port->is_input () && port->is_physical ()) { + port_names.push_back (port->name ()); + } + } +} + +ChanCount +DummyAudioBackend::n_physical_outputs () const +{ + int n_midi = 0; + int n_audio = 0; + for (size_t i = 0; i < _ports.size (); ++i) { + DummyPort* port = _ports[i]; + if (port->is_output () && port->is_physical ()) { + switch (port->type ()) { + case DataType::AUDIO: ++n_audio; break; + case DataType::MIDI: ++n_midi; break; + default: break; + } + } + } + ChanCount cc; + cc.set (DataType::AUDIO, n_audio); + cc.set (DataType::MIDI, n_midi); + return cc; +} + +ChanCount +DummyAudioBackend::n_physical_inputs () const +{ + int n_midi = 0; + int n_audio = 0; + for (size_t i = 0; i < _ports.size (); ++i) { + DummyPort* port = _ports[i]; + if (port->is_input () && port->is_physical ()) { + switch (port->type ()) { + case DataType::AUDIO: ++n_audio; break; + case DataType::MIDI: ++n_midi; break; + default: break; + } + } + } + ChanCount cc; + cc.set (DataType::AUDIO, n_audio); + cc.set (DataType::MIDI, n_midi); + return cc; +} + +/* Getting access to the data buffer for a port */ + +void* +DummyAudioBackend::get_buffer (PortEngine::PortHandle port, pframes_t nframes) +{ + assert (port && _running); + assert (valid_port (port)); + return static_cast(port)->get_buffer (nframes); +} + +/* Engine Process */ +void * +DummyAudioBackend::main_process_thread () +{ + AudioEngine::thread_init_callback (this); + _running = true; + _processed_samples = 0; + + struct timeval clock1, clock2; + ::gettimeofday (&clock1, NULL); + while (_running) { + if (engine.process_callback (_samples_per_period)) { + return 0; + } + _processed_samples += _samples_per_period; + if (!_freewheeling) { + ::gettimeofday (&clock2, NULL); + const int elapsed_time = (clock2.tv_sec - clock1.tv_sec) * 1000000 + (clock2.tv_usec - clock1.tv_usec); + const int nomial_time = 1000000 * _samples_per_period / _samplerate; + _dsp_load = elapsed_time / (float) nomial_time; + if (elapsed_time < nomial_time) { + ::usleep (nomial_time - elapsed_time); + } else { + ::usleep (100); // don't hog cpu + } + } else { + _dsp_load = 1.0; + ::usleep (100); // don't hog cpu + } + ::gettimeofday (&clock1, NULL); + } + _running = false; + return 0; +} + + +/******************************************************************************/ + +static boost::shared_ptr _instance; + +static boost::shared_ptr +backend_factory (AudioEngine& e) +{ + if (!_instance) { + _instance.reset (new DummyAudioBackend (e)); + } + return _instance; +} + +static int +instantiate (const std::string& arg1, const std::string& /* arg2 */) +{ + s_instance_name = arg1; + return 0; +} + +static int +deinstantiate () +{ + _instance.reset (); + return 0; +} + +static bool +already_configured () +{ + return false; +} + +static ARDOUR::AudioBackendInfo _descriptor = { + "Dummy", + instantiate, + deinstantiate, + backend_factory, + already_configured, +}; + +extern "C" ARDOURBACKEND_API ARDOUR::AudioBackendInfo* descriptor () +{ + return &_descriptor; +} + + +/******************************************************************************/ +DummyPort::DummyPort (const std::string& name, PortFlags flags) + : _name (name) + , _flags (flags) +{ + _capture_latency_range.min = 0; + _capture_latency_range.max = 0; + _playback_latency_range.min = 0; + _playback_latency_range.max = 0; +} + +DummyPort::~DummyPort () { + disconnect_all (); +} + + +int DummyPort::connect (DummyPort *port) +{ + if (!port) { + PBD::error << _("DummyPort::connect (): invalid (null) port") << endmsg; + return -1; + } + + if (type () != port->type ()) { + PBD::error << _("DummyPort::connect (): wrong port-type") << endmsg; + return -1; + } + + if (is_output () && port->is_output ()) { + PBD::error << _("DummyPort::connect (): cannot inter-connect output ports.") << endmsg; + return -1; + } + + if (is_input () && port->is_input ()) { + PBD::error << _("DummyPort::connect (): cannot inter-connect input ports.") << endmsg; + return -1; + } + + if (this == port) { + PBD::error << _("DummyPort::connect (): cannot self-connect ports.") << endmsg; + return -1; + } + + if (is_connected (port)) { +#if 0 // don't bother to warn about this for now. just ignore it + PBD::error << _("DummyPort::connect (): ports are already connected:") + << " (" << name () << ") -> (" << port->name () << ")" + << endmsg; +#endif + return -1; + } + + _connect (port, true); + return 0; +} + + +void DummyPort::_connect (DummyPort *port, bool callback) +{ + _connections.push_back (port); + if (callback) { + port->_connect (this, false); + } +} + +int DummyPort::disconnect (DummyPort *port) +{ + if (!port) { + PBD::error << _("DummyPort::disconnect (): invalid (null) port") << endmsg; + return -1; + } + + if (!is_connected (port)) { + PBD::error << _("DummyPort::disconnect (): ports are not connected:") + << " (" << name () << ") -> (" << port->name () << ")" + << endmsg; + return -1; + } + _disconnect (port, true); + return 0; +} + +void DummyPort::_disconnect (DummyPort *port, bool callback) +{ + std::vector::iterator it = std::find (_connections.begin (), _connections.end (), port); + + assert (it != _connections.end ()); + + _connections.erase (it); + + if (callback) { + port->_disconnect (this, false); + } +} + + +void DummyPort::disconnect_all () +{ + while (!_connections.empty ()) { + _connections.back ()->_disconnect (this, false); + _connections.pop_back (); + } +} + +bool +DummyPort::is_connected (const DummyPort *port) const +{ + return std::find (_connections.begin (), _connections.end (), port) != _connections.end (); +} + +bool DummyPort::is_physically_connected () const +{ + for (std::vector::const_iterator it = _connections.begin (); it != _connections.end (); ++it) { + if ((*it)->is_physical ()) { + return true; + } + } + return false; +} + +/******************************************************************************/ + +DummyAudioPort::DummyAudioPort (const std::string& name, PortFlags flags) + : DummyPort (name, flags) +{ + memset (_buffer, 0, sizeof (_buffer)); +} + +DummyAudioPort::~DummyAudioPort () { } + +void* DummyAudioPort::get_buffer (pframes_t n_samples) +{ + if (is_input ()) { + std::vector::const_iterator it = get_connections ().begin (); + if (it == get_connections ().end ()) { + memset (_buffer, 0, n_samples * sizeof (Sample)); + } else { + DummyAudioPort const * source = static_cast(*it); + assert (source && source->is_output ()); + memcpy (_buffer, source->const_buffer (), n_samples * sizeof (Sample)); + while (++it != get_connections ().end ()) { + source = static_cast(*it); + assert (source && source->is_output ()); + Sample* dst = buffer (); + const Sample* src = source->const_buffer (); + for (uint32_t s = 0; s < n_samples; ++s, ++dst, ++src) { + *dst += *src; + } + } + } + } else if (is_output () && is_physical () && is_terminal()) { + memset (_buffer, 0, n_samples * sizeof (Sample)); + } + return _buffer; +} + + +DummyMidiPort::DummyMidiPort (const std::string& name, PortFlags flags) + : DummyPort (name, flags) +{ + _buffer.clear (); +} + +DummyMidiPort::~DummyMidiPort () { } + +void* DummyMidiPort::get_buffer (pframes_t /* nframes */) +{ + if (is_input ()) { + _buffer.clear (); + for (std::vector::const_iterator i = get_connections ().begin (); + i != get_connections ().end (); + ++i) { + const DummyMidiBuffer src = static_cast(*i)->const_buffer (); + for (DummyMidiBuffer::const_iterator it = src.begin (); it != src.end (); ++it) { + _buffer.push_back (boost::shared_ptr(new DummyMidiEvent (**it))); + } + } + std::sort (_buffer.begin (), _buffer.end ()); + } else if (is_output () && is_physical () && is_terminal()) { + _buffer.clear (); + } + return &_buffer; +} + +DummyMidiEvent::DummyMidiEvent (const pframes_t timestamp, const uint8_t* data, size_t size) + : _size (size) + , _timestamp (timestamp) + , _data (0) +{ + if (size > 0) { + _data = (uint8_t*) malloc (size); + memcpy (_data, data, size); + } +} + +DummyMidiEvent::DummyMidiEvent (const DummyMidiEvent& other) + : _size (other.size ()) + , _timestamp (other.timestamp ()) + , _data (0) +{ + if (other.size () && other.const_data ()) { + _data = (uint8_t*) malloc (other.size ()); + memcpy (_data, other.const_data (), other.size ()); + } +}; + +DummyMidiEvent::~DummyMidiEvent () { + free (_data); +}; diff --git a/libs/backends/dummy/dummy_audiobackend.h b/libs/backends/dummy/dummy_audiobackend.h new file mode 100644 index 0000000000..18d14cb867 --- /dev/null +++ b/libs/backends/dummy/dummy_audiobackend.h @@ -0,0 +1,328 @@ +/* + * Copyright (C) 2014 Robin Gareus + * Copyright (C) 2013 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 __libbackend_dummy_audiobackend_h__ +#define __libbackend_dummy_audiobackend_h__ + +#include +#include +#include +#include + +#include +#include + +#include + +#include "ardour/types.h" +#include "ardour/audio_backend.h" + +namespace ARDOUR { + +class DummyMidiEvent { + public: + DummyMidiEvent (const pframes_t timestamp, const uint8_t* data, size_t size); + DummyMidiEvent (const DummyMidiEvent& other); + ~DummyMidiEvent (); + size_t size () const { return _size; }; + pframes_t timestamp () const { return _timestamp; }; + const unsigned char* const_data () const { return _data; }; + unsigned char* data () { return _data; }; + bool operator< (const DummyMidiEvent &other) const { return timestamp () < other.timestamp (); }; + private: + size_t _size; + pframes_t _timestamp; + uint8_t *_data; +}; + +typedef std::vector > DummyMidiBuffer; + +class DummyPort { + protected: + DummyPort (const std::string&, PortFlags); + public: + virtual ~DummyPort (); + + const std::string& name () const { return _name; } + PortFlags flags () const { return _flags; } + + int set_name (const std::string &name) { _name = name; return 0; } + + virtual DataType type () const = 0; + + bool is_input () const { return flags () & IsInput; } + bool is_output () const { return flags () & IsOutput; } + bool is_physical () const { return flags () & IsPhysical; } + bool is_terminal () const { return flags () & IsTerminal; } + bool is_connected () const { return _connections.size () != 0; } + bool is_connected (const DummyPort *port) const; + bool is_physically_connected () const; + + const std::vector& get_connections () const { return _connections; } + + int connect (DummyPort *port); + int disconnect (DummyPort *port); + void disconnect_all (); + + virtual void* get_buffer (pframes_t nframes) = 0; + + const LatencyRange& latency_range (bool for_playback) const + { + return for_playback ? _playback_latency_range : _capture_latency_range; + } + + void set_latency_range (const LatencyRange &latency_range, bool for_playback) + { + if (for_playback) + { + _playback_latency_range = latency_range; + } + else + { + _capture_latency_range = latency_range; + } + } + + private: + std::string _name; + const PortFlags _flags; + LatencyRange _capture_latency_range; + LatencyRange _playback_latency_range; + std::vector _connections; + + void _connect (DummyPort* , bool); + void _disconnect (DummyPort* , bool); + +}; // class DummyPort + +class DummyAudioPort : public DummyPort { + public: + DummyAudioPort (const std::string&, PortFlags); + ~DummyAudioPort (); + + DataType type () const { return DataType::AUDIO; }; + + Sample* buffer () { return _buffer; } + const Sample* const_buffer () const { return _buffer; } + void* get_buffer (pframes_t nframes); + + private: + Sample _buffer[8192]; +}; // class DummyAudioPort + +class DummyMidiPort : public DummyPort { + public: + DummyMidiPort (const std::string&, PortFlags); + ~DummyMidiPort (); + + DataType type () const { return DataType::MIDI; }; + + void* get_buffer (pframes_t nframes); + const DummyMidiBuffer const_buffer () const { return _buffer; } + + private: + DummyMidiBuffer _buffer; +}; // class DummyMidiPort + +class DummyAudioBackend : public AudioBackend { + public: + DummyAudioBackend (AudioEngine& e); + ~DummyAudioBackend (); + + /* AUDIOBACKEND API */ + + std::string name () const; + bool is_realtime () const; + + std::vector enumerate_devices () const; + std::vector available_sample_rates (const std::string& device) const; + std::vector available_buffer_sizes (const std::string& device) const; + uint32_t available_input_channel_count (const std::string& device) const; + uint32_t available_output_channel_count (const std::string& device) const; + + bool can_change_sample_rate_when_running () const; + bool can_change_buffer_size_when_running () const; + + int set_device_name (const std::string&); + int set_sample_rate (float); + int set_buffer_size (uint32_t); + int set_interleaved (bool yn); + int set_input_channels (uint32_t); + int set_output_channels (uint32_t); + int set_systemic_input_latency (uint32_t); + int set_systemic_output_latency (uint32_t); + + /* Retrieving parameters */ + std::string device_name () const; + float sample_rate () const; + uint32_t buffer_size () const; + bool interleaved () const; + uint32_t input_channels () const; + uint32_t output_channels () const; + uint32_t systemic_input_latency () const; + uint32_t systemic_output_latency () const; + + /* External control app */ + std::string control_app_name () const { return std::string (); } + void launch_control_app () {} + + /* MIDI */ + std::vector enumerate_midi_options () const; + int set_midi_option (const std::string&); + std::string midi_option () const; + + /* State Control */ + protected: + int _start (bool for_latency_measurement); + public: + int stop (); + int freewheel (bool); + float dsp_load () const; + size_t raw_buffer_size (DataType t); + + /* Process time */ + pframes_t sample_time (); + pframes_t sample_time_at_cycle_start (); + pframes_t samples_since_cycle_start (); + + int create_process_thread (boost::function func); + int join_process_threads (); + bool in_process_thread (); + uint32_t process_thread_count (); + + void update_latencies (); + + /* PORTENGINE API */ + + void* private_handle () const; + const std::string& my_name () const; + bool available () const; + uint32_t port_name_size () const; + + int set_port_name (PortHandle, const std::string&); + std::string get_port_name (PortHandle) const; + PortHandle get_port_by_name (const std::string&) const; + + int get_ports (const std::string& port_name_pattern, DataType type, PortFlags flags, std::vector&) const; + + DataType port_data_type (PortHandle) const; + + PortHandle register_port (const std::string& shortname, ARDOUR::DataType, ARDOUR::PortFlags); + void unregister_port (PortHandle); + + int connect (const std::string& src, const std::string& dst); + int disconnect (const std::string& src, const std::string& dst); + int connect (PortHandle, const std::string&); + int disconnect (PortHandle, const std::string&); + int disconnect_all (PortHandle); + + bool connected (PortHandle, bool process_callback_safe); + bool connected_to (PortHandle, const std::string&, bool process_callback_safe); + bool physically_connected (PortHandle, bool process_callback_safe); + int get_connections (PortHandle, std::vector&, bool process_callback_safe); + + /* MIDI */ + int midi_event_get (pframes_t& timestamp, size_t& size, uint8_t** buf, void* port_buffer, uint32_t event_index); + int midi_event_put (void* port_buffer, pframes_t timestamp, const uint8_t* buffer, size_t size); + uint32_t get_midi_event_count (void* port_buffer); + void midi_clear (void* port_buffer); + + /* Monitoring */ + + bool can_monitor_input () const; + int request_input_monitoring (PortHandle, bool); + int ensure_input_monitoring (PortHandle, bool); + bool monitoring_input (PortHandle); + + /* Latency management */ + + void set_latency_range (PortHandle, bool for_playback, LatencyRange); + LatencyRange get_latency_range (PortHandle, bool for_playback); + + /* Discovering physical ports */ + + bool port_is_physical (PortHandle) const; + void get_physical_outputs (DataType type, std::vector&); + void get_physical_inputs (DataType type, std::vector&); + ChanCount n_physical_outputs () const; + ChanCount n_physical_inputs () const; + + /* Getting access to the data buffer for a port */ + + void* get_buffer (PortHandle, pframes_t); + + void* main_process_thread (); + + private: + std::string _instance_name; + bool _running; + bool _freewheeling; + + float _samplerate; + size_t _samples_per_period; + float _dsp_load; + static size_t _max_buffer_size; + + uint32_t _n_inputs; + uint32_t _n_outputs; + + uint32_t _systemic_input_latency; + uint32_t _systemic_output_latency; + + uint64_t _processed_samples; + + pthread_t _main_thread; + + /* process threads */ + static void* dummy_process_thread (void *); + std::vector _threads; + + struct ThreadData { + DummyAudioBackend* engine; + boost::function f; + size_t stacksize; + + ThreadData (DummyAudioBackend* e, boost::function fp, size_t stacksz) + : engine (e) , f (fp) , stacksize (stacksz) {} + }; + + /* port engine */ + PortHandle add_port (const std::string& shortname, ARDOUR::DataType, ARDOUR::PortFlags); + int register_system_ports (); + void unregister_system_ports (); + + std::vector _ports; + + bool valid_port (PortHandle port) const { + return std::find (_ports.begin (), _ports.end (), (DummyPort*)port) != _ports.end (); + } + DummyPort * find_port (const std::string& port_name) const { + for (std::vector::const_iterator it = _ports.begin (); it != _ports.end (); ++it) { + if ((*it)->name () == port_name) { + return *it; + } + } + return NULL; + } + +}; // class DummyAudioBackend + +} // namespace + +#endif /* __libbackend_dummy_audiobackend_h__ */ diff --git a/libs/backends/dummy/wscript b/libs/backends/dummy/wscript new file mode 100644 index 0000000000..8c8db6a9f4 --- /dev/null +++ b/libs/backends/dummy/wscript @@ -0,0 +1,37 @@ +#!/usr/bin/env python +from waflib.extras import autowaf as autowaf +import os +import sys +import re + +# Library version (UNIX style major, minor, micro) +# major increment <=> incompatible changes +# minor increment <=> compatible changes (additions) +# micro increment <=> no interface changes +DUMMYBACKEND_VERSION = '0.0.1' +I18N_PACKAGE = 'dummy-backend' + +# Mandatory variables +top = '.' +out = 'build' + +def options(opt): + autowaf.set_options(opt) + +def configure(conf): + autowaf.configure(conf) + +def build(bld): + obj = bld(features = 'cxx cxxshlib') + obj.source = [ + 'dummy_audiobackend.cc', + ] + obj.includes = ['.'] + obj.name = 'dummy_audiobackend' + obj.target = 'dummy_audiobackend' + obj.use = 'libardour libpbd' + obj.vnum = DUMMYBACKEND_VERSION + obj.install_path = os.path.join(bld.env['LIBDIR'], 'backends') + obj.defines = ['PACKAGE="' + I18N_PACKAGE + '"', + 'ARDOURBACKEND_DLL_EXPORTS' + ] diff --git a/libs/backends/jack/wscript b/libs/backends/jack/wscript index b31d9b5d56..1f54e7a5e7 100644 --- a/libs/backends/jack/wscript +++ b/libs/backends/jack/wscript @@ -51,7 +51,7 @@ def build(bld): obj.uselib = [ 'JACK' ] obj.use = 'libardour libpbd' obj.vnum = JACKBACKEND_VERSION - obj.install_path = os.path.join(bld.env['LIBDIR'], 'ardour3', 'backends') + obj.install_path = os.path.join(bld.env['LIBDIR'], 'backends') obj.defines = ['PACKAGE="' + I18N_PACKAGE + '"', 'ARDOURBACKEND_DLL_EXPORTS' ] diff --git a/libs/backends/wavesaudio/wscript b/libs/backends/wavesaudio/wscript index 372470c859..814f16bc44 100644 --- a/libs/backends/wavesaudio/wscript +++ b/libs/backends/wavesaudio/wscript @@ -67,7 +67,7 @@ def build(bld): obj.target = 'waves_audiobackend' obj.use = [ 'libardour', 'libpbd' ] obj.vnum = WAVESAUDIOBACKEND_VERSION - obj.install_path = os.path.join(bld.env['LIBDIR'], 'ardour3', 'backends') + obj.install_path = os.path.join(bld.env['LIBDIR'], 'backends') obj.defines = ['PACKAGE="' + I18N_PACKAGE + '"', '__MACOS__', 'ARDOURBACKEND_DLL_EXPORTS' diff --git a/libs/backends/wscript b/libs/backends/wscript index f27071127a..36ef5c1ecd 100644 --- a/libs/backends/wscript +++ b/libs/backends/wscript @@ -7,7 +7,7 @@ import sys top = '.' out = 'build' -backends = [ 'jack' ] +backends = [ 'jack', 'dummy' ] if sys.platform == 'darwin': backends += ['wavesaudio' ] diff --git a/libs/canvas/wscript b/libs/canvas/wscript index 05add846c3..0c5192645c 100644 --- a/libs/canvas/wscript +++ b/libs/canvas/wscript @@ -85,7 +85,7 @@ def build(bld): obj.name = 'libcanvas' obj.target = 'canvas' obj.vnum = CANVAS_LIB_VERSION - obj.install_path = os.path.join(bld.env['LIBDIR'], 'ardour3') + obj.install_path = bld.env['LIBDIR'] obj.defines += [ 'PACKAGE="' + I18N_PACKAGE + '"' ] if bld.env['BUILD_TESTS'] and bld.env['HAVE_CPPUNIT']: diff --git a/libs/clearlooks-newer/wscript b/libs/clearlooks-newer/wscript index e0e5d5fb8a..6ef94e3aff 100644 --- a/libs/clearlooks-newer/wscript +++ b/libs/clearlooks-newer/wscript @@ -35,7 +35,7 @@ def build(bld): obj.target = 'clearlooks' obj.uselib = 'GTK' obj.includes = '.' - obj.install_path = os.path.join(bld.env['LIBDIR'], 'ardour3', 'engines') + obj.install_path = os.path.join(bld.env['LIBDIR'], 'engines') autowaf.ensure_visible_symbols (obj, True) if sys.platform == 'darwin': diff --git a/libs/evoral/wscript b/libs/evoral/wscript index 55c03fcdf5..a4aea3a3a2 100644 --- a/libs/evoral/wscript +++ b/libs/evoral/wscript @@ -73,7 +73,7 @@ def build(bld): libsmf.uselib = 'GLIB' libsmf.cxxflags = [ '-fPIC' ] libsmf.cflags = [ '-fPIC' ] - libsmf.install_path = os.path.join(bld.env['LIBDIR'], 'ardour3') + libsmf.install_path = bld.env['LIBDIR'] lib_source = ''' src/Control.cpp @@ -108,7 +108,7 @@ def build(bld): obj.uselib = 'GLIBMM GTHREAD SMF' obj.use = 'libsmf libpbd' obj.vnum = EVORAL_LIB_VERSION - obj.install_path = os.path.join(bld.env['LIBDIR'], 'ardour3') + obj.install_path = bld.env['LIBDIR'] obj.defines += [ 'PACKAGE="libevoral"', 'EVORAL_MIDI_XML=1' ] if bld.env['BUILD_TESTS'] and bld.is_defined('HAVE_CPPUNIT'): diff --git a/libs/fst/wscript b/libs/fst/wscript index 809f13840b..91273f3d79 100644 --- a/libs/fst/wscript +++ b/libs/fst/wscript @@ -51,7 +51,7 @@ def build(bld): obj.source = 'scanner.wine' obj.target = 'ardour-vst-scanner' obj.chmod = Utils.O755 - obj.install_path = os.path.join(bld.env['LIBDIR'], 'ardour3/fst') + obj.install_path = os.path.join(bld.env['LIBDIR'], 'fst') obj.dict = { 'VERSION' : bld.env['VERSION'], } @@ -83,4 +83,4 @@ def build(bld): 'VST_SCANNER_APP', 'PACKAGE="' + I18N_PACKAGE + '"', ] - obj.install_path = os.path.join(bld.env['LIBDIR'], 'ardour3/fst') + obj.install_path = os.path.join(bld.env['LIBDIR'], 'fst') diff --git a/libs/gtkmm2ext/wscript b/libs/gtkmm2ext/wscript index 6d9f1edbe8..c2de82d0c2 100644 --- a/libs/gtkmm2ext/wscript +++ b/libs/gtkmm2ext/wscript @@ -101,7 +101,7 @@ def build(bld): obj.uselib = 'GTKMM GTK GTKOSX OSX GDK' obj.use = [ 'libpbd' ] obj.vnum = GTKMM2EXT_LIB_VERSION - obj.install_path = os.path.join(bld.env['LIBDIR'], 'ardour3') + obj.install_path = bld.env['LIBDIR'] obj.defines += [ 'PACKAGE="' + I18N_PACKAGE + '"', 'LOCALEDIR="' + os.path.join( diff --git a/libs/midi++2/channel.cc b/libs/midi++2/channel.cc index ed8f4da5bc..190ea18568 100644 --- a/libs/midi++2/channel.cc +++ b/libs/midi++2/channel.cc @@ -115,7 +115,7 @@ Channel::process_controller (Parser & /*parser*/, EventTwoBytes *tb) all changes *are* atomic. */ - if (tb->controller_number <= 31) { /* unsigned: no test for >= 0 */ + if (tb->controller_number < 32) { /* unsigned: no test for >= 0 */ /* if this controller is already known to use 14 bits, then treat this value as the MSB, and combine it @@ -128,7 +128,7 @@ Channel::process_controller (Parser & /*parser*/, EventTwoBytes *tb) cv = (unsigned short) _controller_val[tb->controller_number]; if (_controller_14bit[tb->controller_number]) { - cv = ((tb->value << 7) | (cv & 0x7f)); + cv = ((tb->value & 0x7f) << 7) | (cv & 0x7f); } else { cv = tb->value; } @@ -160,8 +160,14 @@ Channel::process_controller (Parser & /*parser*/, EventTwoBytes *tb) cv = (cv & 0x3f80) | (tb->value & 0x7f); } - _controller_val[tb->controller_number] = - (controller_value_t) cv; + /* update the 14 bit value */ + _controller_val[cn] = (controller_value_t) cv; + + /* also store the "raw" 7 bit value in the incoming controller + value store + */ + _controller_val[tb->controller_number] = (controller_value_t) tb->value; + } else { /* controller can only take 7 bit values */ @@ -173,12 +179,11 @@ Channel::process_controller (Parser & /*parser*/, EventTwoBytes *tb) /* bank numbers are special, in that they have their own signal */ - if (tb->controller_number == 0) { - _bank_number = (unsigned short) _controller_val[0]; + if (tb->controller_number == 0 || tb->controller_number == 0x20) { + _bank_number = _controller_val[0]; _port.parser()->bank_change (*_port.parser(), _bank_number); _port.parser()->channel_bank_change[_channel_number] (*_port.parser(), _bank_number); } - } void diff --git a/libs/midi++2/midi++/channel.h b/libs/midi++2/midi++/channel.h index 02c16e6729..f3ec434ca5 100644 --- a/libs/midi++2/midi++/channel.h +++ b/libs/midi++2/midi++/channel.h @@ -42,7 +42,7 @@ class LIBMIDIPP_API Channel : public PBD::ScopedConnectionList { Port &midi_port() { return _port; } byte channel() { return _channel_number; } byte program() { return _program_number; } - byte bank() { return _bank_number; } + unsigned short bank() { return _bank_number; } byte pressure () { return _chanpress; } byte poly_pressure (byte n) { return _polypress[n]; } @@ -117,7 +117,7 @@ class LIBMIDIPP_API Channel : public PBD::ScopedConnectionList { /* Current channel values */ byte _channel_number; - byte _bank_number; + unsigned short _bank_number; byte _program_number; byte _rpn_msb; byte _rpn_lsb; diff --git a/libs/midi++2/midi++/parser.h b/libs/midi++2/midi++/parser.h index e4126b210b..420e7fcb7b 100644 --- a/libs/midi++2/midi++/parser.h +++ b/libs/midi++2/midi++/parser.h @@ -34,6 +34,7 @@ class Port; class Parser; typedef PBD::Signal1 ZeroByteSignal; +typedef PBD::Signal2 BankSignal; typedef PBD::Signal2 TimestampedSignal; typedef PBD::Signal2 OneByteSignal; typedef PBD::Signal2 TwoByteSignal; @@ -55,7 +56,7 @@ class LIBMIDIPP_API Parser { /* signals that anyone can connect to */ - OneByteSignal bank_change; + BankSignal bank_change; TwoByteSignal note_on; TwoByteSignal note_off; TwoByteSignal poly_pressure; @@ -64,7 +65,7 @@ class LIBMIDIPP_API Parser { PitchBendSignal pitchbend; TwoByteSignal controller; - OneByteSignal channel_bank_change[16]; + BankSignal channel_bank_change[16]; TwoByteSignal channel_note_on[16]; TwoByteSignal channel_note_off[16]; TwoByteSignal channel_poly_pressure[16]; diff --git a/libs/midi++2/wscript b/libs/midi++2/wscript index 928b381326..e8bfdab217 100644 --- a/libs/midi++2/wscript +++ b/libs/midi++2/wscript @@ -75,7 +75,7 @@ def build(bld): obj.uselib = 'GLIBMM SIGCPP XML OSX' obj.use = 'libpbd libevoral libtimecode' obj.vnum = LIBMIDIPP_LIB_VERSION - obj.install_path = os.path.join(bld.env['LIBDIR'], 'ardour3') + obj.install_path = bld.env['LIBDIR'] if bld.env['BUILD_TESTS'] and bld.is_defined('HAVE_CPPUNIT'): # Unit tests diff --git a/libs/panners/1in2out/wscript b/libs/panners/1in2out/wscript index de6b21af25..6b7a04a7d9 100644 --- a/libs/panners/1in2out/wscript +++ b/libs/panners/1in2out/wscript @@ -29,7 +29,7 @@ def build(bld): obj.target = 'pan1in2out' obj.use = 'libardour libardour_cp libpbd' obj.vnum = LIBARDOUR_PAN1IN2OUT_LIB_VERSION - obj.install_path = os.path.join(bld.env['LIBDIR'], 'ardour3', 'panners') + obj.install_path = os.path.join(bld.env['LIBDIR'], 'panners') def shutdown(): autowaf.shutdown() diff --git a/libs/panners/2in2out/wscript b/libs/panners/2in2out/wscript index 63f029f287..f71f514b73 100644 --- a/libs/panners/2in2out/wscript +++ b/libs/panners/2in2out/wscript @@ -29,7 +29,7 @@ def build(bld): obj.target = 'pan2in2out' obj.use = 'libardour libardour_cp libpbd' obj.vnum = LIBARDOUR_PAN2IN2OUT_LIB_VERSION - obj.install_path = os.path.join(bld.env['LIBDIR'], 'ardour3', 'panners') + obj.install_path = os.path.join(bld.env['LIBDIR'], 'panners') def shutdown(): autowaf.shutdown() diff --git a/libs/panners/stereobalance/wscript b/libs/panners/stereobalance/wscript index b66a2ffee6..25ab9e5835 100644 --- a/libs/panners/stereobalance/wscript +++ b/libs/panners/stereobalance/wscript @@ -29,7 +29,7 @@ def build(bld): obj.target = 'panbalance' obj.use = 'libardour libardour_cp libpbd' obj.vnum = LIBARDOUR_PAN2IN2OUT_LIB_VERSION - obj.install_path = os.path.join(bld.env['LIBDIR'], 'ardour3', 'panners') + obj.install_path = os.path.join(bld.env['LIBDIR'], 'panners') def shutdown(): autowaf.shutdown() diff --git a/libs/panners/vbap/wscript b/libs/panners/vbap/wscript index 8fe9dc8e8a..c2bb313e27 100644 --- a/libs/panners/vbap/wscript +++ b/libs/panners/vbap/wscript @@ -29,7 +29,7 @@ def build(bld): obj.target = 'panvbap' obj.use = 'libardour libardour_cp libpbd' obj.vnum = LIBARDOUR_PANVBAP_LIB_VERSION - obj.install_path = os.path.join(bld.env['LIBDIR'], 'ardour3', 'panners') + obj.install_path = os.path.join(bld.env['LIBDIR'], 'panners') def shutdown(): autowaf.shutdown() diff --git a/libs/pbd/wscript b/libs/pbd/wscript index e009cc4ed2..3e7b423fc7 100644 --- a/libs/pbd/wscript +++ b/libs/pbd/wscript @@ -146,7 +146,7 @@ def build(bld): obj.source += [ 'cocoa_open_uri.mm' ] obj.uselib += ' OSX' obj.vnum = LIBPBD_LIB_VERSION - obj.install_path = os.path.join(bld.env['LIBDIR'], 'ardour3') + obj.install_path = bld.env['LIBDIR'] obj.defines += [ 'PACKAGE="' + I18N_PACKAGE + '"', ] diff --git a/libs/qm-dsp/wscript b/libs/qm-dsp/wscript index 96d0b629dd..ef15abffb7 100644 --- a/libs/qm-dsp/wscript +++ b/libs/qm-dsp/wscript @@ -53,7 +53,7 @@ def build(bld): obj.name = 'libqmdsp' obj.target = 'qmdsp' obj.vnum = QM_DSP_VERSION - obj.install_path = os.path.join(bld.env['LIBDIR'], 'ardour3') + obj.install_path = bld.env['LIBDIR'] def shutdown(): autowaf.shutdown() diff --git a/libs/surfaces/control_protocol/wscript b/libs/surfaces/control_protocol/wscript index f14cee042f..1c4235bd14 100644 --- a/libs/surfaces/control_protocol/wscript +++ b/libs/surfaces/control_protocol/wscript @@ -41,7 +41,7 @@ def build(bld): obj.target = 'ardourcp' obj.use = 'libardour' obj.vnum = LIBARDOUR_CP_LIB_VERSION - obj.install_path = os.path.join(bld.env['LIBDIR'], 'ardour3') + obj.install_path = bld.env['LIBDIR'] def shutdown(): autowaf.shutdown() diff --git a/libs/surfaces/frontier/wscript b/libs/surfaces/frontier/wscript index 4fcbe6fde3..614c6f2841 100644 --- a/libs/surfaces/frontier/wscript +++ b/libs/surfaces/frontier/wscript @@ -41,7 +41,7 @@ def build(bld): obj.target = 'generic_midi' obj.use = 'libardour libardourcp' obj.vnum = LIBSURFACES_LIB_VERSION - obj.install_path = os.path.join(bld.env['LIBDIR'], 'ardour3', 'surfaces') + obj.install_path = os.path.join(bld.env['LIBDIR'], 'surfaces') def shutdown(): autowaf.shutdown() diff --git a/libs/surfaces/generic_midi/wscript b/libs/surfaces/generic_midi/wscript index 40cb76c9e4..b083423eec 100644 --- a/libs/surfaces/generic_midi/wscript +++ b/libs/surfaces/generic_midi/wscript @@ -38,7 +38,7 @@ def build(bld): obj.uselib = 'GTKMM GTK GDK' obj.use = 'libardour libardour_cp libgtkmm2ext libpbd' obj.vnum = LIBARDOUR_GENERIC_MIDI_LIB_VERSION - obj.install_path = os.path.join(bld.env['LIBDIR'], 'ardour3', 'surfaces') + obj.install_path = os.path.join(bld.env['LIBDIR'], 'surfaces') def shutdown(): autowaf.shutdown() diff --git a/libs/surfaces/mackie/wscript b/libs/surfaces/mackie/wscript index c1e63e2894..fd3e9552be 100644 --- a/libs/surfaces/mackie/wscript +++ b/libs/surfaces/mackie/wscript @@ -50,7 +50,7 @@ def build(bld): obj.uselib = 'GTKMM' obj.use = 'libardour libardour_cp libgtkmm2ext' obj.vnum = LIBARDOUR_MCP_LIB_VERSION - obj.install_path = os.path.join(bld.env['LIBDIR'], 'ardour3', 'surfaces') + obj.install_path = os.path.join(bld.env['LIBDIR'], 'surfaces') def shutdown(): autowaf.shutdown() diff --git a/libs/surfaces/osc/wscript b/libs/surfaces/osc/wscript index a63b580e12..fa4918a8f1 100644 --- a/libs/surfaces/osc/wscript +++ b/libs/surfaces/osc/wscript @@ -35,7 +35,7 @@ def build(bld): obj.uselib = ' LO ' obj.use = 'libardour libardour_cp libpbd' obj.vnum = LIBARDOUR_OSC_LIB_VERSION - obj.install_path = os.path.join(bld.env['LIBDIR'], 'ardour3', 'surfaces') + obj.install_path = os.path.join(bld.env['LIBDIR'], 'surfaces') def shutdown(): autowaf.shutdown() diff --git a/libs/surfaces/tranzport/wscript b/libs/surfaces/tranzport/wscript index 83c39f1d11..6cbafb5173 100644 --- a/libs/surfaces/tranzport/wscript +++ b/libs/surfaces/tranzport/wscript @@ -46,7 +46,7 @@ def build(bld): obj.target = 'ardour_tranzport' obj.use = 'libardour libardour_cp' obj.vnum = LIBARDOUR_TRANZPORT_LIB_VERSION - obj.install_path = os.path.join(bld.env['LIBDIR'], 'ardour3', 'surfaces') + obj.install_path = os.path.join(bld.env['LIBDIR'], 'surfaces') def shutdown(): autowaf.shutdown() diff --git a/libs/surfaces/wiimote/wscript b/libs/surfaces/wiimote/wscript index 8513a5be51..b3ea4404a8 100644 --- a/libs/surfaces/wiimote/wscript +++ b/libs/surfaces/wiimote/wscript @@ -33,7 +33,7 @@ def build(bld): obj.uselib = 'GTKMM CWIID' obj.use = 'libardour libardour_cp libgtkmm2ext' obj.vnum = LIBARDOUR_WIIMOTE_LIB_VERSION - obj.install_path = os.path.join(bld.env['LIBDIR'], 'ardour3', 'surfaces') + obj.install_path = os.path.join(bld.env['LIBDIR'], 'surfaces') def shutdown(): autowaf.shutdown() diff --git a/libs/vamp-plugins/wscript b/libs/vamp-plugins/wscript index 4808d33612..06d440fa57 100644 --- a/libs/vamp-plugins/wscript +++ b/libs/vamp-plugins/wscript @@ -55,7 +55,7 @@ def build(bld): obj.source += ' Onset.cpp ' obj.uselib += ' AUBIO ' obj.vnum = LIBARDOURVAMPPLUGINS_LIB_VERSION - obj.install_path = os.path.join(bld.env['LIBDIR'], 'ardour3', 'vamp') + obj.install_path = os.path.join(bld.env['LIBDIR'], 'vamp') def shutdown(): autowaf.shutdown() diff --git a/libs/vfork/wscript b/libs/vfork/wscript index 48526d0783..10f11149d8 100644 --- a/libs/vfork/wscript +++ b/libs/vfork/wscript @@ -21,7 +21,7 @@ def build(bld): obj = bld (features = 'c cprogram') obj.source = 'exec_wrapper.c' obj.target = 'ardour-exec-wrapper' - obj.install_path = os.path.join(bld.env['LIBDIR'], 'ardour3/vfork') + obj.install_path = os.path.join(bld.env['LIBDIR'], 'vfork') obj.defines = [ '_POSIX_SOURCE', '_XOPEN_SOURCE=500', diff --git a/mcp/wscript b/mcp/wscript index b1ff9c156b..e3cc341419 100644 --- a/mcp/wscript +++ b/mcp/wscript @@ -11,8 +11,8 @@ def configure(conf): def build(bld): devinfo = bld.path.ant_glob ('*.device') profiles = bld.path.ant_glob ('*.profile') - bld.install_files (os.path.join(bld.env['DATADIR'], 'ardour3', 'mcp'), devinfo) - bld.install_files (os.path.join(bld.env['DATADIR'], 'ardour3', 'mcp'), profiles) + bld.install_files (os.path.join(bld.env['DATADIR'], 'mcp'), devinfo) + bld.install_files (os.path.join(bld.env['DATADIR'], 'mcp'), profiles) def options(opt): pass diff --git a/midi_maps/wscript b/midi_maps/wscript index 727f3b6b29..ec2e65316a 100644 --- a/midi_maps/wscript +++ b/midi_maps/wscript @@ -10,7 +10,7 @@ def configure(conf): def build(bld): maps = bld.path.ant_glob ('*.map') - bld.install_files (os.path.join(bld.env['DATADIR'], 'ardour3', 'midi_maps'), + bld.install_files (os.path.join(bld.env['DATADIR'], 'midi_maps'), maps) def options(opt): diff --git a/patchfiles/wscript b/patchfiles/wscript index 3b852974c3..ea54532e13 100644 --- a/patchfiles/wscript +++ b/patchfiles/wscript @@ -10,7 +10,7 @@ def configure(conf): def build(bld): patchfiles = bld.path.ant_glob ('*.midnam') - bld.install_files (os.path.join(bld.env['DATADIR'], 'ardour3', 'patchfiles'), patchfiles) + bld.install_files (os.path.join(bld.env['DATADIR'], 'patchfiles'), patchfiles) def options(opt): pass diff --git a/templates/wscript b/templates/wscript index 8dff100d1a..3ea6c4a1e5 100644 --- a/templates/wscript +++ b/templates/wscript @@ -35,7 +35,7 @@ def build(bld): name = 'template', source = [ t ], target = [ os.path.join(dir_name, file_name) ], - install_path = os.path.join(bld.env['DATADIR'], 'ardour3', os.path.join('templates', dir_name))) + install_path = os.path.join(bld.env['DATADIR'], os.path.join('templates', dir_name))) def options(opt): pass diff --git a/tools/linux_packaging/build b/tools/linux_packaging/build index 95f052537e..647fc88f4b 100755 --- a/tools/linux_packaging/build +++ b/tools/linux_packaging/build @@ -389,7 +389,9 @@ done cp $BUILD_ROOT/libs/panners/*/lib*.so* $Panners # Backends -cp $BUILD_ROOT/libs/backends/*/lib*.so* $Backends +for backend in jack wavesaudio ; do + cp $BUILD_ROOT/libs/backends/$backend/lib*.so* $Backends +done # VAMP plugins that we use cp $BUILD_ROOT/libs/vamp-plugins/libardourvampplugins.so* $Libraries diff --git a/tools/osx_packaging/osx_build b/tools/osx_packaging/osx_build index 384eeeac3d..31ac58e2a2 100755 --- a/tools/osx_packaging/osx_build +++ b/tools/osx_packaging/osx_build @@ -337,7 +337,9 @@ cp $BUILD_ROOT/libs/surfaces/control_protocol/libardourcp*.dylib $Frameworks cp $BUILD_ROOT/libs/panners/*/lib*.dylib $Panners # Backends -cp $BUILD_ROOT/libs/backends/*/lib*.dylib $Backends +for backend in jack wavesaudio ; do + cp $BUILD_ROOT/libs/backends/$backend/lib*.dylib $Backends +done # Export Formats/Presets for f in $BUILD_ROOT/../export/*.preset $BUILD_ROOT/../export/*.format ; do diff --git a/tools/sanity_check/wscript b/tools/sanity_check/wscript index 6bb2274f3f..000753e18b 100644 --- a/tools/sanity_check/wscript +++ b/tools/sanity_check/wscript @@ -20,4 +20,4 @@ def build(bld): obj.source = [ 'main.cpp', 'systemtest.cpp' ] obj.target = 'sanityCheck' obj.name = 'sanityCheck' - obj.install_path = os.path.join(bld.env['LIBDIR'], 'ardour3') + obj.install_path = bld.env['LIBDIR'] diff --git a/wscript b/wscript index 3f0a01dcf5..52d1ec6e28 100644 --- a/wscript +++ b/wscript @@ -874,8 +874,9 @@ def build(bld): bld.env['CONFDIR'] = os.path.join(bld.env['SYSCONFDIR'], lwrcase_dirname) # data files loaded at run time go here bld.env['DATADIR'] = os.path.join(bld.env['DATADIR'], lwrcase_dirname) - # shared objects loaded at runtime go here + # shared objects loaded at runtime go here (two aliases) bld.env['DLLDIR'] = os.path.join(bld.env['LIBDIR'], lwrcase_dirname) + bld.env['LIBDIR'] = bld.env['DLLDIR'] autowaf.set_recursive() @@ -890,7 +891,7 @@ def build(bld): for i in children: bld.recurse(i) - bld.install_files (os.path.join(bld.env['SYSCONFDIR'], 'ardour3', ), 'ardour_system.rc') + bld.install_files (bld.env['SYSCONFDIR'], 'ardour_system.rc') if bld.env['RUN_TESTS']: bld.add_post_fun(test)