Ticket #4556: dccconv.c

File dccconv.c, 10.4 KB (added by Carl Eugen Hoyos, 11 years ago)

The C source of a converter that contains a lengthy explanation in the header

Line 
1/*
2 DCCCONV 2.0 (C) 1994-2003 Jac Goudsmit (Thanks to Kees Haasnoot and
3 Franc Zijderveld)
4 This program is freeware. You are allowed to use it in any way you wish,
5 as long as the above copyright statement (and this notice) is retained.
6
7 This command-line program converts a PASC file (as generated by the
8 PHILIPS DCC-Studio program) so that it can be played with all popular
9 MPEG1 layer I decoders.
10
11 What is the problem?
12
13 After much investigation, I have finally found out what the difference
14 between MPEG1 layer I (MP1) and PASC (Precision Adaptive Subband Coding,
15 the format used by Philips Digital Compact Cassette - DCC) is: NOTHING!
16
17 The problem is in the decoders that most people use, that are not fully
18 compatible with one little thing in the MP1 standard (ISO 11172-3, which
19 also describes the better-known MP3 standard). Thanks to Kees Haasnoot,
20 Franc Zijderveld (Philips Digital Systems Laboratories) and an anonymous
21 source who let me have a quick glance at the ISO 11172-3 document, I now
22 know this.
23
24 When a 44.1kHz audio stream is converted to a 384kbps MP1 frame, the
25 result is an average frame size of 417.959183 bytes. The MP1 standard
26 however requires frames to have lengths of multiples of 4 bytes. Every
27 DCC recorder knows how to intermingle frames of 416 bytes with frames
28 of 420 bytes to make an average of 384000 bits per second. In order to
29 make the servo system (which controls the speed of the tape during
30 playback) and other systems inside DCC recorders as simple as possible,
31 PASC always uses 420 bytes on the tape, and on the harddisk if you
32 use the DCC-Studio program.
33
34 An MPEG header consists of the following bits:
35 sync word: always 1111 1111 1111
36 ID: always 1 for MPEG audio
37 layer: 11 for layer I
38 protection bits: 1 for no CRC
39 bitrate: 1100 for 384kbps
40 sample frequency: 00 for 44.1kHz
41 PADDING BIT: 1 if the frame "contains an additional slot to adjust the
42 mean bitrate to the sampling frequency".
43 private bit: 0 (reserved)
44
45 In hexadecimal bytes, this is represented as FF FF FF C0 or FF FF FF C2.
46
47 So, PASC uses the padding bit (and adds 32 bits with value 0) at the end
48 of each frame, whenever only 416 bytes are needed to store the MPEG1 layer
49 I information. This is perfectly legal according to ISO11172-3.
50
51 Of course, this padding is only necessary because the storage medium is
52 tape instead of disk. In a disk file, the extra 4 bytes are a waste of
53 space. So, most MP1 encoders don't use padding and most decoders simply
54 ignore the padding bits because the programmers were too lazy or didn't
55 understand what is meant by "contains an additional slot to adjust the
56 mean bitrate to the sampling frequency". This wouldn't be such a big
57 deal if those decoders would also simply ignore the extra bits on the
58 padded frames, but they don't, get out of sync and stop playing, or
59 just play garbage.
60
61 This program will read a PASC-encoded file and will remove all padding
62 from all the frames in the file. It also removes the 2-byte header at
63 the start of the file which the DCC-Studio puts there, and which has no
64 apparent function. (If MPEG decoders would ignore any data until they
65 would find the sync word, this would also not be necessary - but they
66 don't). The program copies all the other data as-is. Therefore there
67 is no loss whatsoever from using this program.
68
69 The use of 420-byte tape frames makes it MUCH easier for programmers to
70 write a program like DCC-Studio to edit the PASC data on harddisk,
71 because each frame of data is exactly n * 420 bytes from the start of
72 the file (actually 2 + n * 420 bytes because of the 2-byte file header).
73 The .TRK files that DCC-Studio uses, use frame numbers instead of
74 byte-offsets. Maybe one day I will write a Windows program that
75 interprets the .TRK files, but for now you will have to use the "Save
76 Audio as One File" option in DCC-Studio.
77
78 Acknowledgements:
79 - The person at Philips who I got in contact with around 1994, who
80 explained that PASC was basically MPEG, sorry I lost your name
81 - Maarten Eijkhout for keeping the first (crude) version of this program
82 on his website, then taking it off and not replying emails. (Stupidly,
83 I lost the original source code so this is a complete rewrite)
84 - C.A.G. (Kees) Haasnoot for biting into the problem once again and
85 sending me source code for a fix in Turbo Pascal, and offering to put
86 it online on the DCC-FAQ website.
87 - Franc Zijderveld at Philips Digital System Laboratories, Eindhoven,
88 The Netherlands, for answering Kees' questions.
89 - The anonymous person(s) who put the ISO11172-3 document online in a
90 dark corner of the internet (Google for it yourself), which finally
91 solved the last piece of the puzzle.
92
93 The reasons I'm not using Kees Haasnoot's code are:
94 - It was written in Turbo Pascal and I don't have a compiler for it
95 - It needed interactive input for the file names instead of using command
96 line input.
97 - The program didn't really interpret the bit fields as this program does
98 (it just checked for C2 or C0), and didn't reset the padding bit (which
99 I know now actually makes the bitstream NON-compliant with MP1).
100 - The program handled one frame at a time: not very efficient, and error
101 handling was far from perfect.
102 Nevertheless, your input was very much appreciated, Kees!
103
104 The following program is ANSI C. It has been tested under Visual C++ 1.5
105 (16-bit DOS application) and Visual C++ 6.0 (32-bit Windows console app).
106 Other compilers should work equally well.
107 Note that because of the buffering that the C runtime library may do on
108 fread/fwrite, it's possible that the program seems to "hang" sometimes
109 when the buffers get flushed. Be patient, all will go back to normal after
110 a while. The program was tested with several files that contained more
111 than an hour's worth of PASC (more than you can get on one side of a tape!)
112 and the results were played from front to back on a pair of headphones
113 to check the result.
114
115 For more information on DCC, go to the DCC FAQ at
116 http://www.xs4all.nl/~jacg/dcc-faq.html
117*/
118
119#include <stdio.h>
120#include <malloc.h>
121#include <string.h>
122
123
124#ifdef _DOS
125 /* poor 16-bit Visual C++ 1.5 can't use arrays that are more than 32K
126 77*420 = 32340 bytes */
127 #define BLOCKSATONCE (77)
128#else
129 #define BLOCKSATONCE (100)
130#endif
131
132void processfile(FILE *fin, FILE*fout)
133{
134 unsigned char buf[BLOCKSATONCE * 420];
135 unsigned long offset = 2;
136 unsigned long oldoffset;
137 unsigned long outdone = 0;
138 size_t numtowrite = 0;
139 size_t numread = BLOCKSATONCE;
140
141 do
142 {
143 unsigned long u;
144 unsigned char *s = buf;
145 unsigned char *t = buf;
146
147 numread = fread(buf, 420, BLOCKSATONCE, fin);
148
149 oldoffset = offset;
150
151 for (u = 0; u < numread; u++)
152 {
153 unsigned sz = 420;
154
155 /* Even though the padding bit and the extra 4 bytes at the end of the
156 block are compliant to the MPEG1 layer 1 standard (ISO 11172),
157 most decoders don't understand them so they are removed here. */
158 if ((s[2] & 2) == 0) /* Padding bit in MPEG1 header */
159 {
160 s[2] &= ~2; /* Reset padding bit */
161 sz -= 4;
162 }
163
164 if (t != s)
165 {
166 memcpy(t, s, sz);
167 }
168
169 s += 420;
170 t += sz;
171
172 offset += 420;
173 }
174
175 numtowrite = t - buf;
176
177 u = oldoffset/1024;
178 printf("%luK\x08", u);
179 do
180 {
181 printf("\x08");
182 } while ((u/=10)!=0);
183
184 if (numtowrite)
185 {
186 unsigned long oldoutdone = outdone;
187 outdone += fwrite(buf, 1, numtowrite, fout);
188
189 if (oldoutdone == outdone)
190 {
191 break;
192 }
193 }
194 } while (numread == BLOCKSATONCE);
195
196 printf("%lu input bytes, %lu output bytes.\n", offset, outdone);
197}
198
199int main(int argc, char *argv[])
200{
201 int result = 0;
202
203 fprintf(stderr,
204 "DCC to MP1 converter 2.00 (C) 1994-2003 Jac Goudsmit\n\n");
205
206 if (argc < 2)
207 {
208 fprintf(stderr,
209 "Syntax: dccconv inputfile [inputfile...]\n"
210 "\n"
211 "Target files have their name changed to *.mp1 and are stored in the same\n"
212 "folder as the source file\n");
213
214 result = 1;
215 }
216 else
217 {
218 int i;
219
220 for (i = 1; i < argc; i++)
221 {
222 FILE *fin;
223
224 fin = fopen(argv[i], "rb");
225 if (fin)
226 {
227 unsigned char header[2];
228
229 if ( (fread(header, 1, 2, fin) == 2)
230 && (header[0] == 0x2C)
231 && (header[1] == 0))
232 {
233 FILE *fout;
234 char *fname;
235 char *p = NULL;
236 char *t, *s;
237
238 fname = malloc(strlen(argv[i]) + 4);
239 if (fname)
240 {
241 for (t = fname, s = argv[i]; *s; t++, s++)
242 {
243 if (*s == '.')
244 {
245 p = t;
246 }
247 *t = *s;
248 }
249
250 if (!p)
251 {
252 p = t;
253 }
254
255 strcpy(p, ".mp1");
256
257 fout = fopen(fname, "wb");
258 if (fout)
259 {
260 fprintf(stderr, "%s -> %s: ", argv[i], fname);
261
262 processfile(fin, fout);
263 if (ferror(fin))
264 {
265 fprintf(stderr, "Error reading input, partial file processed");
266 result = 2;
267 }
268 if (ferror(fout))
269 {
270 fprintf(stderr, "Error writing output, partial file processed");
271 result = 2;
272 }
273 fprintf(stderr, "\n");
274
275 fclose(fout);
276 }
277 else
278 {
279 fprintf(stderr, "%s: error opening file for output\n", fname);
280 result = 2;
281 }
282 free(fname);
283 }
284 else
285 {
286 fprintf(stderr, "Error allocating memory for output filename for %s\n", argv[i]);
287 result = 2;
288 }
289 }
290 else
291 {
292 fprintf(stderr, "File %s does not seem to be a PASC file (Starts with \\x%02X \\x%02X\n instead of 2C 00)\n", argv[i], header[0], header[1]);
293 result = 2;
294 }
295
296 fclose(fin);
297 }
298 else
299 {
300 fprintf(stderr, "%s: error opening file for input.\n", argv[i]);
301 result = 2;
302 }
303 }
304 }
305
306 return result;
307}
308