X-Git-Url: https://git.sesse.net/?p=c64tapwav;a=blobdiff_plain;f=decode.c;h=7bbb250f973ce0f307bd3062e45573b7a11a90d9;hp=e20c8c9d39743f3b0c011fe1d25bc754f81f9152;hb=9a4bbd988d9c24f56275c2c21e030d08f5c9d861;hpb=e3e486002a6873305b9e858126fe5119e2e4077d diff --git a/decode.c b/decode.c index e20c8c9..7bbb250 100644 --- a/decode.c +++ b/decode.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -7,7 +8,22 @@ #define LANCZOS_RADIUS 30 #define BUFSIZE 4096 -#define HYSTERESIS_LIMIT 1000 +#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_TEST_TOLERANCE 1.10 + +struct tap_header { + char identifier[12]; + char version; + char reserved[3]; + unsigned int data_len; +}; double sinc(double x) { @@ -75,6 +91,11 @@ double find_zerocrossing(const std::vector &pcm, int x) return 0.5f * (upper + lower); } +struct pulse { + double time; // in seconds from start + double len; // in seconds +}; + int main(int argc, char **argv) { std::vector pcm; @@ -98,18 +119,23 @@ int main(int argc, char **argv) printf("%d\n", in[i]); } #endif + + std::vector pulses; // in seconds + + // Find the flanks. int last_bit = -1; double last_upflank = -1; int last_max_level = 0; - for (int i = 0; i < pcm.size(); ++i) { + for (unsigned i = 0; i < pcm.size(); ++i) { int bit = (pcm[i] > 0) ? 1 : 0; if (bit == 1 && last_bit == 0 && last_max_level > HYSTERESIS_LIMIT) { // up-flank! - double t = find_zerocrossing(pcm, i - 1) * (123156.0/44100.0); + double t = find_zerocrossing(pcm, i - 1) * (1.0 / SAMPLE_RATE); if (last_upflank > 0) { -// fprintf(stderr, "length: %f (0x%x)\n", t - last_upflank, lrintf(t - last_upflank)); - int len = lrintf(t - last_upflank); - printf("0x%x\n", len); + pulse p; + p.time = t; + p.len = t - last_upflank; + pulses.push_back(p); } last_upflank = t; last_max_level = 0; @@ -117,4 +143,66 @@ int main(int argc, char **argv) last_max_level = std::max(last_max_level, abs(pcm[i])); last_bit = bit; } + + // Calibrate on the first ~25k pulses (skip a few, just to be sure). + double calibration_factor = 1.0f; + if (pulses.size() < SYNC_PULSE_END) { + fprintf(stderr, "Too few pulses, not calibrating!\n"); + } else { + double sum = 0.0; + for (int i = SYNC_PULSE_START; i < SYNC_PULSE_END; ++i) { + sum += pulses[i].len; + } + 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)); + + // 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", + pulses[i].time, cycles); + } + } + + // Compute the standard deviation (to check for uneven speeds). + double sum2 = 0.0; + for (int i = SYNC_PULSE_START; i < SYNC_PULSE_END; ++i) { + double cycles = pulses[i].len * calibration_factor * C64_FREQUENCY; + sum2 += (cycles - SYNC_PULSE_LENGTH) * (cycles - SYNC_PULSE_LENGTH); + } + double stddev = sqrt(sum2 / (SYNC_PULSE_END - SYNC_PULSE_START - 1)); + fprintf(stderr, "Sync pulse length standard deviation: %.2f cycles\n", + stddev); + } + + std::vector tap_data; + for (unsigned i = 0; i < pulses.size(); ++i) { + double cycles = pulses[i].len * calibration_factor * C64_FREQUENCY; + 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", + pulses[i].time, cycles); + } + if (len <= 255) { + tap_data.push_back(len); + } else { + int overflow_len = lrintf(cycles); + tap_data.push_back(0); + tap_data.push_back(overflow_len & 0xff); + tap_data.push_back((overflow_len >> 8) & 0xff); + tap_data.push_back(overflow_len >> 16); + } + } + + tap_header hdr; + memcpy(hdr.identifier, "C64-TAPE-RAW", 12); + hdr.version = 1; + hdr.reserved[0] = hdr.reserved[1] = hdr.reserved[2] = 0; + hdr.data_len = tap_data.size(); + + fwrite(&hdr, sizeof(hdr), 1, stdout); + fwrite(tap_data.data(), tap_data.size(), 1, stdout); }