From 8af0757b61990767f2a85e68f535a5af9976fd79 Mon Sep 17 00:00:00 2001 From: Taybin Rutkin Date: Sat, 24 Sep 2005 19:13:41 +0000 Subject: libardour added. git-svn-id: svn://localhost/trunk/ardour2@17 d708f5d6-7413-0410-9779-e7cbd77b26cf --- libs/ardour/mtc_slave.cc | 321 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 321 insertions(+) create mode 100644 libs/ardour/mtc_slave.cc (limited to 'libs/ardour/mtc_slave.cc') diff --git a/libs/ardour/mtc_slave.cc b/libs/ardour/mtc_slave.cc new file mode 100644 index 0000000000..906a0372a4 --- /dev/null +++ b/libs/ardour/mtc_slave.cc @@ -0,0 +1,321 @@ +/* + Copyright (C) 2002-4 Paul Davis + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id$ +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "i18n.h" + +using namespace ARDOUR; +using namespace sigc; +using namespace MIDI; + +MTC_Slave::MTC_Slave (Session& s, MIDI::Port& p) + : session (s) +{ + rebind (p); + reset (); +} + +MTC_Slave::~MTC_Slave() +{ +} + +void +MTC_Slave::rebind (MIDI::Port& p) +{ + for (vector::iterator i = connections.begin(); i != connections.end(); ++i) { + (*i).disconnect (); + } + + port = &p; + + connections.push_back (port->input()->mtc_time.connect (mem_fun (*this, &MTC_Slave::update_mtc_time))); + connections.push_back (port->input()->mtc_qtr.connect (mem_fun (*this, &MTC_Slave::update_mtc_qtr))); + connections.push_back (port->input()->mtc_status.connect (mem_fun (*this, &MTC_Slave::update_mtc_status))); +} + +void +MTC_Slave::update_mtc_qtr (Parser& p) +{ + cycles_t cnow = get_cycles (); + jack_nframes_t now = session.engine().frame_time(); + jack_nframes_t qtr; + static cycles_t last_qtr = 0; + + qtr = (long) (session.frames_per_smpte_frame() / 4); + mtc_frame += qtr; + last_qtr = cnow; + + current.guard1++; + current.position = mtc_frame; + current.timestamp = now; + current.guard2++; + + last_inbound_frame = now; +} + +void +MTC_Slave::update_mtc_time (const byte *msg, bool was_full) +{ + jack_nframes_t now = session.engine().frame_time(); + SMPTE_Time smpte; + + smpte.hours = msg[3]; + smpte.minutes = msg[2]; + smpte.seconds = msg[1]; + smpte.frames = msg[0]; + + session.smpte_to_sample( smpte, mtc_frame, true, false ); + + if (was_full) { + + current.guard1++; + current.position = mtc_frame; + current.timestamp = 0; + current.guard2++; + + session.request_locate (mtc_frame, false); + update_mtc_status (MIDI::Parser::MTC_Stopped); + + reset (); + + } else { + + /* We received the last quarter frame 7 quarter frames (1.75 mtc + frames) after the instance when the contents of the mtc quarter + frames were decided. Add time to compensate for the elapsed 1.75 + frames + */ + + mtc_frame += (long) (1.75 * session.frames_per_smpte_frame()); + + if (first_mtc_frame == 0) { + first_mtc_frame = mtc_frame; + first_mtc_time = now; + } + + current.guard1++; + current.position = mtc_frame; + current.timestamp = now; + current.guard2++; + } + + last_inbound_frame = now; +} + +void +MTC_Slave::handle_locate (const MIDI::byte* mmc_tc) +{ + MIDI::byte mtc[4]; + + mtc[3] = mmc_tc[0] & 0xf; /* hrs only */ + mtc[2] = mmc_tc[1]; + mtc[1] = mmc_tc[2]; + mtc[0] = mmc_tc[3]; + + update_mtc_time (mtc, true); +} + +void +MTC_Slave::update_mtc_status (MIDI::Parser::MTC_Status status) +{ + + switch (status) { + case MTC_Stopped: + mtc_speed = 0.0f; + mtc_frame = 0; + + current.guard1++; + current.position = mtc_frame; + current.timestamp = 0; + current.guard2++; + + break; + + case MTC_Forward: + mtc_speed = 0.0f; + mtc_frame = 0; + + current.guard1++; + current.position = mtc_frame; + current.timestamp = 0; + current.guard2++; + + break; + + case MTC_Backward: + mtc_speed = 0.0f; + mtc_frame = 0; + + current.guard1++; + current.position = mtc_frame; + current.timestamp = 0; + current.guard2++; + + break; + } +} + +void +MTC_Slave::read_current (SafeTime *st) const +{ + int tries = 0; + do { + if (tries == 10) { + error << _("MTC Slave: atomic read of current time failed, sleeping!") << endmsg; + usleep (20); + tries = 0; + } + + *st = current; + tries++; + + } while (st->guard1 != st->guard2); +} + +bool +MTC_Slave::locked () const +{ + return port->input()->mtc_locked(); +} + +bool +MTC_Slave::ok() const +{ + return true; +} + +bool +MTC_Slave::speed_and_position (float& speed, jack_nframes_t& pos) +{ + jack_nframes_t now = session.engine().frame_time(); + SafeTime last; + jack_nframes_t frame_rate; + jack_nframes_t elapsed; + float speed_now; + + read_current (&last); + + if (first_mtc_time == 0) { + speed = 0; + pos = last.position; + return true; + } + + /* no timecode for 1/4 second ? conclude that its stopped */ + + if (last_inbound_frame && now > last_inbound_frame && now - last_inbound_frame > session.frame_rate() / 4) { + mtc_speed = 0; + pos = last.position; + session.request_locate (pos, false); + update_mtc_status (MIDI::Parser::MTC_Stopped); + reset(); + return false; + } + + frame_rate = session.frame_rate(); + + speed_now = (float) ((last.position - first_mtc_frame) / (double) (now - first_mtc_time)); + + accumulator[accumulator_index++] = speed_now; + + if (accumulator_index >= accumulator_size) { + have_first_accumulated_speed = true; + accumulator_index = 0; + } + + if (have_first_accumulated_speed) { + float total = 0; + + for (int32_t i = 0; i < accumulator_size; ++i) { + total += accumulator[i]; + } + + mtc_speed = total / accumulator_size; + + } else { + + mtc_speed = speed_now; + + } + + if (mtc_speed == 0.0f) { + + elapsed = 0; + + } else { + + /* scale elapsed time by the current MTC speed */ + + if (last.timestamp && (now > last.timestamp)) { + elapsed = (jack_nframes_t) floor (mtc_speed * (now - last.timestamp)); + } else { + elapsed = 0; /* XXX is this right? */ + } + } + + /* now add the most recent timecode value plus the estimated elapsed interval */ + + pos = elapsed + last.position; + + speed = mtc_speed; + return true; +} + +jack_nframes_t +MTC_Slave::resolution() const +{ + return (jack_nframes_t) session.frames_per_smpte_frame(); +} + +void +MTC_Slave::reset () +{ + /* XXX massive thread safety issue here. MTC could + be being updated as we call this. but this + supposed to be a realtime-safe call. + */ + + port->input()->reset_mtc_state (); + + last_inbound_frame = 0; + current.guard1++; + current.position = 0; + current.timestamp = 0; + current.guard2++; + first_mtc_frame = 0; + first_mtc_time = 0; + + accumulator_index = 0; + have_first_accumulated_speed = false; + mtc_speed = 0; +} -- cgit v1.2.3