add finite state machine to control/manage transport state
This commit is contained in:
parent
fc3e7623e7
commit
bd229936ec
|
@ -383,7 +383,7 @@ ExportDialog::show_progress ()
|
|||
}
|
||||
}
|
||||
|
||||
status->finish ();
|
||||
status->finish (TRS_UI);
|
||||
|
||||
if (!status->aborted() && UIConfiguration::instance().get_save_export_mixer_screenshot ()) {
|
||||
ExportProfileManager::TimespanStateList const& timespans = profile_manager->get_timespans();
|
||||
|
|
|
@ -793,7 +793,7 @@ ExportVideoDialog::launch_export ()
|
|||
}
|
||||
}
|
||||
audio_progress_connection.disconnect();
|
||||
status->finish ();
|
||||
status->finish (TRS_UI);
|
||||
if (status->aborted()) {
|
||||
::g_unlink (_insnd.c_str());
|
||||
delete _transcoder; _transcoder = 0;
|
||||
|
|
|
@ -24,8 +24,6 @@
|
|||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include <boost/variant.hpp>
|
||||
|
||||
#include "pbd/xml++.h"
|
||||
#include "pbd/id.h"
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ namespace PBD {
|
|||
namespace DEBUG {
|
||||
LIBARDOUR_API extern DebugBits MidiSourceIO;
|
||||
LIBARDOUR_API extern DebugBits MidiPlaylistIO;
|
||||
LIBARDOUR_API extern DebugBits MidiDiskstreamIO;
|
||||
LIBARDOUR_API extern DebugBits MidiDiskIO;
|
||||
LIBARDOUR_API extern DebugBits MidiRingBuffer;
|
||||
LIBARDOUR_API extern DebugBits SnapBBT;
|
||||
LIBARDOUR_API extern DebugBits Latency;
|
||||
|
@ -49,6 +49,8 @@ namespace PBD {
|
|||
LIBARDOUR_API extern DebugBits LTC;
|
||||
LIBARDOUR_API extern DebugBits TXLTC;
|
||||
LIBARDOUR_API extern DebugBits Transport;
|
||||
LIBARDOUR_API extern DebugBits TFSMEvents;
|
||||
LIBARDOUR_API extern DebugBits TFSMState;
|
||||
LIBARDOUR_API extern DebugBits Slave;
|
||||
LIBARDOUR_API extern DebugBits SessionEvents;
|
||||
LIBARDOUR_API extern DebugBits MidiIO;
|
||||
|
|
|
@ -117,8 +117,6 @@ protected:
|
|||
protected:
|
||||
Flag _flags;
|
||||
uint32_t i_am_the_modifier;
|
||||
double _actual_speed;
|
||||
double _target_speed;
|
||||
bool _slaved;
|
||||
bool in_set_state;
|
||||
samplepos_t playback_sample;
|
||||
|
|
|
@ -97,8 +97,17 @@ public:
|
|||
|
||||
static void set_midi_readahead_samples (samplecnt_t samples_ahead) { midi_readahead = samples_ahead; }
|
||||
|
||||
static void set_no_disk_output (bool yn);
|
||||
static bool no_disk_output() { return _no_disk_output; }
|
||||
/* inc/dec variants MUST be called as part of the process call tree, before any
|
||||
disk readers are invoked. We use it when the session needs the
|
||||
transport (and thus effective read position for DiskReaders) to keep
|
||||
advancing as part of syncing up with a transport master, but we
|
||||
don't want any actual disk output yet because we are still not
|
||||
synced.
|
||||
*/
|
||||
|
||||
static void inc_no_disk_output () { g_atomic_int_inc (&_no_disk_output); }
|
||||
static void dec_no_disk_output();
|
||||
static bool no_disk_output () { return g_atomic_int_get (&_no_disk_output); }
|
||||
|
||||
protected:
|
||||
friend class Track;
|
||||
|
@ -156,7 +165,7 @@ private:
|
|||
|
||||
static samplecnt_t _chunk_samples;
|
||||
static samplecnt_t midi_readahead;
|
||||
static bool _no_disk_output;
|
||||
static gint _no_disk_output;
|
||||
|
||||
int audio_read (PBD::PlaybackBuffer<Sample>*,
|
||||
Sample* sum_buffer,
|
||||
|
|
|
@ -54,8 +54,8 @@ class LIBARDOUR_API ExportStatus {
|
|||
}
|
||||
Glib::Threads::Mutex& lock () { return _run_lock; }
|
||||
|
||||
PBD::Signal0<void> Finished;
|
||||
void finish ();
|
||||
PBD::Signal1<void,TransportRequestSource> Finished;
|
||||
void finish (TransportRequestSource);
|
||||
|
||||
void cleanup ();
|
||||
|
||||
|
|
|
@ -88,7 +88,7 @@
|
|||
#include "ardour/presentation_info.h"
|
||||
#include "ardour/route.h"
|
||||
#include "ardour/route_graph.h"
|
||||
|
||||
#include "ardour/transport_api.h"
|
||||
|
||||
class XMLTree;
|
||||
class XMLNode;
|
||||
|
@ -167,6 +167,7 @@ class Source;
|
|||
class Speakers;
|
||||
class TempoMap;
|
||||
class TransportMaster;
|
||||
struct TransportFSM;
|
||||
class Track;
|
||||
class UI_TransportMaster;
|
||||
class VCAManager;
|
||||
|
@ -186,7 +187,7 @@ private:
|
|||
};
|
||||
|
||||
/** Ardour Session */
|
||||
class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionList, public SessionEventManager
|
||||
class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionList, public SessionEventManager, public TransportAPI
|
||||
{
|
||||
private:
|
||||
|
||||
|
@ -457,7 +458,8 @@ public:
|
|||
void adjust_capture_buffering();
|
||||
|
||||
bool global_locate_pending() const { return _global_locate_pending; }
|
||||
bool locate_pending() const { return static_cast<bool>(post_transport_work()&PostTransportLocate); }
|
||||
bool locate_pending() const;
|
||||
bool declick_in_progress () const;
|
||||
bool transport_locked () const;
|
||||
|
||||
int wipe ();
|
||||
|
@ -752,8 +754,8 @@ public:
|
|||
return 0;
|
||||
}
|
||||
double transport_speed() const { return _count_in_samples > 0 ? 0. : _transport_speed; }
|
||||
bool transport_stopped() const { return _transport_speed == 0.0; }
|
||||
bool transport_rolling() const { return _transport_speed != 0.0 && _count_in_samples == 0 && _remaining_latency_preroll == 0; }
|
||||
bool transport_stopped() const;
|
||||
bool transport_rolling() const;
|
||||
|
||||
bool silent () { return _silent; }
|
||||
|
||||
|
@ -1237,6 +1239,16 @@ protected:
|
|||
friend class Route;
|
||||
void update_latency_compensation (bool force = false);
|
||||
|
||||
/* transport API */
|
||||
|
||||
void locate (samplepos_t, bool with_roll, bool with_flush, bool with_loop=false, bool force=false, bool with_mmc=true);
|
||||
void stop_transport (bool abort = false, bool clear_state = false);
|
||||
void start_transport ();
|
||||
void butler_completed_transport_work ();
|
||||
void post_locate ();
|
||||
void schedule_butler_for_transport_work ();
|
||||
bool should_roll_after_locate () const;
|
||||
|
||||
private:
|
||||
int create (const std::string& mix_template, BusProfile*);
|
||||
void destroy ();
|
||||
|
@ -1262,7 +1274,6 @@ private:
|
|||
samplecnt_t _base_sample_rate; // sample-rate of the session at creation time, "native" SR
|
||||
samplecnt_t _nominal_sample_rate; // overridden by audioengine setting
|
||||
samplecnt_t _current_sample_rate; // this includes video pullup offset
|
||||
int transport_sub_state;
|
||||
mutable gint _record_status;
|
||||
samplepos_t _transport_sample;
|
||||
gint _seek_counter;
|
||||
|
@ -1357,7 +1368,7 @@ private:
|
|||
|
||||
int pre_export ();
|
||||
int stop_audio_export ();
|
||||
void finalize_audio_export ();
|
||||
void finalize_audio_export (TransportRequestSource trs);
|
||||
void finalize_export_internal (bool stop_freewheel);
|
||||
bool _pre_export_mmc_enabled;
|
||||
|
||||
|
@ -1436,11 +1447,11 @@ private:
|
|||
|
||||
Butler* _butler;
|
||||
|
||||
boost::shared_ptr<TransportFSM> _transport_fsm;
|
||||
|
||||
static const PostTransportWork ProcessCannotProceedMask =
|
||||
PostTransportWork (
|
||||
PostTransportReverse|
|
||||
PostTransportAudition|
|
||||
PostTransportStop|
|
||||
PostTransportClearSubstate);
|
||||
|
||||
gint _post_transport_work; /* accessed only atomic ops */
|
||||
|
@ -1671,19 +1682,16 @@ private:
|
|||
int start_midi_thread ();
|
||||
|
||||
bool should_ignore_transport_request (TransportRequestSource, TransportRequestType) const;
|
||||
bool declick_in_progress () const;
|
||||
|
||||
void set_play_loop (bool yn, double speed);
|
||||
void unset_play_loop ();
|
||||
void overwrite_some_buffers (Track *);
|
||||
void flush_all_inserts ();
|
||||
int micro_locate (samplecnt_t distance);
|
||||
void locate (samplepos_t, bool with_roll, bool with_flush, bool with_loop=false, bool force=false, bool with_mmc=true);
|
||||
void start_locate (samplepos_t, bool with_roll, bool with_flush, bool for_loop_enabled=false, bool force=false);
|
||||
|
||||
void do_locate (samplepos_t, bool with_roll, bool with_flush, bool for_loop_enabled, bool force, bool with_mmc);
|
||||
void force_locate (samplepos_t sample, bool with_roll = false);
|
||||
void set_transport_speed (double speed, samplepos_t destination_sample, bool abort = false, bool clear_state = false, bool as_default = false);
|
||||
void stop_transport (bool abort = false, bool clear_state = false);
|
||||
void start_transport ();
|
||||
void realtime_stop (bool abort, bool clear_state);
|
||||
void realtime_locate ();
|
||||
void non_realtime_start_scrub ();
|
||||
|
@ -1691,8 +1699,8 @@ private:
|
|||
void non_realtime_locate ();
|
||||
void non_realtime_stop (bool abort, int entry_request_count, bool& finished);
|
||||
void non_realtime_overwrite (int entry_request_count, bool& finished);
|
||||
void post_transport ();
|
||||
void engine_halted ();
|
||||
void engine_running ();
|
||||
void xrun_recovery ();
|
||||
void set_track_loop (bool);
|
||||
bool select_playhead_priority_target (samplepos_t&);
|
||||
|
|
|
@ -63,7 +63,6 @@ public:
|
|||
|
||||
/* only one of each of these events can be queued at any one time */
|
||||
|
||||
StopOnce,
|
||||
AutoLoop,
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright (C) 2019 Paul Davis <paul@linuxaudiosystems.com>
|
||||
*
|
||||
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef _ardour_transport_api_h_
|
||||
#define _ardour_transport_api_h_
|
||||
|
||||
#include "ardour/types.h"
|
||||
#include "ardour/libardour_visibility.h"
|
||||
|
||||
namespace ARDOUR
|
||||
{
|
||||
|
||||
class LIBARDOUR_API TransportAPI
|
||||
{
|
||||
public:
|
||||
TransportAPI() {}
|
||||
virtual ~TransportAPI() {}
|
||||
|
||||
private:
|
||||
friend struct TransportFSM;
|
||||
|
||||
virtual void locate (samplepos_t, bool with_roll, bool with_flush, bool with_loop=false, bool force=false, bool with_mmc=true) = 0;
|
||||
virtual void stop_transport (bool abort = false, bool clear_state = false) = 0;
|
||||
virtual void start_transport () = 0;
|
||||
virtual void butler_completed_transport_work () = 0;
|
||||
virtual void schedule_butler_for_transport_work () = 0;
|
||||
virtual bool should_roll_after_locate () const = 0;
|
||||
};
|
||||
|
||||
} /* end namespace ARDOUR */
|
||||
|
||||
#endif
|
|
@ -0,0 +1,213 @@
|
|||
#ifndef _ardour_transport_fsm_h_
|
||||
#define _ardour_transport_fsm_h_
|
||||
|
||||
#include <boost/weak_ptr.hpp>
|
||||
#include <boost/msm/back/state_machine.hpp>
|
||||
#include <boost/msm/back/tools.hpp>
|
||||
#include <boost/msm/front/state_machine_def.hpp>
|
||||
#include <boost/msm/front/functor_row.hpp>
|
||||
|
||||
#include "pbd/demangle.h"
|
||||
#include "pbd/stacktrace.h"
|
||||
|
||||
#include "ardour/debug.h"
|
||||
|
||||
/* state machine */
|
||||
namespace msm = boost::msm;
|
||||
namespace mpl = boost::mpl;
|
||||
|
||||
namespace ARDOUR
|
||||
{
|
||||
|
||||
class TransportAPI;
|
||||
|
||||
struct TransportFSM : public msm::front::state_machine_def<TransportFSM>
|
||||
{
|
||||
/* events to be delivered to the FSM */
|
||||
|
||||
struct butler_done {};
|
||||
struct butler_required {};
|
||||
struct declick_done {};
|
||||
struct start_transport {};
|
||||
|
||||
struct stop_transport {
|
||||
stop_transport (bool ab = false, bool cl = false)
|
||||
: abort (ab)
|
||||
, clear_state (cl) {}
|
||||
|
||||
bool abort;
|
||||
bool clear_state;
|
||||
};
|
||||
|
||||
struct locate {
|
||||
locate ()
|
||||
: target (0)
|
||||
, with_roll (false)
|
||||
, with_flush (false)
|
||||
, with_loop (false)
|
||||
, force (false) {}
|
||||
|
||||
locate (samplepos_t target, bool roll, bool flush, bool loop, bool f4c)
|
||||
: target (target)
|
||||
, with_roll (roll)
|
||||
, with_flush (flush)
|
||||
, with_loop (loop)
|
||||
, force (f4c) {}
|
||||
|
||||
samplepos_t target;
|
||||
bool with_roll;
|
||||
bool with_flush;
|
||||
bool with_loop;
|
||||
bool force;
|
||||
};
|
||||
|
||||
struct locate_done {};
|
||||
|
||||
/* Flags */
|
||||
|
||||
struct DeclickInProgress {};
|
||||
struct LocateInProgress {};
|
||||
struct IsRolling {};
|
||||
struct IsStopped {};
|
||||
struct IsWaitingForButler {};
|
||||
|
||||
typedef msm::active_state_switch_before_transition active_state_switch_policy;
|
||||
|
||||
/* transition actions */
|
||||
|
||||
void start_playback (start_transport const& p);
|
||||
void roll_after_locate (locate_done const& p);
|
||||
void stop_playback (declick_done const& s);
|
||||
void start_locate (locate const& s);
|
||||
void start_saved_locate (declick_done const& s);
|
||||
void interrupt_locate (locate const& s);
|
||||
void schedule_butler_for_transport_work (butler_required const&);
|
||||
void save_locate_and_start_declick (locate const &);
|
||||
void start_declick (stop_transport const &);
|
||||
|
||||
/* guards */
|
||||
|
||||
bool should_roll_after_locate (locate_done const &);
|
||||
bool should_not_roll_after_locate (locate_done const & e) { return !should_roll_after_locate (e); }
|
||||
|
||||
#define define_state(State) \
|
||||
struct State : public msm::front::state<> \
|
||||
{ \
|
||||
template <class Event,class FSM> void on_entry (Event const&, FSM&) { DEBUG_TRACE (PBD::DEBUG::TFSMState, "entering: " # State "\n"); } \
|
||||
template <class Event,class FSM> void on_exit (Event const&, FSM&) { DEBUG_TRACE (PBD::DEBUG::TFSMState, "leaving: " # State "\n"); } \
|
||||
}
|
||||
|
||||
#define define_state_flag(State,Flag) \
|
||||
struct State : public msm::front::state<> \
|
||||
{ \
|
||||
template <class Event,class FSM> void on_entry (Event const&, FSM&) { DEBUG_TRACE (PBD::DEBUG::TFSMState, "entering: " # State "\n"); } \
|
||||
template <class Event,class FSM> void on_exit (Event const&, FSM&) { DEBUG_TRACE (PBD::DEBUG::TFSMState, "leaving: " # State "\n"); } \
|
||||
typedef mpl::vector1<Flag> flag_list; \
|
||||
}
|
||||
|
||||
#define define_state_flag2(State,Flag1,Flag2) \
|
||||
struct State : public msm::front::state<> \
|
||||
{ \
|
||||
template <class Event,class FSM> void on_entry (Event const&, FSM&) { DEBUG_TRACE (PBD::DEBUG::TFSMState, "entering: " # State "\n"); } \
|
||||
template <class Event,class FSM> void on_exit (Event const&, FSM&) { DEBUG_TRACE (PBD::DEBUG::TFSMState, "leaving: " # State "\n"); } \
|
||||
typedef mpl::vector2<Flag1,Flag2> flag_list; \
|
||||
}
|
||||
|
||||
/* FSM states */
|
||||
|
||||
define_state_flag (WaitingForButler, IsWaitingForButler);
|
||||
define_state (NotWaitingForButler);
|
||||
define_state_flag (Stopped,IsStopped);
|
||||
define_state_flag (Rolling,IsRolling);
|
||||
define_state_flag (DeclickToLocate,DeclickInProgress);
|
||||
define_state_flag (WaitingForLocate,LocateInProgress);
|
||||
define_state_flag (DeclickToStop,DeclickInProgress);
|
||||
|
||||
// Pick a back-end
|
||||
typedef msm::back::state_machine<TransportFSM> back;
|
||||
|
||||
boost::weak_ptr<back> wp;
|
||||
|
||||
bool locating () { return backend()->is_flag_active<LocateInProgress>(); }
|
||||
bool locating (declick_done const &) { return locating(); }
|
||||
bool rolling () { return backend()->is_flag_active<IsRolling>(); }
|
||||
bool stopped () { return backend()->is_flag_active<IsStopped>(); }
|
||||
bool waiting_for_butler() { return backend()->is_flag_active<IsWaitingForButler>(); }
|
||||
bool declick_in_progress() { return backend()->is_flag_active<DeclickInProgress>(); }
|
||||
|
||||
static boost::shared_ptr<back> create(TransportAPI& api) {
|
||||
|
||||
boost::shared_ptr<back> p (new back ());
|
||||
|
||||
p->wp = p;
|
||||
p->api = &api;
|
||||
return p;
|
||||
}
|
||||
|
||||
boost::shared_ptr<back> backend() { return wp.lock(); }
|
||||
|
||||
template<typename Event> void enqueue (Event const & e) {
|
||||
backend()->process_event (e);
|
||||
}
|
||||
|
||||
/* the initial state */
|
||||
typedef boost::mpl::vector<Stopped,NotWaitingForButler> initial_state;
|
||||
|
||||
/* transition table */
|
||||
typedef TransportFSM T; // makes transition table cleaner
|
||||
|
||||
struct transition_table : mpl::vector<
|
||||
// Start Event Next Action Guard
|
||||
// +----------------------+----------------+------------------+---------------------+----------------------+
|
||||
a_row < Stopped, start_transport, Rolling, &T::start_playback >,
|
||||
_row < Stopped, stop_transport, Stopped >,
|
||||
a_row < Stopped, locate, WaitingForLocate, &T::start_locate >,
|
||||
g_row < WaitingForLocate, locate_done, Stopped, &T::should_not_roll_after_locate >,
|
||||
_row < Rolling, butler_done, Rolling >,
|
||||
_row < Rolling, start_transport, Rolling >,
|
||||
a_row < Rolling, stop_transport, DeclickToStop, &T::start_declick >,
|
||||
a_row < DeclickToStop, declick_done, Stopped, &T::stop_playback >,
|
||||
a_row < Rolling, locate, DeclickToLocate, &T::save_locate_and_start_declick >,
|
||||
a_row < DeclickToLocate, declick_done, WaitingForLocate, &T::start_saved_locate >,
|
||||
row < WaitingForLocate, locate_done, Rolling, &T::roll_after_locate, &T::should_roll_after_locate >,
|
||||
a_row < NotWaitingForButler, butler_required, WaitingForButler, &T::schedule_butler_for_transport_work >,
|
||||
a_row < WaitingForButler, butler_required, WaitingForButler, &T::schedule_butler_for_transport_work >,
|
||||
_row < WaitingForButler, butler_done, NotWaitingForButler >,
|
||||
a_row < WaitingForLocate, locate, WaitingForLocate, &T::interrupt_locate >,
|
||||
a_row < DeclickToLocate, locate, DeclickToLocate, &T::interrupt_locate >,
|
||||
|
||||
// Deferrals
|
||||
|
||||
#define defer(start_state,ev) boost::msm::front::Row<start_state, ev, start_state, boost::msm::front::Defer, boost::msm::front::none >
|
||||
|
||||
defer (DeclickToLocate, start_transport),
|
||||
defer (DeclickToLocate, stop_transport),
|
||||
defer (DeclickToStop, start_transport),
|
||||
defer (WaitingForLocate, start_transport),
|
||||
defer (WaitingForLocate, stop_transport)
|
||||
|
||||
#undef defer
|
||||
> {};
|
||||
|
||||
typedef int activate_deferred_events;
|
||||
|
||||
locate _last_locate;
|
||||
stop_transport _last_stop;
|
||||
|
||||
TransportAPI* api;
|
||||
|
||||
// Replaces the default no-transition response.
|
||||
template <class FSM,class Event>
|
||||
void no_transition(Event const& e, FSM&,int state)
|
||||
{
|
||||
typedef typename boost::msm::back::recursive_get_transition_table<FSM>::type recursive_stt;
|
||||
typedef typename boost::msm::back::generate_state_set<recursive_stt>::type all_states;
|
||||
std::string stateName;
|
||||
boost::mpl::for_each<all_states,boost::msm::wrap<boost::mpl::placeholders::_1> >(boost::msm::back::get_state_name<recursive_stt>(stateName, state));
|
||||
std::cout << "No transition from state: " << PBD::demangle (stateName) << " on event " << typeid(e).name() << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
} /* end namespace ARDOUR */
|
||||
|
||||
#endif
|
|
@ -346,8 +346,6 @@ class LIBARDOUR_API TransportMaster : public PBD::Stateful {
|
|||
TransportRequestType request_mask() const { return _request_mask; }
|
||||
void set_request_mask (TransportRequestType);
|
||||
|
||||
void get_current (double&, samplepos_t&, samplepos_t);
|
||||
|
||||
/* this is set at construction, and not changeable later, so it is not
|
||||
* a property
|
||||
*/
|
||||
|
@ -358,6 +356,8 @@ class LIBARDOUR_API TransportMaster : public PBD::Stateful {
|
|||
std::string display_name (bool sh/*ort*/ = true) const;
|
||||
|
||||
virtual void unregister_port ();
|
||||
void connect_port_using_state ();
|
||||
virtual void create_port () = 0;
|
||||
|
||||
protected:
|
||||
SyncSource _type;
|
||||
|
@ -385,6 +385,8 @@ class LIBARDOUR_API TransportMaster : public PBD::Stateful {
|
|||
|
||||
boost::shared_ptr<Port> _port;
|
||||
|
||||
XMLNode port_node;
|
||||
|
||||
PBD::ScopedConnection port_connection;
|
||||
bool connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn);
|
||||
|
||||
|
@ -452,6 +454,8 @@ class LIBARDOUR_API MTC_TransportMaster : public TimecodeTransportMaster, public
|
|||
std::string position_string() const;
|
||||
std::string delta_string() const;
|
||||
|
||||
void create_port ();
|
||||
|
||||
private:
|
||||
PBD::ScopedConnectionList port_connections;
|
||||
PBD::ScopedConnection config_connection;
|
||||
|
@ -518,6 +522,8 @@ public:
|
|||
std::string position_string() const;
|
||||
std::string delta_string() const;
|
||||
|
||||
void create_port ();
|
||||
|
||||
private:
|
||||
void parse_ltc(const pframes_t, const Sample* const, const samplecnt_t);
|
||||
void process_ltc(samplepos_t const);
|
||||
|
@ -586,6 +592,8 @@ class LIBARDOUR_API MIDIClock_TransportMaster : public TransportMaster, public T
|
|||
|
||||
float bpm() const { return _bpm; }
|
||||
|
||||
void create_port ();
|
||||
|
||||
protected:
|
||||
PBD::ScopedConnectionList port_connections;
|
||||
|
||||
|
@ -648,6 +656,8 @@ class LIBARDOUR_API Engine_TransportMaster : public TransportMaster
|
|||
std::string position_string() const;
|
||||
std::string delta_string() const;
|
||||
|
||||
void create_port () { }
|
||||
|
||||
private:
|
||||
AudioEngine& engine;
|
||||
bool _starting;
|
||||
|
|
|
@ -33,6 +33,7 @@ class UI_TransportMaster;
|
|||
class LIBARDOUR_API TransportMasterManager : public boost::noncopyable
|
||||
{
|
||||
public:
|
||||
static TransportMasterManager& create ();
|
||||
~TransportMasterManager ();
|
||||
|
||||
int set_default_configuration ();
|
||||
|
@ -82,6 +83,8 @@ class LIBARDOUR_API TransportMasterManager : public boost::noncopyable
|
|||
|
||||
static const std::string state_node_name;
|
||||
|
||||
void reconnect_ports ();
|
||||
|
||||
private:
|
||||
TransportMasterManager();
|
||||
|
||||
|
|
|
@ -478,8 +478,8 @@ Bundle::connected_to (boost::shared_ptr<Bundle> other, AudioEngine & engine,
|
|||
Bundle::PortList const & other_ports =
|
||||
other->channel_ports (other->type_channel_to_overall(type, i));
|
||||
|
||||
for (Bundle::PortList::const_iterator j = our_ports.begin();
|
||||
j != our_ports.end(); ++j) {
|
||||
for (Bundle::PortList::const_iterator j = our_ports.begin(); j != our_ports.end(); ++j) {
|
||||
|
||||
boost::shared_ptr<Port> p = engine.get_port_by_name(*j);
|
||||
|
||||
for (Bundle::PortList::const_iterator k = other_ports.begin();
|
||||
|
|
|
@ -29,7 +29,7 @@ using namespace std;
|
|||
|
||||
PBD::DebugBits PBD::DEBUG::MidiSourceIO = PBD::new_debug_bit ("midisourceio");
|
||||
PBD::DebugBits PBD::DEBUG::MidiPlaylistIO = PBD::new_debug_bit ("midiplaylistio");
|
||||
PBD::DebugBits PBD::DEBUG::MidiDiskstreamIO = PBD::new_debug_bit ("mididiskstreamio");
|
||||
PBD::DebugBits PBD::DEBUG::MidiDiskIO = PBD::new_debug_bit ("mididiskio");
|
||||
PBD::DebugBits PBD::DEBUG::MidiRingBuffer = PBD::new_debug_bit ("midiringbuffer");
|
||||
PBD::DebugBits PBD::DEBUG::SnapBBT = PBD::new_debug_bit ("snapbbt");
|
||||
PBD::DebugBits PBD::DEBUG::Latency = PBD::new_debug_bit ("latency");
|
||||
|
@ -44,6 +44,8 @@ PBD::DebugBits PBD::DEBUG::MTC = PBD::new_debug_bit ("mtc");
|
|||
PBD::DebugBits PBD::DEBUG::LTC = PBD::new_debug_bit ("ltc");
|
||||
PBD::DebugBits PBD::DEBUG::TXLTC = PBD::new_debug_bit ("tx-ltc");
|
||||
PBD::DebugBits PBD::DEBUG::Transport = PBD::new_debug_bit ("transport");
|
||||
PBD::DebugBits PBD::DEBUG::TFSMEvents = PBD::new_debug_bit ("tfsmevents");
|
||||
PBD::DebugBits PBD::DEBUG::TFSMState = PBD::new_debug_bit ("tfsmstate");
|
||||
PBD::DebugBits PBD::DEBUG::Slave = PBD::new_debug_bit ("slave");
|
||||
PBD::DebugBits PBD::DEBUG::SessionEvents = PBD::new_debug_bit ("sessionevents");
|
||||
PBD::DebugBits PBD::DEBUG::MidiIO = PBD::new_debug_bit ("midiio");
|
||||
|
|
|
@ -51,7 +51,7 @@ Sample* DiskReader::_sum_buffer = 0;
|
|||
Sample* DiskReader::_mixdown_buffer = 0;
|
||||
gain_t* DiskReader::_gain_buffer = 0;
|
||||
samplecnt_t DiskReader::midi_readahead = 4096;
|
||||
bool DiskReader::_no_disk_output = false;
|
||||
gint DiskReader::_no_disk_output (0);
|
||||
|
||||
DiskReader::DiskReader (Session& s, string const & str, DiskIOProcessor::Flag f)
|
||||
: DiskIOProcessor (s, str, f)
|
||||
|
@ -74,8 +74,8 @@ void
|
|||
DiskReader::ReaderChannelInfo::resize (samplecnt_t bufsize)
|
||||
{
|
||||
delete rbuf;
|
||||
/* touch memory to lock it */
|
||||
rbuf = new PlaybackBuffer<Sample> (bufsize);
|
||||
/* touch memory to lock it */
|
||||
memset (rbuf->buffer(), 0, sizeof (Sample) * rbuf->bufsize());
|
||||
}
|
||||
|
||||
|
@ -262,13 +262,14 @@ DiskReader::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp
|
|||
}
|
||||
}
|
||||
|
||||
const gain_t target_gain = (speed == 0.0 || ((ms & MonitoringDisk) == 0)) ? 0.0 : 1.0;
|
||||
const bool declick_out = _session.declick_in_progress();
|
||||
const gain_t target_gain = (declick_out || (speed == 0.0) || ((ms & MonitoringDisk) == 0)) ? 0.0 : 1.0;
|
||||
|
||||
if (!_session.cfg ()->get_use_transport_fades ()) {
|
||||
_declick_amp.set_gain (target_gain);
|
||||
}
|
||||
|
||||
if ((speed == 0.0) && (ms == MonitoringDisk) && _declick_amp.gain () == target_gain) {
|
||||
if (declick_out && (ms == MonitoringDisk) && _declick_amp.gain () == target_gain) {
|
||||
/* no channels, or stopped. Don't accidentally pass any data
|
||||
* from disk into our outputs (e.g. via interpolation)
|
||||
*/
|
||||
|
@ -348,13 +349,13 @@ DiskReader::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp
|
|||
if (can_internal_playback_seek (start_sample - playback_sample)) {
|
||||
internal_playback_seek (start_sample - playback_sample);
|
||||
} else {
|
||||
cerr << owner()->name() << " playback not possible: ss = " << start_sample << " ps = " << playback_sample << endl;
|
||||
cerr << owner()->name() << " playback at " << speed << " not possible: ss = " << start_sample << " ps = " << playback_sample << endl;
|
||||
abort (); // XXX -- now what?
|
||||
goto midi;
|
||||
/*NOTREACHED*/
|
||||
}
|
||||
}
|
||||
|
||||
if (speed != 0.0) {
|
||||
if (!declick_out) {
|
||||
const samplecnt_t total = chaninfo->rbuf->read (disk_buf.data(), disk_samples_to_consume);
|
||||
if (disk_samples_to_consume > total) {
|
||||
cerr << _name << " Need " << disk_samples_to_consume << " total = " << total << endl;
|
||||
|
@ -384,7 +385,7 @@ DiskReader::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp
|
|||
/* MIDI data handling */
|
||||
|
||||
midi:
|
||||
if (/*!_session.declick_out_pending() && */ bufs.count().n_midi() && _midi_buf) {
|
||||
if (!declick_in_progress() && bufs.count().n_midi() && _midi_buf) {
|
||||
MidiBuffer* dst;
|
||||
|
||||
if (_no_disk_output) {
|
||||
|
@ -482,10 +483,8 @@ DiskReader::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp
|
|||
}
|
||||
|
||||
bool
|
||||
DiskReader::declick_in_progress () const {
|
||||
/* TODO use an atomic-get.
|
||||
* this may be called from the butler thread
|
||||
*/
|
||||
DiskReader::declick_in_progress () const
|
||||
{
|
||||
return _declick_amp.gain() != 0; // declick-out
|
||||
}
|
||||
|
||||
|
@ -544,8 +543,6 @@ DiskReader::overwrite_existing_buffers ()
|
|||
samplepos_t start = overwrite_sample;
|
||||
samplecnt_t to_read = size;
|
||||
|
||||
cerr << owner()->name() << " over-read: " << to_read << endl;
|
||||
|
||||
if (audio_read ((*chan)->rbuf, sum_buffer.get(), mixdown_buffer.get(), gain_buffer.get(), start, to_read, n, reversed)) {
|
||||
error << string_compose(_("DiskReader %1: when refilling, cannot read %2 from playlist at sample %3"), id(), size, overwrite_sample) << endmsg;
|
||||
goto midi;
|
||||
|
@ -1167,7 +1164,7 @@ DiskReader::get_midi_playback (MidiBuffer& dst, samplepos_t start_sample, sample
|
|||
|
||||
Location* loc = _loop_location;
|
||||
|
||||
DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose (
|
||||
DEBUG_TRACE (DEBUG::MidiDiskIO, string_compose (
|
||||
"%1 MDS pre-read read %8 offset = %9 @ %4..%5 from %2 write to %3, LOOPED ? %6 .. %7\n", _name,
|
||||
_midi_buf->get_read_ptr(), _midi_buf->get_write_ptr(), start_sample, end_sample,
|
||||
(loc ? loc->start() : -1), (loc ? loc->end() : -1), nframes, Port::port_offset()));
|
||||
|
@ -1184,7 +1181,7 @@ DiskReader::get_midi_playback (MidiBuffer& dst, samplepos_t start_sample, sample
|
|||
Evoral::Range<samplepos_t> loop_range (loc->start(), loc->end() - 1);
|
||||
effective_start = loop_range.squish (start_sample);
|
||||
|
||||
DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("looped, effective start adjusted to %1\n", effective_start));
|
||||
DEBUG_TRACE (DEBUG::MidiDiskIO, string_compose ("looped, effective start adjusted to %1\n", effective_start));
|
||||
|
||||
if (effective_start == loc->start()) {
|
||||
/* We need to turn off notes that may extend
|
||||
|
@ -1208,23 +1205,23 @@ DiskReader::get_midi_playback (MidiBuffer& dst, samplepos_t start_sample, sample
|
|||
first = loc->end() - effective_start;
|
||||
second = nframes - first;
|
||||
|
||||
DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("loop read for eff %1 end %2: %3 and %4, cycle offset %5\n",
|
||||
DEBUG_TRACE (DEBUG::MidiDiskIO, string_compose ("loop read for eff %1 end %2: %3 and %4, cycle offset %5\n",
|
||||
effective_start, loc->end(), first, second));
|
||||
|
||||
if (first) {
|
||||
DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("loop read #1, from %1 for %2\n",
|
||||
DEBUG_TRACE (DEBUG::MidiDiskIO, string_compose ("loop read #1, from %1 for %2\n",
|
||||
effective_start, first));
|
||||
events_read = _midi_buf->read (*target, effective_start, first);
|
||||
}
|
||||
|
||||
if (second) {
|
||||
DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("loop read #2, from %1 for %2\n",
|
||||
DEBUG_TRACE (DEBUG::MidiDiskIO, string_compose ("loop read #2, from %1 for %2\n",
|
||||
loc->start(), second));
|
||||
events_read += _midi_buf->read (*target, loc->start(), second);
|
||||
}
|
||||
|
||||
} else {
|
||||
DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("loop read #3, adjusted start as %1 for %2\n",
|
||||
DEBUG_TRACE (DEBUG::MidiDiskIO, string_compose ("loop read #3, adjusted start as %1 for %2\n",
|
||||
effective_start, nframes));
|
||||
events_read = _midi_buf->read (*target, effective_start, effective_start + nframes);
|
||||
}
|
||||
|
@ -1233,11 +1230,11 @@ DiskReader::get_midi_playback (MidiBuffer& dst, samplepos_t start_sample, sample
|
|||
if (n_skipped > 0) {
|
||||
warning << string_compose(_("MidiDiskstream %1: skipped %2 events, possible underflow"), id(), n_skipped) << endmsg;
|
||||
}
|
||||
DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("playback buffer read, from %1 to %2 (%3)", start_sample, end_sample, nframes));
|
||||
DEBUG_TRACE (DEBUG::MidiDiskIO, string_compose ("playback buffer read, from %1 to %2 (%3)", start_sample, end_sample, nframes));
|
||||
events_read = _midi_buf->read (*target, start_sample, end_sample, Port::port_offset ());
|
||||
}
|
||||
|
||||
DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose (
|
||||
DEBUG_TRACE (DEBUG::MidiDiskIO, string_compose (
|
||||
"%1 MDS events read %2 range %3 .. %4 rspace %5 wspace %6 r@%7 w@%8\n",
|
||||
_name, events_read, playback_sample, playback_sample + nframes,
|
||||
_midi_buf->read_space(), _midi_buf->write_space(),
|
||||
|
@ -1246,7 +1243,7 @@ DiskReader::get_midi_playback (MidiBuffer& dst, samplepos_t start_sample, sample
|
|||
|
||||
g_atomic_int_add (&_samples_read_from_ringbuffer, nframes);
|
||||
|
||||
if (ms & MonitoringInput) {
|
||||
if (!_no_disk_output && (ms & MonitoringInput)) {
|
||||
dst.merge_from (*target, nframes);
|
||||
}
|
||||
|
||||
|
@ -1285,7 +1282,7 @@ DiskReader::midi_read (samplepos_t& start, samplecnt_t dur, bool reversed)
|
|||
|
||||
assert(_midi_buf);
|
||||
|
||||
DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("MDS::midi_read @ %1 cnt %2\n", start, dur));
|
||||
DEBUG_TRACE (DEBUG::MidiDiskIO, string_compose ("MDS::midi_read @ %1 cnt %2\n", start, dur));
|
||||
|
||||
boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack>(_route);
|
||||
MidiChannelFilter* filter = mt ? &mt->playback_filter() : 0;
|
||||
|
@ -1330,7 +1327,7 @@ DiskReader::midi_read (samplepos_t& start, samplecnt_t dur, bool reversed)
|
|||
|
||||
this_read = min (dur,this_read);
|
||||
|
||||
DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("MDS ::read at %1 for %2 loffset %3\n", effective_start, this_read, loop_offset));
|
||||
DEBUG_TRACE (DEBUG::MidiDiskIO, string_compose ("MDS ::read at %1 for %2 loffset %3\n", effective_start, this_read, loop_offset));
|
||||
|
||||
if (midi_playlist()->read (*_midi_buf, effective_start, this_read, loop_range, 0, filter) != this_read) {
|
||||
error << string_compose(
|
||||
|
@ -1419,16 +1416,24 @@ DiskReader::refill_midi ()
|
|||
}
|
||||
|
||||
void
|
||||
DiskReader::set_no_disk_output (bool yn)
|
||||
DiskReader::dec_no_disk_output ()
|
||||
{
|
||||
/* this MUST be called as part of the process call tree, before any
|
||||
disk readers are invoked. We use it when the session needs the
|
||||
transport (and thus effective read position for DiskReaders) to keep
|
||||
advancing as part of syncing up with a transport master, but we
|
||||
don't want any actual disk output yet because we are still not
|
||||
synced.
|
||||
/* this is called unconditionally when things happen that ought to end
|
||||
a period of "no disk output". It's OK for that to happen when there
|
||||
was no corresponding call to ::inc_no_disk_output(), but we must
|
||||
stop the value from becoming negative.
|
||||
*/
|
||||
_no_disk_output = yn;
|
||||
|
||||
do {
|
||||
gint v = g_atomic_int_get (&_no_disk_output);
|
||||
if (v > 0) {
|
||||
if (g_atomic_int_compare_and_exchange (&_no_disk_output, v, v - 1)) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} while (true);
|
||||
}
|
||||
|
||||
DiskReader::DeclickAmp::DeclickAmp (samplecnt_t sample_rate)
|
||||
|
@ -1459,7 +1464,6 @@ DiskReader::DeclickAmp::apply_gain (AudioBuffer& buf, samplecnt_t n_samples, con
|
|||
uint32_t offset = 0;
|
||||
while (remain > 0) {
|
||||
uint32_t n_proc = remain > max_nproc ? max_nproc : remain;
|
||||
std::cerr << "g = " << g << std::endl;
|
||||
for (uint32_t i = 0; i < n_proc; ++i) {
|
||||
buffer[offset + i] *= g;
|
||||
}
|
||||
|
|
|
@ -448,7 +448,6 @@ setup_enum_writer ()
|
|||
REGISTER_CLASS_ENUM (SessionEvent, SetTimecodeTransmission);
|
||||
REGISTER_CLASS_ENUM (SessionEvent, Skip);
|
||||
REGISTER_CLASS_ENUM (SessionEvent, SetTransportMaster);
|
||||
REGISTER_CLASS_ENUM (SessionEvent, StopOnce);
|
||||
REGISTER_CLASS_ENUM (SessionEvent, AutoLoop);
|
||||
REGISTER (_SessionEvent_Type);
|
||||
|
||||
|
|
|
@ -66,11 +66,11 @@ ExportStatus::abort (bool error_occurred)
|
|||
}
|
||||
|
||||
void
|
||||
ExportStatus::finish ()
|
||||
ExportStatus::finish (TransportRequestSource trs)
|
||||
{
|
||||
Glib::Threads::Mutex::Lock l (_run_lock);
|
||||
set_running (false);
|
||||
Finished(); /* EMIT SIGNAL */
|
||||
Finished (trs); /* EMIT SIGNAL */
|
||||
}
|
||||
|
||||
} // namespace ARDOUR
|
||||
|
|
|
@ -574,6 +574,7 @@ ARDOUR::init (bool use_windows_vst, bool try_optimization, const char* localedir
|
|||
PannerManager::instance().discover_panners();
|
||||
|
||||
ARDOUR::AudioEngine::create ();
|
||||
TransportMasterManager::create ();
|
||||
|
||||
/* it is unfortunate that we need to include reserved names here that
|
||||
refer to control surfaces. But there's no way to ensure a complete
|
||||
|
@ -619,14 +620,13 @@ ARDOUR::init_post_engine (uint32_t start_cnt)
|
|||
/* find plugins */
|
||||
|
||||
ARDOUR::PluginManager::instance().refresh (!Config->get_discover_vst_on_start());
|
||||
}
|
||||
|
||||
if (start_cnt == 0) {
|
||||
|
||||
if ((node = Config->control_protocol_state()) != 0) {
|
||||
ControlProtocolManager::instance().set_state (*node, 0 /* here: global-config state */);
|
||||
}
|
||||
}
|
||||
|
||||
if (start_cnt > 0) {
|
||||
TransportMasterManager::instance().restart ();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,10 +64,6 @@ LTC_TransportMaster::LTC_TransportMaster (std::string const & name)
|
|||
, a3e_timecode (Timecode::timecode_24)
|
||||
, samples_per_timecode_frame (0)
|
||||
{
|
||||
if ((_port = AudioEngine::instance()->register_input_port (DataType::AUDIO, string_compose ("%1 in", _name, false, TransportMasterPort))) == 0) {
|
||||
throw failed_constructor();
|
||||
}
|
||||
|
||||
DEBUG_TRACE (DEBUG::Slave, string_compose ("LTC registered %1\n", _port->name()));
|
||||
|
||||
memset (&prev_frame, 0, sizeof(LTCFrameExt));
|
||||
|
@ -84,6 +80,14 @@ LTC_TransportMaster::init ()
|
|||
reset (true);
|
||||
}
|
||||
|
||||
void
|
||||
LTC_TransportMaster::create_port ()
|
||||
{
|
||||
if ((_port = AudioEngine::instance()->register_input_port (DataType::AUDIO, string_compose ("%1 in", _name, false, TransportMasterPort))) == 0) {
|
||||
throw failed_constructor();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
LTC_TransportMaster::set_session (Session *s)
|
||||
{
|
||||
|
|
|
@ -58,9 +58,6 @@ MIDIClock_TransportMaster::MIDIClock_TransportMaster (std::string const & name,
|
|||
, _running (false)
|
||||
, _bpm (0)
|
||||
{
|
||||
if ((_port = create_midi_port (string_compose ("%1 in", name))) == 0) {
|
||||
throw failed_constructor();
|
||||
}
|
||||
}
|
||||
|
||||
MIDIClock_TransportMaster::~MIDIClock_TransportMaster()
|
||||
|
@ -75,6 +72,14 @@ MIDIClock_TransportMaster::init ()
|
|||
current.reset ();
|
||||
}
|
||||
|
||||
void
|
||||
MIDIClock_TransportMaster::create_port ()
|
||||
{
|
||||
if ((_port = create_midi_port (string_compose ("%1 in", _name))) == 0) {
|
||||
throw failed_constructor();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MIDIClock_TransportMaster::set_session (Session *session)
|
||||
{
|
||||
|
|
|
@ -68,10 +68,6 @@ MTC_TransportMaster::MTC_TransportMaster (std::string const & name)
|
|||
, busy_guard2 (0)
|
||||
, printed_timecode_warning (false)
|
||||
{
|
||||
if ((_port = create_midi_port (string_compose ("%1 in", name))) == 0) {
|
||||
throw failed_constructor();
|
||||
}
|
||||
|
||||
DEBUG_TRACE (DEBUG::Slave, string_compose ("MTC registered %1\n", _port->name()));
|
||||
|
||||
init ();
|
||||
|
@ -103,6 +99,14 @@ MTC_TransportMaster::init ()
|
|||
reset (true);
|
||||
}
|
||||
|
||||
void
|
||||
MTC_TransportMaster::create_port ()
|
||||
{
|
||||
if ((_port = create_midi_port (string_compose ("%1 in", _name))) == 0) {
|
||||
throw failed_constructor();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MTC_TransportMaster::set_session (Session *s)
|
||||
{
|
||||
|
|
|
@ -1788,6 +1788,8 @@ Playlist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<
|
|||
notify_region_end_trimmed (region);
|
||||
} else if (what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
|
||||
notify_region_start_trimmed (region);
|
||||
} else if (what_changed.contains (Properties::start)) {
|
||||
notify_contents_changed ();
|
||||
}
|
||||
|
||||
/* don't notify about layer changes, since we are the only object that can initiate
|
||||
|
|
|
@ -645,7 +645,7 @@ Port::set_state (const XMLNode& node, int)
|
|||
/*static*/ void
|
||||
Port::set_speed_ratio (double s) {
|
||||
/* see VMResampler::set_rratio() for min/max range */
|
||||
_speed_ratio = std::min ((double) Config->get_max_transport_speed(), std::max (0.5, s));
|
||||
_speed_ratio = std::min ((double) Config->get_max_transport_speed(), std::max (0.5, fabs (s)));
|
||||
}
|
||||
|
||||
/*static*/ void
|
||||
|
|
|
@ -51,8 +51,7 @@ RTTaskList::drop_threads ()
|
|||
for (uint32_t i = 0; i < nt; ++i) {
|
||||
_task_run_sem.signal ();
|
||||
}
|
||||
for (std::vector<pthread_t>::const_iterator i = _threads.begin (); i != _threads.end (); ++i)
|
||||
{
|
||||
for (std::vector<pthread_t>::const_iterator i = _threads.begin (); i != _threads.end (); ++i) {
|
||||
pthread_join (*i, NULL);
|
||||
}
|
||||
_threads.clear ();
|
||||
|
@ -122,7 +121,7 @@ RTTaskList::run ()
|
|||
|
||||
boost::function<void ()> to_run;
|
||||
tm.acquire ();
|
||||
if (_tasklist.size () > 0) {
|
||||
if (!_tasklist.empty ()) {
|
||||
to_run = _tasklist.front();
|
||||
_tasklist.pop_front ();
|
||||
}
|
||||
|
@ -145,20 +144,29 @@ void
|
|||
RTTaskList::process (TaskList const& tl)
|
||||
{
|
||||
Glib::Threads::Mutex::Lock pm (_process_mutex);
|
||||
Glib::Threads::Mutex::Lock tm (_tasklist_mutex, Glib::Threads::NOT_LOCK);
|
||||
|
||||
tm.acquire ();
|
||||
_tasklist = tl;
|
||||
tm.release ();
|
||||
|
||||
process_tasklist ();
|
||||
|
||||
tm.acquire ();
|
||||
_tasklist.clear ();
|
||||
tm.release ();
|
||||
}
|
||||
|
||||
void
|
||||
RTTaskList::process_tasklist ()
|
||||
{
|
||||
if (0 == g_atomic_int_get (&_threads_active) || _threads.size () == 0) {
|
||||
// if (0 == g_atomic_int_get (&_threads_active) || _threads.size () == 0) {
|
||||
|
||||
for (TaskList::iterator i = _tasklist.begin (); i != _tasklist.end(); ++i) {
|
||||
(*i)();
|
||||
}
|
||||
return;
|
||||
}
|
||||
// }
|
||||
|
||||
uint32_t nt = std::min (_threads.size (), _tasklist.size ());
|
||||
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
#include "pbd/convert.h"
|
||||
#include "pbd/error.h"
|
||||
#include "pbd/file_utils.h"
|
||||
#include "pbd/i18n.h"
|
||||
#include "pbd/md5.h"
|
||||
#include "pbd/pthread_utils.h"
|
||||
#include "pbd/search_path.h"
|
||||
|
@ -116,7 +117,9 @@
|
|||
#include "ardour/speakers.h"
|
||||
#include "ardour/tempo.h"
|
||||
#include "ardour/ticker.h"
|
||||
#include "ardour/transport_fsm.h"
|
||||
#include "ardour/transport_master.h"
|
||||
#include "ardour/transport_master_manager.h"
|
||||
#include "ardour/track.h"
|
||||
#include "ardour/types_convert.h"
|
||||
#include "ardour/user_bundle.h"
|
||||
|
@ -129,8 +132,6 @@
|
|||
|
||||
#include "LuaBridge/LuaBridge.h"
|
||||
|
||||
#include "pbd/i18n.h"
|
||||
|
||||
#include <glibmm/checksum.h>
|
||||
|
||||
namespace ARDOUR {
|
||||
|
@ -189,7 +190,6 @@ Session::Session (AudioEngine &eng,
|
|||
, _base_sample_rate (0)
|
||||
, _nominal_sample_rate (0)
|
||||
, _current_sample_rate (0)
|
||||
, transport_sub_state (0)
|
||||
, _record_status (Disabled)
|
||||
, _transport_sample (0)
|
||||
, _seek_counter (0)
|
||||
|
@ -251,6 +251,7 @@ Session::Session (AudioEngine &eng,
|
|||
, lua (lua_newstate (&PBD::ReallocPool::lalloc, &_mempool))
|
||||
, _n_lua_scripts (0)
|
||||
, _butler (new Butler (*this))
|
||||
, _transport_fsm (TransportFSM::create (*this))
|
||||
, _post_transport_work (0)
|
||||
, _locations (new Locations (*this))
|
||||
, _ignore_skips_updates (false)
|
||||
|
@ -606,9 +607,13 @@ Session::immediately_post_engine ()
|
|||
_process_graph.reset (new Graph (*this));
|
||||
}
|
||||
|
||||
/* every time we reconnect, recompute worst case output latencies */
|
||||
/* Restart transport FSM */
|
||||
|
||||
_engine.Running.connect_same_thread (*this, boost::bind (&Session::initialize_latencies, this));
|
||||
_transport_fsm->backend()->start ();
|
||||
|
||||
/* every time we reconnect, do stuff ... */
|
||||
|
||||
_engine.Running.connect_same_thread (*this, boost::bind (&Session::engine_running, this));
|
||||
|
||||
if (synced_to_engine()) {
|
||||
_engine.transport_stop ();
|
||||
|
@ -848,7 +853,6 @@ Session::destroy ()
|
|||
case SessionEvent::Skip:
|
||||
case SessionEvent::PunchIn:
|
||||
case SessionEvent::PunchOut:
|
||||
case SessionEvent::StopOnce:
|
||||
case SessionEvent::RangeStop:
|
||||
case SessionEvent::RangeLocate:
|
||||
remove = false;
|
||||
|
@ -880,6 +884,9 @@ Session::destroy ()
|
|||
delete _selection;
|
||||
_selection = 0;
|
||||
|
||||
_transport_fsm->backend()->stop ();
|
||||
_transport_fsm.reset ();
|
||||
|
||||
DEBUG_TRACE (DEBUG::Destruction, "Session::destroy() done\n");
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
@ -1584,6 +1591,7 @@ Session::hookup_io ()
|
|||
*/
|
||||
|
||||
AudioEngine::instance()->reconnect_ports ();
|
||||
TransportMasterManager::instance().reconnect_ports ();
|
||||
|
||||
/* Anyone who cares about input state, wake up and do something */
|
||||
|
||||
|
@ -2015,7 +2023,7 @@ void
|
|||
Session::_locations_changed (const Locations::LocationList& locations)
|
||||
{
|
||||
/* There was some mass-change in the Locations object.
|
||||
*
|
||||
*
|
||||
* We might be re-adding a location here but it doesn't actually matter
|
||||
* for all the locations that the Session takes an interest in.
|
||||
*/
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "pbd/stacktrace.h"
|
||||
|
||||
#include "ardour/butler.h"
|
||||
#include "ardour/disk_reader.h"
|
||||
#include "ardour/route.h"
|
||||
#include "ardour/session.h"
|
||||
#include "ardour/session_event.h"
|
||||
|
@ -61,6 +62,7 @@ void
|
|||
Session::schedule_playback_buffering_adjustment ()
|
||||
{
|
||||
add_post_transport_work (PostTransportAdjustPlaybackBuffering);
|
||||
DiskReader::inc_no_disk_output ();
|
||||
_butler->schedule_transport_work ();
|
||||
}
|
||||
|
||||
|
@ -108,6 +110,7 @@ Session::overwrite_some_buffers (Track* t)
|
|||
}
|
||||
|
||||
add_post_transport_work (PostTransportOverWrite);
|
||||
|
||||
_butler->schedule_transport_work ();
|
||||
}
|
||||
|
||||
|
|
|
@ -225,7 +225,6 @@ SessionEventManager::merge_event (SessionEvent* ev)
|
|||
|
||||
switch (ev->type) {
|
||||
case SessionEvent::AutoLoop:
|
||||
case SessionEvent::StopOnce:
|
||||
_clear_event_type (ev->type);
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -95,7 +95,7 @@ Session::pre_export ()
|
|||
|
||||
_exporting = true;
|
||||
export_status->set_running (true);
|
||||
export_status->Finished.connect_same_thread (*this, boost::bind (&Session::finalize_audio_export, this));
|
||||
export_status->Finished.connect_same_thread (*this, boost::bind (&Session::finalize_audio_export, this, _1));
|
||||
|
||||
/* disable MMC output early */
|
||||
|
||||
|
@ -153,7 +153,7 @@ Session::start_audio_export (samplepos_t position, bool realtime, bool region_ex
|
|||
}
|
||||
}
|
||||
|
||||
/* we just did the core part of a locate() call above, but
|
||||
/* we just did the core part of a locate call above, but
|
||||
for the sake of any GUI, put the _transport_sample in
|
||||
the right place too.
|
||||
*/
|
||||
|
@ -256,7 +256,7 @@ Session::process_export_fw (pframes_t nframes)
|
|||
set_transport_speed (1.0, 0, false);
|
||||
butler_transport_work ();
|
||||
g_atomic_int_set (&_butler->should_do_transport_work, 0);
|
||||
post_transport ();
|
||||
butler_completed_transport_work ();
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -299,7 +299,7 @@ int
|
|||
Session::stop_audio_export ()
|
||||
{
|
||||
/* can't use stop_transport() here because we need
|
||||
an immediate halt and don't require all the declick
|
||||
an synchronous halt and don't require all the declick
|
||||
stuff that stop_transport() implements.
|
||||
*/
|
||||
|
||||
|
@ -311,8 +311,14 @@ Session::stop_audio_export ()
|
|||
}
|
||||
|
||||
void
|
||||
Session::finalize_audio_export ()
|
||||
Session::finalize_audio_export (TransportRequestSource trs)
|
||||
{
|
||||
/* This is called as a handler for the Finished signal, which is
|
||||
emitted by a UI component once the ExportStatus object associated
|
||||
with this export indicates that it has finished. It runs in the UI
|
||||
thread that emits the signal.
|
||||
*/
|
||||
|
||||
_exporting = false;
|
||||
|
||||
if (_export_rolling) {
|
||||
|
@ -340,6 +346,6 @@ Session::finalize_audio_export ()
|
|||
if (post_export_sync) {
|
||||
config.set_external_sync (true);
|
||||
} else {
|
||||
locate (post_export_position, false, false, false, false, false);
|
||||
request_locate (post_export_position, false, trs);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,9 +26,9 @@
|
|||
#include <algorithm>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <boost/msm/back/state_machine.hpp>
|
||||
#include <boost/msm/front/state_machine_def.hpp>
|
||||
#include <boost/algorithm/string/erase.hpp>
|
||||
|
||||
#include "pbd/i18n.h"
|
||||
#include "pbd/error.h"
|
||||
#include "pbd/enumwriter.h"
|
||||
|
||||
|
@ -45,6 +45,7 @@
|
|||
#include "ardour/process_thread.h"
|
||||
#include "ardour/scene_changer.h"
|
||||
#include "ardour/session.h"
|
||||
#include "ardour/transport_fsm.h"
|
||||
#include "ardour/transport_master.h"
|
||||
#include "ardour/transport_master_manager.h"
|
||||
#include "ardour/ticker.h"
|
||||
|
@ -54,95 +55,11 @@
|
|||
|
||||
#include "midi++/mmc.h"
|
||||
|
||||
#include "pbd/i18n.h"
|
||||
|
||||
using namespace ARDOUR;
|
||||
using namespace PBD;
|
||||
using namespace std;
|
||||
|
||||
/* state machine */
|
||||
namespace msm = boost::msm;
|
||||
namespace mpl = boost::mpl;
|
||||
|
||||
namespace TransportState
|
||||
{
|
||||
/* events */
|
||||
struct play {};
|
||||
struct stop {};
|
||||
|
||||
/* front-end: define the FSM structure */
|
||||
struct TransportFSM : public msm::front::state_machine_def<TransportFSM>
|
||||
{
|
||||
|
||||
/* FSM states */
|
||||
struct Stopped : public msm::front::state<>
|
||||
{
|
||||
template <class Event,class FSM> void
|
||||
on_entry (Event const&, FSM&)
|
||||
{
|
||||
std::cout << "entering: Stopped" << std::endl;
|
||||
}
|
||||
template <class Event,class FSM> void
|
||||
on_exit (Event const&, FSM&)
|
||||
{
|
||||
std::cout << "leaving: Stopped" << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
struct Playing : public msm::front::state<>
|
||||
{
|
||||
template <class Event,class FSM> void
|
||||
on_entry (Event const&, FSM&)
|
||||
{
|
||||
std::cout << "entering: Playing" << std::endl;
|
||||
}
|
||||
|
||||
template <class Event,class FSM> void
|
||||
on_exit (Event const&, FSM&)
|
||||
{
|
||||
std::cout << "leaving: Playing" << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
/* the initial state */
|
||||
typedef Stopped initial_state;
|
||||
|
||||
/* transition actions */
|
||||
void start_playback (play const&)
|
||||
{
|
||||
std::cout << "player::start_playback\n";
|
||||
}
|
||||
|
||||
void stop_playback (stop const&)
|
||||
{
|
||||
std::cout << "player::stop_playback\n";
|
||||
}
|
||||
|
||||
typedef TransportFSM _t; // makes transition table cleaner
|
||||
|
||||
struct transition_table : mpl::vector<
|
||||
// Start Event Next Action Guard
|
||||
// +---------+-------------+---------+---------------------+----------------------+
|
||||
a_row < Stopped , play , Playing , &_t::start_playback >,
|
||||
_row < Stopped , stop , Stopped >,
|
||||
// +---------+-------------+---------+---------------------+----------------------+
|
||||
a_row < Playing , stop , Stopped , &_t::stop_playback >
|
||||
// +---------+-------------+---------+---------------------+----------------------+
|
||||
> {};
|
||||
};
|
||||
|
||||
typedef msm::back::state_machine<TransportFSM> transport_fsm;
|
||||
|
||||
void test()
|
||||
{
|
||||
transport_fsm t;
|
||||
t.start ();
|
||||
t.process_event (play());
|
||||
t.process_event (stop());
|
||||
t.stop();
|
||||
}
|
||||
|
||||
};
|
||||
#define TFSM_EVENT(ev) { DEBUG_TRACE (DEBUG::TFSMEvents, string_compose ("TFSM(%1)\n", typeid(ev).name())); _transport_fsm->enqueue (ev); }
|
||||
|
||||
/** Called by the audio engine when there is work to be done with JACK.
|
||||
* @param nframes Number of samples to process.
|
||||
|
@ -161,8 +78,12 @@ Session::process (pframes_t nframes)
|
|||
}
|
||||
|
||||
if (non_realtime_work_pending()) {
|
||||
DEBUG_TRACE (DEBUG::Butler, string_compose ("non-realtime work pending: %1\n", enum_2_string (post_transport_work())));
|
||||
if (!_butler->transport_work_requested ()) {
|
||||
post_transport ();
|
||||
DEBUG_TRACE (DEBUG::Butler, string_compose ("done, waiting? %1\n", _transport_fsm->waiting_for_butler()));
|
||||
butler_completed_transport_work ();
|
||||
} else {
|
||||
DEBUG_TRACE (DEBUG::Butler, "not done yet\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -177,11 +98,16 @@ Session::process (pframes_t nframes)
|
|||
* callig it hold a _processor_lock reader-lock
|
||||
*/
|
||||
boost::shared_ptr<RouteList> r = routes.reader ();
|
||||
bool one_or_more_routes_declicking = false;
|
||||
for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
|
||||
if ((*i)->apply_processor_changes_rt()) {
|
||||
_rt_emit_pending = true;
|
||||
}
|
||||
if ((*i)->declick_in_progress()) {
|
||||
one_or_more_routes_declicking = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (_rt_emit_pending) {
|
||||
if (!_rt_thread_active) {
|
||||
emit_route_signals ();
|
||||
|
@ -193,6 +119,17 @@ Session::process (pframes_t nframes)
|
|||
}
|
||||
}
|
||||
|
||||
/* We are checking two things here:
|
||||
*
|
||||
* 1) whether or not all tracks have finished a declick out.
|
||||
* 2) is the transport FSM waiting to be told this
|
||||
*/
|
||||
|
||||
if (!one_or_more_routes_declicking && declick_in_progress()) {
|
||||
/* end of the declick has been reached by all routes */
|
||||
TFSM_EVENT (TransportFSM::declick_done());
|
||||
}
|
||||
|
||||
_engine.main_thread()->drop_buffers ();
|
||||
|
||||
/* deliver MIDI clock. Note that we need to use the transport sample
|
||||
|
@ -241,6 +178,8 @@ Session::no_roll (pframes_t nframes)
|
|||
(*i)->automation_run (_transport_sample, nframes);
|
||||
}
|
||||
|
||||
_global_locate_pending = locate_pending ();
|
||||
|
||||
if (_process_graph) {
|
||||
DEBUG_TRACE(DEBUG::ProcessThreads,"calling graph/no-roll\n");
|
||||
_process_graph->routes_no_roll( nframes, _transport_sample, end_sample, non_realtime_work_pending());
|
||||
|
@ -281,7 +220,7 @@ Session::process_routes (pframes_t nframes, bool& need_butler)
|
|||
(*i)->automation_run (start_sample, nframes);
|
||||
}
|
||||
|
||||
_global_locate_pending = locate_pending ();
|
||||
_global_locate_pending = locate_pending();
|
||||
|
||||
if (_process_graph) {
|
||||
DEBUG_TRACE(DEBUG::ProcessThreads,"calling graph/process-routes\n");
|
||||
|
@ -302,7 +241,7 @@ Session::process_routes (pframes_t nframes, bool& need_butler)
|
|||
bool b = false;
|
||||
|
||||
if ((ret = (*i)->roll (nframes, start_sample, end_sample, b)) < 0) {
|
||||
stop_transport ();
|
||||
TFSM_EVENT (TransportFSM::stop_transport (false, false));
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -404,7 +343,7 @@ Session::process_with_events (pframes_t nframes)
|
|||
|
||||
assert (_count_in_samples == 0 || _remaining_latency_preroll == 0 || _count_in_samples == _remaining_latency_preroll);
|
||||
|
||||
DEBUG_TRACE (DEBUG::Transport, string_compose ("Running count in/latency preroll of %1 & %2\n", _count_in_samples, _remaining_latency_preroll));
|
||||
// DEBUG_TRACE (DEBUG::Transport, string_compose ("Running count in/latency preroll of %1 & %2\n", _count_in_samples, _remaining_latency_preroll));
|
||||
|
||||
while (_count_in_samples > 0 || _remaining_latency_preroll > 0) {
|
||||
samplecnt_t ns;
|
||||
|
@ -615,10 +554,8 @@ Session::process_with_events (pframes_t nframes)
|
|||
|
||||
if (samples_moved < 0) {
|
||||
decrement_transport_position (-samples_moved);
|
||||
DEBUG_TRACE (DEBUG::Transport, string_compose ("DEcrement transport by %1 to %2\n", samples_moved, _transport_sample));
|
||||
} else if (samples_moved) {
|
||||
increment_transport_position (samples_moved);
|
||||
DEBUG_TRACE (DEBUG::Transport, string_compose ("INcrement transport by %1 to %2\n", samples_moved, _transport_sample));
|
||||
} else {
|
||||
DEBUG_TRACE (DEBUG::Transport, "no transport motion\n");
|
||||
}
|
||||
|
@ -699,7 +636,6 @@ Session::process_without_events (pframes_t nframes)
|
|||
return;
|
||||
} else {
|
||||
samples_moved = (samplecnt_t) nframes * _transport_speed;
|
||||
DEBUG_TRACE (DEBUG::Transport, string_compose ("no-events, plan to move transport by %1 (%2 @ %3)\n", samples_moved, nframes, _transport_speed));
|
||||
}
|
||||
|
||||
if (!_exporting && !timecode_transmission_suspended()) {
|
||||
|
@ -730,10 +666,10 @@ Session::process_without_events (pframes_t nframes)
|
|||
|
||||
if (samples_moved < 0) {
|
||||
decrement_transport_position (-samples_moved);
|
||||
DEBUG_TRACE (DEBUG::Transport, string_compose ("DEcrement transport by %1 to %2\n", samples_moved, _transport_sample));
|
||||
//DEBUG_TRACE (DEBUG::Transport, string_compose ("DEcrement transport by %1 to %2\n", samples_moved, _transport_sample));
|
||||
} else if (samples_moved) {
|
||||
increment_transport_position (samples_moved);
|
||||
DEBUG_TRACE (DEBUG::Transport, string_compose ("INcrement transport by %1 to %2\n", samples_moved, _transport_sample));
|
||||
//DEBUG_TRACE (DEBUG::Transport, string_compose ("INcrement transport by %1 to %2\n", samples_moved, _transport_sample));
|
||||
} else {
|
||||
DEBUG_TRACE (DEBUG::Transport, "no transport motion\n");
|
||||
}
|
||||
|
@ -915,37 +851,27 @@ Session::process_event (SessionEvent* ev)
|
|||
/* roll after locate, do not flush, set "with loop"
|
||||
true only if we are seamless looping
|
||||
*/
|
||||
start_locate (ev->target_sample, true, false, Config->get_seamless_loop());
|
||||
TFSM_EVENT (TransportFSM::locate (ev->target_sample, true, false, Config->get_seamless_loop(), false));
|
||||
}
|
||||
remove = false;
|
||||
del = false;
|
||||
break;
|
||||
|
||||
case SessionEvent::Locate:
|
||||
if (ev->yes_or_no) { /* force locate */
|
||||
/* args: do not roll after locate, do flush, not with loop */
|
||||
locate (ev->target_sample, false, true, false);
|
||||
} else {
|
||||
/* args: do not roll after locate, do flush, not with loop */
|
||||
start_locate (ev->target_sample, false, true, false);
|
||||
}
|
||||
/* args: do not roll after locate, do flush, not with loop, force */
|
||||
TFSM_EVENT (TransportFSM::locate (ev->target_sample, false, true, false, ev->yes_or_no));
|
||||
_send_timecode_update = true;
|
||||
break;
|
||||
|
||||
case SessionEvent::LocateRoll:
|
||||
if (ev->yes_or_no) {
|
||||
/* args: roll after locate, do flush, not with loop */
|
||||
locate (ev->target_sample, true, true, false);
|
||||
} else {
|
||||
/* args: roll after locate, do flush, not with loop */
|
||||
start_locate (ev->target_sample, true, true, false);
|
||||
}
|
||||
/* args: roll after locate, do flush, not with loop, force */
|
||||
TFSM_EVENT (TransportFSM::locate (ev->target_sample, true, true, false, ev->yes_or_no));
|
||||
_send_timecode_update = true;
|
||||
break;
|
||||
|
||||
case SessionEvent::Skip:
|
||||
if (Config->get_skip_playback()) {
|
||||
start_locate (ev->target_sample, true, true, false);
|
||||
TFSM_EVENT (TransportFSM::locate (ev->target_sample, true, true, false, false));
|
||||
_send_timecode_update = true;
|
||||
}
|
||||
remove = false;
|
||||
|
@ -985,26 +911,15 @@ Session::process_event (SessionEvent* ev)
|
|||
del = false;
|
||||
break;
|
||||
|
||||
case SessionEvent::StopOnce:
|
||||
if (!non_realtime_work_pending()) {
|
||||
_clear_event_type (SessionEvent::StopOnce);
|
||||
stop_transport (ev->yes_or_no);
|
||||
}
|
||||
remove = false;
|
||||
del = false;
|
||||
break;
|
||||
|
||||
case SessionEvent::RangeStop:
|
||||
if (!non_realtime_work_pending()) {
|
||||
stop_transport (ev->yes_or_no);
|
||||
}
|
||||
TFSM_EVENT (TransportFSM::stop_transport (ev->yes_or_no, false));
|
||||
remove = false;
|
||||
del = false;
|
||||
break;
|
||||
|
||||
case SessionEvent::RangeLocate:
|
||||
/* args: roll after locate, do flush, not with loop */
|
||||
start_locate (ev->target_sample, true, true, false);
|
||||
TFSM_EVENT (TransportFSM::locate (ev->target_sample, true, true, false, false));
|
||||
remove = false;
|
||||
del = false;
|
||||
break;
|
||||
|
@ -1179,35 +1094,53 @@ Session::follow_transport_master (pframes_t nframes)
|
|||
|
||||
slave_speed = tmm.get_current_speed_in_process_context();
|
||||
slave_transport_sample = tmm.get_current_position_in_process_context ();
|
||||
|
||||
track_transport_master (slave_speed, slave_transport_sample);
|
||||
|
||||
/* transport sample may have been moved during ::track_transport_master() */
|
||||
|
||||
delta = _transport_sample - slave_transport_sample;
|
||||
|
||||
DEBUG_TRACE (DEBUG::Slave, string_compose ("session at %1, master at %2, delta: %3 res: %4\n", _transport_sample, slave_transport_sample, delta, tmm.current()->resolution()));
|
||||
|
||||
if (transport_master_tracking_state == Running) {
|
||||
/* This is a heuristic rather than a strictly provable rule. The idea
|
||||
* is that if we're "far away" from the master, we should locate to its
|
||||
* current position, and then varispeed to sync with it.
|
||||
*
|
||||
* On the other hand, if we're close to it, just varispeed.
|
||||
*/
|
||||
|
||||
if (!actively_recording() && abs (delta) > tmm.current()->resolution()) {
|
||||
DEBUG_TRACE (DEBUG::Slave, string_compose ("current slave delta %1 greater than slave resolution %2\n", delta, tmm.current()->resolution()));
|
||||
if (micro_locate (-delta) != 0) {
|
||||
DEBUG_TRACE (DEBUG::Slave, "micro-locate didn't work, set no disk output true\n");
|
||||
|
||||
/* run routes as normal, but no disk output */
|
||||
DiskReader::set_no_disk_output (true);
|
||||
}
|
||||
return true;
|
||||
if (!actively_recording() && abs (delta) > (5 * current_block_size)) {
|
||||
DiskReader::inc_no_disk_output ();
|
||||
if (!_transport_fsm->locating()) {
|
||||
DEBUG_TRACE (DEBUG::Slave, string_compose ("request locate to master position %1\n", slave_transport_sample));
|
||||
TFSM_EVENT (TransportFSM::locate (slave_transport_sample, true, true, false, false));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (transport_master_tracking_state == Running) {
|
||||
/* speed is set, we're locked, and good to go */
|
||||
DiskReader::set_no_disk_output (false);
|
||||
return true;
|
||||
if (slave_speed != 0.0) {
|
||||
if (_transport_speed == 0.0f) {
|
||||
DEBUG_TRACE (DEBUG::Slave, string_compose ("slave starts transport: %1 sample %2 tf %3\n", slave_speed, slave_transport_sample, _transport_sample));
|
||||
TFSM_EVENT (TransportFSM::start_transport ());
|
||||
}
|
||||
} else {
|
||||
if (_transport_speed != 0.0f) {
|
||||
DEBUG_TRACE (DEBUG::Slave, string_compose ("slave stops transport: %1 sample %2 tf %3\n", slave_speed, slave_transport_sample, _transport_sample));
|
||||
TFSM_EVENT (TransportFSM::stop_transport (false, false));
|
||||
}
|
||||
}
|
||||
|
||||
/* This is the second part of the "we're not synced yet" code. If we're
|
||||
* close, but not within the resolution of the master, silence disk
|
||||
* output but continue to varispeed to get in sync.
|
||||
*/
|
||||
|
||||
if (!actively_recording() && abs (delta) > tmm.current()->resolution()) {
|
||||
/* just varispeed to chase the master, and be silent till we're synced */
|
||||
DiskReader::inc_no_disk_output ();
|
||||
return true;
|
||||
}
|
||||
|
||||
/* speed is set, we're locked, and good to go */
|
||||
DiskReader::dec_no_disk_output ();
|
||||
return true;
|
||||
|
||||
noroll:
|
||||
/* don't move at all */
|
||||
DEBUG_TRACE (DEBUG::Slave, "no roll\n")
|
||||
|
@ -1215,81 +1148,9 @@ Session::follow_transport_master (pframes_t nframes)
|
|||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
Session::track_transport_master (float slave_speed, samplepos_t slave_transport_sample)
|
||||
{
|
||||
boost::shared_ptr<TransportMaster> master (TransportMasterManager::instance().current());
|
||||
|
||||
assert (master);
|
||||
|
||||
DEBUG_TRACE (DEBUG::Slave, string_compose ("session has master tracking state as %1\n", transport_master_tracking_state));
|
||||
|
||||
if (slave_speed != 0.0f) {
|
||||
|
||||
/* slave is running */
|
||||
|
||||
switch (transport_master_tracking_state) {
|
||||
case Stopped:
|
||||
master_wait_end = slave_transport_sample + worst_latency_preroll() + master->seekahead_distance ();
|
||||
DEBUG_TRACE (DEBUG::Slave, string_compose ("slave stopped, but running, requires seekahead to %1, now WAITING\n", master_wait_end));
|
||||
/* we can call locate() here because we are in process context */
|
||||
if (micro_locate (master_wait_end - _transport_sample) != 0) {
|
||||
locate (master_wait_end, false, false);
|
||||
}
|
||||
transport_master_tracking_state = Waiting;
|
||||
|
||||
case Waiting:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (transport_master_tracking_state == Waiting) {
|
||||
|
||||
DEBUG_TRACE (DEBUG::Slave, string_compose ("master currently at %1, waiting to pass %2\n", slave_transport_sample, master_wait_end));
|
||||
|
||||
if (slave_transport_sample >= master_wait_end) {
|
||||
|
||||
DEBUG_TRACE (DEBUG::Slave, string_compose ("slave start at %1 vs %2\n", slave_transport_sample, _transport_sample));
|
||||
|
||||
transport_master_tracking_state = Running;
|
||||
|
||||
/* now perform a "micro-seek" within the disk buffers to realign ourselves
|
||||
precisely with the master.
|
||||
*/
|
||||
|
||||
if (micro_locate (slave_transport_sample - _transport_sample) != 0) {
|
||||
cerr << "cannot micro-seek\n";
|
||||
/* XXX what? */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (transport_master_tracking_state == Running && _transport_speed == 0.0f) {
|
||||
DEBUG_TRACE (DEBUG::Slave, "slave starts transport\n");
|
||||
start_transport ();
|
||||
}
|
||||
|
||||
} else { // slave_speed is 0
|
||||
|
||||
/* slave has stopped */
|
||||
|
||||
if (_transport_speed != 0.0f) {
|
||||
DEBUG_TRACE (DEBUG::Slave, string_compose ("slave stops transport: %1 sample %2 tf %3\n", slave_speed, slave_transport_sample, _transport_sample));
|
||||
stop_transport ();
|
||||
}
|
||||
|
||||
if (slave_transport_sample != _transport_sample) {
|
||||
DEBUG_TRACE (DEBUG::Slave, string_compose ("slave stopped, move to %1\n", slave_transport_sample));
|
||||
force_locate (slave_transport_sample, false);
|
||||
}
|
||||
|
||||
reset_slave_state();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Session::reset_slave_state ()
|
||||
{
|
||||
transport_master_tracking_state = Stopped;
|
||||
DiskReader::set_no_disk_output (false);
|
||||
DiskReader::dec_no_disk_output ();
|
||||
}
|
||||
|
|
|
@ -4123,7 +4123,6 @@ Session::config_changed (std::string p, bool ours)
|
|||
first_file_data_format_reset = false;
|
||||
|
||||
} else if (p == "external-sync") {
|
||||
std::cerr << "param change, rss to " << TransportMasterManager::instance().current() << std::endl;
|
||||
request_sync_source (TransportMasterManager::instance().current());
|
||||
} else if (p == "denormal-model") {
|
||||
setup_fpu ();
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
|
||||
#include "ardour/session.h"
|
||||
#include "ardour/tempo.h"
|
||||
#include "ardour/transport_fsm.h"
|
||||
|
||||
#include "pbd/i18n.h"
|
||||
|
||||
|
@ -42,6 +43,8 @@ using namespace std;
|
|||
using namespace ARDOUR;
|
||||
using namespace PBD;
|
||||
|
||||
#define TFSM_EVENT(ev) { DEBUG_TRACE (DEBUG::TFSMEvents, string_compose ("TFSM(%1)\n", typeid(ev).name())); _transport_fsm->enqueue (ev); }
|
||||
|
||||
/* BBT TIME*/
|
||||
|
||||
void
|
||||
|
@ -214,7 +217,7 @@ Session::backend_sync_callback (TransportState state, samplepos_t pos)
|
|||
case TransportRolling:
|
||||
// cerr << "SYNC: rolling slave = " << slave << endl;
|
||||
if (slave) {
|
||||
start_transport ();
|
||||
TFSM_EVENT (TransportFSM::start_transport());
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
|
@ -32,12 +32,15 @@
|
|||
#include <cerrno>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "pbd/undo.h"
|
||||
#include <boost/algorithm/string/erase.hpp>
|
||||
|
||||
#include "pbd/error.h"
|
||||
#include "pbd/enumwriter.h"
|
||||
#include "pbd/pthread_utils.h"
|
||||
#include "pbd/i18n.h"
|
||||
#include "pbd/memento_command.h"
|
||||
#include "pbd/pthread_utils.h"
|
||||
#include "pbd/stacktrace.h"
|
||||
#include "pbd/undo.h"
|
||||
|
||||
#include "midi++/mmc.h"
|
||||
#include "midi++/port.h"
|
||||
|
@ -54,6 +57,7 @@
|
|||
#include "ardour/profile.h"
|
||||
#include "ardour/scene_changer.h"
|
||||
#include "ardour/session.h"
|
||||
#include "ardour/transport_fsm.h"
|
||||
#include "ardour/transport_master.h"
|
||||
#include "ardour/transport_master_manager.h"
|
||||
#include "ardour/tempo.h"
|
||||
|
@ -61,8 +65,6 @@
|
|||
#include "ardour/vca.h"
|
||||
#include "ardour/vca_manager.h"
|
||||
|
||||
#include "pbd/i18n.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace ARDOUR;
|
||||
using namespace PBD;
|
||||
|
@ -80,8 +82,10 @@ using namespace PBD;
|
|||
#endif
|
||||
|
||||
|
||||
#define TFSM_EVENT(ev) { DEBUG_TRACE (DEBUG::TFSMEvents, string_compose ("TFSM(%1)\n", typeid(ev).name())); _transport_fsm->enqueue (ev); }
|
||||
|
||||
/* *****************************************************************************
|
||||
* REALTIME ACTIONS (to be called on state transtion
|
||||
* REALTIME ACTIONS (to be called on state transitions)
|
||||
* ****************************************************************************/
|
||||
|
||||
void
|
||||
|
@ -89,14 +93,13 @@ Session::realtime_stop (bool abort, bool clear_state)
|
|||
{
|
||||
ENSURE_PROCESS_THREAD;
|
||||
|
||||
DEBUG_TRACE (DEBUG::Transport, string_compose ("realtime stop @ %1\n", _transport_sample));
|
||||
DEBUG_TRACE (DEBUG::Transport, string_compose ("realtime stop @ %1 speed = %2\n", _transport_sample, _transport_speed));
|
||||
PostTransportWork todo = PostTransportWork (0);
|
||||
|
||||
/* assume that when we start, we'll be moving forwards */
|
||||
|
||||
if (_transport_speed < 0.0f) {
|
||||
if (_last_transport_speed < 0.0f) {
|
||||
todo = (PostTransportWork (todo | PostTransportStop | PostTransportReverse));
|
||||
_default_transport_speed = 1.0;
|
||||
DiskReader::inc_no_disk_output (); // for the buffer reversal
|
||||
} else {
|
||||
todo = PostTransportWork (todo | PostTransportStop);
|
||||
}
|
||||
|
@ -127,7 +130,6 @@ Session::realtime_stop (bool abort, bool clear_state)
|
|||
add_post_transport_work (todo);
|
||||
}
|
||||
|
||||
_clear_event_type (SessionEvent::StopOnce);
|
||||
_clear_event_type (SessionEvent::RangeStop);
|
||||
_clear_event_type (SessionEvent::RangeLocate);
|
||||
|
||||
|
@ -156,22 +158,13 @@ Session::realtime_stop (bool abort, bool clear_state)
|
|||
waiting_for_sync_offset = true;
|
||||
}
|
||||
|
||||
transport_sub_state = 0;
|
||||
}
|
||||
|
||||
void
|
||||
Session::realtime_locate ()
|
||||
{
|
||||
ENSURE_PROCESS_THREAD;
|
||||
|
||||
boost::shared_ptr<RouteList> r = routes.reader ();
|
||||
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
|
||||
(*i)->realtime_locate ();
|
||||
if (todo) {
|
||||
TFSM_EVENT (TransportFSM::butler_required());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Session::start_locate (samplepos_t target_sample, bool with_roll, bool with_flush, bool for_loop_enabled, bool force)
|
||||
Session::locate (samplepos_t target_sample, bool with_roll, bool with_flush, bool for_loop_enabled, bool force, bool with_mmc)
|
||||
{
|
||||
ENSURE_PROCESS_THREAD;
|
||||
|
||||
|
@ -195,7 +188,7 @@ Session::start_locate (samplepos_t target_sample, bool with_roll, bool with_flus
|
|||
will use the incorrect _transport_sample and report an old
|
||||
and incorrect time to Jack transport
|
||||
*/
|
||||
locate (target_sample, with_roll, with_flush, for_loop_enabled, force);
|
||||
do_locate (target_sample, with_roll, with_flush, for_loop_enabled, force, with_mmc);
|
||||
}
|
||||
|
||||
/* tell JACK to change transport position, and we will
|
||||
|
@ -211,13 +204,13 @@ Session::start_locate (samplepos_t target_sample, bool with_roll, bool with_flus
|
|||
}
|
||||
|
||||
} else {
|
||||
locate (target_sample, with_roll, with_flush, for_loop_enabled, force);
|
||||
do_locate (target_sample, with_roll, with_flush, for_loop_enabled, force, with_mmc);
|
||||
}
|
||||
}
|
||||
|
||||
/** @param with_mmc true to send a MMC locate command when the locate is done */
|
||||
void
|
||||
Session::locate (samplepos_t target_sample, bool with_roll, bool with_flush, bool for_loop_enabled, bool force, bool with_mmc)
|
||||
Session::do_locate (samplepos_t target_sample, bool with_roll, bool with_flush, bool for_loop_enabled, bool force, bool with_mmc)
|
||||
{
|
||||
ENSURE_PROCESS_THREAD;
|
||||
|
||||
|
@ -246,12 +239,11 @@ Session::locate (samplepos_t target_sample, bool with_roll, bool with_flush, boo
|
|||
set_transport_speed (1.0, 0, false);
|
||||
}
|
||||
loop_changing = false;
|
||||
TFSM_EVENT (TransportFSM::locate_done());
|
||||
Located (); /* EMIT SIGNAL */
|
||||
return;
|
||||
}
|
||||
|
||||
cerr << "... now doing the actual locate to " << target_sample << " from " << _transport_sample << endl;
|
||||
|
||||
// Update Timecode time
|
||||
_transport_sample = target_sample;
|
||||
// Bump seek counter so that any in-process locate in the butler
|
||||
|
@ -266,19 +258,30 @@ Session::locate (samplepos_t target_sample, bool with_roll, bool with_flush, boo
|
|||
* we are rolling AND
|
||||
* no autoplay in effect AND
|
||||
* we're not going to keep rolling after the locate AND
|
||||
* !(playing a loop with JACK sync)
|
||||
* !(playing a loop with JACK sync) AND
|
||||
* we're not synced to an external transport master
|
||||
*
|
||||
*/
|
||||
|
||||
bool transport_was_stopped = !transport_rolling();
|
||||
|
||||
if (!transport_was_stopped && (!auto_play_legal || !config.get_auto_play()) && !with_roll && !(synced_to_engine() && play_loop) &&
|
||||
if (!transport_was_stopped &&
|
||||
(!auto_play_legal || !config.get_auto_play()) &&
|
||||
!with_roll &&
|
||||
!(synced_to_engine() && play_loop) &&
|
||||
(!Profile->get_trx() || !(config.get_external_sync() && !synced_to_engine()))) {
|
||||
|
||||
realtime_stop (false, true); // XXX paul - check if the 2nd arg is really correct
|
||||
transport_was_stopped = true;
|
||||
|
||||
} else {
|
||||
/* otherwise tell the world that we located */
|
||||
realtime_locate ();
|
||||
|
||||
/* Tell all routes to do the RT part of locate */
|
||||
|
||||
boost::shared_ptr<RouteList> r = routes.reader ();
|
||||
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
|
||||
(*i)->realtime_locate ();
|
||||
}
|
||||
}
|
||||
|
||||
if (force || !for_loop_enabled || loop_changing) {
|
||||
|
@ -379,7 +382,9 @@ Session::locate (samplepos_t target_sample, bool with_roll, bool with_flush, boo
|
|||
}
|
||||
|
||||
if (need_butler) {
|
||||
_butler->schedule_transport_work ();
|
||||
TFSM_EVENT (TransportFSM::butler_required());
|
||||
} else {
|
||||
TFSM_EVENT (TransportFSM::locate_done());
|
||||
}
|
||||
|
||||
loop_changing = false;
|
||||
|
@ -396,6 +401,17 @@ Session::locate (samplepos_t target_sample, bool with_roll, bool with_flush, boo
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
Session::post_locate ()
|
||||
{
|
||||
if (transport_master_is_external() && !synced_to_engine()) {
|
||||
const samplepos_t current_master_position = TransportMasterManager::instance().get_current_position_in_process_context();
|
||||
if (abs (current_master_position - _transport_sample) > TransportMasterManager::instance().current()->resolution()) {
|
||||
_last_roll_location = _last_roll_or_reversal_location = _transport_sample;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Set the transport speed.
|
||||
* Called from the process thread.
|
||||
* @param speed New speed
|
||||
|
@ -468,7 +484,7 @@ Session::set_transport_speed (double speed, samplepos_t destination_sample, bool
|
|||
_requested_return_sample = destination_sample;
|
||||
}
|
||||
|
||||
stop_transport (abort);
|
||||
TFSM_EVENT (TransportFSM::stop_transport (abort, false));
|
||||
}
|
||||
|
||||
} else if (transport_stopped() && speed == 1.0) {
|
||||
|
@ -505,7 +521,7 @@ Session::set_transport_speed (double speed, samplepos_t destination_sample, bool
|
|||
_engine.transport_start ();
|
||||
_count_in_once = false;
|
||||
} else {
|
||||
start_transport ();
|
||||
TFSM_EVENT (TransportFSM::start_transport());
|
||||
}
|
||||
|
||||
} else {
|
||||
|
@ -547,6 +563,7 @@ Session::set_transport_speed (double speed, samplepos_t destination_sample, bool
|
|||
|
||||
if ((_transport_speed && speed * _transport_speed < 0.0) || (_last_transport_speed * speed < 0.0) || (_last_transport_speed == 0.0 && speed < 0.0)) {
|
||||
todo = PostTransportWork (todo | PostTransportReverse);
|
||||
DiskReader::inc_no_disk_output (); // for the buffer reversal
|
||||
_last_roll_or_reversal_location = _transport_sample;
|
||||
}
|
||||
|
||||
|
@ -559,7 +576,7 @@ Session::set_transport_speed (double speed, samplepos_t destination_sample, bool
|
|||
|
||||
if (todo) {
|
||||
add_post_transport_work (todo);
|
||||
_butler->schedule_transport_work ();
|
||||
TFSM_EVENT (TransportFSM::butler_required());
|
||||
}
|
||||
|
||||
DEBUG_TRACE (DEBUG::Transport, string_compose ("send TSC3 with speed = %1\n", _transport_speed));
|
||||
|
@ -595,14 +612,10 @@ Session::stop_transport (bool abort, bool clear_state)
|
|||
ENSURE_PROCESS_THREAD;
|
||||
|
||||
_count_in_once = false;
|
||||
if (_transport_speed == 0.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG_TRACE (DEBUG::Transport, "time to actually stop\n");
|
||||
DEBUG_TRACE (DEBUG::Transport, string_compose ("time to actually stop with TS @ %1\n", _transport_sample));
|
||||
|
||||
realtime_stop (abort, clear_state);
|
||||
_butler->schedule_transport_work ();
|
||||
}
|
||||
|
||||
/** Called from the process thread */
|
||||
|
@ -705,15 +718,26 @@ Session::start_transport ()
|
|||
TransportStateChange (); /* EMIT SIGNAL */
|
||||
}
|
||||
|
||||
bool
|
||||
Session::should_roll_after_locate () const
|
||||
{
|
||||
/* a locate must previously have been requested and completed */
|
||||
|
||||
return ((!config.get_external_sync() && (auto_play_legal && config.get_auto_play())) && !_exporting) || (post_transport_work() & PostTransportRoll);
|
||||
|
||||
}
|
||||
|
||||
/** Do any transport work in the audio thread that needs to be done after the
|
||||
* transport thread is finished. Audio thread, realtime safe.
|
||||
* butler thread is finished. Audio thread, realtime safe.
|
||||
*/
|
||||
void
|
||||
Session::post_transport ()
|
||||
Session::butler_completed_transport_work ()
|
||||
{
|
||||
ENSURE_PROCESS_THREAD;
|
||||
PostTransportWork ptw = post_transport_work ();
|
||||
|
||||
DEBUG_TRACE (DEBUG::Transport, string_compose ("Butler done, RT cleanup for %1\n", enum_2_string (ptw)));
|
||||
|
||||
if (ptw & PostTransportAudition) {
|
||||
if (auditioner && auditioner->auditioning()) {
|
||||
process_function = &Session::process_audition;
|
||||
|
@ -722,19 +746,14 @@ Session::post_transport ()
|
|||
}
|
||||
}
|
||||
|
||||
if (ptw & PostTransportStop) {
|
||||
|
||||
transport_sub_state = 0;
|
||||
if (ptw & PostTransportLocate) {
|
||||
post_locate ();
|
||||
TFSM_EVENT (TransportFSM::locate_done());
|
||||
}
|
||||
|
||||
if (ptw & PostTransportLocate) {
|
||||
|
||||
if (((!config.get_external_sync() && (auto_play_legal && config.get_auto_play())) && !_exporting) || (ptw & PostTransportRoll)) {
|
||||
_count_in_once = false;
|
||||
start_transport ();
|
||||
} else {
|
||||
transport_sub_state = 0;
|
||||
}
|
||||
if (ptw & PostTransportAdjustPlaybackBuffering) {
|
||||
/* we blocked output while this happened */
|
||||
DiskReader::dec_no_disk_output ();
|
||||
}
|
||||
|
||||
set_next_event ();
|
||||
|
@ -742,6 +761,20 @@ Session::post_transport ()
|
|||
know were handled ?
|
||||
*/
|
||||
set_post_transport_work (PostTransportWork (0));
|
||||
|
||||
if (_transport_fsm->waiting_for_butler()) {
|
||||
TFSM_EVENT (TransportFSM::butler_done());
|
||||
}
|
||||
|
||||
DiskReader::dec_no_disk_output ();
|
||||
}
|
||||
|
||||
void
|
||||
Session::schedule_butler_for_transport_work ()
|
||||
{
|
||||
assert (_transport_fsm->waiting_for_butler ());
|
||||
DEBUG_TRACE (DEBUG::Butler, "summon butler for transport work\n");
|
||||
_butler->schedule_transport_work ();
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -752,7 +785,7 @@ Session::maybe_stop (samplepos_t limit)
|
|||
if (synced_to_engine () && config.get_jack_time_master ()) {
|
||||
_engine.transport_stop ();
|
||||
} else if (!synced_to_engine ()) {
|
||||
stop_transport ();
|
||||
TFSM_EVENT (TransportFSM::stop_transport ());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -861,11 +894,11 @@ Session::set_play_loop (bool yn, double speed)
|
|||
rolling, do not locate to loop start.
|
||||
*/
|
||||
if (!transport_rolling() && (speed != 0.0)) {
|
||||
start_locate (loc->start(), true, true, false, true);
|
||||
TFSM_EVENT (TransportFSM::locate (loc->start(), true, true, false, true));
|
||||
}
|
||||
} else {
|
||||
if (speed != 0.0) {
|
||||
start_locate (loc->start(), true, true, false, true);
|
||||
TFSM_EVENT (TransportFSM::locate (loc->start(), true, true, false, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1114,14 +1147,14 @@ Session::request_cancel_play_range ()
|
|||
bool
|
||||
Session::solo_selection_active ()
|
||||
{
|
||||
if ( _soloSelection.empty() ) {
|
||||
if (_soloSelection.empty()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
Session::solo_selection ( StripableList &list, bool new_state )
|
||||
Session::solo_selection (StripableList &list, bool new_state)
|
||||
{
|
||||
boost::shared_ptr<ControlList> solo_list (new ControlList);
|
||||
boost::shared_ptr<ControlList> unsolo_list (new ControlList);
|
||||
|
@ -1176,7 +1209,7 @@ Session::butler_transport_work ()
|
|||
PostTransportWork ptw = post_transport_work();
|
||||
uint64_t before;
|
||||
|
||||
DEBUG_TRACE (DEBUG::Transport, string_compose ("Butler transport work, todo = %1 at %2\n", enum_2_string (ptw), (before = g_get_monotonic_time())));
|
||||
DEBUG_TRACE (DEBUG::Transport, string_compose ("Butler transport work, todo = [%1] (0x%3%4%5) at %2\n", enum_2_string (ptw), (before = g_get_monotonic_time()), std::hex, ptw, std::dec));
|
||||
|
||||
if (ptw & PostTransportLocate) {
|
||||
|
||||
|
@ -1311,18 +1344,6 @@ Session::non_realtime_overwrite (int on_entry, bool& finished)
|
|||
}
|
||||
}
|
||||
|
||||
bool
|
||||
Session::declick_in_progress () const
|
||||
{
|
||||
boost::shared_ptr<RouteList> rl = routes.reader();
|
||||
for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
|
||||
if ((*i)->declick_in_progress ()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
Session::non_realtime_locate ()
|
||||
{
|
||||
|
@ -1370,6 +1391,7 @@ Session::non_realtime_locate ()
|
|||
for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
|
||||
(*i)->non_realtime_locate (tf);
|
||||
if (sc != g_atomic_int_get (&_seek_counter)) {
|
||||
std::cerr << "\n\nLOCATE INTERRUPTED BY LOCATE!!!\n\n";
|
||||
goto restart;
|
||||
}
|
||||
}
|
||||
|
@ -1757,7 +1779,7 @@ Session::unset_play_loop ()
|
|||
if (Config->get_seamless_loop()) {
|
||||
/* likely need to flush track buffers: this will locate us to wherever we are */
|
||||
add_post_transport_work (PostTransportLocate);
|
||||
_butler->schedule_transport_work ();
|
||||
TFSM_EVENT (TransportFSM::butler_required());
|
||||
}
|
||||
TransportStateChange (); /* EMIT SIGNAL */
|
||||
}
|
||||
|
@ -1901,27 +1923,37 @@ Session::request_roll_at_and_return (samplepos_t start, samplepos_t return_to)
|
|||
void
|
||||
Session::engine_halted ()
|
||||
{
|
||||
bool ignored;
|
||||
|
||||
/* there will be no more calls to process(), so
|
||||
we'd better clean up for ourselves, right now.
|
||||
|
||||
but first, make sure the butler is out of
|
||||
the picture.
|
||||
We can't queue SessionEvents because they only get
|
||||
handled from within a process callback.
|
||||
*/
|
||||
|
||||
if (_butler) {
|
||||
_butler->stop ();
|
||||
}
|
||||
/* this just stops the FSM engine ... it doesn't change the state of
|
||||
* the FSM directly or anything else ... but the FSM will be
|
||||
* reinitialized when we call its ::start() method from
|
||||
* ::engine_running() (if we ever get there)
|
||||
*/
|
||||
|
||||
_transport_fsm->backend()->stop ();
|
||||
|
||||
/* Synchronously do the realtime part of a transport stop.
|
||||
*
|
||||
* Calling this will cause the butler to asynchronously run
|
||||
* ::non_realtime_stop() where the rest of the "stop" work will be
|
||||
* done.
|
||||
*/
|
||||
|
||||
realtime_stop (false, true);
|
||||
non_realtime_stop (false, 0, ignored);
|
||||
transport_sub_state = 0;
|
||||
|
||||
DEBUG_TRACE (DEBUG::Transport, string_compose ("send TSC6 with speed = %1\n", _transport_speed));
|
||||
TransportStateChange (); /* EMIT SIGNAL */
|
||||
}
|
||||
|
||||
void
|
||||
Session::engine_running ()
|
||||
{
|
||||
initialize_latencies ();
|
||||
_transport_fsm->backend()->start ();
|
||||
}
|
||||
|
||||
void
|
||||
Session::xrun_recovery ()
|
||||
|
@ -2054,7 +2086,7 @@ Session::sync_source_changed (SyncSource type, samplepos_t pos, pframes_t cycle_
|
|||
longer valid with a new slave.
|
||||
*/
|
||||
|
||||
DiskReader::set_no_disk_output (false);
|
||||
DiskReader::dec_no_disk_output ();
|
||||
|
||||
#if 0
|
||||
we should not be treating specific transport masters as special cases because there maybe > 1 of a particular type
|
||||
|
@ -2101,3 +2133,27 @@ Session::sync_source_changed (SyncSource type, samplepos_t pos, pframes_t cycle_
|
|||
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
bool
|
||||
Session::transport_stopped() const
|
||||
{
|
||||
return _transport_fsm->stopped();
|
||||
}
|
||||
|
||||
bool
|
||||
Session::transport_rolling() const
|
||||
{
|
||||
return _transport_speed != 0.0 && _count_in_samples == 0 && _remaining_latency_preroll == 0;
|
||||
}
|
||||
|
||||
bool
|
||||
Session::locate_pending () const
|
||||
{
|
||||
return _transport_fsm->locating();
|
||||
}
|
||||
|
||||
bool
|
||||
Session::declick_in_progress () const
|
||||
{
|
||||
return _transport_fsm->declick_in_progress();
|
||||
}
|
||||
|
|
|
@ -264,7 +264,7 @@ Track::freeze_state() const
|
|||
bool
|
||||
Track::declick_in_progress () const
|
||||
{
|
||||
return _disk_reader->declick_in_progress ();
|
||||
return active() && _disk_reader->declick_in_progress ();
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* Copyright (C) 2019 Robin Gareus <robin@gareus.org>
|
||||
*
|
||||
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "ardour/debug.h"
|
||||
#include "ardour/session.h"
|
||||
#include "ardour/transport_fsm.h"
|
||||
|
||||
using namespace ARDOUR;
|
||||
using namespace PBD;
|
||||
|
||||
/* transition actions */
|
||||
|
||||
void
|
||||
TransportFSM::start_playback (TransportFSM::start_transport const& p)
|
||||
{
|
||||
DEBUG_TRACE (DEBUG::TFSMEvents, "tfsm::start_playback\n");
|
||||
api->start_transport();
|
||||
}
|
||||
|
||||
void
|
||||
TransportFSM::start_declick (TransportFSM::stop_transport const &s)
|
||||
{
|
||||
DEBUG_TRACE (DEBUG::TFSMEvents, "tfsm::start_declick\n");
|
||||
_last_stop = s;
|
||||
}
|
||||
|
||||
void
|
||||
TransportFSM::stop_playback (TransportFSM::declick_done const& /*ignored*/)
|
||||
{
|
||||
DEBUG_TRACE (DEBUG::TFSMEvents, "tfsm::stop_playback\n");
|
||||
api->stop_transport (_last_stop.abort, _last_stop.clear_state);
|
||||
}
|
||||
|
||||
void
|
||||
TransportFSM::save_locate_and_start_declick (TransportFSM::locate const & l)
|
||||
{
|
||||
DEBUG_TRACE (DEBUG::TFSMEvents, "tfsm::save_locate_and_stop\n");
|
||||
_last_locate = l;
|
||||
start_declick (stop_transport (false, false));
|
||||
}
|
||||
|
||||
void
|
||||
TransportFSM::start_locate (TransportFSM::locate const& l)
|
||||
{
|
||||
DEBUG_TRACE (DEBUG::TFSMEvents, "tfsm::start_locate\n");
|
||||
api->locate (l.target, l.with_roll, l.with_flush, l.with_loop, l.force);
|
||||
}
|
||||
|
||||
void
|
||||
TransportFSM::start_saved_locate (TransportFSM::declick_done const&)
|
||||
{
|
||||
DEBUG_TRACE (DEBUG::TFSMEvents, "tfsm::start_save\n");
|
||||
api->locate (_last_locate.target, _last_locate.with_roll, _last_locate.with_flush, _last_locate.with_loop, _last_locate.force);
|
||||
}
|
||||
|
||||
void
|
||||
TransportFSM::interrupt_locate (TransportFSM::locate const& l)
|
||||
{
|
||||
DEBUG_TRACE (DEBUG::TFSMEvents, "tfsm::interrupt\n");
|
||||
/* maintain original "with-roll" choice of initial locate, even though
|
||||
* we are interrupting the locate to start a new one.
|
||||
*/
|
||||
api->locate (l.target, _last_locate.with_roll, l.with_flush, l.with_loop, l.force);
|
||||
}
|
||||
|
||||
void
|
||||
TransportFSM::schedule_butler_for_transport_work (TransportFSM::butler_required const&)
|
||||
{
|
||||
api->schedule_butler_for_transport_work ();
|
||||
}
|
||||
|
||||
bool
|
||||
TransportFSM::should_roll_after_locate (TransportFSM::locate_done const &)
|
||||
{
|
||||
bool ret = api->should_roll_after_locate ();
|
||||
DEBUG_TRACE (DEBUG::TFSMEvents, string_compose ("tfsm::should_roll_after_locate() ? %1\n", ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
TransportFSM::roll_after_locate (TransportFSM::locate_done const &)
|
||||
{
|
||||
DEBUG_TRACE (DEBUG::TFSMEvents, "rolling after locate\n");
|
||||
api->start_transport ();
|
||||
}
|
||||
|
|
@ -74,6 +74,7 @@ TransportMaster::TransportMaster (SyncSource t, std::string const & name)
|
|||
, _sclock_synced (Properties::sclock_synced, false)
|
||||
, _collect (Properties::collect, true)
|
||||
, _connected (Properties::connected, false)
|
||||
, port_node (X_(""))
|
||||
{
|
||||
register_properties ();
|
||||
|
||||
|
@ -260,7 +261,27 @@ TransportMaster::set_state (XMLNode const & node, int /* version */)
|
|||
XMLNode* pnode = node.child (X_("Port"));
|
||||
|
||||
if (pnode) {
|
||||
XMLNodeList const & children = pnode->children();
|
||||
port_node = *pnode;
|
||||
|
||||
if (AudioEngine::instance()->running()) {
|
||||
connect_port_using_state ();
|
||||
}
|
||||
}
|
||||
|
||||
PropertyChanged (what_changed);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
TransportMaster::connect_port_using_state ()
|
||||
{
|
||||
if (!_port) {
|
||||
create_port ();
|
||||
}
|
||||
|
||||
if (_port) {
|
||||
XMLNodeList const & children = port_node.children();
|
||||
for (XMLNodeList::const_iterator ci = children.begin(); ci != children.end(); ++ci) {
|
||||
|
||||
XMLProperty const *prop;
|
||||
|
@ -273,10 +294,6 @@ TransportMaster::set_state (XMLNode const & node, int /* version */)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
PropertyChanged (what_changed);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
XMLNode&
|
||||
|
@ -360,21 +377,27 @@ TransportMaster::factory (SyncSource type, std::string const& name, bool removea
|
|||
|
||||
DEBUG_TRACE (DEBUG::Slave, string_compose ("factory-construct %1 name %2 removeable %3\n", enum_2_string (type), name, removeable));
|
||||
|
||||
switch (type) {
|
||||
case MTC:
|
||||
tm.reset (new MTC_TransportMaster (name));
|
||||
break;
|
||||
case LTC:
|
||||
tm.reset (new LTC_TransportMaster (name));
|
||||
break;
|
||||
case MIDIClock:
|
||||
tm.reset (new MIDIClock_TransportMaster (name));
|
||||
break;
|
||||
case Engine:
|
||||
tm.reset (new Engine_TransportMaster (*AudioEngine::instance()));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
try {
|
||||
switch (type) {
|
||||
case MTC:
|
||||
tm.reset (new MTC_TransportMaster (name));
|
||||
break;
|
||||
case LTC:
|
||||
tm.reset (new LTC_TransportMaster (name));
|
||||
break;
|
||||
case MIDIClock:
|
||||
tm.reset (new MIDIClock_TransportMaster (name));
|
||||
break;
|
||||
case Engine:
|
||||
tm.reset (new Engine_TransportMaster (*AudioEngine::instance()));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} catch (...) {
|
||||
error << string_compose (_("Construction of transport master object of type %1 failed"), enum_2_string (type)) << endmsg;
|
||||
std::cerr << string_compose (_("Construction of transport master object of type %1 failed"), enum_2_string (type)) << std::endl;
|
||||
return boost::shared_ptr<TransportMaster>();
|
||||
}
|
||||
|
||||
if (tm) {
|
||||
|
@ -444,7 +467,7 @@ TransportMasterViaMIDI::create_midi_port (std::string const & port_name)
|
|||
{
|
||||
boost::shared_ptr<Port> p;
|
||||
|
||||
if ((p = AudioEngine::instance()->register_input_port (DataType::MIDI, port_name)) == 0) {
|
||||
if ((p = AudioEngine::instance()->register_input_port (DataType::MIDI, port_name, false, TransportMasterPort)) == 0) {
|
||||
return boost::shared_ptr<Port> ();
|
||||
}
|
||||
|
||||
|
@ -490,4 +513,3 @@ TimecodeTransportMaster::set_fr2997 (bool yn)
|
|||
PropertyChanged (Properties::fr2997);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,16 +20,12 @@
|
|||
#include "ardour/debug.h"
|
||||
#include "ardour/disk_reader.h"
|
||||
#include "ardour/session.h"
|
||||
#include "ardour/rc_configuration.h"
|
||||
#include "ardour/transport_master_manager.h"
|
||||
|
||||
#include "pbd/boost_debug.cc"
|
||||
#include "pbd/i18n.h"
|
||||
|
||||
#if __cplusplus > 199711L
|
||||
#define local_signbit(x) std::signbit (x)
|
||||
#else
|
||||
#define local_signbit(x) ((((__int64*)(&z))*) & 0x8000000000000000)
|
||||
#endif
|
||||
#include "pbd/stateful.h"
|
||||
|
||||
using namespace ARDOUR;
|
||||
using namespace PBD;
|
||||
|
@ -51,6 +47,27 @@ TransportMasterManager::~TransportMasterManager ()
|
|||
clear ();
|
||||
}
|
||||
|
||||
TransportMasterManager&
|
||||
TransportMasterManager::create ()
|
||||
{
|
||||
assert (!_instance);
|
||||
|
||||
cerr << "TMM::create(), Config = " << Config << " size will be " << sizeof (TransportMasterManager) << endl;
|
||||
|
||||
_instance = new TransportMasterManager;
|
||||
|
||||
XMLNode* tmm_node = Config->extra_xml (X_("TransportMasters"));
|
||||
if (tmm_node) {
|
||||
cerr << " setting state via XML\n";
|
||||
_instance->set_state (*tmm_node, Stateful::current_state_version);
|
||||
} else {
|
||||
cerr << " setting default config\n";
|
||||
_instance->set_default_configuration ();
|
||||
}
|
||||
|
||||
return *_instance;
|
||||
}
|
||||
|
||||
int
|
||||
TransportMasterManager::set_default_configuration ()
|
||||
{
|
||||
|
@ -106,7 +123,7 @@ TransportMasterManager::parameter_changed (std::string const & what)
|
|||
if (what == "external-sync") {
|
||||
if (!_session->config.get_external_sync()) {
|
||||
/* disabled */
|
||||
DiskReader::set_no_disk_output (false);
|
||||
DiskReader::dec_no_disk_output ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -115,7 +132,8 @@ TransportMasterManager&
|
|||
TransportMasterManager::instance()
|
||||
{
|
||||
if (!_instance) {
|
||||
_instance = new TransportMasterManager();
|
||||
fatal << string_compose (_("programming error:%1"), X_("TransportMasterManager::instance() called without an instance!")) << endmsg;
|
||||
/* NOTREACHED */
|
||||
}
|
||||
return *_instance;
|
||||
}
|
||||
|
@ -196,7 +214,7 @@ TransportMasterManager::pre_process_transport_masters (pframes_t nframes, sample
|
|||
if (master_dll_initstate == 0) {
|
||||
|
||||
init_transport_master_dll (_master_speed, _master_position);
|
||||
// _master_invalid_this_cycle = true;
|
||||
_master_invalid_this_cycle = true;
|
||||
DEBUG_TRACE (DEBUG::Slave, "no roll3 - still initializing master DLL\n");
|
||||
master_dll_initstate = _master_speed > 0.0 ? 1 : -1;
|
||||
|
||||
|
@ -220,12 +238,12 @@ TransportMasterManager::pre_process_transport_masters (pframes_t nframes, sample
|
|||
if (!_session->actively_recording()) {
|
||||
DEBUG_TRACE (DEBUG::Slave, string_compose ("slave delta %1 greater than slave resolution %2 => no disk output\n", delta, _current_master->resolution()));
|
||||
/* run routes as normal, but no disk output */
|
||||
DiskReader::set_no_disk_output (true);
|
||||
DiskReader::inc_no_disk_output ();
|
||||
} else {
|
||||
DiskReader::set_no_disk_output (false);
|
||||
DiskReader::dec_no_disk_output ();
|
||||
}
|
||||
} else {
|
||||
DiskReader::set_no_disk_output (false);
|
||||
DiskReader::dec_no_disk_output ();
|
||||
}
|
||||
|
||||
/* inject DLL with new data */
|
||||
|
@ -330,6 +348,11 @@ TransportMasterManager::add (SyncSource type, std::string const & name, bool rem
|
|||
}
|
||||
|
||||
tm = TransportMaster::factory (type, name, removeable);
|
||||
|
||||
if (!tm) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
boost_debug_shared_ptr_mark_interesting (tm.get(), "tm");
|
||||
ret = add_locked (tm);
|
||||
}
|
||||
|
@ -396,6 +419,10 @@ TransportMasterManager::set_current_locked (boost::shared_ptr<TransportMaster> c
|
|||
}
|
||||
}
|
||||
|
||||
if (!c->usable()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
_current_master = c;
|
||||
_master_speed = 0;
|
||||
_master_position = 0;
|
||||
|
@ -490,6 +517,7 @@ TransportMasterManager::clear ()
|
|||
int
|
||||
TransportMasterManager::set_state (XMLNode const & node, int version)
|
||||
{
|
||||
PBD::stacktrace (std::cerr, 20);
|
||||
assert (node.name() == state_node_name);
|
||||
|
||||
XMLNodeList const & children = node.children();
|
||||
|
@ -500,11 +528,22 @@ TransportMasterManager::set_state (XMLNode const & node, int version)
|
|||
_current_master.reset ();
|
||||
boost_debug_list_ptrs ();
|
||||
|
||||
_transport_masters.clear ();
|
||||
/* TramsportMasters live for the entire life of the
|
||||
* program. TransportMasterManager::set_state() should only be
|
||||
* called at the start of the program, and there should be no
|
||||
* transport masters at that time.
|
||||
*/
|
||||
|
||||
assert (_transport_masters.empty());
|
||||
|
||||
for (XMLNodeList::const_iterator c = children.begin(); c != children.end(); ++c) {
|
||||
|
||||
boost::shared_ptr<TransportMaster> tm = TransportMaster::factory (**c);
|
||||
|
||||
if (!tm) {
|
||||
continue;
|
||||
}
|
||||
|
||||
boost_debug_shared_ptr_mark_interesting (tm.get(), "tm");
|
||||
|
||||
if (add_locked (tm)) {
|
||||
|
@ -518,8 +557,17 @@ TransportMasterManager::set_state (XMLNode const & node, int version)
|
|||
}
|
||||
|
||||
std::string current_master;
|
||||
|
||||
if (node.get_property (X_("current"), current_master)) {
|
||||
|
||||
/* may fal if current_master is not usable */
|
||||
|
||||
set_current (current_master);
|
||||
|
||||
if (!current()) {
|
||||
set_current (MTC); // always available
|
||||
}
|
||||
|
||||
} else {
|
||||
set_current (MTC);
|
||||
}
|
||||
|
@ -578,10 +626,14 @@ TransportMasterManager::restart ()
|
|||
XMLNode* node;
|
||||
|
||||
if ((node = Config->transport_master_state()) != 0) {
|
||||
if (TransportMasterManager::instance().set_state (*node, Stateful::loading_state_version)) {
|
||||
error << _("Cannot restore transport master manager") << endmsg;
|
||||
/* XXX now what? */
|
||||
|
||||
Glib::Threads::RWLock::ReaderLock lm (lock);
|
||||
|
||||
for (TransportMasters::const_iterator tm = _transport_masters.begin(); tm != _transport_masters.end(); ++tm) {
|
||||
(*tm)->connect_port_using_state ();
|
||||
(*tm)->reset (false);
|
||||
}
|
||||
|
||||
} else {
|
||||
if (TransportMasterManager::instance().set_default_configuration ()) {
|
||||
error << _("Cannot initialize transport master manager") << endmsg;
|
||||
|
@ -589,3 +641,16 @@ TransportMasterManager::restart ()
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TransportMasterManager::reconnect_ports ()
|
||||
{
|
||||
DEBUG_TRACE (DEBUG::Slave, "reconnecting all transport master ports\n");
|
||||
{
|
||||
Glib::Threads::RWLock::ReaderLock lm (lock);
|
||||
|
||||
for (TransportMasters::const_iterator tm = _transport_masters.begin(); tm != _transport_masters.end(); ++tm) {
|
||||
(*tm)->connect_port_using_state ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -248,6 +248,7 @@ libardour_sources = [
|
|||
'track.cc',
|
||||
'transient_detector.cc',
|
||||
'transform.cc',
|
||||
'transport_fsm.cc',
|
||||
'transport_master.cc',
|
||||
'transport_master_manager.cc',
|
||||
'transpose.cc',
|
||||
|
|
|
@ -57,8 +57,6 @@ void
|
|||
Transmitter::deliver ()
|
||||
|
||||
{
|
||||
string foo;
|
||||
|
||||
/* NOTE: this is just a default action for a Transmitter or a
|
||||
derived class. Any class can override this to produce some
|
||||
other action when deliver() is called.
|
||||
|
@ -68,8 +66,7 @@ Transmitter::deliver ()
|
|||
|
||||
/* send the SigC++ signal */
|
||||
|
||||
foo = str();
|
||||
(*send) (channel, foo.c_str());
|
||||
(*send) (channel, str().c_str());
|
||||
|
||||
/* XXX when or how can we delete this ? */
|
||||
// delete foo;
|
||||
|
|
|
@ -201,7 +201,7 @@ static int export_session (Session *session,
|
|||
}
|
||||
printf("\n");
|
||||
|
||||
status->finish ();
|
||||
status->finish (TRS_UI);
|
||||
|
||||
printf ("* Done.\n");
|
||||
return 0;
|
||||
|
|
11
wscript
11
wscript
|
@ -383,6 +383,17 @@ int main() { return 0; }''',
|
|||
|
||||
autowaf.set_basic_compiler_flags (conf,flags_dict)
|
||||
|
||||
#
|
||||
# the transition table for the libardour transport state machine
|
||||
# is larger than the default that is hard-coded in boost::mpl.
|
||||
# These need to be defined before any boost headers are used,
|
||||
# and just about the only way to be sure that is true is to define
|
||||
# them on the "command line" here.
|
||||
#
|
||||
cxx_flags.append ('-DBOOST_MPL_CFG_NO_PREPROCESSED_HEADERS')
|
||||
cxx_flags.append ('-DBOOST_MPL_LIMIT_VECTOR_SIZE=30')
|
||||
cxx_flags.append ('-DBOOST_MPL_LIMIT_MAP_SIZE=30')
|
||||
|
||||
if conf.options.asan:
|
||||
conf.check_cxx(cxxflags=["-fsanitize=address", "-fno-omit-frame-pointer"], linkflags=["-fsanitize=address"])
|
||||
cxx_flags.append('-fsanitize=address')
|
||||
|
|
Loading…
Reference in New Issue