From bce3184ff5383f47675a97a8184f7739552cb04e Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Fri, 12 Oct 2012 09:45:22 +0000 Subject: skeleton framework for LTC-slave git-svn-id: svn://localhost/ardour2/branches/3.0@13256 d708f5d6-7413-0410-9779-e7cbd77b26cf --- libs/ardour/ardour/audioengine.h | 8 ++ libs/ardour/ardour/slave.h | 37 +++++++++ libs/ardour/ardour/types.h | 3 +- libs/ardour/audioengine.cc | 3 + libs/ardour/enums.cc | 1 + libs/ardour/globals.cc | 3 + libs/ardour/ltc_slave.cc | 175 +++++++++++++++++++++++++++++++++++++++ libs/ardour/session_transport.cc | 18 ++++ libs/ardour/utils.cc | 3 + libs/ardour/wscript | 6 ++ 10 files changed, 256 insertions(+), 1 deletion(-) create mode 100644 libs/ardour/ltc_slave.cc (limited to 'libs') diff --git a/libs/ardour/ardour/audioengine.h b/libs/ardour/ardour/audioengine.h index 1c0d0f1a55..4e55ea2140 100644 --- a/libs/ardour/ardour/audioengine.h +++ b/libs/ardour/ardour/audioengine.h @@ -260,6 +260,10 @@ _ the regular process() call to session->process() is not made. int create_process_thread (boost::function, pthread_t*, size_t stacksize); +#ifdef HAVE_LTC + Port *ltc_input_port() const { return _ltc_input; } +#endif + private: static AudioEngine* _instance; @@ -289,6 +293,10 @@ private: Glib::Threads::Thread* m_meter_thread; ProcessThread* _main_thread; +#ifdef HAVE_LTC + Port* _ltc_input; +#endif + SerializedRCUManager ports; boost::shared_ptr register_port (DataType type, const std::string& portname, bool input); diff --git a/libs/ardour/ardour/slave.h b/libs/ardour/ardour/slave.h index a8bf28b8ce..b64ba3f42e 100644 --- a/libs/ardour/ardour/slave.h +++ b/libs/ardour/ardour/slave.h @@ -32,6 +32,10 @@ #include "midi++/parser.h" #include "midi++/types.h" +#ifdef HAVE_LTC +#include +#endif + namespace MIDI { class Port; } @@ -293,6 +297,39 @@ class MTC_Slave : public Slave { void init_engine_dll (framepos_t, framepos_t); }; +#ifdef HAVE_LTC +class LTC_Slave : public Slave { + public: + LTC_Slave (Session&); + ~LTC_Slave (); + + bool speed_and_position (double&, framepos_t&); + + bool locked() const; + bool ok() const; + + framecnt_t resolution () const; + bool requires_seekahead () const { return true; } + framecnt_t seekahead_distance() const; + bool give_slave_full_control_over_transport_speed() const; + + private: + int parse_ltc(const jack_nframes_t nframes, const jack_default_audio_sample_t * const in, const framecnt_t posinfo); + void process_ltc(); + + Session& session; + bool did_reset_tc_format; + Timecode::TimecodeFormat saved_tc_format; + + LTCDecoder *decoder; + framecnt_t current_frames_per_ltc_frame; + framecnt_t monotonic_fcnt; + + framepos_t ltc_transport_pos; + double ltc_speed; +}; +#endif + class MIDIClock_Slave : public Slave { public: MIDIClock_Slave (Session&, MIDI::Port&, int ppqn = 24); diff --git a/libs/ardour/ardour/types.h b/libs/ardour/ardour/types.h index b84eb8e7f6..5bd03d2788 100644 --- a/libs/ardour/ardour/types.h +++ b/libs/ardour/ardour/types.h @@ -462,7 +462,8 @@ namespace ARDOUR { enum SyncSource { JACK, MTC, - MIDIClock + MIDIClock, + LTC }; enum ShuttleBehaviour { diff --git a/libs/ardour/audioengine.cc b/libs/ardour/audioengine.cc index 450bf2a296..9fb1d1d706 100644 --- a/libs/ardour/audioengine.cc +++ b/libs/ardour/audioengine.cc @@ -89,6 +89,9 @@ AudioEngine::AudioEngine (string client_name, string session_uuid) } Port::set_engine (this); +#ifdef HAVE_LTC + _ltc_input = new AudioPort ("LTC in", Port::IsInput); +#endif } AudioEngine::~AudioEngine () diff --git a/libs/ardour/enums.cc b/libs/ardour/enums.cc index 5776db21c7..6c82792328 100644 --- a/libs/ardour/enums.cc +++ b/libs/ardour/enums.cc @@ -307,6 +307,7 @@ setup_enum_writer () REGISTER_ENUM (MTC); REGISTER_ENUM (JACK); REGISTER_ENUM (MIDIClock); + REGISTER_ENUM (LTC); REGISTER (_SyncSource); REGISTER_ENUM (Sprung); diff --git a/libs/ardour/globals.cc b/libs/ardour/globals.cc index 78ed2ff472..3895bd76a7 100644 --- a/libs/ardour/globals.cc +++ b/libs/ardour/globals.cc @@ -485,6 +485,9 @@ ARDOUR::get_available_sync_options () ret.push_back (JACK); ret.push_back (MTC); ret.push_back (MIDIClock); +#ifdef HAVE_LTC + ret.push_back (LTC); +#endif return ret; } diff --git a/libs/ardour/ltc_slave.cc b/libs/ardour/ltc_slave.cc new file mode 100644 index 0000000000..cc86189d8c --- /dev/null +++ b/libs/ardour/ltc_slave.cc @@ -0,0 +1,175 @@ +/* + Copyright (C) 2012 Paul Davis + Witten by 2012 Robin Gareus + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#include +#include +#include +#include +#include + +#include "pbd/error.h" + +#include "ardour/debug.h" +#include "ardour/slave.h" +#include "ardour/session.h" +#include "ardour/audioengine.h" +#include "ardour/audio_port.h" + +#include "i18n.h" + +using namespace std; +using namespace ARDOUR; +using namespace MIDI; +using namespace PBD; +using namespace Timecode; + +LTC_Slave::LTC_Slave (Session& s) + : session (s) +{ + current_frames_per_ltc_frame = 1920; // samplerate / framerate + ltc_transport_pos = 0; + ltc_speed = 1.0; + monotonic_fcnt = 0; + + decoder = ltc_decoder_create(current_frames_per_ltc_frame, 128 /*queue size*/); +} + +LTC_Slave::~LTC_Slave() +{ + if (did_reset_tc_format) { + session.config.set_timecode_format (saved_tc_format); + } + + ltc_decoder_free(decoder); +} + +bool +LTC_Slave::give_slave_full_control_over_transport_speed() const +{ + return true; // DLL align to engine transport + // return false; // for Session-level computed varispeed +} + +ARDOUR::framecnt_t +LTC_Slave::resolution () const +{ + return current_frames_per_ltc_frame; +} + +ARDOUR::framecnt_t +LTC_Slave::seekahead_distance () const +{ + return current_frames_per_ltc_frame * 2; +} + +bool +LTC_Slave::locked () const +{ + return true; +} + +bool +LTC_Slave::ok() const +{ + return true; +} + +int +LTC_Slave::parse_ltc(const jack_nframes_t nframes, const jack_default_audio_sample_t * const in, const framecnt_t posinfo) +{ + jack_nframes_t i; + unsigned char sound[8192]; + if (nframes > 8192) return 1; + + for (i = 0; i < nframes; i++) { + const int snd=(int)rint((127.0*in[i])+128.0); + sound[i] = (unsigned char) (snd&0xff); + } + ltc_decoder_write(decoder, sound, nframes, posinfo); + return 0; +} + +void +LTC_Slave::process_ltc() +{ + LTCFrameExt frame; + while (ltc_decoder_read(decoder,&frame)) { + SMPTETimecode stime; + ltc_frame_to_time(&stime, &frame.ltc, 0); + + fprintf(stdout, "%02d:%02d:%02d%c%02d | %8lld %8lld%s\n", + stime.hours, + stime.mins, + stime.secs, + (frame.ltc.dfbit) ? '.' : ':', + stime.frame, + frame.off_start, + frame.off_end, + frame.reverse ? " R" : " " + ); + + /* when a full LTC frame is decoded, the timecode the LTC frame + * is referring has just passed. + * So we send the _next_ timecode which + * is expected to start at the end of the current frame + */ + + int detected_fps = 25; // XXX + if (!frame.reverse) { + ltc_frame_increment(&frame.ltc, detected_fps , 0); + ltc_frame_to_time(&stime, &frame.ltc, 0); + } else { + ltc_frame_decrement(&frame.ltc, detected_fps , 0); + int off = frame.off_end - frame.off_start; + frame.off_start += off; + frame.off_end += off; + } + } +} + +/* main entry point from session_process.cc + * called from jack_process callback context + */ +bool +LTC_Slave::speed_and_position (double& speed, framepos_t& pos) +{ + DEBUG_TRACE (DEBUG::MTC, string_compose ("LTC_Slave::speed_and_position - TID:%1\n", ::pthread_self())); + framepos_t now = session.engine().frame_time_at_cycle_start(); + framepos_t sess_pos = session.transport_frame(); // corresponds to now + framecnt_t nframes = session.engine().frames_per_cycle(); + + jack_port_t *ltc_port = session.engine().ltc_input_port()->jack_port(); + jack_default_audio_sample_t *in; + in = (jack_default_audio_sample_t*) jack_port_get_buffer (ltc_port, nframes); + + //in = session.engine().ltc_input_port()->engine_get_whole_audio_buffer() + // TODO: get capture latency for ltc_port. + + if (in) { + parse_ltc(nframes, in, monotonic_fcnt /* + jltc_latency*/ ); + process_ltc(); + } + + /* fake for testing */ + ltc_transport_pos += nframes * ltc_speed; + pos = ltc_transport_pos; + speed = ltc_speed; + + monotonic_fcnt += nframes; +} diff --git a/libs/ardour/session_transport.cc b/libs/ardour/session_transport.cc index b55fdfaa33..d4a0a010c9 100644 --- a/libs/ardour/session_transport.cc +++ b/libs/ardour/session_transport.cc @@ -1372,6 +1372,24 @@ Session::switch_to_sync_source (SyncSource src) } break; + case LTC: +#ifdef HAVE_LTC + if (_slave && dynamic_cast(_slave)) { + return; + } + + try { + new_slave = new LTC_Slave (*this); + } + + catch (failed_constructor& err) { + return; + } +#else + return; +#endif + break; + case MIDIClock: if (_slave && dynamic_cast(_slave)) { return; diff --git a/libs/ardour/utils.cc b/libs/ardour/utils.cc index 7751ea20ca..904750e9d8 100644 --- a/libs/ardour/utils.cc +++ b/libs/ardour/utils.cc @@ -452,6 +452,9 @@ sync_source_to_string (SyncSource src, bool sh) case MIDIClock: return _("MIDI Clock"); + + case LTC: + return _("LTC"); } /* GRRRR .... stupid, stupid gcc - you can't get here from there, all enum values are handled */ return _("JACK"); diff --git a/libs/ardour/wscript b/libs/ardour/wscript index 3a61df11d7..ddcb5e00e5 100644 --- a/libs/ardour/wscript +++ b/libs/ardour/wscript @@ -281,6 +281,8 @@ def configure(conf): atleast_version='1.2.1') autowaf.check_pkg(conf, 'libcurl', uselib_store='CURL', atleast_version='7.0.0') + autowaf.check_pkg(conf, 'ltc', uselib_store='LTC', + atleast_version='0.3.2', mandatory=False) # we don't try to detect this, since its part of our source tree @@ -381,6 +383,10 @@ def build(bld): #obj.uselib += ' SOUNDTOUCH ' #obj.add_objects = 'default/libs/surfaces/control_protocol/smpte_1.o' + if bld.is_defined('HAVE_LTC') : + obj.source += ['ltc_slave.cc'] + obj.uselib += ['LTC'] + if bld.is_defined('HAVE_LILV') : obj.source += ['lv2_plugin.cc', 'lv2_evbuf.c', 'uri_map.cc'] obj.uselib += ['LILV'] -- cgit v1.2.3