summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorOwen Williams <owilliams@mixxx.org>2014-01-10 22:33:44 -0500
committerOwen Williams <owilliams@mixxx.org>2014-01-10 22:33:44 -0500
commit7c50246ce375161d7f4dcda2bc2cf5a58ebef970 (patch)
tree5a23c281f5c1165075e66d0a9dba467073c034df /lib
parentbed60ca6d326a397fea175bbd0a75fe85989d7db (diff)
Update xwax to 1.4, no major changes.
Diffstat (limited to 'lib')
-rw-r--r--lib/xwax/debug.h8
-rw-r--r--lib/xwax/lut.c2
-rw-r--r--lib/xwax/lut.cpp6
-rw-r--r--lib/xwax/lut.h2
-rw-r--r--lib/xwax/pitch.h2
-rw-r--r--lib/xwax/timecoder.c118
-rw-r--r--lib/xwax/timecoder.h7
-rwxr-xr-xlib/xwax/timecoder_win32.cpp125
8 files changed, 165 insertions, 105 deletions
diff --git a/lib/xwax/debug.h b/lib/xwax/debug.h
index 4b06ba4f73..cc2872b6a7 100644
--- a/lib/xwax/debug.h
+++ b/lib/xwax/debug.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 Mark Hills <mark@pogo.org.uk>
+ * Copyright (C) 2013 Mark Hills <mark@xwax.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -23,7 +23,11 @@
#include <stdio.h>
#ifdef DEBUG
-#define debug(...) fprintf(stderr, __VA_ARGS__)
+#define debug(...) { \
+ fprintf(stderr, "%s:%d: ", __FILE__, __LINE__); \
+ fprintf(stderr, __VA_ARGS__); \
+ fputc('\n', stderr); \
+}
#define dassert(x) assert(x)
#else
#define debug(...)
diff --git a/lib/xwax/lut.c b/lib/xwax/lut.c
index 7ae7733cf7..6f02680e5c 100644
--- a/lib/xwax/lut.c
+++ b/lib/xwax/lut.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 Mark Hills <mark@pogo.org.uk>
+ * Copyright (C) 2013 Mark Hills <mark@xwax.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/lib/xwax/lut.cpp b/lib/xwax/lut.cpp
index 30dadfd95e..a10face3f4 100644
--- a/lib/xwax/lut.cpp
+++ b/lib/xwax/lut.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 Mark Hills <mark@pogo.org.uk>
+ * Copyright (C) 2013 Mark Hills <mark@xwax.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -46,13 +46,13 @@ int lut_init(struct lut *lut, int nslots)
" (%d slots per hash, %zuKb)\n",
hashes, nslots, nslots / hashes, bytes / 1024);
- lut->slot = (struct slot*)malloc(sizeof(struct slot) * nslots);
+ lut->slot = static_cast<struct slot*>(malloc(sizeof(struct slot) * nslots));
if (lut->slot == NULL) {
perror("malloc");
return -1;
}
- lut->table = (slot_no_t*)malloc(sizeof(slot_no_t) * hashes);
+ lut->table = static_cast<slot_no_t*>(malloc(sizeof(slot_no_t) * hashes));
if (lut->table == NULL) {
perror("malloc");
return -1;
diff --git a/lib/xwax/lut.h b/lib/xwax/lut.h
index a156f492a3..29361c5b98 100644
--- a/lib/xwax/lut.h
+++ b/lib/xwax/lut.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 Mark Hills <mark@pogo.org.uk>
+ * Copyright (C) 2013 Mark Hills <mark@xwax.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/lib/xwax/pitch.h b/lib/xwax/pitch.h
index ab0cad5dd8..abf35e3c4d 100644
--- a/lib/xwax/pitch.h
+++ b/lib/xwax/pitch.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 Mark Hills <mark@pogo.org.uk>
+ * Copyright (C) 2013 Mark Hills <mark@xwax.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/lib/xwax/timecoder.c b/lib/xwax/timecoder.c
index ec11bbd0de..082af25d87 100644
--- a/lib/xwax/timecoder.c
+++ b/lib/xwax/timecoder.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 Mark Hills <mark@pogo.org.uk>
+ * Copyright (C) 2013 Mark Hills <mark@xwax.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -18,6 +18,7 @@
*/
#include <assert.h>
+#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -26,7 +27,7 @@
#include "debug.h"
#include "timecoder.h"
-#define ZERO_THRESHOLD 128
+#define ZERO_THRESHOLD (128 << 16)
#define ZERO_RC 0.001 /* time constant for zero/rumble filter */
@@ -194,7 +195,7 @@ static inline bits_t rev(bits_t current, struct timecode_def *def)
static int build_lookup(struct timecode_def *def)
{
unsigned int n;
- bits_t current, last;
+ bits_t current;
if (def->lookup)
return 0;
@@ -208,12 +209,17 @@ static int build_lookup(struct timecode_def *def)
current = def->seed;
for (n = 0; n < def->length; n++) {
+ bits_t next;
+
/* timecode must not wrap */
dassert(lut_lookup(&def->lut, current) == (unsigned)-1);
lut_push(&def->lut, current);
- last = current;
- current = fwd(current, def);
- dassert(rev(current, def) == last);
+
+ /* check symmetry of the lfsr functions */
+ next = fwd(current, def);
+ dassert(rev(next, def) == current);
+
+ current = next;
}
def->lookup = true;
@@ -284,7 +290,7 @@ static void init_channel(struct timecoder_channel *ch)
*/
void timecoder_init(struct timecoder *tc, struct timecode_def *def,
- double speed, unsigned int sample_rate)
+ double speed, unsigned int sample_rate, bool phono)
{
assert(def != NULL);
@@ -297,13 +303,16 @@ void timecoder_init(struct timecoder *tc, struct timecode_def *def,
tc->dt = 1.0 / sample_rate;
tc->zero_alpha = tc->dt / (ZERO_RC + tc->dt);
+ tc->threshold = ZERO_THRESHOLD;
+ if (phono)
+ tc->threshold >>= 5; /* approx -36dB */
tc->forwards = 1;
init_channel(&tc->primary);
init_channel(&tc->secondary);
pitch_init(&tc->pitch, tc->dt);
- tc->ref_level = 32768.0;
+ tc->ref_level = INT_MAX;
tc->bitstream = 0;
tc->timecode = 0;
tc->valid_counter = 0;
@@ -360,16 +369,17 @@ void timecoder_monitor_clear(struct timecoder *tc)
*/
static void detect_zero_crossing(struct timecoder_channel *ch,
- signed int v, double alpha)
+ signed int v, double alpha,
+ signed int threshold)
{
ch->crossing_ticker++;
ch->swapped = false;
- if (v > ch->zero + ZERO_THRESHOLD && !ch->positive) {
+ if (v > ch->zero + threshold && !ch->positive) {
ch->swapped = true;
ch->positive = true;
ch->crossing_ticker = 0;
- } else if (v < ch->zero - ZERO_THRESHOLD && ch->positive) {
+ } else if (v < ch->zero - threshold && ch->positive) {
ch->swapped = true;
ch->positive = false;
ch->crossing_ticker = 0;
@@ -384,31 +394,35 @@ static void detect_zero_crossing(struct timecoder_channel *ch,
static void update_monitor(struct timecoder *tc, signed int x, signed int y)
{
- int px, py, p;
- double v, w;
+ int px, py, size, ref;
if (!tc->mon)
return;
+ size = tc->mon_size;
+ ref = tc->ref_level;
+
/* Decay the pixels already in the montior */
if (++tc->mon_counter % MONITOR_DECAY_EVERY == 0) {
- for (p = 0; p < SQ(tc->mon_size); p++) {
+ int p;
+
+ for (p = 0; p < SQ(size); p++) {
if (tc->mon[p])
tc->mon[p] = tc->mon[p] * 7 / 8;
}
}
- v = (double)x / tc->ref_level / 2;
- w = (double)y / tc->ref_level / 2;
+ assert(ref > 0);
- px = tc->mon_size / 2 + (v * tc->mon_size / 2);
- py = tc->mon_size / 2 + (w * tc->mon_size / 2);
+ /* ref_level is half the prevision of signal level */
+ px = size / 2 + (long long)x * size / ref / 8;
+ py = size / 2 + (long long)y * size / ref / 8;
- /* Set the pixel value to white */
+ if (px < 0 || px >= size || py < 0 || py >= size)
+ return;
- if (px > 0 && px < tc->mon_size && py > 0 && py < tc->mon_size)
- tc->mon[py * tc->mon_size + px] = 0xff;
+ tc->mon[py * size + px] = 0xff; /* white */
}
/*
@@ -453,9 +467,10 @@ static void process_bitstream(struct timecoder *tc, signed int m)
/* Adjust the reference level based on this new peak */
- tc->ref_level = (tc->ref_level * (REF_PEAKS_AVG - 1) + m) / REF_PEAKS_AVG;
+ tc->ref_level -= tc->ref_level / REF_PEAKS_AVG;
+ tc->ref_level += m / REF_PEAKS_AVG;
- debug("%+6d zero, %+6d (ref %+6d)\t= %d%c (%5d)\n",
+ debug("%+6d zero, %+6d (ref %+6d)\t= %d%c (%5d)",
tc->primary.zero,
m, tc->ref_level,
b, tc->valid_counter == 0 ? 'x' : ' ',
@@ -464,17 +479,16 @@ static void process_bitstream(struct timecoder *tc, signed int m)
/*
* Process a single sample from the incoming audio
+ *
+ * The two input signals (primary and secondary) are in the full range
+ * of a signed int; ie. 32-bit signed.
*/
static void process_sample(struct timecoder *tc,
signed int primary, signed int secondary)
{
- signed int m; /* pcm sample, sum of two shorts */
-
- detect_zero_crossing(&tc->primary, primary, tc->zero_alpha);
- detect_zero_crossing(&tc->secondary, secondary, tc->zero_alpha);
-
- m = abs(primary - tc->primary.zero);
+ detect_zero_crossing(&tc->primary, primary, tc->zero_alpha, tc->threshold);
+ detect_zero_crossing(&tc->secondary, secondary, tc->zero_alpha, tc->threshold);
/* If an axis has been crossed, use the direction of the crossing
* to work out the direction of the vinyl */
@@ -517,6 +531,10 @@ static void process_sample(struct timecoder *tc,
if (tc->secondary.swapped &&
tc->primary.positive == ((tc->def->flags & SWITCH_POLARITY) == 0))
{
+ signed int m;
+
+ /* scale to avoid clipping */
+ m = abs(primary / 2 - tc->primary.zero / 2);
process_bitstream(tc, m);
}
@@ -557,24 +575,29 @@ void timecoder_cycle_definition(struct timecoder *tc)
/*
* Submit and decode a block of PCM audio data to the timecode decoder
+ *
+ * PCM data is in the full range of signed short; ie. 16-bit signed.
*/
-void timecoder_submit(struct timecoder *tc, const signed short *pcm, size_t npcm)
+void timecoder_submit(struct timecoder *tc, signed short *pcm, size_t npcm)
{
while (npcm--) {
- signed int primary, secondary;
+ signed int left, right, primary, secondary;
+
+ left = pcm[0] << 16;
+ right = pcm[1] << 16;
if (tc->def->flags & SWITCH_PRIMARY) {
- primary = pcm[0];
- secondary = pcm[1];
+ primary = left;
+ secondary = right;
} else {
- primary = pcm[1];
- secondary = pcm[0];
+ primary = right;
+ secondary = left;
}
process_sample(tc, primary, secondary);
+ update_monitor(tc, left, right);
- update_monitor(tc, pcm[0], pcm[1]);
pcm += TIMECODER_CHANNELS;
}
}
@@ -595,17 +618,20 @@ signed int timecoder_get_position(struct timecoder *tc, double *when)
{
signed int r;
- if (tc->valid_counter > VALID_BITS) {
- r = lut_lookup(&tc->def->lut, tc->bitstream);
+ if (tc->valid_counter <= VALID_BITS)
+ return -1;
+
+ r = lut_lookup(&tc->def->lut, tc->bitstream);
+ if (r == -1)
+ return -1;
- if (r >= 0) {
- //normalize position to milliseconds, not timecode steps -- Owen
- r = (float)r * (1000.0 / (tc->def->resolution * tc->speed));
- if (when)
- *when = tc->timecode_ticker * tc->dt;
- return r;
- }
+ if (r >= 0) {
+ // normalize position to milliseconds, not timecode steps -- Owen
+ r = (double)r * (1000.0 / ((double)tc->def->resolution * tc->speed));
}
- return -1;
+ if (when)
+ *when = tc->timecode_ticker * tc->dt;
+
+ return r;
}
diff --git a/lib/xwax/timecoder.h b/lib/xwax/timecoder.h
index fda86fff5e..3304eee641 100644
--- a/lib/xwax/timecoder.h
+++ b/lib/xwax/timecoder.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 Mark Hills <mark@pogo.org.uk>
+ * Copyright (C) 2013 Mark Hills <mark@xwax.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -58,6 +58,7 @@ struct timecoder {
/* Precomputed values */
double dt, zero_alpha;
+ signed int threshold;
/* Pitch information */
@@ -83,14 +84,14 @@ struct timecode_def* timecoder_find_definition(const char *name);
void timecoder_free_lookup(void);
void timecoder_init(struct timecoder *tc, struct timecode_def *def,
- double speed, unsigned int sample_rate);
+ double speed, unsigned int sample_rate, bool phono);
void timecoder_clear(struct timecoder *tc);
int timecoder_monitor_init(struct timecoder *tc, int size);
void timecoder_monitor_clear(struct timecoder *tc);
void timecoder_cycle_definition(struct timecoder *tc);
-void timecoder_submit(struct timecoder *tc, const signed short *pcm, size_t npcm);
+void timecoder_submit(struct timecoder *tc, signed short *pcm, size_t npcm);
signed int timecoder_get_position(struct timecoder *tc, double *when);
/*
diff --git a/lib/xwax/timecoder_win32.cpp b/lib/xwax/timecoder_win32.cpp
index 2059ebdd53..18910382f5 100755
--- a/lib/xwax/timecoder_win32.cpp
+++ b/lib/xwax/timecoder_win32.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 Mark Hills <mark@pogo.org.uk>
+ * Copyright (C) 2013 Mark Hills <mark@xwax.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -18,6 +18,7 @@
*/
#include <assert.h>
+#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -28,7 +29,7 @@
#include "debug.h"
#include "timecoder.h"
-#define ZERO_THRESHOLD 128
+#define ZERO_THRESHOLD (128 << 16)
#define ZERO_RC 0.001 /* time constant for zero/rumble filter */
@@ -98,7 +99,7 @@ static struct timecode_def timecodes[] = {
0x041040,
1500000,
605000,
- false
+ false
},
{
"traktor_b",
@@ -140,6 +141,7 @@ static struct timecode_def timecodes[] = {
NULL
}
};
+
/*
* Calculate LFSR bit
*/
@@ -198,7 +200,7 @@ static inline bits_t rev(bits_t current, struct timecode_def *def)
static int build_lookup(struct timecode_def *def)
{
unsigned int n;
- bits_t current, last;
+ bits_t current;
if (def->lookup)
return 0;
@@ -212,12 +214,17 @@ static int build_lookup(struct timecode_def *def)
current = def->seed;
for (n = 0; n < def->length; n++) {
+ bits_t next;
+
/* timecode must not wrap */
dassert(lut_lookup(&def->lut, current) == (unsigned)-1);
lut_push(&def->lut, current);
- last = current;
- current = fwd(current, def);
- dassert(rev(current, def) == last);
+
+ /* check symmetry of the lfsr functions */
+ next = fwd(current, def);
+ dassert(rev(next, def) == current);
+
+ current = next;
}
def->lookup = true;
@@ -288,7 +295,7 @@ static void init_channel(struct timecoder_channel *ch)
*/
void timecoder_init(struct timecoder *tc, struct timecode_def *def,
- double speed, unsigned int sample_rate)
+ double speed, unsigned int sample_rate, bool phono)
{
assert(def != NULL);
@@ -301,13 +308,16 @@ void timecoder_init(struct timecoder *tc, struct timecode_def *def,
tc->dt = 1.0 / sample_rate;
tc->zero_alpha = tc->dt / (ZERO_RC + tc->dt);
+ tc->threshold = ZERO_THRESHOLD;
+ if (phono)
+ tc->threshold >>= 5; /* approx -36dB */
tc->forwards = 1;
init_channel(&tc->primary);
init_channel(&tc->secondary);
pitch_init(&tc->pitch, tc->dt);
- tc->ref_level = 32768.0;
+ tc->ref_level = INT_MAX;
tc->bitstream = 0;
tc->timecode = 0;
tc->valid_counter = 0;
@@ -338,7 +348,7 @@ int timecoder_monitor_init(struct timecoder *tc, int size)
{
assert(tc->mon == NULL);
tc->mon_size = size;
- tc->mon = (unsigned char*)malloc(SQ(tc->mon_size));
+ tc->mon = static_cast<unsigned char*>(malloc(SQ(tc->mon_size)));
if (tc->mon == NULL) {
perror("malloc");
return -1;
@@ -364,16 +374,17 @@ void timecoder_monitor_clear(struct timecoder *tc)
*/
static void detect_zero_crossing(struct timecoder_channel *ch,
- signed int v, double alpha)
+ signed int v, double alpha,
+ signed int threshold)
{
ch->crossing_ticker++;
ch->swapped = false;
- if (v > ch->zero + ZERO_THRESHOLD && !ch->positive) {
+ if (v > ch->zero + threshold && !ch->positive) {
ch->swapped = true;
ch->positive = true;
ch->crossing_ticker = 0;
- } else if (v < ch->zero - ZERO_THRESHOLD && ch->positive) {
+ } else if (v < ch->zero - threshold && ch->positive) {
ch->swapped = true;
ch->positive = false;
ch->crossing_ticker = 0;
@@ -388,31 +399,35 @@ static void detect_zero_crossing(struct timecoder_channel *ch,
static void update_monitor(struct timecoder *tc, signed int x, signed int y)
{
- int px, py, p;
- double v, w;
+ int px, py, size, ref;
if (!tc->mon)
return;
+ size = tc->mon_size;
+ ref = tc->ref_level;
+
/* Decay the pixels already in the montior */
if (++tc->mon_counter % MONITOR_DECAY_EVERY == 0) {
- for (p = 0; p < SQ(tc->mon_size); p++) {
+ int p;
+
+ for (p = 0; p < SQ(size); p++) {
if (tc->mon[p])
tc->mon[p] = tc->mon[p] * 7 / 8;
}
}
- v = (double)x / tc->ref_level / 2;
- w = (double)y / tc->ref_level / 2;
+ assert(ref > 0);
- px = tc->mon_size / 2 + (v * tc->mon_size / 2);
- py = tc->mon_size / 2 + (w * tc->mon_size / 2);
+ /* ref_level is half the prevision of signal level */
+ px = size / 2 + (long long)x * size / ref / 8;
+ py = size / 2 + (long long)y * size / ref / 8;
- /* Set the pixel value to white */
+ if (px < 0 || px >= size || py < 0 || py >= size)
+ return;
- if (px > 0 && px < tc->mon_size && py > 0 && py < tc->mon_size)
- tc->mon[py * tc->mon_size + px] = 0xff;
+ tc->mon[py * size + px] = 0xff; /* white */
}
/*
@@ -457,9 +472,10 @@ static void process_bitstream(struct timecoder *tc, signed int m)
/* Adjust the reference level based on this new peak */
- tc->ref_level = (tc->ref_level * (REF_PEAKS_AVG - 1) + m) / REF_PEAKS_AVG;
+ tc->ref_level -= tc->ref_level / REF_PEAKS_AVG;
+ tc->ref_level += m / REF_PEAKS_AVG;
- debug("%+6d zero, %+6d (ref %+6d)\t= %d%c (%5d)\n",
+ debug("%+6d zero, %+6d (ref %+6d)\t= %d%c (%5d)",
tc->primary.zero,
m, tc->ref_level,
b, tc->valid_counter == 0 ? 'x' : ' ',
@@ -468,17 +484,16 @@ static void process_bitstream(struct timecoder *tc, signed int m)
/*
* Process a single sample from the incoming audio
+ *
+ * The two input signals (primary and secondary) are in the full range
+ * of a signed int; ie. 32-bit signed.
*/
static void process_sample(struct timecoder *tc,
signed int primary, signed int secondary)
{
- signed int m; /* pcm sample, sum of two shorts */
-
- detect_zero_crossing(&tc->primary, primary, tc->zero_alpha);
- detect_zero_crossing(&tc->secondary, secondary, tc->zero_alpha);
-
- m = abs(primary - tc->primary.zero);
+ detect_zero_crossing(&tc->primary, primary, tc->zero_alpha, tc->threshold);
+ detect_zero_crossing(&tc->secondary, secondary, tc->zero_alpha, tc->threshold);
/* If an axis has been crossed, use the direction of the crossing
* to work out the direction of the vinyl */
@@ -521,6 +536,10 @@ static void process_sample(struct timecoder *tc,
if (tc->secondary.swapped &&
tc->primary.positive == ((tc->def->flags & SWITCH_POLARITY) == 0))
{
+ signed int m;
+
+ /* scale to avoid clipping */
+ m = abs(primary / 2 - tc->primary.zero / 2);
process_bitstream(tc, m);
}
@@ -561,24 +580,29 @@ void timecoder_cycle_definition(struct timecoder *tc)
/*
* Submit and decode a block of PCM audio data to the timecode decoder
+ *
+ * PCM data is in the full range of signed short; ie. 16-bit signed.
*/
-void timecoder_submit(struct timecoder *tc, const signed short *pcm, size_t npcm)
+void timecoder_submit(struct timecoder *tc, signed short *pcm, size_t npcm)
{
while (npcm--) {
- signed int primary, secondary;
+ signed int left, right, primary, secondary;
+
+ left = pcm[0] << 16;
+ right = pcm[1] << 16;
if (tc->def->flags & SWITCH_PRIMARY) {
- primary = pcm[0];
- secondary = pcm[1];
+ primary = left;
+ secondary = right;
} else {
- primary = pcm[1];
- secondary = pcm[0];
+ primary = right;
+ secondary = left;
}
process_sample(tc, primary, secondary);
+ update_monitor(tc, left, right);
- update_monitor(tc, pcm[0], pcm[1]);
pcm += TIMECODER_CHANNELS;
}
}
@@ -599,17 +623,22 @@ signed int timecoder_get_position(struct timecoder *tc, double *when)
{
signed int r;
- if (tc->valid_counter > VALID_BITS) {
- r = lut_lookup(&tc->def->lut, tc->bitstream);
+ if (tc->valid_counter <= VALID_BITS)
+ return -1;
- if (r >= 0) {
- //normalize position to milliseconds, not timecode steps -- Owen
- r = (float)r * (1000.0 / (tc->def->resolution * tc->speed));
- if (when)
- *when = tc->timecode_ticker * tc->dt;
- return r;
- }
+ r = lut_lookup(&tc->def->lut, tc->bitstream);
+ if (r == -1)
+ return -1;
+
+ if (r >= 0) {
+ // normalize position to milliseconds, not timecode steps -- Owen
+ r = static_cast<double>(r)
+ * (1000.0
+ / (static_cast<double>(tc->def->resolution) * tc->speed));
}
- return -1;
+ if (when)
+ *when = tc->timecode_ticker * tc->dt;
+
+ return r;
}