X-Git-Url: https://git.sesse.net/?p=c64tapwav;a=blobdiff_plain;f=decode.cpp;h=49be647ea2cc67442718f2765e66ff508cd91064;hp=e3ec6775161edf854b2522c99dd9eb724e60af27;hb=8df851a0c512f869ef6b928cff20dd84a258e959;hpb=2dcc74e788a0d2bda5cb9b2a62dc11fbef99fb5d diff --git a/decode.cpp b/decode.cpp index e3ec677..49be647 100644 --- a/decode.cpp +++ b/decode.cpp @@ -1,69 +1,24 @@ #include #include #include -#include #include +#include #include #include -#define LANCZOS_RADIUS 30 +#include "interpolate.h" +#include "tap.h" + #define BUFSIZE 4096 #define HYSTERESIS_LIMIT 3000 #define SAMPLE_RATE 44100 #define C64_FREQUENCY 985248 -#define TAP_RESOLUTION 8 #define SYNC_PULSE_START 1000 #define SYNC_PULSE_END 15000 -#define SYNC_PULSE_LENGTH 380.0 +#define SYNC_PULSE_LENGTH 378.0 #define SYNC_TEST_TOLERANCE 1.10 -struct tap_header { - char identifier[12]; - char version; - char reserved[3]; - unsigned int data_len; -}; - -double sinc(double x) -{ - if (fabs(x) < 1e-6) { - return 1.0f - fabs(x); - } else { - return sin(x) / x; - } -} - -#if 0 -double weight(double x) -{ - if (fabs(x) > LANCZOS_RADIUS) { - return 0.0f; - } - return sinc(M_PI * x) * sinc(M_PI * x / LANCZOS_RADIUS); -} -#else -double weight(double x) -{ - if (fabs(x) > 1.0f) { - return 0.0f; - } - return 1.0f - fabs(x); -} -#endif - -double interpolate(const std::vector &pcm, double i) -{ - int lower = std::max(ceil(i - LANCZOS_RADIUS), 0); - int upper = std::min(floor(i + LANCZOS_RADIUS), pcm.size() - 1); - double sum = 0.0f; - - for (int x = lower; x <= upper; ++x) { - sum += pcm[x] * weight(i - x); - } - return sum; -} - // between [x,x+1] double find_zerocrossing(const std::vector &pcm, int x) { @@ -74,14 +29,14 @@ double find_zerocrossing(const std::vector &pcm, int x) return x + 1; } - assert(pcm[x + 1] > 0); - assert(pcm[x] < 0); + assert(pcm[x + 1] < 0); + assert(pcm[x] > 0); - double lower = x; - double upper = x + 1; - while (upper - lower > 1e-6) { + double upper = x; + double lower = x + 1; + while (lower - upper > 1e-3) { double mid = 0.5f * (upper + lower); - if (interpolate(pcm, mid) > 0) { + if (lanczos_interpolate(pcm, mid) > 0) { upper = mid; } else { lower = mid; @@ -98,6 +53,7 @@ struct pulse { int main(int argc, char **argv) { + make_lanczos_weight_table(); std::vector pcm; while (!feof(stdin)) { @@ -124,18 +80,18 @@ int main(int argc, char **argv) // Find the flanks. int last_bit = -1; - double last_upflank = -1; + double last_downflank = -1; for (unsigned i = 0; i < pcm.size(); ++i) { int bit = (pcm[i] > 0) ? 1 : 0; - if (bit == 1 && last_bit == 0) { + if (bit == 0 && last_bit == 1) { // Check if we ever go up above HYSTERESIS_LIMIT before we dip down again. bool true_pulse = false; unsigned j; - int max_level_after = -32768; + int min_level_after = 32767; for (j = i; j < pcm.size(); ++j) { - max_level_after = std::max(max_level_after, pcm[j]); - if (pcm[j] < 0) break; - if (pcm[j] > HYSTERESIS_LIMIT) { + min_level_after = std::min(min_level_after, pcm[j]); + if (pcm[j] > 0) break; + if (pcm[j] < -HYSTERESIS_LIMIT) { true_pulse = true; break; } @@ -143,22 +99,22 @@ int main(int argc, char **argv) if (!true_pulse) { #if 0 - fprintf(stderr, "Ignored up-flank at %.6f seconds due to hysteresis (%d < %d).\n", - double(i) / SAMPLE_RATE, max_level_after, HYSTERESIS_LIMIT); + fprintf(stderr, "Ignored down-flank at %.6f seconds due to hysteresis (%d < %d).\n", + double(i) / SAMPLE_RATE, -min_level_after, HYSTERESIS_LIMIT); #endif i = j; continue; } - // up-flank! + // down-flank! double t = find_zerocrossing(pcm, i - 1) * (1.0 / SAMPLE_RATE); - if (last_upflank > 0) { + if (last_downflank > 0) { pulse p; p.time = t; - p.len = t - last_upflank; + p.len = t - last_downflank; pulses.push_back(p); } - last_upflank = t; + last_downflank = t; } last_bit = bit; } @@ -174,14 +130,14 @@ int main(int argc, char **argv) } double mean_length = C64_FREQUENCY * sum / (SYNC_PULSE_END - SYNC_PULSE_START); calibration_factor = SYNC_PULSE_LENGTH / mean_length; - fprintf(stderr, "Calibrated sync pulse length: %.2f -> 380.0 (change %+.2f%%)\n", - mean_length, 100.0 * (calibration_factor - 1.0)); + fprintf(stderr, "Calibrated sync pulse length: %.2f -> %.2f (change %+.2f%%)\n", + mean_length, SYNC_PULSE_LENGTH, 100.0 * (calibration_factor - 1.0)); // Check for pulses outside +/- 10% (sign of misdetection). for (int i = SYNC_PULSE_START; i < SYNC_PULSE_END; ++i) { double cycles = pulses[i].len * calibration_factor * C64_FREQUENCY; if (cycles < SYNC_PULSE_LENGTH / SYNC_TEST_TOLERANCE || cycles > SYNC_PULSE_LENGTH * SYNC_TEST_TOLERANCE) { - fprintf(stderr, "Sync cycle with upflank at %.6f was detected at %.0f cycles; misdetect?\n", + fprintf(stderr, "Sync cycle with downflank at %.6f was detected at %.0f cycles; misdetect?\n", pulses[i].time, cycles); } } @@ -204,7 +160,7 @@ int main(int argc, char **argv) fprintf(fp, "%f %f\n", pulses[i].time, cycles); int len = lrintf(cycles / TAP_RESOLUTION); if (i > SYNC_PULSE_END && (cycles < 100 || cycles > 800)) { - fprintf(stderr, "Cycle with upflank at %.6f was detected at %.0f cycles; misdetect?\n", + fprintf(stderr, "Cycle with downflank at %.6f was detected at %.0f cycles; misdetect?\n", pulses[i].time, cycles); } if (len <= 255) { @@ -227,4 +183,20 @@ int main(int argc, char **argv) fwrite(&hdr, sizeof(hdr), 1, stdout); fwrite(tap_data.data(), tap_data.size(), 1, stdout); + + // Output a debug raw file with pulse detection points. + fp = fopen("debug.raw", "wb"); + short one = 32767; + short zero = 0; + unsigned pulsenum = 0; + for (unsigned i = 0; i < pcm.size(); ++i) { + unsigned next_pulse = (pulsenum >= pulses.size()) ? INT_MAX : int(pulses[pulsenum].time * SAMPLE_RATE); + if (i >= next_pulse) { + fwrite(&one, sizeof(one), 1, fp); + ++pulsenum; + } else { + fwrite(&zero, sizeof(zero), 1, fp); + } + } + fclose(fp); }