Make sound manipulation commands platform-independent
[lsnes.git] / sdmp2sox.cpp
blob9817d8c516bbd469fa14b9038ad025ad4dbdd7d5
1 #include <cstdint>
2 #include <iostream>
3 #include <fstream>
4 #include <sstream>
5 #include <stdexcept>
6 #include <cstring>
8 #define FLAG_WIDTH 1
9 #define FLAG_HEIGHT 2
10 #define FLAG_FRAMERATE 4
11 #define FLAG_FULLRANGE 8
12 #define FLAG_ITU601 0
13 #define FLAG_ITU709 16
14 #define FLAG_SMPTE240M 32
15 #define FLAG_CS_MASK 48
16 #define FLAG_8BIT 64
18 //Heh, this happens to be exact hardware capacity of 1.44MB 90mm floppy. :-)
19 unsigned char yuv_buffer[1474560];
21 //30 bit values.
22 uint32_t ymatrix[0x80000];
23 uint32_t cbmatrix[0x80000];
24 uint32_t crmatrix[0x80000];
27 #define TOYUV(src, idx) do {\
28 uint32_t c = (static_cast<uint32_t>(src[(idx) + 0]) << 24) |\
29 (static_cast<uint32_t>(src[(idx) + 1]) << 16) |\
30 (static_cast<uint32_t>(src[(idx) + 2]) << 8) |\
31 static_cast<uint32_t>(src[(idx) + 3]);\
32 Y += ymatrix[c & 0x7FFFF];\
33 Cb += cbmatrix[c & 0x7FFFF];\
34 Cr += crmatrix[c & 0x7FFFF];\
35 } while(0)
37 #define STORE16(buffer,idx,shift,psep,v1,v2,v3)\
38 buffer[(idx)] = (v1 >> ((shift) + 8));\
39 buffer[(idx) + 1] = (v1 >> (shift));\
40 buffer[(idx) + (psep)] = (v2 >> ((shift) + 8));\
41 buffer[(idx) + 1 + (psep)] = (v2 >> (shift));\
42 buffer[(idx) + 2 * (psep)] = (v3 >> ((shift) + 8));\
43 buffer[(idx) + 1 + 2 * (psep)] = (v3 >> (shift));
45 #define STORE8(buffer,idx,shift,psep,v1,v2,v3)\
46 buffer[(idx)] = (v1 >> ((shift) + 8));\
47 buffer[(idx) + (psep)] = (v2 >> ((shift) + 8));\
48 buffer[(idx) + 2 * (psep)] = (v3 >> ((shift) + 8);\
50 template<unsigned shift>
51 struct store16
53 static const size_t esize = 2;
54 static void store(unsigned char* buffer, size_t idx, size_t psep, uint32_t v1, uint32_t v2,
55 uint32_t v3)
57 *reinterpret_cast<uint16_t*>(buffer + idx) = (v1 >> shift);
58 *reinterpret_cast<uint16_t*>(buffer + idx + psep) = (v2 >> shift);
59 *reinterpret_cast<uint16_t*>(buffer + idx + 2 * psep) = (v3 >> shift);
63 template<unsigned shift>
64 struct store8
66 static const size_t esize = 1;
67 static void store(unsigned char* buffer, size_t idx, size_t psep, uint32_t v1, uint32_t v2,
68 uint32_t v3)
70 buffer[idx] = (v1 >> (shift + 8));
71 buffer[idx + psep] = (v2 >> (shift + 8));
72 buffer[idx + 2 * psep] = (v3 >> (shift + 8));
76 template<typename T>
77 struct store_11
79 static const size_t esize = T::esize;
80 static void store(unsigned char* buffer, size_t idx, size_t psep, uint32_t v1, uint32_t v2,
81 uint32_t v3)
83 T::store(buffer, idx, psep, v1, v2, v3);
87 template<typename T>
88 struct store_12
90 static const size_t esize = 2 * T::esize;
91 static void store(unsigned char* buffer, size_t idx, size_t psep, uint32_t v1, uint32_t v2,
92 uint32_t v3)
94 T::store(buffer, idx, psep, v1, v2, v3);
95 T::store(buffer, idx + T::esize, psep, v1, v2, v3);
99 template<typename T, size_t llen>
100 struct store_21
102 static const size_t esize = T::esize;
103 static void store(unsigned char* buffer, size_t idx, size_t psep, uint32_t v1, uint32_t v2,
104 uint32_t v3)
106 T::store(buffer, idx, psep, v1, v2, v3);
107 T::store(buffer, idx + T::esize * llen, psep, v1, v2, v3);
111 template<typename T, size_t llen>
112 struct store_22
114 static const size_t esize = 2 * T::esize;
115 static void store(unsigned char* buffer, size_t idx, size_t psep, uint32_t v1, uint32_t v2,
116 uint32_t v3)
118 T::store(buffer, idx, psep, v1, v2, v3);
119 T::store(buffer, idx + T::esize, psep, v1, v2, v3);
120 T::store(buffer, idx + 2 * T::esize * llen, psep, v1, v2, v3);
121 T::store(buffer, idx + 2 * T::esize * llen + T::esize, psep, v1, v2, v3);
125 template<typename T>
126 struct convert_11
128 static const size_t isize = 4;
129 static const size_t esize = 2 * T::esize;
130 static void convert(unsigned char* obuffer, size_t oidx, size_t psep, const unsigned char* ibuffer,
131 size_t iidx)
133 uint32_t Y = 0;
134 uint32_t Cb = 0;
135 uint32_t Cr = 0;
136 TOYUV(ibuffer, iidx);
137 T::store(obuffer, oidx, psep, Y, Cb, Cr);
141 template<typename T, size_t s>
142 struct convert_x_helper
144 static void convert(unsigned char* obuffer, size_t oidx, size_t psep, const unsigned char* ibuffer,
145 size_t iidx)
147 uint32_t Y = 0;
148 uint32_t Cb = 0;
149 uint32_t Cr = 0;
150 TOYUV(ibuffer, iidx);
151 TOYUV(ibuffer, iidx + s);
152 T::store(obuffer, oidx, psep, Y, Cb, Cr);
156 template<typename T>
157 struct convert_12
159 static const size_t isize = 8;
160 static const size_t esize = T::esize;
161 static void convert(unsigned char* obuffer, size_t oidx, size_t psep, const unsigned char* ibuffer,
162 size_t iidx)
164 convert_x_helper<T, 4>::convert(obuffer, oidx, psep, ibuffer, iidx);
168 template<typename T>
169 struct convert_21
171 static const size_t isize = 4;
172 static const size_t esize = T::esize;
173 static void convert(unsigned char* obuffer, size_t oidx, size_t psep, const unsigned char* ibuffer,
174 size_t iidx)
176 convert_x_helper<T, 2048>::convert(obuffer, oidx, psep, ibuffer, iidx);
180 template<typename T>
181 struct convert_22
183 static const size_t isize = 8;
184 static const size_t esize = T::esize;
185 static void convert(unsigned char* obuffer, size_t oidx, size_t psep, const unsigned char* ibuffer,
186 size_t iidx)
188 uint32_t Y = 0;
189 uint32_t Cb = 0;
190 uint32_t Cr = 0;
191 TOYUV(ibuffer, iidx);
192 TOYUV(ibuffer, iidx + 4);
193 TOYUV(ibuffer, iidx + 2048);
194 TOYUV(ibuffer, iidx + 2052);
195 T::store(obuffer, oidx, psep, Y, Cb, Cr);
199 template<typename T, size_t ents>
200 struct convert_line
202 static void convert(unsigned char* obuffer, size_t psep, const unsigned char* ibuffer)
204 for(unsigned i = 0; i < ents; i++)
205 T::convert(obuffer, T::esize * i, psep, ibuffer, T::isize * i);
209 #define RGB2YUV_SHIFT 14
211 void init_matrix(double Kb, double Kr, bool fullrange)
213 double RY = Kr;
214 double GY = 1 - Kr - Kb;
215 double BY = Kb;
216 double RPb = -0.5 * Kr / (1 - Kb);
217 double GPb = -0.5 * (1 - Kr - Kb) / (1 - Kb);
218 double BPb = 0.5;
219 double RPr = 0.5;
220 double GPr = -0.5 * (1 - Kr - Kb) / (1 - Kr);
221 double BPr = -0.5 * Kb / (1 - Kr);
222 for(uint32_t i = 0; i < 0x80000; i++) {
223 uint32_t l = (i >> 15) & 0xF;
224 //Range of (r,g,b) is 0...465.
225 uint32_t r = (l * ((i >> 0) & 0x1F));
226 uint32_t g = (l * ((i >> 5) & 0x1F));
227 uint32_t b = (l * ((i >> 10) & 0x1F));
228 double Y = (RY * r + GY * g + BY * b) / 465 * (fullrange ? 255 : 219) + (fullrange ? 0 : 16);
229 double Cb = (RPb * r + GPb * g + BPb * b) / 465 * (fullrange ? 255 : 224) + 128;
230 double Cr = (RPr * r + GPr * g + BPr * b) / 465 * (fullrange ? 255 : 224) + 128;
231 ymatrix[i] = static_cast<uint32_t>(Y * 4194304 + 0.5);
232 cbmatrix[i] = static_cast<uint32_t>(Cb * 4194304 + 0.5);
233 crmatrix[i] = static_cast<uint32_t>(Cr * 4194304 + 0.5);
237 //Load RGB to YUV conversion matrix.
238 void load_rgb2yuv_matrix(uint32_t flags)
240 switch(flags & (FLAG_CS_MASK | FLAG_FULLRANGE))
242 case FLAG_ITU601:
243 init_matrix(0.114, 0.229, false);
244 break;
245 case FLAG_ITU601 | FLAG_FULLRANGE:
246 init_matrix(0.114, 0.229, true);
247 break;
248 case FLAG_ITU709:
249 init_matrix(0.0722, 0.2126, false);
250 break;
251 case FLAG_ITU709 | FLAG_FULLRANGE:
252 init_matrix(0.0722, 0.2126, true);
253 break;
254 case FLAG_SMPTE240M:
255 init_matrix(0.087, 0.212, false);
256 break;
257 case FLAG_SMPTE240M | FLAG_FULLRANGE:
258 init_matrix(0.087, 0.212, true);
259 break;
260 default:
261 init_matrix(0.114, 0.229, false);
262 break;
266 //Render a line pair of YUV.
267 void render_yuv(unsigned char* buffer, const unsigned char* src, size_t psep, uint32_t flags, bool hires,
268 bool interlaced)
270 unsigned c = 0;
271 if(flags & FLAG_WIDTH)
272 c |= 1;
273 if(flags & FLAG_HEIGHT)
274 c |= 2;
275 if(hires)
276 c |= 4;
277 if(interlaced)
278 c |= 8;
279 if(flags & FLAG_8BIT)
280 c |= 16;
281 switch(c) {
282 case 0: { //256 x 224/240 -> 256 x 224/240 16 bit.
283 convert_line<convert_11<store_11<store16<14>>>, 256>::convert(buffer, psep, src);
284 break;
286 case 1: { //256 x 224/240 -> 512 x 224/240 16 bit.
287 convert_line<convert_11<store_12<store16<14>>>, 256>::convert(buffer, psep, src);
288 break;
290 case 2: { //256 x 224/240 -> 256 x 448/480 16 bit.
291 convert_line<convert_11<store_21<store16<14>, 256>>, 256>::convert(buffer, psep, src);
292 break;
294 case 3: { //256 x 224/240 -> 512 x 448/480 16 bit.
295 convert_line<convert_11<store_22<store16<14>, 256>>, 256>::convert(buffer, psep, src);
296 break;
298 case 4: { //512 x 224/240 -> 256 x 224/240 16 bit.
299 convert_line<convert_12<store_11<store16<15>>>, 256>::convert(buffer, psep, src);
300 break;
302 case 5: { //512 x 224/240 -> 512 x 224/240 16 bit.
303 convert_line<convert_11<store_11<store16<14>>>, 512>::convert(buffer, psep, src);
304 break;
306 case 6: { //512 x 224/240 -> 256 x 448/480 16 bit.
307 convert_line<convert_12<store_21<store16<15>, 256>>, 256>::convert(buffer, psep, src);
308 break;
310 case 7: { //512 x 224/240 -> 512 x 448/480 16 bit.
311 convert_line<convert_11<store_21<store16<14>, 512>>, 512>::convert(buffer, psep, src);
312 break;
314 case 8: { //256 x 448x480 -> 256 x 224/240 16 bit.
315 convert_line<convert_21<store_11<store16<15>>>, 256>::convert(buffer, psep, src);
316 break;
318 case 9: { //256 x 448x480 -> 512 x 224/240 16 bit.
319 convert_line<convert_21<store_12<store16<15>>>, 256>::convert(buffer, psep, src);
320 break;
322 case 10: { //256 x 448x480 -> 256 x 448/480 16 bit.
323 convert_line<convert_11<store_11<store16<14>>>, 256>::convert(buffer, psep, src);
324 convert_line<convert_11<store_11<store16<14>>>, 256>::convert(buffer + 512, psep, src + 2048);
325 break;
327 case 11: { //256 x 448x480 -> 512 x 448/480 16 bit.
328 convert_line<convert_11<store_12<store16<14>>>, 256>::convert(buffer, psep, src);
329 convert_line<convert_11<store_12<store16<14>>>, 256>::convert(buffer + 1024, psep, src + 2048);
330 break;
332 case 12: { //512 x 448x480 -> 256 x 224/240 16 bit.
333 convert_line<convert_22<store_11<store16<16>>>, 256>::convert(buffer, psep, src);
334 break;
336 case 13: { //512 x 448x480 -> 512 x 224/240 16 bit.
337 convert_line<convert_21<store_11<store16<15>>>, 512>::convert(buffer, psep, src);
338 break;
340 case 14: { //512 x 448x480 -> 256 x 448/480 16 bit.
341 convert_line<convert_11<store_21<store16<14>, 256>>, 256>::convert(buffer, psep, src);
342 convert_line<convert_11<store_21<store16<14>, 256>>, 256>::convert(buffer + 512, psep, src + 2048);
343 break;
345 case 15: { //512 x 448x480 -> 512 x 448/480 16 bit.
346 convert_line<convert_11<store_11<store16<14>>>, 512>::convert(buffer, psep, src);
347 convert_line<convert_11<store_11<store16<14>>>, 512>::convert(buffer + 1024, psep, src + 2048);
348 break;
350 case 16: { //256 x 224/240 -> 256 x 224/240 16 bit.
351 convert_line<convert_11<store_11<store8<14>>>, 256>::convert(buffer, psep, src);
352 break;
354 case 17: { //256 x 224/240 -> 512 x 224/240 16 bit.
355 convert_line<convert_11<store_12<store8<14>>>, 256>::convert(buffer, psep, src);
356 break;
358 case 18: { //256 x 224/240 -> 256 x 448/480 16 bit.
359 convert_line<convert_11<store_21<store8<14>, 256>>, 256>::convert(buffer, psep, src);
360 break;
362 case 19: { //256 x 224/240 -> 512 x 448/480 16 bit.
363 convert_line<convert_11<store_22<store8<14>, 256>>, 256>::convert(buffer, psep, src);
364 break;
366 case 20: { //512 x 224/240 -> 256 x 224/240 16 bit.
367 convert_line<convert_12<store_11<store8<15>>>, 256>::convert(buffer, psep, src);
368 break;
370 case 21: { //512 x 224/240 -> 512 x 224/240 16 bit.
371 convert_line<convert_11<store_11<store8<14>>>, 512>::convert(buffer, psep, src);
372 break;
374 case 22: { //512 x 224/240 -> 256 x 448/480 16 bit.
375 convert_line<convert_12<store_21<store8<15>, 256>>, 256>::convert(buffer, psep, src);
376 break;
378 case 23: { //512 x 224/240 -> 512 x 448/480 16 bit.
379 convert_line<convert_11<store_21<store8<14>, 512>>, 512>::convert(buffer, psep, src);
380 break;
382 case 24: { //256 x 448x480 -> 256 x 224/240 16 bit.
383 convert_line<convert_21<store_11<store8<15>>>, 256>::convert(buffer, psep, src);
384 break;
386 case 25: { //256 x 448x480 -> 512 x 224/240 16 bit.
387 convert_line<convert_21<store_12<store8<15>>>, 256>::convert(buffer, psep, src);
388 break;
390 case 26: { //256 x 448x480 -> 256 x 448/480 16 bit.
391 convert_line<convert_11<store_11<store8<14>>>, 256>::convert(buffer, psep, src);
392 convert_line<convert_11<store_11<store8<14>>>, 256>::convert(buffer + 256, psep, src + 2048);
393 break;
395 case 27: { //256 x 448x480 -> 512 x 448/480 16 bit.
396 convert_line<convert_11<store_12<store8<14>>>, 256>::convert(buffer, psep, src);
397 convert_line<convert_11<store_12<store8<14>>>, 256>::convert(buffer + 512, psep, src + 2048);
398 break;
400 case 28: { //512 x 448x480 -> 256 x 224/240 16 bit.
401 convert_line<convert_22<store_11<store8<16>>>, 256>::convert(buffer, psep, src);
402 break;
404 case 29: { //512 x 448x480 -> 512 x 224/240 16 bit.
405 convert_line<convert_21<store_11<store8<15>>>, 512>::convert(buffer, psep, src);
406 break;
408 case 30: { //512 x 448x480 -> 256 x 448/480 16 bit.
409 convert_line<convert_11<store_21<store8<14>, 256>>, 256>::convert(buffer, psep, src);
410 convert_line<convert_11<store_21<store8<14>, 256>>, 256>::convert(buffer + 256, psep, src + 2048);
411 break;
413 case 31: { //512 x 448x480 -> 512 x 448/480 16 bit.
414 convert_line<convert_11<store_11<store8<14>>>, 512>::convert(buffer, psep, src);
415 convert_line<convert_11<store_11<store8<14>>>, 512>::convert(buffer + 512, psep, src + 2048);
416 break;
421 uint64_t double_to_ieeefp(double v)
423 unsigned mag = 1023;
424 while(v >= 2) {
425 mag++;
426 v /= 2;
428 while(v < 1) {
429 mag--;
430 v *= 2;
432 uint64_t v2 = mag;
433 v -= 1;
434 for(unsigned i = 0; i < 52; i++) {
435 v *= 2;
436 v2 = 2 * v2 + ((v >= 1) ? 1 : 0);
437 if(v >= 1)
438 v -= 1;
440 return v2;
443 void sdump2sox(std::istream& in, std::ostream& yout, std::ostream& sout, uint32_t flags)
445 unsigned char header[12];
446 in.read(reinterpret_cast<char*>(header), 12);
447 if(!in)
448 throw std::runtime_error("Can't read sdump header");
449 if(header[0] != 'S' || header[1] != 'D' || header[2] != 'M' || header[3] != 'P')
450 throw std::runtime_error("Bad sdump magic");
451 uint32_t apurate;
452 uint32_t cpurate;
453 cpurate = (static_cast<uint32_t>(header[4]) << 24) |
454 (static_cast<uint32_t>(header[5]) << 16) |
455 (static_cast<uint32_t>(header[6]) << 8) |
456 static_cast<uint32_t>(header[7]);
457 apurate = (static_cast<uint32_t>(header[8]) << 24) |
458 (static_cast<uint32_t>(header[9]) << 16) |
459 (static_cast<uint32_t>(header[10]) << 8) |
460 static_cast<uint32_t>(header[11]);
461 uint64_t sndrateR = double_to_ieeefp(static_cast<double>(apurate) / 768.0);
462 unsigned char sox_header[32] = {0};
463 sox_header[0] = 0x2E; //Magic
464 sox_header[1] = 0x53; //Magic
465 sox_header[2] = 0x6F; //Magic
466 sox_header[3] = 0x58; //Magic
467 sox_header[4] = 0x1C; //Magic
468 sox_header[16] = sndrateR;
469 sox_header[17] = sndrateR >> 8;
470 sox_header[18] = sndrateR >> 16;
471 sox_header[19] = sndrateR >> 24;
472 sox_header[20] = sndrateR >> 32;
473 sox_header[21] = sndrateR >> 40;
474 sox_header[22] = sndrateR >> 48;
475 sox_header[23] = sndrateR >> 56;
476 sox_header[24] = 2;
477 sout.write(reinterpret_cast<char*>(sox_header), 32);
478 if(!sout)
479 throw std::runtime_error("Can't write audio header");
480 uint64_t samples = 0;
481 uint64_t frames = 0;
482 unsigned wrongrate = 0;
483 bool is_pal = false;
484 load_rgb2yuv_matrix(flags);
485 while(true) {
486 unsigned char cmd;
487 bool lf = false;
488 in >> cmd;
489 if(!in)
490 break; //End of stream.
491 if((cmd & 0xF0) == 0) {
492 //Pictrue. Read the 1MiB of picture data one line pair at a time.
493 unsigned char buf[4096];
494 unsigned physline = 0;
495 bool hires = (cmd & 1);
496 bool interlaced = (cmd & 2);
497 bool overscan = (cmd & 4);
498 bool pal = (cmd & 8);
499 bool ohires = (flags & FLAG_WIDTH);
500 bool ointerlaced = (flags & FLAG_HEIGHT);
501 bool bits8 = (flags & FLAG_8BIT);
502 size_t psep = (ohires ? 512 : 256) * (ointerlaced ? 2 : 1) * (pal ? 240 : 224) *
503 (bits8 ? 1 : 2);
504 size_t lsep = (ohires ? 512 : 256) * (ointerlaced ? 2 : 1) * (bits8 ? 1 : 2);
505 for(unsigned i = 0; i < 256; i++) {
506 in.read(reinterpret_cast<char*>(buf), 4096);
507 if(!in)
508 throw std::runtime_error("Can't read picture payload");
509 is_pal = is_pal || pal;
510 if(overscan && i < 9)
511 continue;
512 if(!overscan && i < 1)
513 continue;
514 if(pal & physline >= 239)
515 continue;
516 if(!pal & physline >= 224)
517 continue;
518 render_yuv(yuv_buffer + physline * lsep, buf, psep, flags, hires, interlaced);
519 physline++;
521 if(pal) {
522 //Render a black line to pad the image.
523 memset(buf, 0, 4096);
524 render_yuv(yuv_buffer + 239 * lsep, buf, psep, flags, hires, interlaced);
526 size_t yuvsize = 3 * psep;
527 unsigned times = 1;
528 if((flags & FLAG_FRAMERATE) == 0 && !is_pal && interlaced) {
529 //This uses 357368 TU instead of 357366 TU.
530 //-> Every 178683rd frame is duplicated.
531 if(wrongrate == 178682) {
532 times = 2;
533 wrongrate = 0;
534 } else
535 wrongrate++;
537 if((flags & FLAG_FRAMERATE) != 0 && !is_pal && !interlaced) {
538 //This uses 357366 TU instead of 357368 TU.
539 //-> Every 178684th frame is dropped.
540 if(wrongrate == 178683) {
541 times = 0;
542 wrongrate = 0;
543 } else
544 wrongrate++;
546 for(unsigned k = 0; k < times; k++)
547 yout.write(reinterpret_cast<char*>(yuv_buffer), yuvsize);
548 if(!yout)
549 throw std::runtime_error("Can't write frame");
550 frames += times;
551 lf = true;
552 } else if(cmd == 16) {
553 //Sound packet. Interesting.
554 unsigned char ibuf[4];
555 unsigned char obuf[8];
556 in.read(reinterpret_cast<char*>(ibuf), 4);
557 if(!in)
558 throw std::runtime_error("Can't read sound packet payload");
559 obuf[0] = 0;
560 obuf[1] = 0;
561 obuf[2] = ibuf[1];
562 obuf[3] = ibuf[0];
563 obuf[4] = 0;
564 obuf[5] = 0;
565 obuf[6] = ibuf[3];
566 obuf[7] = ibuf[2];
567 sout.write(reinterpret_cast<char*>(obuf), 8);
568 if(!sout)
569 throw std::runtime_error("Can't write audio sample");
570 samples++;
571 } else {
572 std::ostringstream str;
573 str << "Unknown command byte " << static_cast<unsigned>(cmd);
574 throw std::runtime_error(str.str());
576 if(lf && frames % 100 == 0) {
577 std::cout << "\e[1G" << frames << " frames, " << samples << " samples." << std::flush;
580 //Sox internally multiplies sample count by channel count.
581 sox_header[8] = samples << 1;
582 sox_header[9] = samples >> 7;
583 sox_header[10] = samples >> 15;
584 sox_header[11] = samples >> 23;
585 sox_header[12] = samples >> 31;
586 sox_header[13] = samples >> 39;
587 sox_header[14] = samples >> 47;
588 sox_header[15] = samples >> 55;
589 sout.seekp(0, std::ios::beg);
590 if(!sout)
591 throw std::runtime_error("Can't seek to fix .sox header");
592 sout.write(reinterpret_cast<char*>(sox_header), 32);
593 if(!sout)
594 throw std::runtime_error("Can't fix audio header");
595 std::cout << "Sound sampling rate is " << static_cast<double>(apurate) / 768.0 << "Hz" << std::endl;
596 std::cout << "Wrote " << samples << " samples." << std::endl;
597 std::cout << "Audio length is " << 768.0 * samples / apurate << "s." << std::endl;
598 double vrate = 0;
599 double vrate2 = 0;
600 if(is_pal)
601 vrate2 = 425568.0;
602 else if(flags & FLAG_FRAMERATE)
603 vrate2 = 357368.0;
604 else
605 vrate2 = 357366.0;
606 vrate = cpurate / vrate2;
607 std::cout << "Video frame rate is " << cpurate << "/" << vrate2 << "Hz" << std::endl;
608 std::cout << "Wrote " << frames << " frames." << std::endl;
609 std::cout << "Video length is " << frames / vrate << "s." << std::endl;
612 void syntax()
614 std::cerr << "Syntax: sdump2sox [<options>] <input-file> <yuv-output-file> <sox-output-file>" << std::endl;
615 std::cerr << "-W\tDump 512-wide instead of 256-wide." << std::endl;
616 std::cerr << "-H\tDump 448/480-high instead of 224/240-high." << std::endl;
617 std::cerr << "-F\tDump at interlaced framerate instead of non-interlaced." << std::endl;
618 std::cerr << "-f\tDump using full range instead of TV range." << std::endl;
619 std::cerr << "-7\tDump using ITU.709 instead of ITU.601." << std::endl;
620 std::cerr << "-2\tDump using SMPTE-240M instead of ITU.601." << std::endl;
621 std::cerr << "-8\tDump using 8 bits instead of 16 bits." << std::endl;
624 int main(int argc, char** argv)
626 if(argc < 4) {
627 syntax();
628 return 1;
630 uint32_t flags = 0;
631 uint32_t idx1 = 0;
632 uint32_t idx2 = 0;
633 uint32_t idx3 = 0;
634 for(unsigned i = 1; i < argc; i++) {
635 if(argv[i][0] == '-')
636 for(unsigned j = 1; argv[i][j]; j++)
637 switch(argv[i][j]) {
638 case 'W':
639 flags |= FLAG_WIDTH;
640 break;
641 case 'H':
642 flags |= FLAG_HEIGHT;
643 break;
644 case 'F':
645 flags |= FLAG_FRAMERATE;
646 break;
647 case 'f':
648 flags |= FLAG_FULLRANGE;
649 break;
650 case '7':
651 if(flags & FLAG_CS_MASK) {
652 syntax();
653 return 1;
655 flags |= FLAG_ITU709;
656 break;
657 case '2':
658 if(flags & FLAG_CS_MASK) {
659 syntax();
660 return 1;
662 flags |= FLAG_SMPTE240M;
663 break;
664 case '8':
665 flags |= FLAG_8BIT;
666 break;
667 default:
668 syntax();
669 return 1;
671 else if(!idx1)
672 idx1 = i;
673 else if(!idx2)
674 idx2 = i;
675 else if(!idx3)
676 idx3 = i;
677 else {
678 syntax();
679 return 1;
682 std::ifstream in(argv[idx1], std::ios::in | std::ios::binary);
683 if(!in) {
684 std::cerr << "Error: Can't open '" << argv[idx1] << "'" << std::endl;
685 return 2;
687 std::ofstream yout(argv[idx2], std::ios::out | std::ios::binary);
688 if(!yout) {
689 std::cerr << "Error: Can't open '" << argv[idx2] << "'" << std::endl;
690 return 2;
692 std::ofstream sout(argv[idx3], std::ios::out | std::ios::binary);
693 if(!sout) {
694 std::cerr << "Error: Can't open '" << argv[idx3] << "'" << std::endl;
695 return 2;
697 try {
698 sdump2sox(in, yout, sout, flags);
699 in.close();
700 yout.close();
701 sout.close();
702 } catch(std::exception& e) {
703 std::cerr << "Error: " << e.what() << std::endl;
704 in.close();
705 yout.close();
706 sout.close();
707 return 3;
709 return 0;