skeleton framework for LTC-slave
git-svn-id: svn://localhost/ardour2/branches/3.0@13256 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
parent
8f55ca6a50
commit
bce3184ff5
|
@ -1093,6 +1093,9 @@ AudioClock::set_timecode (framepos_t when, bool /*force*/)
|
|||
case MIDIClock:
|
||||
_left_layout->set_text ("M-Clock");
|
||||
break;
|
||||
case LTC:
|
||||
_left_layout->set_text ("LTC");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
_left_layout->set_text ("INT");
|
||||
|
|
|
@ -445,6 +445,7 @@ PortGroupList::gather (ARDOUR::Session* session, ARDOUR::DataType type, bool inp
|
|||
boost::shared_ptr<Bundle> sync (new Bundle (_("Sync"), inputs));
|
||||
MIDI::MachineControl* mmc = midi_manager->mmc ();
|
||||
AudioEngine& ae = session->engine ();
|
||||
|
||||
if (inputs) {
|
||||
sync->add_channel (
|
||||
_("MTC in"), DataType::MIDI, ae.make_port_name_non_relative (midi_manager->mtc_input_port()->name())
|
||||
|
@ -458,6 +459,11 @@ PortGroupList::gather (ARDOUR::Session* session, ARDOUR::DataType type, bool inp
|
|||
sync->add_channel (
|
||||
_("MMC in"), DataType::MIDI, ae.make_port_name_non_relative (mmc->input_port()->name())
|
||||
);
|
||||
#ifdef HAVE_LIBLTC
|
||||
sync->add_channel (
|
||||
_("LTC in"), DataType::AUDIO, ae.make_port_name_non_relative (ae->ltc_input_port()->name())
|
||||
);
|
||||
#endif
|
||||
} else {
|
||||
sync->add_channel (
|
||||
_("MTC out"), DataType::MIDI, ae.make_port_name_non_relative (midi_manager->mtc_output_port()->name())
|
||||
|
|
|
@ -260,6 +260,10 @@ _ the regular process() call to session->process() is not made.
|
|||
|
||||
int create_process_thread (boost::function<void()>, 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> ports;
|
||||
|
||||
boost::shared_ptr<Port> register_port (DataType type, const std::string& portname, bool input);
|
||||
|
|
|
@ -32,6 +32,10 @@
|
|||
#include "midi++/parser.h"
|
||||
#include "midi++/types.h"
|
||||
|
||||
#ifdef HAVE_LTC
|
||||
#include <ltc.h>
|
||||
#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);
|
||||
|
|
|
@ -462,7 +462,8 @@ namespace ARDOUR {
|
|||
enum SyncSource {
|
||||
JACK,
|
||||
MTC,
|
||||
MIDIClock
|
||||
MIDIClock,
|
||||
LTC
|
||||
};
|
||||
|
||||
enum ShuttleBehaviour {
|
||||
|
|
|
@ -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 ()
|
||||
|
|
|
@ -307,6 +307,7 @@ setup_enum_writer ()
|
|||
REGISTER_ENUM (MTC);
|
||||
REGISTER_ENUM (JACK);
|
||||
REGISTER_ENUM (MIDIClock);
|
||||
REGISTER_ENUM (LTC);
|
||||
REGISTER (_SyncSource);
|
||||
|
||||
REGISTER_ENUM (Sprung);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,175 @@
|
|||
/*
|
||||
Copyright (C) 2012 Paul Davis
|
||||
Witten by 2012 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
#include <iostream>
|
||||
#include <errno.h>
|
||||
#include <poll.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#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;
|
||||
}
|
|
@ -1372,6 +1372,24 @@ Session::switch_to_sync_source (SyncSource src)
|
|||
}
|
||||
break;
|
||||
|
||||
case LTC:
|
||||
#ifdef HAVE_LTC
|
||||
if (_slave && dynamic_cast<LTC_Slave*>(_slave)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
new_slave = new LTC_Slave (*this);
|
||||
}
|
||||
|
||||
catch (failed_constructor& err) {
|
||||
return;
|
||||
}
|
||||
#else
|
||||
return;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case MIDIClock:
|
||||
if (_slave && dynamic_cast<MIDIClock_Slave*>(_slave)) {
|
||||
return;
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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']
|
||||
|
|
Loading…
Reference in New Issue