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 ()) {
|
if (!status->aborted() && UIConfiguration::instance().get_save_export_mixer_screenshot ()) {
|
||||||
ExportProfileManager::TimespanStateList const& timespans = profile_manager->get_timespans();
|
ExportProfileManager::TimespanStateList const& timespans = profile_manager->get_timespans();
|
||||||
|
|
|
@ -793,7 +793,7 @@ ExportVideoDialog::launch_export ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
audio_progress_connection.disconnect();
|
audio_progress_connection.disconnect();
|
||||||
status->finish ();
|
status->finish (TRS_UI);
|
||||||
if (status->aborted()) {
|
if (status->aborted()) {
|
||||||
::g_unlink (_insnd.c_str());
|
::g_unlink (_insnd.c_str());
|
||||||
delete _transcoder; _transcoder = 0;
|
delete _transcoder; _transcoder = 0;
|
||||||
|
|
|
@ -24,8 +24,6 @@
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <boost/variant.hpp>
|
|
||||||
|
|
||||||
#include "pbd/xml++.h"
|
#include "pbd/xml++.h"
|
||||||
#include "pbd/id.h"
|
#include "pbd/id.h"
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@ namespace PBD {
|
||||||
namespace DEBUG {
|
namespace DEBUG {
|
||||||
LIBARDOUR_API extern DebugBits MidiSourceIO;
|
LIBARDOUR_API extern DebugBits MidiSourceIO;
|
||||||
LIBARDOUR_API extern DebugBits MidiPlaylistIO;
|
LIBARDOUR_API extern DebugBits MidiPlaylistIO;
|
||||||
LIBARDOUR_API extern DebugBits MidiDiskstreamIO;
|
LIBARDOUR_API extern DebugBits MidiDiskIO;
|
||||||
LIBARDOUR_API extern DebugBits MidiRingBuffer;
|
LIBARDOUR_API extern DebugBits MidiRingBuffer;
|
||||||
LIBARDOUR_API extern DebugBits SnapBBT;
|
LIBARDOUR_API extern DebugBits SnapBBT;
|
||||||
LIBARDOUR_API extern DebugBits Latency;
|
LIBARDOUR_API extern DebugBits Latency;
|
||||||
|
@ -49,6 +49,8 @@ namespace PBD {
|
||||||
LIBARDOUR_API extern DebugBits LTC;
|
LIBARDOUR_API extern DebugBits LTC;
|
||||||
LIBARDOUR_API extern DebugBits TXLTC;
|
LIBARDOUR_API extern DebugBits TXLTC;
|
||||||
LIBARDOUR_API extern DebugBits Transport;
|
LIBARDOUR_API extern DebugBits Transport;
|
||||||
|
LIBARDOUR_API extern DebugBits TFSMEvents;
|
||||||
|
LIBARDOUR_API extern DebugBits TFSMState;
|
||||||
LIBARDOUR_API extern DebugBits Slave;
|
LIBARDOUR_API extern DebugBits Slave;
|
||||||
LIBARDOUR_API extern DebugBits SessionEvents;
|
LIBARDOUR_API extern DebugBits SessionEvents;
|
||||||
LIBARDOUR_API extern DebugBits MidiIO;
|
LIBARDOUR_API extern DebugBits MidiIO;
|
||||||
|
|
|
@ -117,8 +117,6 @@ protected:
|
||||||
protected:
|
protected:
|
||||||
Flag _flags;
|
Flag _flags;
|
||||||
uint32_t i_am_the_modifier;
|
uint32_t i_am_the_modifier;
|
||||||
double _actual_speed;
|
|
||||||
double _target_speed;
|
|
||||||
bool _slaved;
|
bool _slaved;
|
||||||
bool in_set_state;
|
bool in_set_state;
|
||||||
samplepos_t playback_sample;
|
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_midi_readahead_samples (samplecnt_t samples_ahead) { midi_readahead = samples_ahead; }
|
||||||
|
|
||||||
static void set_no_disk_output (bool yn);
|
/* inc/dec variants MUST be called as part of the process call tree, before any
|
||||||
static bool no_disk_output() { return _no_disk_output; }
|
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:
|
protected:
|
||||||
friend class Track;
|
friend class Track;
|
||||||
|
@ -156,7 +165,7 @@ private:
|
||||||
|
|
||||||
static samplecnt_t _chunk_samples;
|
static samplecnt_t _chunk_samples;
|
||||||
static samplecnt_t midi_readahead;
|
static samplecnt_t midi_readahead;
|
||||||
static bool _no_disk_output;
|
static gint _no_disk_output;
|
||||||
|
|
||||||
int audio_read (PBD::PlaybackBuffer<Sample>*,
|
int audio_read (PBD::PlaybackBuffer<Sample>*,
|
||||||
Sample* sum_buffer,
|
Sample* sum_buffer,
|
||||||
|
|
|
@ -54,8 +54,8 @@ class LIBARDOUR_API ExportStatus {
|
||||||
}
|
}
|
||||||
Glib::Threads::Mutex& lock () { return _run_lock; }
|
Glib::Threads::Mutex& lock () { return _run_lock; }
|
||||||
|
|
||||||
PBD::Signal0<void> Finished;
|
PBD::Signal1<void,TransportRequestSource> Finished;
|
||||||
void finish ();
|
void finish (TransportRequestSource);
|
||||||
|
|
||||||
void cleanup ();
|
void cleanup ();
|
||||||
|
|
||||||
|
|
|
@ -88,7 +88,7 @@
|
||||||
#include "ardour/presentation_info.h"
|
#include "ardour/presentation_info.h"
|
||||||
#include "ardour/route.h"
|
#include "ardour/route.h"
|
||||||
#include "ardour/route_graph.h"
|
#include "ardour/route_graph.h"
|
||||||
|
#include "ardour/transport_api.h"
|
||||||
|
|
||||||
class XMLTree;
|
class XMLTree;
|
||||||
class XMLNode;
|
class XMLNode;
|
||||||
|
@ -167,6 +167,7 @@ class Source;
|
||||||
class Speakers;
|
class Speakers;
|
||||||
class TempoMap;
|
class TempoMap;
|
||||||
class TransportMaster;
|
class TransportMaster;
|
||||||
|
struct TransportFSM;
|
||||||
class Track;
|
class Track;
|
||||||
class UI_TransportMaster;
|
class UI_TransportMaster;
|
||||||
class VCAManager;
|
class VCAManager;
|
||||||
|
@ -186,7 +187,7 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Ardour Session */
|
/** 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:
|
private:
|
||||||
|
|
||||||
|
@ -457,7 +458,8 @@ public:
|
||||||
void adjust_capture_buffering();
|
void adjust_capture_buffering();
|
||||||
|
|
||||||
bool global_locate_pending() const { return _global_locate_pending; }
|
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;
|
bool transport_locked () const;
|
||||||
|
|
||||||
int wipe ();
|
int wipe ();
|
||||||
|
@ -752,8 +754,8 @@ public:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
double transport_speed() const { return _count_in_samples > 0 ? 0. : _transport_speed; }
|
double transport_speed() const { return _count_in_samples > 0 ? 0. : _transport_speed; }
|
||||||
bool transport_stopped() const { return _transport_speed == 0.0; }
|
bool transport_stopped() const;
|
||||||
bool transport_rolling() const { return _transport_speed != 0.0 && _count_in_samples == 0 && _remaining_latency_preroll == 0; }
|
bool transport_rolling() const;
|
||||||
|
|
||||||
bool silent () { return _silent; }
|
bool silent () { return _silent; }
|
||||||
|
|
||||||
|
@ -1237,6 +1239,16 @@ protected:
|
||||||
friend class Route;
|
friend class Route;
|
||||||
void update_latency_compensation (bool force = false);
|
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:
|
private:
|
||||||
int create (const std::string& mix_template, BusProfile*);
|
int create (const std::string& mix_template, BusProfile*);
|
||||||
void destroy ();
|
void destroy ();
|
||||||
|
@ -1262,7 +1274,6 @@ private:
|
||||||
samplecnt_t _base_sample_rate; // sample-rate of the session at creation time, "native" SR
|
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 _nominal_sample_rate; // overridden by audioengine setting
|
||||||
samplecnt_t _current_sample_rate; // this includes video pullup offset
|
samplecnt_t _current_sample_rate; // this includes video pullup offset
|
||||||
int transport_sub_state;
|
|
||||||
mutable gint _record_status;
|
mutable gint _record_status;
|
||||||
samplepos_t _transport_sample;
|
samplepos_t _transport_sample;
|
||||||
gint _seek_counter;
|
gint _seek_counter;
|
||||||
|
@ -1357,7 +1368,7 @@ private:
|
||||||
|
|
||||||
int pre_export ();
|
int pre_export ();
|
||||||
int stop_audio_export ();
|
int stop_audio_export ();
|
||||||
void finalize_audio_export ();
|
void finalize_audio_export (TransportRequestSource trs);
|
||||||
void finalize_export_internal (bool stop_freewheel);
|
void finalize_export_internal (bool stop_freewheel);
|
||||||
bool _pre_export_mmc_enabled;
|
bool _pre_export_mmc_enabled;
|
||||||
|
|
||||||
|
@ -1436,11 +1447,11 @@ private:
|
||||||
|
|
||||||
Butler* _butler;
|
Butler* _butler;
|
||||||
|
|
||||||
|
boost::shared_ptr<TransportFSM> _transport_fsm;
|
||||||
|
|
||||||
static const PostTransportWork ProcessCannotProceedMask =
|
static const PostTransportWork ProcessCannotProceedMask =
|
||||||
PostTransportWork (
|
PostTransportWork (
|
||||||
PostTransportReverse|
|
|
||||||
PostTransportAudition|
|
PostTransportAudition|
|
||||||
PostTransportStop|
|
|
||||||
PostTransportClearSubstate);
|
PostTransportClearSubstate);
|
||||||
|
|
||||||
gint _post_transport_work; /* accessed only atomic ops */
|
gint _post_transport_work; /* accessed only atomic ops */
|
||||||
|
@ -1671,19 +1682,16 @@ private:
|
||||||
int start_midi_thread ();
|
int start_midi_thread ();
|
||||||
|
|
||||||
bool should_ignore_transport_request (TransportRequestSource, TransportRequestType) const;
|
bool should_ignore_transport_request (TransportRequestSource, TransportRequestType) const;
|
||||||
bool declick_in_progress () const;
|
|
||||||
|
|
||||||
void set_play_loop (bool yn, double speed);
|
void set_play_loop (bool yn, double speed);
|
||||||
void unset_play_loop ();
|
void unset_play_loop ();
|
||||||
void overwrite_some_buffers (Track *);
|
void overwrite_some_buffers (Track *);
|
||||||
void flush_all_inserts ();
|
void flush_all_inserts ();
|
||||||
int micro_locate (samplecnt_t distance);
|
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 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 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_stop (bool abort, bool clear_state);
|
||||||
void realtime_locate ();
|
void realtime_locate ();
|
||||||
void non_realtime_start_scrub ();
|
void non_realtime_start_scrub ();
|
||||||
|
@ -1691,8 +1699,8 @@ private:
|
||||||
void non_realtime_locate ();
|
void non_realtime_locate ();
|
||||||
void non_realtime_stop (bool abort, int entry_request_count, bool& finished);
|
void non_realtime_stop (bool abort, int entry_request_count, bool& finished);
|
||||||
void non_realtime_overwrite (int entry_request_count, bool& finished);
|
void non_realtime_overwrite (int entry_request_count, bool& finished);
|
||||||
void post_transport ();
|
|
||||||
void engine_halted ();
|
void engine_halted ();
|
||||||
|
void engine_running ();
|
||||||
void xrun_recovery ();
|
void xrun_recovery ();
|
||||||
void set_track_loop (bool);
|
void set_track_loop (bool);
|
||||||
bool select_playhead_priority_target (samplepos_t&);
|
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 */
|
/* only one of each of these events can be queued at any one time */
|
||||||
|
|
||||||
StopOnce,
|
|
||||||
AutoLoop,
|
AutoLoop,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
47
libs/ardour/ardour/transport_api.h
Normal file
47
libs/ardour/ardour/transport_api.h
Normal file
|
@ -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
|
213
libs/ardour/ardour/transport_fsm.h
Normal file
213
libs/ardour/ardour/transport_fsm.h
Normal file
|
@ -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; }
|
TransportRequestType request_mask() const { return _request_mask; }
|
||||||
void set_request_mask (TransportRequestType);
|
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
|
/* this is set at construction, and not changeable later, so it is not
|
||||||
* a property
|
* a property
|
||||||
*/
|
*/
|
||||||
|
@ -358,6 +356,8 @@ class LIBARDOUR_API TransportMaster : public PBD::Stateful {
|
||||||
std::string display_name (bool sh/*ort*/ = true) const;
|
std::string display_name (bool sh/*ort*/ = true) const;
|
||||||
|
|
||||||
virtual void unregister_port ();
|
virtual void unregister_port ();
|
||||||
|
void connect_port_using_state ();
|
||||||
|
virtual void create_port () = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
SyncSource _type;
|
SyncSource _type;
|
||||||
|
@ -385,6 +385,8 @@ class LIBARDOUR_API TransportMaster : public PBD::Stateful {
|
||||||
|
|
||||||
boost::shared_ptr<Port> _port;
|
boost::shared_ptr<Port> _port;
|
||||||
|
|
||||||
|
XMLNode port_node;
|
||||||
|
|
||||||
PBD::ScopedConnection port_connection;
|
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);
|
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 position_string() const;
|
||||||
std::string delta_string() const;
|
std::string delta_string() const;
|
||||||
|
|
||||||
|
void create_port ();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PBD::ScopedConnectionList port_connections;
|
PBD::ScopedConnectionList port_connections;
|
||||||
PBD::ScopedConnection config_connection;
|
PBD::ScopedConnection config_connection;
|
||||||
|
@ -518,6 +522,8 @@ public:
|
||||||
std::string position_string() const;
|
std::string position_string() const;
|
||||||
std::string delta_string() const;
|
std::string delta_string() const;
|
||||||
|
|
||||||
|
void create_port ();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void parse_ltc(const pframes_t, const Sample* const, const samplecnt_t);
|
void parse_ltc(const pframes_t, const Sample* const, const samplecnt_t);
|
||||||
void process_ltc(samplepos_t const);
|
void process_ltc(samplepos_t const);
|
||||||
|
@ -586,6 +592,8 @@ class LIBARDOUR_API MIDIClock_TransportMaster : public TransportMaster, public T
|
||||||
|
|
||||||
float bpm() const { return _bpm; }
|
float bpm() const { return _bpm; }
|
||||||
|
|
||||||
|
void create_port ();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
PBD::ScopedConnectionList port_connections;
|
PBD::ScopedConnectionList port_connections;
|
||||||
|
|
||||||
|
@ -648,6 +656,8 @@ class LIBARDOUR_API Engine_TransportMaster : public TransportMaster
|
||||||
std::string position_string() const;
|
std::string position_string() const;
|
||||||
std::string delta_string() const;
|
std::string delta_string() const;
|
||||||
|
|
||||||
|
void create_port () { }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
AudioEngine& engine;
|
AudioEngine& engine;
|
||||||
bool _starting;
|
bool _starting;
|
||||||
|
|
|
@ -33,6 +33,7 @@ class UI_TransportMaster;
|
||||||
class LIBARDOUR_API TransportMasterManager : public boost::noncopyable
|
class LIBARDOUR_API TransportMasterManager : public boost::noncopyable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
static TransportMasterManager& create ();
|
||||||
~TransportMasterManager ();
|
~TransportMasterManager ();
|
||||||
|
|
||||||
int set_default_configuration ();
|
int set_default_configuration ();
|
||||||
|
@ -82,6 +83,8 @@ class LIBARDOUR_API TransportMasterManager : public boost::noncopyable
|
||||||
|
|
||||||
static const std::string state_node_name;
|
static const std::string state_node_name;
|
||||||
|
|
||||||
|
void reconnect_ports ();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TransportMasterManager();
|
TransportMasterManager();
|
||||||
|
|
||||||
|
|
|
@ -478,8 +478,8 @@ Bundle::connected_to (boost::shared_ptr<Bundle> other, AudioEngine & engine,
|
||||||
Bundle::PortList const & other_ports =
|
Bundle::PortList const & other_ports =
|
||||||
other->channel_ports (other->type_channel_to_overall(type, i));
|
other->channel_ports (other->type_channel_to_overall(type, i));
|
||||||
|
|
||||||
for (Bundle::PortList::const_iterator j = our_ports.begin();
|
for (Bundle::PortList::const_iterator j = our_ports.begin(); j != our_ports.end(); ++j) {
|
||||||
j != our_ports.end(); ++j) {
|
|
||||||
boost::shared_ptr<Port> p = engine.get_port_by_name(*j);
|
boost::shared_ptr<Port> p = engine.get_port_by_name(*j);
|
||||||
|
|
||||||
for (Bundle::PortList::const_iterator k = other_ports.begin();
|
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::MidiSourceIO = PBD::new_debug_bit ("midisourceio");
|
||||||
PBD::DebugBits PBD::DEBUG::MidiPlaylistIO = PBD::new_debug_bit ("midiplaylistio");
|
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::MidiRingBuffer = PBD::new_debug_bit ("midiringbuffer");
|
||||||
PBD::DebugBits PBD::DEBUG::SnapBBT = PBD::new_debug_bit ("snapbbt");
|
PBD::DebugBits PBD::DEBUG::SnapBBT = PBD::new_debug_bit ("snapbbt");
|
||||||
PBD::DebugBits PBD::DEBUG::Latency = PBD::new_debug_bit ("latency");
|
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::LTC = PBD::new_debug_bit ("ltc");
|
||||||
PBD::DebugBits PBD::DEBUG::TXLTC = PBD::new_debug_bit ("tx-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::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::Slave = PBD::new_debug_bit ("slave");
|
||||||
PBD::DebugBits PBD::DEBUG::SessionEvents = PBD::new_debug_bit ("sessionevents");
|
PBD::DebugBits PBD::DEBUG::SessionEvents = PBD::new_debug_bit ("sessionevents");
|
||||||
PBD::DebugBits PBD::DEBUG::MidiIO = PBD::new_debug_bit ("midiio");
|
PBD::DebugBits PBD::DEBUG::MidiIO = PBD::new_debug_bit ("midiio");
|
||||||
|
|
|
@ -51,7 +51,7 @@ Sample* DiskReader::_sum_buffer = 0;
|
||||||
Sample* DiskReader::_mixdown_buffer = 0;
|
Sample* DiskReader::_mixdown_buffer = 0;
|
||||||
gain_t* DiskReader::_gain_buffer = 0;
|
gain_t* DiskReader::_gain_buffer = 0;
|
||||||
samplecnt_t DiskReader::midi_readahead = 4096;
|
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)
|
DiskReader::DiskReader (Session& s, string const & str, DiskIOProcessor::Flag f)
|
||||||
: DiskIOProcessor (s, str, f)
|
: DiskIOProcessor (s, str, f)
|
||||||
|
@ -74,8 +74,8 @@ void
|
||||||
DiskReader::ReaderChannelInfo::resize (samplecnt_t bufsize)
|
DiskReader::ReaderChannelInfo::resize (samplecnt_t bufsize)
|
||||||
{
|
{
|
||||||
delete rbuf;
|
delete rbuf;
|
||||||
/* touch memory to lock it */
|
|
||||||
rbuf = new PlaybackBuffer<Sample> (bufsize);
|
rbuf = new PlaybackBuffer<Sample> (bufsize);
|
||||||
|
/* touch memory to lock it */
|
||||||
memset (rbuf->buffer(), 0, sizeof (Sample) * rbuf->bufsize());
|
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 ()) {
|
if (!_session.cfg ()->get_use_transport_fades ()) {
|
||||||
_declick_amp.set_gain (target_gain);
|
_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
|
/* no channels, or stopped. Don't accidentally pass any data
|
||||||
* from disk into our outputs (e.g. via interpolation)
|
* 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)) {
|
if (can_internal_playback_seek (start_sample - playback_sample)) {
|
||||||
internal_playback_seek (start_sample - playback_sample);
|
internal_playback_seek (start_sample - playback_sample);
|
||||||
} else {
|
} 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?
|
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);
|
const samplecnt_t total = chaninfo->rbuf->read (disk_buf.data(), disk_samples_to_consume);
|
||||||
if (disk_samples_to_consume > total) {
|
if (disk_samples_to_consume > total) {
|
||||||
cerr << _name << " Need " << disk_samples_to_consume << " total = " << total << endl;
|
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 data handling */
|
||||||
|
|
||||||
midi:
|
midi:
|
||||||
if (/*!_session.declick_out_pending() && */ bufs.count().n_midi() && _midi_buf) {
|
if (!declick_in_progress() && bufs.count().n_midi() && _midi_buf) {
|
||||||
MidiBuffer* dst;
|
MidiBuffer* dst;
|
||||||
|
|
||||||
if (_no_disk_output) {
|
if (_no_disk_output) {
|
||||||
|
@ -482,10 +483,8 @@ DiskReader::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
DiskReader::declick_in_progress () const {
|
DiskReader::declick_in_progress () const
|
||||||
/* TODO use an atomic-get.
|
{
|
||||||
* this may be called from the butler thread
|
|
||||||
*/
|
|
||||||
return _declick_amp.gain() != 0; // declick-out
|
return _declick_amp.gain() != 0; // declick-out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -544,8 +543,6 @@ DiskReader::overwrite_existing_buffers ()
|
||||||
samplepos_t start = overwrite_sample;
|
samplepos_t start = overwrite_sample;
|
||||||
samplecnt_t to_read = size;
|
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)) {
|
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;
|
error << string_compose(_("DiskReader %1: when refilling, cannot read %2 from playlist at sample %3"), id(), size, overwrite_sample) << endmsg;
|
||||||
goto midi;
|
goto midi;
|
||||||
|
@ -1167,7 +1164,7 @@ DiskReader::get_midi_playback (MidiBuffer& dst, samplepos_t start_sample, sample
|
||||||
|
|
||||||
Location* loc = _loop_location;
|
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,
|
"%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,
|
_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()));
|
(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);
|
Evoral::Range<samplepos_t> loop_range (loc->start(), loc->end() - 1);
|
||||||
effective_start = loop_range.squish (start_sample);
|
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()) {
|
if (effective_start == loc->start()) {
|
||||||
/* We need to turn off notes that may extend
|
/* 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;
|
first = loc->end() - effective_start;
|
||||||
second = nframes - first;
|
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));
|
effective_start, loc->end(), first, second));
|
||||||
|
|
||||||
if (first) {
|
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));
|
effective_start, first));
|
||||||
events_read = _midi_buf->read (*target, effective_start, first);
|
events_read = _midi_buf->read (*target, effective_start, first);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (second) {
|
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));
|
loc->start(), second));
|
||||||
events_read += _midi_buf->read (*target, loc->start(), second);
|
events_read += _midi_buf->read (*target, loc->start(), second);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} 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));
|
effective_start, nframes));
|
||||||
events_read = _midi_buf->read (*target, effective_start, 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) {
|
if (n_skipped > 0) {
|
||||||
warning << string_compose(_("MidiDiskstream %1: skipped %2 events, possible underflow"), id(), n_skipped) << endmsg;
|
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 ());
|
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",
|
"%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,
|
_name, events_read, playback_sample, playback_sample + nframes,
|
||||||
_midi_buf->read_space(), _midi_buf->write_space(),
|
_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);
|
g_atomic_int_add (&_samples_read_from_ringbuffer, nframes);
|
||||||
|
|
||||||
if (ms & MonitoringInput) {
|
if (!_no_disk_output && (ms & MonitoringInput)) {
|
||||||
dst.merge_from (*target, nframes);
|
dst.merge_from (*target, nframes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1285,7 +1282,7 @@ DiskReader::midi_read (samplepos_t& start, samplecnt_t dur, bool reversed)
|
||||||
|
|
||||||
assert(_midi_buf);
|
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);
|
boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack>(_route);
|
||||||
MidiChannelFilter* filter = mt ? &mt->playback_filter() : 0;
|
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);
|
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) {
|
if (midi_playlist()->read (*_midi_buf, effective_start, this_read, loop_range, 0, filter) != this_read) {
|
||||||
error << string_compose(
|
error << string_compose(
|
||||||
|
@ -1419,16 +1416,24 @@ DiskReader::refill_midi ()
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
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
|
/* this is called unconditionally when things happen that ought to end
|
||||||
disk readers are invoked. We use it when the session needs the
|
a period of "no disk output". It's OK for that to happen when there
|
||||||
transport (and thus effective read position for DiskReaders) to keep
|
was no corresponding call to ::inc_no_disk_output(), but we must
|
||||||
advancing as part of syncing up with a transport master, but we
|
stop the value from becoming negative.
|
||||||
don't want any actual disk output yet because we are still not
|
|
||||||
synced.
|
|
||||||
*/
|
*/
|
||||||
_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)
|
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;
|
uint32_t offset = 0;
|
||||||
while (remain > 0) {
|
while (remain > 0) {
|
||||||
uint32_t n_proc = remain > max_nproc ? max_nproc : remain;
|
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) {
|
for (uint32_t i = 0; i < n_proc; ++i) {
|
||||||
buffer[offset + i] *= g;
|
buffer[offset + i] *= g;
|
||||||
}
|
}
|
||||||
|
|
|
@ -448,7 +448,6 @@ setup_enum_writer ()
|
||||||
REGISTER_CLASS_ENUM (SessionEvent, SetTimecodeTransmission);
|
REGISTER_CLASS_ENUM (SessionEvent, SetTimecodeTransmission);
|
||||||
REGISTER_CLASS_ENUM (SessionEvent, Skip);
|
REGISTER_CLASS_ENUM (SessionEvent, Skip);
|
||||||
REGISTER_CLASS_ENUM (SessionEvent, SetTransportMaster);
|
REGISTER_CLASS_ENUM (SessionEvent, SetTransportMaster);
|
||||||
REGISTER_CLASS_ENUM (SessionEvent, StopOnce);
|
|
||||||
REGISTER_CLASS_ENUM (SessionEvent, AutoLoop);
|
REGISTER_CLASS_ENUM (SessionEvent, AutoLoop);
|
||||||
REGISTER (_SessionEvent_Type);
|
REGISTER (_SessionEvent_Type);
|
||||||
|
|
||||||
|
|
|
@ -66,11 +66,11 @@ ExportStatus::abort (bool error_occurred)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ExportStatus::finish ()
|
ExportStatus::finish (TransportRequestSource trs)
|
||||||
{
|
{
|
||||||
Glib::Threads::Mutex::Lock l (_run_lock);
|
Glib::Threads::Mutex::Lock l (_run_lock);
|
||||||
set_running (false);
|
set_running (false);
|
||||||
Finished(); /* EMIT SIGNAL */
|
Finished (trs); /* EMIT SIGNAL */
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace ARDOUR
|
} // namespace ARDOUR
|
||||||
|
|
|
@ -574,6 +574,7 @@ ARDOUR::init (bool use_windows_vst, bool try_optimization, const char* localedir
|
||||||
PannerManager::instance().discover_panners();
|
PannerManager::instance().discover_panners();
|
||||||
|
|
||||||
ARDOUR::AudioEngine::create ();
|
ARDOUR::AudioEngine::create ();
|
||||||
|
TransportMasterManager::create ();
|
||||||
|
|
||||||
/* it is unfortunate that we need to include reserved names here that
|
/* 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
|
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 */
|
/* find plugins */
|
||||||
|
|
||||||
ARDOUR::PluginManager::instance().refresh (!Config->get_discover_vst_on_start());
|
ARDOUR::PluginManager::instance().refresh (!Config->get_discover_vst_on_start());
|
||||||
}
|
|
||||||
|
|
||||||
if (start_cnt == 0) {
|
|
||||||
|
|
||||||
if ((node = Config->control_protocol_state()) != 0) {
|
if ((node = Config->control_protocol_state()) != 0) {
|
||||||
ControlProtocolManager::instance().set_state (*node, 0 /* here: global-config state */);
|
ControlProtocolManager::instance().set_state (*node, 0 /* here: global-config state */);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start_cnt > 0) {
|
||||||
TransportMasterManager::instance().restart ();
|
TransportMasterManager::instance().restart ();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,10 +64,6 @@ LTC_TransportMaster::LTC_TransportMaster (std::string const & name)
|
||||||
, a3e_timecode (Timecode::timecode_24)
|
, a3e_timecode (Timecode::timecode_24)
|
||||||
, samples_per_timecode_frame (0)
|
, 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()));
|
DEBUG_TRACE (DEBUG::Slave, string_compose ("LTC registered %1\n", _port->name()));
|
||||||
|
|
||||||
memset (&prev_frame, 0, sizeof(LTCFrameExt));
|
memset (&prev_frame, 0, sizeof(LTCFrameExt));
|
||||||
|
@ -84,6 +80,14 @@ LTC_TransportMaster::init ()
|
||||||
reset (true);
|
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
|
void
|
||||||
LTC_TransportMaster::set_session (Session *s)
|
LTC_TransportMaster::set_session (Session *s)
|
||||||
{
|
{
|
||||||
|
|
|
@ -58,9 +58,6 @@ MIDIClock_TransportMaster::MIDIClock_TransportMaster (std::string const & name,
|
||||||
, _running (false)
|
, _running (false)
|
||||||
, _bpm (0)
|
, _bpm (0)
|
||||||
{
|
{
|
||||||
if ((_port = create_midi_port (string_compose ("%1 in", name))) == 0) {
|
|
||||||
throw failed_constructor();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MIDIClock_TransportMaster::~MIDIClock_TransportMaster()
|
MIDIClock_TransportMaster::~MIDIClock_TransportMaster()
|
||||||
|
@ -75,6 +72,14 @@ MIDIClock_TransportMaster::init ()
|
||||||
current.reset ();
|
current.reset ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MIDIClock_TransportMaster::create_port ()
|
||||||
|
{
|
||||||
|
if ((_port = create_midi_port (string_compose ("%1 in", _name))) == 0) {
|
||||||
|
throw failed_constructor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MIDIClock_TransportMaster::set_session (Session *session)
|
MIDIClock_TransportMaster::set_session (Session *session)
|
||||||
{
|
{
|
||||||
|
|
|
@ -68,10 +68,6 @@ MTC_TransportMaster::MTC_TransportMaster (std::string const & name)
|
||||||
, busy_guard2 (0)
|
, busy_guard2 (0)
|
||||||
, printed_timecode_warning (false)
|
, 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()));
|
DEBUG_TRACE (DEBUG::Slave, string_compose ("MTC registered %1\n", _port->name()));
|
||||||
|
|
||||||
init ();
|
init ();
|
||||||
|
@ -103,6 +99,14 @@ MTC_TransportMaster::init ()
|
||||||
reset (true);
|
reset (true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MTC_TransportMaster::create_port ()
|
||||||
|
{
|
||||||
|
if ((_port = create_midi_port (string_compose ("%1 in", _name))) == 0) {
|
||||||
|
throw failed_constructor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MTC_TransportMaster::set_session (Session *s)
|
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);
|
notify_region_end_trimmed (region);
|
||||||
} else if (what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
|
} else if (what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
|
||||||
notify_region_start_trimmed (region);
|
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
|
/* 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
|
/*static*/ void
|
||||||
Port::set_speed_ratio (double s) {
|
Port::set_speed_ratio (double s) {
|
||||||
/* see VMResampler::set_rratio() for min/max range */
|
/* 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
|
/*static*/ void
|
||||||
|
|
|
@ -51,8 +51,7 @@ RTTaskList::drop_threads ()
|
||||||
for (uint32_t i = 0; i < nt; ++i) {
|
for (uint32_t i = 0; i < nt; ++i) {
|
||||||
_task_run_sem.signal ();
|
_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);
|
pthread_join (*i, NULL);
|
||||||
}
|
}
|
||||||
_threads.clear ();
|
_threads.clear ();
|
||||||
|
@ -122,7 +121,7 @@ RTTaskList::run ()
|
||||||
|
|
||||||
boost::function<void ()> to_run;
|
boost::function<void ()> to_run;
|
||||||
tm.acquire ();
|
tm.acquire ();
|
||||||
if (_tasklist.size () > 0) {
|
if (!_tasklist.empty ()) {
|
||||||
to_run = _tasklist.front();
|
to_run = _tasklist.front();
|
||||||
_tasklist.pop_front ();
|
_tasklist.pop_front ();
|
||||||
}
|
}
|
||||||
|
@ -145,20 +144,29 @@ void
|
||||||
RTTaskList::process (TaskList const& tl)
|
RTTaskList::process (TaskList const& tl)
|
||||||
{
|
{
|
||||||
Glib::Threads::Mutex::Lock pm (_process_mutex);
|
Glib::Threads::Mutex::Lock pm (_process_mutex);
|
||||||
|
Glib::Threads::Mutex::Lock tm (_tasklist_mutex, Glib::Threads::NOT_LOCK);
|
||||||
|
|
||||||
|
tm.acquire ();
|
||||||
_tasklist = tl;
|
_tasklist = tl;
|
||||||
|
tm.release ();
|
||||||
|
|
||||||
process_tasklist ();
|
process_tasklist ();
|
||||||
|
|
||||||
|
tm.acquire ();
|
||||||
_tasklist.clear ();
|
_tasklist.clear ();
|
||||||
|
tm.release ();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
RTTaskList::process_tasklist ()
|
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) {
|
for (TaskList::iterator i = _tasklist.begin (); i != _tasklist.end(); ++i) {
|
||||||
(*i)();
|
(*i)();
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
// }
|
||||||
|
|
||||||
uint32_t nt = std::min (_threads.size (), _tasklist.size ());
|
uint32_t nt = std::min (_threads.size (), _tasklist.size ());
|
||||||
|
|
||||||
|
|
|
@ -49,6 +49,7 @@
|
||||||
#include "pbd/convert.h"
|
#include "pbd/convert.h"
|
||||||
#include "pbd/error.h"
|
#include "pbd/error.h"
|
||||||
#include "pbd/file_utils.h"
|
#include "pbd/file_utils.h"
|
||||||
|
#include "pbd/i18n.h"
|
||||||
#include "pbd/md5.h"
|
#include "pbd/md5.h"
|
||||||
#include "pbd/pthread_utils.h"
|
#include "pbd/pthread_utils.h"
|
||||||
#include "pbd/search_path.h"
|
#include "pbd/search_path.h"
|
||||||
|
@ -116,7 +117,9 @@
|
||||||
#include "ardour/speakers.h"
|
#include "ardour/speakers.h"
|
||||||
#include "ardour/tempo.h"
|
#include "ardour/tempo.h"
|
||||||
#include "ardour/ticker.h"
|
#include "ardour/ticker.h"
|
||||||
|
#include "ardour/transport_fsm.h"
|
||||||
#include "ardour/transport_master.h"
|
#include "ardour/transport_master.h"
|
||||||
|
#include "ardour/transport_master_manager.h"
|
||||||
#include "ardour/track.h"
|
#include "ardour/track.h"
|
||||||
#include "ardour/types_convert.h"
|
#include "ardour/types_convert.h"
|
||||||
#include "ardour/user_bundle.h"
|
#include "ardour/user_bundle.h"
|
||||||
|
@ -129,8 +132,6 @@
|
||||||
|
|
||||||
#include "LuaBridge/LuaBridge.h"
|
#include "LuaBridge/LuaBridge.h"
|
||||||
|
|
||||||
#include "pbd/i18n.h"
|
|
||||||
|
|
||||||
#include <glibmm/checksum.h>
|
#include <glibmm/checksum.h>
|
||||||
|
|
||||||
namespace ARDOUR {
|
namespace ARDOUR {
|
||||||
|
@ -189,7 +190,6 @@ Session::Session (AudioEngine &eng,
|
||||||
, _base_sample_rate (0)
|
, _base_sample_rate (0)
|
||||||
, _nominal_sample_rate (0)
|
, _nominal_sample_rate (0)
|
||||||
, _current_sample_rate (0)
|
, _current_sample_rate (0)
|
||||||
, transport_sub_state (0)
|
|
||||||
, _record_status (Disabled)
|
, _record_status (Disabled)
|
||||||
, _transport_sample (0)
|
, _transport_sample (0)
|
||||||
, _seek_counter (0)
|
, _seek_counter (0)
|
||||||
|
@ -251,6 +251,7 @@ Session::Session (AudioEngine &eng,
|
||||||
, lua (lua_newstate (&PBD::ReallocPool::lalloc, &_mempool))
|
, lua (lua_newstate (&PBD::ReallocPool::lalloc, &_mempool))
|
||||||
, _n_lua_scripts (0)
|
, _n_lua_scripts (0)
|
||||||
, _butler (new Butler (*this))
|
, _butler (new Butler (*this))
|
||||||
|
, _transport_fsm (TransportFSM::create (*this))
|
||||||
, _post_transport_work (0)
|
, _post_transport_work (0)
|
||||||
, _locations (new Locations (*this))
|
, _locations (new Locations (*this))
|
||||||
, _ignore_skips_updates (false)
|
, _ignore_skips_updates (false)
|
||||||
|
@ -606,9 +607,13 @@ Session::immediately_post_engine ()
|
||||||
_process_graph.reset (new Graph (*this));
|
_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()) {
|
if (synced_to_engine()) {
|
||||||
_engine.transport_stop ();
|
_engine.transport_stop ();
|
||||||
|
@ -848,7 +853,6 @@ Session::destroy ()
|
||||||
case SessionEvent::Skip:
|
case SessionEvent::Skip:
|
||||||
case SessionEvent::PunchIn:
|
case SessionEvent::PunchIn:
|
||||||
case SessionEvent::PunchOut:
|
case SessionEvent::PunchOut:
|
||||||
case SessionEvent::StopOnce:
|
|
||||||
case SessionEvent::RangeStop:
|
case SessionEvent::RangeStop:
|
||||||
case SessionEvent::RangeLocate:
|
case SessionEvent::RangeLocate:
|
||||||
remove = false;
|
remove = false;
|
||||||
|
@ -880,6 +884,9 @@ Session::destroy ()
|
||||||
delete _selection;
|
delete _selection;
|
||||||
_selection = 0;
|
_selection = 0;
|
||||||
|
|
||||||
|
_transport_fsm->backend()->stop ();
|
||||||
|
_transport_fsm.reset ();
|
||||||
|
|
||||||
DEBUG_TRACE (DEBUG::Destruction, "Session::destroy() done\n");
|
DEBUG_TRACE (DEBUG::Destruction, "Session::destroy() done\n");
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
|
@ -1584,6 +1591,7 @@ Session::hookup_io ()
|
||||||
*/
|
*/
|
||||||
|
|
||||||
AudioEngine::instance()->reconnect_ports ();
|
AudioEngine::instance()->reconnect_ports ();
|
||||||
|
TransportMasterManager::instance().reconnect_ports ();
|
||||||
|
|
||||||
/* Anyone who cares about input state, wake up and do something */
|
/* Anyone who cares about input state, wake up and do something */
|
||||||
|
|
||||||
|
@ -2015,7 +2023,7 @@ void
|
||||||
Session::_locations_changed (const Locations::LocationList& locations)
|
Session::_locations_changed (const Locations::LocationList& locations)
|
||||||
{
|
{
|
||||||
/* There was some mass-change in the Locations object.
|
/* There was some mass-change in the Locations object.
|
||||||
*
|
*
|
||||||
* We might be re-adding a location here but it doesn't actually matter
|
* 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.
|
* for all the locations that the Session takes an interest in.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#include "pbd/stacktrace.h"
|
#include "pbd/stacktrace.h"
|
||||||
|
|
||||||
#include "ardour/butler.h"
|
#include "ardour/butler.h"
|
||||||
|
#include "ardour/disk_reader.h"
|
||||||
#include "ardour/route.h"
|
#include "ardour/route.h"
|
||||||
#include "ardour/session.h"
|
#include "ardour/session.h"
|
||||||
#include "ardour/session_event.h"
|
#include "ardour/session_event.h"
|
||||||
|
@ -61,6 +62,7 @@ void
|
||||||
Session::schedule_playback_buffering_adjustment ()
|
Session::schedule_playback_buffering_adjustment ()
|
||||||
{
|
{
|
||||||
add_post_transport_work (PostTransportAdjustPlaybackBuffering);
|
add_post_transport_work (PostTransportAdjustPlaybackBuffering);
|
||||||
|
DiskReader::inc_no_disk_output ();
|
||||||
_butler->schedule_transport_work ();
|
_butler->schedule_transport_work ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,6 +110,7 @@ Session::overwrite_some_buffers (Track* t)
|
||||||
}
|
}
|
||||||
|
|
||||||
add_post_transport_work (PostTransportOverWrite);
|
add_post_transport_work (PostTransportOverWrite);
|
||||||
|
|
||||||
_butler->schedule_transport_work ();
|
_butler->schedule_transport_work ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -225,7 +225,6 @@ SessionEventManager::merge_event (SessionEvent* ev)
|
||||||
|
|
||||||
switch (ev->type) {
|
switch (ev->type) {
|
||||||
case SessionEvent::AutoLoop:
|
case SessionEvent::AutoLoop:
|
||||||
case SessionEvent::StopOnce:
|
|
||||||
_clear_event_type (ev->type);
|
_clear_event_type (ev->type);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -95,7 +95,7 @@ Session::pre_export ()
|
||||||
|
|
||||||
_exporting = true;
|
_exporting = true;
|
||||||
export_status->set_running (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 */
|
/* 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
|
for the sake of any GUI, put the _transport_sample in
|
||||||
the right place too.
|
the right place too.
|
||||||
*/
|
*/
|
||||||
|
@ -256,7 +256,7 @@ Session::process_export_fw (pframes_t nframes)
|
||||||
set_transport_speed (1.0, 0, false);
|
set_transport_speed (1.0, 0, false);
|
||||||
butler_transport_work ();
|
butler_transport_work ();
|
||||||
g_atomic_int_set (&_butler->should_do_transport_work, 0);
|
g_atomic_int_set (&_butler->should_do_transport_work, 0);
|
||||||
post_transport ();
|
butler_completed_transport_work ();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -299,7 +299,7 @@ int
|
||||||
Session::stop_audio_export ()
|
Session::stop_audio_export ()
|
||||||
{
|
{
|
||||||
/* can't use stop_transport() here because we need
|
/* 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.
|
stuff that stop_transport() implements.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -311,8 +311,14 @@ Session::stop_audio_export ()
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
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;
|
_exporting = false;
|
||||||
|
|
||||||
if (_export_rolling) {
|
if (_export_rolling) {
|
||||||
|
@ -340,6 +346,6 @@ Session::finalize_audio_export ()
|
||||||
if (post_export_sync) {
|
if (post_export_sync) {
|
||||||
config.set_external_sync (true);
|
config.set_external_sync (true);
|
||||||
} else {
|
} else {
|
||||||
locate (post_export_position, false, false, false, false, false);
|
request_locate (post_export_position, false, trs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,9 +26,9 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include <boost/msm/back/state_machine.hpp>
|
#include <boost/algorithm/string/erase.hpp>
|
||||||
#include <boost/msm/front/state_machine_def.hpp>
|
|
||||||
|
|
||||||
|
#include "pbd/i18n.h"
|
||||||
#include "pbd/error.h"
|
#include "pbd/error.h"
|
||||||
#include "pbd/enumwriter.h"
|
#include "pbd/enumwriter.h"
|
||||||
|
|
||||||
|
@ -45,6 +45,7 @@
|
||||||
#include "ardour/process_thread.h"
|
#include "ardour/process_thread.h"
|
||||||
#include "ardour/scene_changer.h"
|
#include "ardour/scene_changer.h"
|
||||||
#include "ardour/session.h"
|
#include "ardour/session.h"
|
||||||
|
#include "ardour/transport_fsm.h"
|
||||||
#include "ardour/transport_master.h"
|
#include "ardour/transport_master.h"
|
||||||
#include "ardour/transport_master_manager.h"
|
#include "ardour/transport_master_manager.h"
|
||||||
#include "ardour/ticker.h"
|
#include "ardour/ticker.h"
|
||||||
|
@ -54,95 +55,11 @@
|
||||||
|
|
||||||
#include "midi++/mmc.h"
|
#include "midi++/mmc.h"
|
||||||
|
|
||||||
#include "pbd/i18n.h"
|
|
||||||
|
|
||||||
using namespace ARDOUR;
|
using namespace ARDOUR;
|
||||||
using namespace PBD;
|
using namespace PBD;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
/* state machine */
|
#define TFSM_EVENT(ev) { DEBUG_TRACE (DEBUG::TFSMEvents, string_compose ("TFSM(%1)\n", typeid(ev).name())); _transport_fsm->enqueue (ev); }
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Called by the audio engine when there is work to be done with JACK.
|
/** Called by the audio engine when there is work to be done with JACK.
|
||||||
* @param nframes Number of samples to process.
|
* @param nframes Number of samples to process.
|
||||||
|
@ -161,8 +78,12 @@ Session::process (pframes_t nframes)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (non_realtime_work_pending()) {
|
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 ()) {
|
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
|
* callig it hold a _processor_lock reader-lock
|
||||||
*/
|
*/
|
||||||
boost::shared_ptr<RouteList> r = routes.reader ();
|
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) {
|
for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
|
||||||
if ((*i)->apply_processor_changes_rt()) {
|
if ((*i)->apply_processor_changes_rt()) {
|
||||||
_rt_emit_pending = true;
|
_rt_emit_pending = true;
|
||||||
}
|
}
|
||||||
|
if ((*i)->declick_in_progress()) {
|
||||||
|
one_or_more_routes_declicking = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_rt_emit_pending) {
|
if (_rt_emit_pending) {
|
||||||
if (!_rt_thread_active) {
|
if (!_rt_thread_active) {
|
||||||
emit_route_signals ();
|
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 ();
|
_engine.main_thread()->drop_buffers ();
|
||||||
|
|
||||||
/* deliver MIDI clock. Note that we need to use the transport sample
|
/* 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);
|
(*i)->automation_run (_transport_sample, nframes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_global_locate_pending = locate_pending ();
|
||||||
|
|
||||||
if (_process_graph) {
|
if (_process_graph) {
|
||||||
DEBUG_TRACE(DEBUG::ProcessThreads,"calling graph/no-roll\n");
|
DEBUG_TRACE(DEBUG::ProcessThreads,"calling graph/no-roll\n");
|
||||||
_process_graph->routes_no_roll( nframes, _transport_sample, end_sample, non_realtime_work_pending());
|
_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);
|
(*i)->automation_run (start_sample, nframes);
|
||||||
}
|
}
|
||||||
|
|
||||||
_global_locate_pending = locate_pending ();
|
_global_locate_pending = locate_pending();
|
||||||
|
|
||||||
if (_process_graph) {
|
if (_process_graph) {
|
||||||
DEBUG_TRACE(DEBUG::ProcessThreads,"calling graph/process-routes\n");
|
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;
|
bool b = false;
|
||||||
|
|
||||||
if ((ret = (*i)->roll (nframes, start_sample, end_sample, b)) < 0) {
|
if ((ret = (*i)->roll (nframes, start_sample, end_sample, b)) < 0) {
|
||||||
stop_transport ();
|
TFSM_EVENT (TransportFSM::stop_transport (false, false));
|
||||||
return -1;
|
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);
|
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) {
|
while (_count_in_samples > 0 || _remaining_latency_preroll > 0) {
|
||||||
samplecnt_t ns;
|
samplecnt_t ns;
|
||||||
|
@ -615,10 +554,8 @@ Session::process_with_events (pframes_t nframes)
|
||||||
|
|
||||||
if (samples_moved < 0) {
|
if (samples_moved < 0) {
|
||||||
decrement_transport_position (-samples_moved);
|
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) {
|
} else if (samples_moved) {
|
||||||
increment_transport_position (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 {
|
} else {
|
||||||
DEBUG_TRACE (DEBUG::Transport, "no transport motion\n");
|
DEBUG_TRACE (DEBUG::Transport, "no transport motion\n");
|
||||||
}
|
}
|
||||||
|
@ -699,7 +636,6 @@ Session::process_without_events (pframes_t nframes)
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
samples_moved = (samplecnt_t) nframes * _transport_speed;
|
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()) {
|
if (!_exporting && !timecode_transmission_suspended()) {
|
||||||
|
@ -730,10 +666,10 @@ Session::process_without_events (pframes_t nframes)
|
||||||
|
|
||||||
if (samples_moved < 0) {
|
if (samples_moved < 0) {
|
||||||
decrement_transport_position (-samples_moved);
|
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) {
|
} else if (samples_moved) {
|
||||||
increment_transport_position (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 {
|
} else {
|
||||||
DEBUG_TRACE (DEBUG::Transport, "no transport motion\n");
|
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"
|
/* roll after locate, do not flush, set "with loop"
|
||||||
true only if we are seamless looping
|
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;
|
remove = false;
|
||||||
del = false;
|
del = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SessionEvent::Locate:
|
case SessionEvent::Locate:
|
||||||
if (ev->yes_or_no) { /* force locate */
|
/* args: do not roll after locate, do flush, not with loop, force */
|
||||||
/* args: do not roll after locate, do flush, not with loop */
|
TFSM_EVENT (TransportFSM::locate (ev->target_sample, false, true, false, ev->yes_or_no));
|
||||||
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);
|
|
||||||
}
|
|
||||||
_send_timecode_update = true;
|
_send_timecode_update = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SessionEvent::LocateRoll:
|
case SessionEvent::LocateRoll:
|
||||||
if (ev->yes_or_no) {
|
/* args: roll after locate, do flush, not with loop, force */
|
||||||
/* args: roll after locate, do flush, not with loop */
|
TFSM_EVENT (TransportFSM::locate (ev->target_sample, true, true, false, ev->yes_or_no));
|
||||||
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);
|
|
||||||
}
|
|
||||||
_send_timecode_update = true;
|
_send_timecode_update = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SessionEvent::Skip:
|
case SessionEvent::Skip:
|
||||||
if (Config->get_skip_playback()) {
|
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;
|
_send_timecode_update = true;
|
||||||
}
|
}
|
||||||
remove = false;
|
remove = false;
|
||||||
|
@ -985,26 +911,15 @@ Session::process_event (SessionEvent* ev)
|
||||||
del = false;
|
del = false;
|
||||||
break;
|
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:
|
case SessionEvent::RangeStop:
|
||||||
if (!non_realtime_work_pending()) {
|
TFSM_EVENT (TransportFSM::stop_transport (ev->yes_or_no, false));
|
||||||
stop_transport (ev->yes_or_no);
|
|
||||||
}
|
|
||||||
remove = false;
|
remove = false;
|
||||||
del = false;
|
del = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SessionEvent::RangeLocate:
|
case SessionEvent::RangeLocate:
|
||||||
/* args: roll after locate, do flush, not with loop */
|
/* 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;
|
remove = false;
|
||||||
del = false;
|
del = false;
|
||||||
break;
|
break;
|
||||||
|
@ -1179,35 +1094,53 @@ Session::follow_transport_master (pframes_t nframes)
|
||||||
|
|
||||||
slave_speed = tmm.get_current_speed_in_process_context();
|
slave_speed = tmm.get_current_speed_in_process_context();
|
||||||
slave_transport_sample = tmm.get_current_position_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;
|
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()));
|
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()) {
|
if (!actively_recording() && abs (delta) > (5 * current_block_size)) {
|
||||||
DEBUG_TRACE (DEBUG::Slave, string_compose ("current slave delta %1 greater than slave resolution %2\n", delta, tmm.current()->resolution()));
|
DiskReader::inc_no_disk_output ();
|
||||||
if (micro_locate (-delta) != 0) {
|
if (!_transport_fsm->locating()) {
|
||||||
DEBUG_TRACE (DEBUG::Slave, "micro-locate didn't work, set no disk output true\n");
|
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));
|
||||||
/* run routes as normal, but no disk output */
|
|
||||||
DiskReader::set_no_disk_output (true);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (transport_master_tracking_state == Running) {
|
if (slave_speed != 0.0) {
|
||||||
/* speed is set, we're locked, and good to go */
|
if (_transport_speed == 0.0f) {
|
||||||
DiskReader::set_no_disk_output (false);
|
DEBUG_TRACE (DEBUG::Slave, string_compose ("slave starts transport: %1 sample %2 tf %3\n", slave_speed, slave_transport_sample, _transport_sample));
|
||||||
return true;
|
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:
|
noroll:
|
||||||
/* don't move at all */
|
/* don't move at all */
|
||||||
DEBUG_TRACE (DEBUG::Slave, "no roll\n")
|
DEBUG_TRACE (DEBUG::Slave, "no roll\n")
|
||||||
|
@ -1215,81 +1148,9 @@ Session::follow_transport_master (pframes_t nframes)
|
||||||
return false;
|
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
|
void
|
||||||
Session::reset_slave_state ()
|
Session::reset_slave_state ()
|
||||||
{
|
{
|
||||||
transport_master_tracking_state = Stopped;
|
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;
|
first_file_data_format_reset = false;
|
||||||
|
|
||||||
} else if (p == "external-sync") {
|
} else if (p == "external-sync") {
|
||||||
std::cerr << "param change, rss to " << TransportMasterManager::instance().current() << std::endl;
|
|
||||||
request_sync_source (TransportMasterManager::instance().current());
|
request_sync_source (TransportMasterManager::instance().current());
|
||||||
} else if (p == "denormal-model") {
|
} else if (p == "denormal-model") {
|
||||||
setup_fpu ();
|
setup_fpu ();
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
|
|
||||||
#include "ardour/session.h"
|
#include "ardour/session.h"
|
||||||
#include "ardour/tempo.h"
|
#include "ardour/tempo.h"
|
||||||
|
#include "ardour/transport_fsm.h"
|
||||||
|
|
||||||
#include "pbd/i18n.h"
|
#include "pbd/i18n.h"
|
||||||
|
|
||||||
|
@ -42,6 +43,8 @@ using namespace std;
|
||||||
using namespace ARDOUR;
|
using namespace ARDOUR;
|
||||||
using namespace PBD;
|
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*/
|
/* BBT TIME*/
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -214,7 +217,7 @@ Session::backend_sync_callback (TransportState state, samplepos_t pos)
|
||||||
case TransportRolling:
|
case TransportRolling:
|
||||||
// cerr << "SYNC: rolling slave = " << slave << endl;
|
// cerr << "SYNC: rolling slave = " << slave << endl;
|
||||||
if (slave) {
|
if (slave) {
|
||||||
start_transport ();
|
TFSM_EVENT (TransportFSM::start_transport());
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
|
@ -32,12 +32,15 @@
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "pbd/undo.h"
|
#include <boost/algorithm/string/erase.hpp>
|
||||||
|
|
||||||
#include "pbd/error.h"
|
#include "pbd/error.h"
|
||||||
#include "pbd/enumwriter.h"
|
#include "pbd/enumwriter.h"
|
||||||
#include "pbd/pthread_utils.h"
|
#include "pbd/i18n.h"
|
||||||
#include "pbd/memento_command.h"
|
#include "pbd/memento_command.h"
|
||||||
|
#include "pbd/pthread_utils.h"
|
||||||
#include "pbd/stacktrace.h"
|
#include "pbd/stacktrace.h"
|
||||||
|
#include "pbd/undo.h"
|
||||||
|
|
||||||
#include "midi++/mmc.h"
|
#include "midi++/mmc.h"
|
||||||
#include "midi++/port.h"
|
#include "midi++/port.h"
|
||||||
|
@ -54,6 +57,7 @@
|
||||||
#include "ardour/profile.h"
|
#include "ardour/profile.h"
|
||||||
#include "ardour/scene_changer.h"
|
#include "ardour/scene_changer.h"
|
||||||
#include "ardour/session.h"
|
#include "ardour/session.h"
|
||||||
|
#include "ardour/transport_fsm.h"
|
||||||
#include "ardour/transport_master.h"
|
#include "ardour/transport_master.h"
|
||||||
#include "ardour/transport_master_manager.h"
|
#include "ardour/transport_master_manager.h"
|
||||||
#include "ardour/tempo.h"
|
#include "ardour/tempo.h"
|
||||||
|
@ -61,8 +65,6 @@
|
||||||
#include "ardour/vca.h"
|
#include "ardour/vca.h"
|
||||||
#include "ardour/vca_manager.h"
|
#include "ardour/vca_manager.h"
|
||||||
|
|
||||||
#include "pbd/i18n.h"
|
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace ARDOUR;
|
using namespace ARDOUR;
|
||||||
using namespace PBD;
|
using namespace PBD;
|
||||||
|
@ -80,8 +82,10 @@ using namespace PBD;
|
||||||
#endif
|
#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
|
void
|
||||||
|
@ -89,14 +93,13 @@ Session::realtime_stop (bool abort, bool clear_state)
|
||||||
{
|
{
|
||||||
ENSURE_PROCESS_THREAD;
|
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);
|
PostTransportWork todo = PostTransportWork (0);
|
||||||
|
|
||||||
/* assume that when we start, we'll be moving forwards */
|
if (_last_transport_speed < 0.0f) {
|
||||||
|
|
||||||
if (_transport_speed < 0.0f) {
|
|
||||||
todo = (PostTransportWork (todo | PostTransportStop | PostTransportReverse));
|
todo = (PostTransportWork (todo | PostTransportStop | PostTransportReverse));
|
||||||
_default_transport_speed = 1.0;
|
_default_transport_speed = 1.0;
|
||||||
|
DiskReader::inc_no_disk_output (); // for the buffer reversal
|
||||||
} else {
|
} else {
|
||||||
todo = PostTransportWork (todo | PostTransportStop);
|
todo = PostTransportWork (todo | PostTransportStop);
|
||||||
}
|
}
|
||||||
|
@ -127,7 +130,6 @@ Session::realtime_stop (bool abort, bool clear_state)
|
||||||
add_post_transport_work (todo);
|
add_post_transport_work (todo);
|
||||||
}
|
}
|
||||||
|
|
||||||
_clear_event_type (SessionEvent::StopOnce);
|
|
||||||
_clear_event_type (SessionEvent::RangeStop);
|
_clear_event_type (SessionEvent::RangeStop);
|
||||||
_clear_event_type (SessionEvent::RangeLocate);
|
_clear_event_type (SessionEvent::RangeLocate);
|
||||||
|
|
||||||
|
@ -156,22 +158,13 @@ Session::realtime_stop (bool abort, bool clear_state)
|
||||||
waiting_for_sync_offset = true;
|
waiting_for_sync_offset = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
transport_sub_state = 0;
|
if (todo) {
|
||||||
}
|
TFSM_EVENT (TransportFSM::butler_required());
|
||||||
|
|
||||||
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 ();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
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;
|
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
|
will use the incorrect _transport_sample and report an old
|
||||||
and incorrect time to Jack transport
|
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
|
/* 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 {
|
} 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 */
|
/** @param with_mmc true to send a MMC locate command when the locate is done */
|
||||||
void
|
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;
|
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);
|
set_transport_speed (1.0, 0, false);
|
||||||
}
|
}
|
||||||
loop_changing = false;
|
loop_changing = false;
|
||||||
|
TFSM_EVENT (TransportFSM::locate_done());
|
||||||
Located (); /* EMIT SIGNAL */
|
Located (); /* EMIT SIGNAL */
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
cerr << "... now doing the actual locate to " << target_sample << " from " << _transport_sample << endl;
|
|
||||||
|
|
||||||
// Update Timecode time
|
// Update Timecode time
|
||||||
_transport_sample = target_sample;
|
_transport_sample = target_sample;
|
||||||
// Bump seek counter so that any in-process locate in the butler
|
// 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
|
* we are rolling AND
|
||||||
* no autoplay in effect AND
|
* no autoplay in effect AND
|
||||||
* we're not going to keep rolling after the locate 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();
|
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()))) {
|
(!Profile->get_trx() || !(config.get_external_sync() && !synced_to_engine()))) {
|
||||||
|
|
||||||
realtime_stop (false, true); // XXX paul - check if the 2nd arg is really correct
|
realtime_stop (false, true); // XXX paul - check if the 2nd arg is really correct
|
||||||
transport_was_stopped = true;
|
transport_was_stopped = true;
|
||||||
|
|
||||||
} else {
|
} 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) {
|
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) {
|
if (need_butler) {
|
||||||
_butler->schedule_transport_work ();
|
TFSM_EVENT (TransportFSM::butler_required());
|
||||||
|
} else {
|
||||||
|
TFSM_EVENT (TransportFSM::locate_done());
|
||||||
}
|
}
|
||||||
|
|
||||||
loop_changing = false;
|
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.
|
/** Set the transport speed.
|
||||||
* Called from the process thread.
|
* Called from the process thread.
|
||||||
* @param speed New speed
|
* @param speed New speed
|
||||||
|
@ -468,7 +484,7 @@ Session::set_transport_speed (double speed, samplepos_t destination_sample, bool
|
||||||
_requested_return_sample = destination_sample;
|
_requested_return_sample = destination_sample;
|
||||||
}
|
}
|
||||||
|
|
||||||
stop_transport (abort);
|
TFSM_EVENT (TransportFSM::stop_transport (abort, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (transport_stopped() && speed == 1.0) {
|
} 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 ();
|
_engine.transport_start ();
|
||||||
_count_in_once = false;
|
_count_in_once = false;
|
||||||
} else {
|
} else {
|
||||||
start_transport ();
|
TFSM_EVENT (TransportFSM::start_transport());
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} 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)) {
|
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);
|
todo = PostTransportWork (todo | PostTransportReverse);
|
||||||
|
DiskReader::inc_no_disk_output (); // for the buffer reversal
|
||||||
_last_roll_or_reversal_location = _transport_sample;
|
_last_roll_or_reversal_location = _transport_sample;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -559,7 +576,7 @@ Session::set_transport_speed (double speed, samplepos_t destination_sample, bool
|
||||||
|
|
||||||
if (todo) {
|
if (todo) {
|
||||||
add_post_transport_work (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));
|
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;
|
ENSURE_PROCESS_THREAD;
|
||||||
|
|
||||||
_count_in_once = false;
|
_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);
|
realtime_stop (abort, clear_state);
|
||||||
_butler->schedule_transport_work ();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Called from the process thread */
|
/** Called from the process thread */
|
||||||
|
@ -705,15 +718,26 @@ Session::start_transport ()
|
||||||
TransportStateChange (); /* EMIT SIGNAL */
|
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
|
/** 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
|
void
|
||||||
Session::post_transport ()
|
Session::butler_completed_transport_work ()
|
||||||
{
|
{
|
||||||
ENSURE_PROCESS_THREAD;
|
ENSURE_PROCESS_THREAD;
|
||||||
PostTransportWork ptw = post_transport_work ();
|
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 (ptw & PostTransportAudition) {
|
||||||
if (auditioner && auditioner->auditioning()) {
|
if (auditioner && auditioner->auditioning()) {
|
||||||
process_function = &Session::process_audition;
|
process_function = &Session::process_audition;
|
||||||
|
@ -722,19 +746,14 @@ Session::post_transport ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ptw & PostTransportStop) {
|
if (ptw & PostTransportLocate) {
|
||||||
|
post_locate ();
|
||||||
transport_sub_state = 0;
|
TFSM_EVENT (TransportFSM::locate_done());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ptw & PostTransportLocate) {
|
if (ptw & PostTransportAdjustPlaybackBuffering) {
|
||||||
|
/* we blocked output while this happened */
|
||||||
if (((!config.get_external_sync() && (auto_play_legal && config.get_auto_play())) && !_exporting) || (ptw & PostTransportRoll)) {
|
DiskReader::dec_no_disk_output ();
|
||||||
_count_in_once = false;
|
|
||||||
start_transport ();
|
|
||||||
} else {
|
|
||||||
transport_sub_state = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
set_next_event ();
|
set_next_event ();
|
||||||
|
@ -742,6 +761,20 @@ Session::post_transport ()
|
||||||
know were handled ?
|
know were handled ?
|
||||||
*/
|
*/
|
||||||
set_post_transport_work (PostTransportWork (0));
|
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
|
bool
|
||||||
|
@ -752,7 +785,7 @@ Session::maybe_stop (samplepos_t limit)
|
||||||
if (synced_to_engine () && config.get_jack_time_master ()) {
|
if (synced_to_engine () && config.get_jack_time_master ()) {
|
||||||
_engine.transport_stop ();
|
_engine.transport_stop ();
|
||||||
} else if (!synced_to_engine ()) {
|
} else if (!synced_to_engine ()) {
|
||||||
stop_transport ();
|
TFSM_EVENT (TransportFSM::stop_transport ());
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -861,11 +894,11 @@ Session::set_play_loop (bool yn, double speed)
|
||||||
rolling, do not locate to loop start.
|
rolling, do not locate to loop start.
|
||||||
*/
|
*/
|
||||||
if (!transport_rolling() && (speed != 0.0)) {
|
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 {
|
} else {
|
||||||
if (speed != 0.0) {
|
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
|
bool
|
||||||
Session::solo_selection_active ()
|
Session::solo_selection_active ()
|
||||||
{
|
{
|
||||||
if ( _soloSelection.empty() ) {
|
if (_soloSelection.empty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
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> solo_list (new ControlList);
|
||||||
boost::shared_ptr<ControlList> unsolo_list (new ControlList);
|
boost::shared_ptr<ControlList> unsolo_list (new ControlList);
|
||||||
|
@ -1176,7 +1209,7 @@ Session::butler_transport_work ()
|
||||||
PostTransportWork ptw = post_transport_work();
|
PostTransportWork ptw = post_transport_work();
|
||||||
uint64_t before;
|
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) {
|
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
|
void
|
||||||
Session::non_realtime_locate ()
|
Session::non_realtime_locate ()
|
||||||
{
|
{
|
||||||
|
@ -1370,6 +1391,7 @@ Session::non_realtime_locate ()
|
||||||
for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
|
for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
|
||||||
(*i)->non_realtime_locate (tf);
|
(*i)->non_realtime_locate (tf);
|
||||||
if (sc != g_atomic_int_get (&_seek_counter)) {
|
if (sc != g_atomic_int_get (&_seek_counter)) {
|
||||||
|
std::cerr << "\n\nLOCATE INTERRUPTED BY LOCATE!!!\n\n";
|
||||||
goto restart;
|
goto restart;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1757,7 +1779,7 @@ Session::unset_play_loop ()
|
||||||
if (Config->get_seamless_loop()) {
|
if (Config->get_seamless_loop()) {
|
||||||
/* likely need to flush track buffers: this will locate us to wherever we are */
|
/* likely need to flush track buffers: this will locate us to wherever we are */
|
||||||
add_post_transport_work (PostTransportLocate);
|
add_post_transport_work (PostTransportLocate);
|
||||||
_butler->schedule_transport_work ();
|
TFSM_EVENT (TransportFSM::butler_required());
|
||||||
}
|
}
|
||||||
TransportStateChange (); /* EMIT SIGNAL */
|
TransportStateChange (); /* EMIT SIGNAL */
|
||||||
}
|
}
|
||||||
|
@ -1901,27 +1923,37 @@ Session::request_roll_at_and_return (samplepos_t start, samplepos_t return_to)
|
||||||
void
|
void
|
||||||
Session::engine_halted ()
|
Session::engine_halted ()
|
||||||
{
|
{
|
||||||
bool ignored;
|
|
||||||
|
|
||||||
/* there will be no more calls to process(), so
|
/* there will be no more calls to process(), so
|
||||||
we'd better clean up for ourselves, right now.
|
we'd better clean up for ourselves, right now.
|
||||||
|
|
||||||
but first, make sure the butler is out of
|
We can't queue SessionEvents because they only get
|
||||||
the picture.
|
handled from within a process callback.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (_butler) {
|
/* this just stops the FSM engine ... it doesn't change the state of
|
||||||
_butler->stop ();
|
* 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);
|
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
|
void
|
||||||
Session::xrun_recovery ()
|
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.
|
longer valid with a new slave.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
DiskReader::set_no_disk_output (false);
|
DiskReader::dec_no_disk_output ();
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
we should not be treating specific transport masters as special cases because there maybe > 1 of a particular type
|
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();
|
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
|
bool
|
||||||
Track::declick_in_progress () const
|
Track::declick_in_progress () const
|
||||||
{
|
{
|
||||||
return _disk_reader->declick_in_progress ();
|
return active() && _disk_reader->declick_in_progress ();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
|
101
libs/ardour/transport_fsm.cc
Normal file
101
libs/ardour/transport_fsm.cc
Normal file
|
@ -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)
|
, _sclock_synced (Properties::sclock_synced, false)
|
||||||
, _collect (Properties::collect, true)
|
, _collect (Properties::collect, true)
|
||||||
, _connected (Properties::connected, false)
|
, _connected (Properties::connected, false)
|
||||||
|
, port_node (X_(""))
|
||||||
{
|
{
|
||||||
register_properties ();
|
register_properties ();
|
||||||
|
|
||||||
|
@ -260,7 +261,27 @@ TransportMaster::set_state (XMLNode const & node, int /* version */)
|
||||||
XMLNode* pnode = node.child (X_("Port"));
|
XMLNode* pnode = node.child (X_("Port"));
|
||||||
|
|
||||||
if (pnode) {
|
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) {
|
for (XMLNodeList::const_iterator ci = children.begin(); ci != children.end(); ++ci) {
|
||||||
|
|
||||||
XMLProperty const *prop;
|
XMLProperty const *prop;
|
||||||
|
@ -273,10 +294,6 @@ TransportMaster::set_state (XMLNode const & node, int /* version */)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PropertyChanged (what_changed);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
XMLNode&
|
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));
|
DEBUG_TRACE (DEBUG::Slave, string_compose ("factory-construct %1 name %2 removeable %3\n", enum_2_string (type), name, removeable));
|
||||||
|
|
||||||
switch (type) {
|
try {
|
||||||
case MTC:
|
switch (type) {
|
||||||
tm.reset (new MTC_TransportMaster (name));
|
case MTC:
|
||||||
break;
|
tm.reset (new MTC_TransportMaster (name));
|
||||||
case LTC:
|
break;
|
||||||
tm.reset (new LTC_TransportMaster (name));
|
case LTC:
|
||||||
break;
|
tm.reset (new LTC_TransportMaster (name));
|
||||||
case MIDIClock:
|
break;
|
||||||
tm.reset (new MIDIClock_TransportMaster (name));
|
case MIDIClock:
|
||||||
break;
|
tm.reset (new MIDIClock_TransportMaster (name));
|
||||||
case Engine:
|
break;
|
||||||
tm.reset (new Engine_TransportMaster (*AudioEngine::instance()));
|
case Engine:
|
||||||
break;
|
tm.reset (new Engine_TransportMaster (*AudioEngine::instance()));
|
||||||
default:
|
break;
|
||||||
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) {
|
if (tm) {
|
||||||
|
@ -444,7 +467,7 @@ TransportMasterViaMIDI::create_midi_port (std::string const & port_name)
|
||||||
{
|
{
|
||||||
boost::shared_ptr<Port> p;
|
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> ();
|
return boost::shared_ptr<Port> ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -490,4 +513,3 @@ TimecodeTransportMaster::set_fr2997 (bool yn)
|
||||||
PropertyChanged (Properties::fr2997);
|
PropertyChanged (Properties::fr2997);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,16 +20,12 @@
|
||||||
#include "ardour/debug.h"
|
#include "ardour/debug.h"
|
||||||
#include "ardour/disk_reader.h"
|
#include "ardour/disk_reader.h"
|
||||||
#include "ardour/session.h"
|
#include "ardour/session.h"
|
||||||
|
#include "ardour/rc_configuration.h"
|
||||||
#include "ardour/transport_master_manager.h"
|
#include "ardour/transport_master_manager.h"
|
||||||
|
|
||||||
#include "pbd/boost_debug.cc"
|
#include "pbd/boost_debug.cc"
|
||||||
#include "pbd/i18n.h"
|
#include "pbd/i18n.h"
|
||||||
|
#include "pbd/stateful.h"
|
||||||
#if __cplusplus > 199711L
|
|
||||||
#define local_signbit(x) std::signbit (x)
|
|
||||||
#else
|
|
||||||
#define local_signbit(x) ((((__int64*)(&z))*) & 0x8000000000000000)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
using namespace ARDOUR;
|
using namespace ARDOUR;
|
||||||
using namespace PBD;
|
using namespace PBD;
|
||||||
|
@ -51,6 +47,27 @@ TransportMasterManager::~TransportMasterManager ()
|
||||||
clear ();
|
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
|
int
|
||||||
TransportMasterManager::set_default_configuration ()
|
TransportMasterManager::set_default_configuration ()
|
||||||
{
|
{
|
||||||
|
@ -106,7 +123,7 @@ TransportMasterManager::parameter_changed (std::string const & what)
|
||||||
if (what == "external-sync") {
|
if (what == "external-sync") {
|
||||||
if (!_session->config.get_external_sync()) {
|
if (!_session->config.get_external_sync()) {
|
||||||
/* disabled */
|
/* disabled */
|
||||||
DiskReader::set_no_disk_output (false);
|
DiskReader::dec_no_disk_output ();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -115,7 +132,8 @@ TransportMasterManager&
|
||||||
TransportMasterManager::instance()
|
TransportMasterManager::instance()
|
||||||
{
|
{
|
||||||
if (!_instance) {
|
if (!_instance) {
|
||||||
_instance = new TransportMasterManager();
|
fatal << string_compose (_("programming error:%1"), X_("TransportMasterManager::instance() called without an instance!")) << endmsg;
|
||||||
|
/* NOTREACHED */
|
||||||
}
|
}
|
||||||
return *_instance;
|
return *_instance;
|
||||||
}
|
}
|
||||||
|
@ -196,7 +214,7 @@ TransportMasterManager::pre_process_transport_masters (pframes_t nframes, sample
|
||||||
if (master_dll_initstate == 0) {
|
if (master_dll_initstate == 0) {
|
||||||
|
|
||||||
init_transport_master_dll (_master_speed, _master_position);
|
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");
|
DEBUG_TRACE (DEBUG::Slave, "no roll3 - still initializing master DLL\n");
|
||||||
master_dll_initstate = _master_speed > 0.0 ? 1 : -1;
|
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()) {
|
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()));
|
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 */
|
/* run routes as normal, but no disk output */
|
||||||
DiskReader::set_no_disk_output (true);
|
DiskReader::inc_no_disk_output ();
|
||||||
} else {
|
} else {
|
||||||
DiskReader::set_no_disk_output (false);
|
DiskReader::dec_no_disk_output ();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
DiskReader::set_no_disk_output (false);
|
DiskReader::dec_no_disk_output ();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* inject DLL with new data */
|
/* 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);
|
tm = TransportMaster::factory (type, name, removeable);
|
||||||
|
|
||||||
|
if (!tm) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
boost_debug_shared_ptr_mark_interesting (tm.get(), "tm");
|
boost_debug_shared_ptr_mark_interesting (tm.get(), "tm");
|
||||||
ret = add_locked (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;
|
_current_master = c;
|
||||||
_master_speed = 0;
|
_master_speed = 0;
|
||||||
_master_position = 0;
|
_master_position = 0;
|
||||||
|
@ -490,6 +517,7 @@ TransportMasterManager::clear ()
|
||||||
int
|
int
|
||||||
TransportMasterManager::set_state (XMLNode const & node, int version)
|
TransportMasterManager::set_state (XMLNode const & node, int version)
|
||||||
{
|
{
|
||||||
|
PBD::stacktrace (std::cerr, 20);
|
||||||
assert (node.name() == state_node_name);
|
assert (node.name() == state_node_name);
|
||||||
|
|
||||||
XMLNodeList const & children = node.children();
|
XMLNodeList const & children = node.children();
|
||||||
|
@ -500,11 +528,22 @@ TransportMasterManager::set_state (XMLNode const & node, int version)
|
||||||
_current_master.reset ();
|
_current_master.reset ();
|
||||||
boost_debug_list_ptrs ();
|
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) {
|
for (XMLNodeList::const_iterator c = children.begin(); c != children.end(); ++c) {
|
||||||
|
|
||||||
boost::shared_ptr<TransportMaster> tm = TransportMaster::factory (**c);
|
boost::shared_ptr<TransportMaster> tm = TransportMaster::factory (**c);
|
||||||
|
|
||||||
|
if (!tm) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
boost_debug_shared_ptr_mark_interesting (tm.get(), "tm");
|
boost_debug_shared_ptr_mark_interesting (tm.get(), "tm");
|
||||||
|
|
||||||
if (add_locked (tm)) {
|
if (add_locked (tm)) {
|
||||||
|
@ -518,8 +557,17 @@ TransportMasterManager::set_state (XMLNode const & node, int version)
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string current_master;
|
std::string current_master;
|
||||||
|
|
||||||
if (node.get_property (X_("current"), current_master)) {
|
if (node.get_property (X_("current"), current_master)) {
|
||||||
|
|
||||||
|
/* may fal if current_master is not usable */
|
||||||
|
|
||||||
set_current (current_master);
|
set_current (current_master);
|
||||||
|
|
||||||
|
if (!current()) {
|
||||||
|
set_current (MTC); // always available
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
set_current (MTC);
|
set_current (MTC);
|
||||||
}
|
}
|
||||||
|
@ -578,10 +626,14 @@ TransportMasterManager::restart ()
|
||||||
XMLNode* node;
|
XMLNode* node;
|
||||||
|
|
||||||
if ((node = Config->transport_master_state()) != 0) {
|
if ((node = Config->transport_master_state()) != 0) {
|
||||||
if (TransportMasterManager::instance().set_state (*node, Stateful::loading_state_version)) {
|
|
||||||
error << _("Cannot restore transport master manager") << endmsg;
|
Glib::Threads::RWLock::ReaderLock lm (lock);
|
||||||
/* XXX now what? */
|
|
||||||
|
for (TransportMasters::const_iterator tm = _transport_masters.begin(); tm != _transport_masters.end(); ++tm) {
|
||||||
|
(*tm)->connect_port_using_state ();
|
||||||
|
(*tm)->reset (false);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (TransportMasterManager::instance().set_default_configuration ()) {
|
if (TransportMasterManager::instance().set_default_configuration ()) {
|
||||||
error << _("Cannot initialize transport master manager") << endmsg;
|
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',
|
'track.cc',
|
||||||
'transient_detector.cc',
|
'transient_detector.cc',
|
||||||
'transform.cc',
|
'transform.cc',
|
||||||
|
'transport_fsm.cc',
|
||||||
'transport_master.cc',
|
'transport_master.cc',
|
||||||
'transport_master_manager.cc',
|
'transport_master_manager.cc',
|
||||||
'transpose.cc',
|
'transpose.cc',
|
||||||
|
|
|
@ -57,8 +57,6 @@ void
|
||||||
Transmitter::deliver ()
|
Transmitter::deliver ()
|
||||||
|
|
||||||
{
|
{
|
||||||
string foo;
|
|
||||||
|
|
||||||
/* NOTE: this is just a default action for a Transmitter or a
|
/* NOTE: this is just a default action for a Transmitter or a
|
||||||
derived class. Any class can override this to produce some
|
derived class. Any class can override this to produce some
|
||||||
other action when deliver() is called.
|
other action when deliver() is called.
|
||||||
|
@ -68,8 +66,7 @@ Transmitter::deliver ()
|
||||||
|
|
||||||
/* send the SigC++ signal */
|
/* send the SigC++ signal */
|
||||||
|
|
||||||
foo = str();
|
(*send) (channel, str().c_str());
|
||||||
(*send) (channel, foo.c_str());
|
|
||||||
|
|
||||||
/* XXX when or how can we delete this ? */
|
/* XXX when or how can we delete this ? */
|
||||||
// delete foo;
|
// delete foo;
|
||||||
|
|
|
@ -201,7 +201,7 @@ static int export_session (Session *session,
|
||||||
}
|
}
|
||||||
printf("\n");
|
printf("\n");
|
||||||
|
|
||||||
status->finish ();
|
status->finish (TRS_UI);
|
||||||
|
|
||||||
printf ("* Done.\n");
|
printf ("* Done.\n");
|
||||||
return 0;
|
return 0;
|
||||||
|
|
11
wscript
11
wscript
|
@ -383,6 +383,17 @@ int main() { return 0; }''',
|
||||||
|
|
||||||
autowaf.set_basic_compiler_flags (conf,flags_dict)
|
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:
|
if conf.options.asan:
|
||||||
conf.check_cxx(cxxflags=["-fsanitize=address", "-fno-omit-frame-pointer"], linkflags=["-fsanitize=address"])
|
conf.check_cxx(cxxflags=["-fsanitize=address", "-fno-omit-frame-pointer"], linkflags=["-fsanitize=address"])
|
||||||
cxx_flags.append('-fsanitize=address')
|
cxx_flags.append('-fsanitize=address')
|
||||||
|
|
Loading…
Reference in New Issue
Block a user