Output TAP files, and do some more tests.
[c64tapwav] / decode.c
1 #include <stdio.h>
2 #include <string.h>
3 #include <math.h>
4 #include <unistd.h>
5 #include <assert.h>
6 #include <vector>
7 #include <algorithm>
8
9 #define LANCZOS_RADIUS 30
10 #define BUFSIZE 4096
11 #define HYSTERESIS_LIMIT 3000
12 #define SAMPLE_RATE 44100
13 #define C64_FREQUENCY 985248
14 #define TAP_RESOLUTION 8
15
16 #define SYNC_PULSE_LENGTH 380.0
17 #define SYNC_TEST_TOLERANCE 1.10
18
19 struct tap_header {
20         char identifier[12];
21         char version;
22         char reserved[3];
23         unsigned int data_len;
24 };
25
26 double sinc(double x)
27 {
28         if (fabs(x) < 1e-6) {
29                 return 1.0f - fabs(x);
30         } else {
31                 return sin(x) / x;
32         }
33 }
34
35 #if 1
36 double weight(double x)
37 {
38         if (fabs(x) > LANCZOS_RADIUS) {
39                 return 0.0f;
40         }
41         return sinc(M_PI * x) * sinc(M_PI * x / LANCZOS_RADIUS);
42 }
43 #else
44 double weight(double x)
45 {
46         if (fabs(x) > 1.0f) {
47                 return 0.0f;
48         }
49         return 1.0f - fabs(x);
50 }
51 #endif
52
53 double interpolate(const std::vector<short> &pcm, double i)
54 {
55         int lower = std::max<int>(ceil(i - LANCZOS_RADIUS), 0);
56         int upper = std::min<int>(floor(i + LANCZOS_RADIUS), pcm.size() - 1);
57         double sum = 0.0f;
58
59         for (int x = lower; x <= upper; ++x) {
60                 sum += pcm[x] * weight(i - x);
61         }
62         return sum;
63 }
64         
65 // between [x,x+1]
66 double find_zerocrossing(const std::vector<short> &pcm, int x)
67 {
68         if (pcm[x] == 0) {
69                 return x;
70         }
71         if (pcm[x + 1] == 0) {
72                 return x + 1;
73         }
74
75         assert(pcm[x + 1] > 0);
76         assert(pcm[x] < 0);
77
78         double lower = x;
79         double upper = x + 1;
80         while (upper - lower > 1e-6) {
81                 double mid = 0.5f * (upper + lower);
82                 if (interpolate(pcm, mid) > 0) {
83                         upper = mid;
84                 } else {
85                         lower = mid;
86                 }
87         }
88
89         return 0.5f * (upper + lower);
90 }
91
92 struct pulse {
93         double time;  // in seconds from start
94         double len;   // in seconds
95 };
96         
97 int main(int argc, char **argv)
98 {
99         std::vector<short> pcm;
100
101         while (!feof(stdin)) {
102                 short buf[BUFSIZE];
103                 ssize_t ret = fread(buf, 2, BUFSIZE, stdin);
104                 if (ret >= 0) {
105                         pcm.insert(pcm.end(), buf, buf + ret);
106                 }
107         }       
108
109 #if 0
110         for (int i = 0; i < LEN; ++i) {
111                 in[i] += rand() % 10000;
112         }
113 #endif
114
115 #if 0
116         for (int i = 0; i < LEN; ++i) {
117                 printf("%d\n", in[i]);
118         }
119 #endif
120
121         std::vector<pulse> pulses;  // in seconds
122
123         // Find the flanks.
124         int last_bit = -1;
125         double last_upflank = -1;
126         int last_max_level = 0;
127         for (unsigned i = 0; i < pcm.size(); ++i) {
128                 int bit = (pcm[i] > 0) ? 1 : 0;
129                 if (bit == 1 && last_bit == 0 && last_max_level > HYSTERESIS_LIMIT) {
130                         // up-flank!
131                         double t = find_zerocrossing(pcm, i - 1) * (1.0 / SAMPLE_RATE);
132                         if (last_upflank > 0) {
133                                 pulse p;
134                                 p.time = t;
135                                 p.len = t - last_upflank;
136                                 pulses.push_back(p);
137                         }
138                         last_upflank = t;
139                         last_max_level = 0;
140                 }
141                 last_max_level = std::max(last_max_level, abs(pcm[i]));
142                 last_bit = bit;
143         }
144
145         // Calibrate on the first ~25k pulses (skip a few, just to be sure).
146         double calibration_factor = 1.0f;
147         if (pulses.size() < 20000) {
148                 fprintf(stderr, "Too few pulses, not calibrating!\n");
149         } else {
150                 double sum = 0.0;
151                 for (int i = 1000; i < 26000; ++i) {
152                         sum += pulses[i].len;
153                 }
154                 double mean_length = C64_FREQUENCY * sum / 25000.0f;
155                 calibration_factor = SYNC_PULSE_LENGTH / mean_length;
156                 fprintf(stderr, "Cycle length: %.2f -> 380.0 (change %+.2f%%)\n",
157                         mean_length, 100.0 * (calibration_factor - 1.0));
158         
159                 // Check for pulses outside +/- 10% (sign of misdetection).
160                 for (int i = 1000; i < 25000; ++i) {
161                         double cycles = pulses[i].len * calibration_factor * C64_FREQUENCY;
162                         if (cycles < SYNC_PULSE_LENGTH / SYNC_TEST_TOLERANCE || cycles > SYNC_PULSE_LENGTH * SYNC_TEST_TOLERANCE) {
163                                 fprintf(stderr, "Sync cycle with upflank at %.6f was detected at %.0f cycles; misdetect?\n",
164                                         pulses[i].time, cycles);
165                         }
166                 }
167         }
168
169         std::vector<char> tap_data;
170         for (unsigned i = 0; i < pulses.size(); ++i) {
171                 double cycles = pulses[i].len * calibration_factor * C64_FREQUENCY;
172                 int len = lrintf(cycles / TAP_RESOLUTION);
173                 if (i > 15000 && (cycles < 100 || cycles > 800)) {
174                         fprintf(stderr, "Cycle with upflank at %.6f was detected at %.0f cycles; misdetect?\n",
175                                         pulses[i].time, cycles);
176                 }
177                 if (len <= 255) {
178                         tap_data.push_back(len);
179                 } else {
180                         int overflow_len = lrintf(cycles);
181                         tap_data.push_back(0);
182                         tap_data.push_back(overflow_len & 0xff);
183                         tap_data.push_back((overflow_len >> 8) & 0xff);
184                         tap_data.push_back(overflow_len >> 16);
185                 }
186         }
187
188         tap_header hdr;
189         memcpy(hdr.identifier, "C64-TAPE-RAW", 12);
190         hdr.version = 1;
191         hdr.reserved[0] = hdr.reserved[1] = hdr.reserved[2] = 0;
192         hdr.data_len = tap_data.size();
193
194         fwrite(&hdr, sizeof(hdr), 1, stdout);
195         fwrite(tap_data.data(), tap_data.size(), 1, stdout);
196 }