|
|
1.1 ! root 1: // ! 2: // nono ! 3: // Copyright (C) 2025 nono project ! 4: // Licensed under nono-license.txt ! 5: // ! 6: ! 7: // ADPCM <-> PCM Converter ! 8: ! 9: #include "header.h" ! 10: #include <vector> ! 11: #include <fcntl.h> ! 12: #include <unistd.h> ! 13: #include <sys/stat.h> ! 14: ! 15: //--- ここから adpcm.h 相当 --- ! 16: ! 17: class ADPCMDevice ! 18: { ! 19: public: ! 20: uint32 pcm2adpcm_step(int16 pcm); ! 21: int16 adpcm2pcm_step(uint32 data); ! 22: private: ! 23: inline uint32 adpcm_encode(int32 dn); ! 24: inline int32 adpcm_decode(uint32 data, int32 xn); ! 25: inline void adpcm_calcstep(uint32 data); ! 26: ! 27: int32 xprev {}; ! 28: int stepidx {}; ! 29: static const int adpcm_stepadj[16]; ! 30: static const int32 adpcm_stepsize[49]; ! 31: }; ! 32: ! 33: //--- ここまで adpcm.h 相当 --- ! 34: //--- ここから adpcm.cpp 相当 --- ! 35: ! 36: inline uint32 ! 37: ADPCMDevice::adpcm_encode(int32 dn) ! 38: { ! 39: uint32 Ln = 0; ! 40: ! 41: int ss = adpcm_stepsize[stepidx]; ! 42: ! 43: if (dn < 0) { ! 44: Ln = 0x8; ! 45: dn = -dn; ! 46: } ! 47: if (dn >= ss) { ! 48: Ln |= 0x4; ! 49: dn -= ss; ! 50: } ! 51: ss /= 2; ! 52: if (dn >= ss) { ! 53: Ln |= 0x2; ! 54: dn -= ss; ! 55: } ! 56: ss /= 2; ! 57: if (dn >= ss) { ! 58: Ln |= 0x1; ! 59: } ! 60: return Ln; ! 61: } ! 62: ! 63: inline int32 ! 64: ADPCMDevice::adpcm_decode(uint32 data, int32 xn) ! 65: { ! 66: int b3 = (data & 8) ? 1 : 0; ! 67: int b2 = (data & 4) ? 1 : 0; ! 68: int b1 = (data & 2) ? 1 : 0; ! 69: int b0 = (data & 1); ! 70: ! 71: int32 ss = adpcm_stepsize[stepidx]; ! 72: int32 dn = (ss * b2) + (ss / 2 * b1) + (ss / 4 * b0) + ss / 8; ! 73: if (b3) { ! 74: dn = -dn; ! 75: } ! 76: ! 77: xn += dn; ! 78: ! 79: // Saturate in 12 bits. ! 80: if (__predict_false(xn > 2047)) { ! 81: xn = 2047; ! 82: } else if (__predict_false(xn < -2047)) { ! 83: xn = -2047; ! 84: } ! 85: ! 86: return xn; ! 87: } ! 88: ! 89: inline void ! 90: ADPCMDevice::adpcm_calcstep(uint32 data) ! 91: { ! 92: stepidx += adpcm_stepadj[data]; ! 93: if (__predict_false(stepidx < 0)) { ! 94: stepidx = 0; ! 95: } else if (__predict_false(stepidx > 48)) { ! 96: stepidx = 48; ! 97: } ! 98: } ! 99: ! 100: uint32 ! 101: ADPCMDevice::pcm2adpcm_step(int16 pcm) ! 102: { ! 103: // Input of this algorithm is 12 bit. ! 104: int32 xn = pcm / 16; ! 105: int32 dn = xn - xprev; ! 106: uint32 Ln = adpcm_encode(dn); ! 107: ! 108: // next はこの Ln をデコードして PCM にした値 ! 109: xprev = adpcm_decode(Ln, xprev); ! 110: adpcm_calcstep(Ln); ! 111: ! 112: return Ln; ! 113: } ! 114: ! 115: int16 ! 116: ADPCMDevice::adpcm2pcm_step(uint32 data) ! 117: { ! 118: int32 xn = adpcm_decode(data, xprev); ! 119: ! 120: // next ! 121: xprev = xn; ! 122: adpcm_calcstep(data); ! 123: ! 124: // このアルゴリズムの出力は(下詰めで) 12 bit。 ! 125: // ただし MSM6258 の出力 DAC は 10 bit 分しかない。 ! 126: xn /= 4; ! 127: // その 10 bit を 16 bit PCM にする。 ! 128: xn *= 64; ! 129: return xn; ! 130: } ! 131: ! 132: /*static*/ const int ! 133: ADPCMDevice::adpcm_stepadj[16] = { ! 134: -1, -1, -1, -1, 2, 4, 6, 8, ! 135: -1, -1, -1, -1, 2, 4, 6, 8, ! 136: }; ! 137: ! 138: /*static*/ const int32 ! 139: ADPCMDevice::adpcm_stepsize[49] = { ! 140: 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, ! 141: 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, ! 142: 107, 118, 130, 143, 157, 173, 190, 209, 230, 253, ! 143: 279, 307, 337, 371, 408, 449, 494, 544, 598, 658, ! 144: 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552, ! 145: }; ! 146: ! 147: //--- ここまで adpcm.cpp 相当 --- ! 148: ! 149: #define RIFF(a,b,c,d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d)) ! 150: #define VERBOSE(fmt...) do { if (verbose) printf(fmt); } while (0) ! 151: ! 152: struct chunk_header { ! 153: uint32 chunktype; ! 154: uint32 chunksize; ! 155: }; ! 156: ! 157: struct fmt_chunk { ! 158: uint16 fmtid; ! 159: uint16 nchannels; ! 160: uint32 frequency; ! 161: uint32 byterate; ! 162: uint16 framesize; ! 163: uint16 bitdepth; ! 164: } __packed; ! 165: ! 166: static void usage(); ! 167: static bool is_wavfile(const char *); ! 168: static void encode_file(const char *, const char *); ! 169: static void read_fmt_chunk(int ifd, const char *infile); ! 170: static void read_data_chunk(int ifd, int ofd, uint32 datasize, ! 171: const char *infile, const char *outfile); ! 172: static void decode_file(const char *, const char *); ! 173: ! 174: static const char wav_magic[12] = { ! 175: 'R', 'I', 'F', 'F', 0, 0, 0, 0, 'W', 'A', 'V', 'E' ! 176: }; ! 177: ! 178: static const int freqtable[] = { ! 179: 0, ! 180: 3906, ! 181: 5208, ! 182: 7812, ! 183: 10416, ! 184: 15625, ! 185: }; ! 186: ! 187: static ADPCMDevice *dev; ! 188: static int freq; ! 189: static bool verbose; ! 190: ! 191: int ! 192: main(int ac, char *av[]) ! 193: { ! 194: enum { ! 195: CMD_AUTO, ! 196: CMD_ENCODE, ! 197: CMD_DECODE, ! 198: } cmd {}; ! 199: int c; ! 200: int f = 0; ! 201: ! 202: while ((c = getopt(ac, av, "def:v")) != -1) { ! 203: switch (c) { ! 204: case 'd': ! 205: cmd = CMD_DECODE; ! 206: break; ! 207: case 'e': ! 208: cmd = CMD_ENCODE; ! 209: break; ! 210: case 'f': ! 211: { ! 212: f = atoi(optarg); ! 213: if (f < 0 || f > countof(freqtable)) { ! 214: usage(); ! 215: } ! 216: break; ! 217: } ! 218: case 'v': ! 219: verbose = true; ! 220: break; ! 221: default: ! 222: usage(); ! 223: } ! 224: } ! 225: ac -= optind; ! 226: av += optind; ! 227: ! 228: if (ac < 2) { ! 229: usage(); ! 230: } ! 231: const char *infile = av[0]; ! 232: const char *outfile = av[1]; ! 233: ! 234: if (cmd == CMD_AUTO) { ! 235: if (is_wavfile(infile)) { ! 236: cmd = CMD_ENCODE; ! 237: VERBOSE("Input file looks WAV\n"); ! 238: } else { ! 239: cmd = CMD_DECODE; ! 240: VERBOSE("Input file looks ADPCM\n"); ! 241: } ! 242: } ! 243: assert(cmd == CMD_ENCODE || cmd == CMD_DECODE); ! 244: ! 245: dev = new ADPCMDevice(); ! 246: ! 247: if (cmd == CMD_DECODE) { ! 248: // ADPCM -> PCM ! 249: if (f == 0) { ! 250: f = 5; ! 251: } ! 252: freq = freqtable[f]; ! 253: decode_file(infile, outfile); ! 254: } else { ! 255: // PCM -> ADPCM ! 256: encode_file(infile, outfile); ! 257: } ! 258: ! 259: return 0; ! 260: } ! 261: ! 262: static void ! 263: usage() ! 264: { ! 265: printf("%s [-e] <PCMfile> <out.adpcm>\n", getprogname()); ! 266: printf("%s [-d] [-f<n> ] <ADPCMfile> <out.PCM>\n", getprogname()); ! 267: printf(" -f1: 3.9 kHz, -f2: 5.2 kHz, -f3: 7.8 kHz,"); ! 268: printf(" -f4: 10.4 kHz, -f5: 15.6 kHz\n"); ! 269: exit(1); ! 270: } ! 271: ! 272: // 入力ファイルが .WAV ファイルっぽければ true を返す。 ! 273: static bool ! 274: is_wavfile(const char *infile) ! 275: { ! 276: char buf[12]; ! 277: int fd; ! 278: ! 279: fd = open(infile, O_RDONLY); ! 280: if (fd < 0) { ! 281: err(1, "%s: open %s", __func__, infile); ! 282: } ! 283: ! 284: auto n = read(fd, buf, sizeof(buf)); ! 285: close(fd); ! 286: if (n < 0) { ! 287: err(1, "%s: read %s", __func__, infile); ! 288: } ! 289: if (n < sizeof(buf)) { ! 290: return false; ! 291: } ! 292: memset(buf + 4, 0, 4); ! 293: if (memcmp(buf, wav_magic, sizeof(wav_magic)) != 0) { ! 294: return false; ! 295: } ! 296: ! 297: return true; ! 298: } ! 299: ! 300: // PCM -> ADPCM。 ! 301: // 入力の WAV ファイルは 16bit, 1チャンネル固定。 ! 302: // サンプリングレートは気にせずデータを列挙するだけ。 ! 303: static void ! 304: encode_file(const char *infile, const char *outfile) ! 305: { ! 306: char buf[12]; ! 307: ssize_t n; ! 308: ! 309: int ifd = open(infile, O_RDONLY); ! 310: if (ifd < 0) { ! 311: err(1, "%s: open", infile); ! 312: } ! 313: int ofd = creat(outfile, 0644); ! 314: if (ofd < 0) { ! 315: err(1, "%s: creat", outfile); ! 316: } ! 317: ! 318: // RIFF ヘッダ。 ! 319: n = read(ifd, buf, sizeof(wav_magic)); ! 320: if (n < 0) { ! 321: err(1, "%s: reading header", infile); ! 322: } ! 323: if (n < sizeof(wav_magic)) { ! 324: errx(1, "%s: file too short", infile); ! 325: } ! 326: if (memcmp(buf + 0, wav_magic + 0, 4) != 0 || ! 327: memcmp(buf + 8, wav_magic + 8, 4) != 0) { ! 328: errx(1, "%s: Invalid WAV magic", infile); ! 329: } ! 330: ! 331: // チャンクが続く。 ! 332: for (bool done = false; done == false; ) { ! 333: n = read(ifd, buf, sizeof(chunk_header)); ! 334: if (n < 0) { ! 335: err(1, "%s: reading chunk header", infile); ! 336: } ! 337: if (n == 0) { ! 338: break; ! 339: } ! 340: if (n < sizeof(chunk_header)) { ! 341: errx(1, "%s: file too short", infile); ! 342: } ! 343: ! 344: const chunk_header *ch = (const chunk_header *)&buf[0]; ! 345: uint32 chunktype = be32toh(ch->chunktype); ! 346: uint32 chunksize = le32toh(ch->chunksize); ! 347: ! 348: uint32 nextpos = lseek(ifd, 0, SEEK_CUR); ! 349: nextpos += chunksize; ! 350: ! 351: VERBOSE("Chunk '%c%c%c%c' len=%u\n", ! 352: (chunktype >> 24), ! 353: (chunktype >> 16) & 0xff, ! 354: (chunktype >> 8) & 0xff, ! 355: (chunktype & 0xff), ! 356: chunksize); ! 357: switch (chunktype) { ! 358: case RIFF('f', 'm', 't', ' '): ! 359: read_fmt_chunk(ifd, infile); ! 360: break; ! 361: case RIFF('d', 'a', 't', 'a'): ! 362: read_data_chunk(ifd, ofd, chunksize, infile, outfile); ! 363: // data チャンク処理したら後は無視して終わる? ! 364: done = true; ! 365: break; ! 366: default: ! 367: // 知らないチャンクは読み飛ばす。 ! 368: break; ! 369: } ! 370: lseek(ifd, nextpos, SEEK_SET); ! 371: } ! 372: close(ifd); ! 373: close(ofd); ! 374: } ! 375: ! 376: // ifd から fmt チャンクを読み込んで、パラメータをチェックするだけ。 ! 377: static void ! 378: read_fmt_chunk(int ifd, const char *infile) ! 379: { ! 380: struct fmt_chunk fmt; ! 381: ! 382: auto n = read(ifd, &fmt, sizeof(fmt)); ! 383: if (n < 0) { ! 384: err(1, "%s: read fmt chunk", infile); ! 385: } ! 386: if (n < sizeof(fmt)) { ! 387: errx(1, "%s: file too short", infile); ! 388: } ! 389: ! 390: // 16bit, 1channel, PCM のみサポート。 ! 391: uint32 format = le16toh(fmt.fmtid); ! 392: uint32 nchannels = le16toh(fmt.nchannels); ! 393: uint32 bitdepth = le16toh(fmt.bitdepth); ! 394: if (format != 1/*PCM*/) { ! 395: errx(1, "%s: Unknown WAV format 0x%x", infile, format); ! 396: } ! 397: if (nchannels != 1) { ! 398: errx(1, "%s: Only monaural is supported", infile); ! 399: } ! 400: if (bitdepth != 16) { ! 401: errx(1, "%s: Only 16 bit is supported", infile); ! 402: } ! 403: } ! 404: ! 405: // ifd から data チャンクを読み込んで ADPCM にして ofd に書き出す。 ! 406: static void ! 407: read_data_chunk(int ifd, int ofd, uint32 datasize, ! 408: const char *infile, const char *outfile) ! 409: { ! 410: uint32 srcsize; ! 411: uint32 dstsize; ! 412: ! 413: // 奇数バイトのはずはないけど、一応。 ! 414: srcsize = datasize; ! 415: if ((srcsize & 1U) != 0) { ! 416: srcsize += 1; ! 417: VERBOSE("srcsize ODD! %u\n", srcsize); ! 418: } ! 419: VERBOSE("srcsize = %u\n", srcsize); ! 420: // 奇数サンプルなら dst は偶数サンプルになるよう切り上げる。 ! 421: dstsize = srcsize; ! 422: if ((dstsize & 2U) != 0) { ! 423: dstsize += 2; ! 424: VERBOSE("dst sample count ODD! %u\n", dstsize); ! 425: } ! 426: dstsize /= 4; ! 427: VERBOSE("dstsize = %u [bytes]\n", dstsize); ! 428: ! 429: std::vector<uint8> src; ! 430: std::vector<uint8> dst; ! 431: try { ! 432: src.resize(srcsize); ! 433: dst.resize(dstsize); ! 434: } catch (...) { ! 435: errno = ENOMEM; ! 436: err(1, "data chunk %u", datasize); ! 437: } ! 438: ! 439: auto n = read(ifd, src.data(), datasize); ! 440: if (n < 0) { ! 441: err(1, "%s: read data", infile); ! 442: } ! 443: if (n < src.size()) { ! 444: errx(1, "%s: file too short", infile); ! 445: } ! 446: ! 447: // 2サンプルずつ処理できる。 ! 448: const int16 *s = (const int16 *)src.data(); ! 449: uint8 *d = dst.data(); ! 450: for (const uint8 *end = d + dstsize; d < end; ) { ! 451: uint32 v0 = dev->pcm2adpcm_step(*s++); ! 452: uint32 v1 = dev->pcm2adpcm_step(*s++); ! 453: *d++ = v0 | (v1 << 4); ! 454: } ! 455: ! 456: write(ofd, dst.data(), dst.size()); ! 457: } ! 458: ! 459: // ADPCM -> PCM ! 460: static void ! 461: decode_file(const char *infile, const char *outfile) ! 462: { ! 463: struct stat st; ! 464: std::vector<uint8> src; ! 465: std::vector<int16> dst; ! 466: uint8 wavhdr[12]; ! 467: chunk_header hdr; ! 468: fmt_chunk fmt; ! 469: ssize_t n; ! 470: ! 471: int ifd = open(infile, O_RDONLY); ! 472: if (ifd < 0) { ! 473: err(1, "%s: open", infile); ! 474: } ! 475: if (fstat(ifd, &st) < 0) { ! 476: err(1, "%s: fstat", infile); ! 477: } ! 478: // 一気に確保。 ! 479: try { ! 480: src.resize(st.st_size); ! 481: dst.resize(st.st_size * 2); ! 482: } catch (...) { ! 483: errno = ENOMEM; ! 484: err(1, "buf(%zd)", st.st_size); ! 485: } ! 486: // 出力データ部のバイト数。 ! 487: size_t databytes = dst.size() * sizeof(int16); ! 488: ! 489: // 全体を読み込む。 ! 490: n = read(ifd, src.data(), src.size()); ! 491: if (n < 0) { ! 492: err(1, "%s: read data", infile); ! 493: } ! 494: if (n < src.size()) { ! 495: errx(1, "%s: file too short", infile); ! 496: } ! 497: ! 498: // 出力ファイル。 ! 499: int ofd = creat(outfile, 0644); ! 500: if (ofd < 0) { ! 501: err(1, "%s: creat", outfile); ! 502: } ! 503: // WAV ヘッダ。 ! 504: memcpy(wavhdr, wav_magic, sizeof(wav_magic)); ! 505: *(uint32 *)(wavhdr + 4) = htole32( ! 506: sizeof(wavhdr) + ! 507: sizeof(hdr) + sizeof(fmt) + ! 508: sizeof(hdr) + databytes); ! 509: n = write(ofd, wavhdr, sizeof(wavhdr)); ! 510: if (n < 0) { ! 511: err(1, "%s: write", outfile); ! 512: } ! 513: ! 514: // fmt チャンク。 ! 515: hdr.chunktype = htobe32(RIFF('f', 'm', 't', ' ')); ! 516: hdr.chunksize = htole32(16); ! 517: n = write(ofd, &hdr, sizeof(hdr)); ! 518: if (n < 0) { ! 519: err(1, "%s: write", outfile); ! 520: } ! 521: fmt.fmtid = htole16(1); // PCM ! 522: fmt.nchannels = htole16(1); ! 523: fmt.frequency = htole32(freq); ! 524: fmt.byterate = htole32(freq * 1 * (16 / 8)); ! 525: fmt.framesize = htole16(1 * (16 / 8)); ! 526: fmt.bitdepth = htole16(16); ! 527: n = write(ofd, &fmt, sizeof(fmt)); ! 528: if (n < 0) { ! 529: err(1, "%s: write", outfile); ! 530: } ! 531: ! 532: // data チャンクのヘッダ部。 ! 533: hdr.chunktype = htobe32(RIFF('d', 'a', 't', 'a')); ! 534: hdr.chunksize = databytes; ! 535: n = write(ofd, &hdr, sizeof(hdr)); ! 536: if (n < 0) { ! 537: err(1, "%s: write", outfile); ! 538: } ! 539: ! 540: const uint8 *s = src.data(); ! 541: int16 *d = dst.data(); ! 542: for (const uint8 *end = s + src.size(); s < end; ) { ! 543: uint8 data = *s++; ! 544: int16 pcm0 = dev->adpcm2pcm_step(data & 0x0f); ! 545: int16 pcm1 = dev->adpcm2pcm_step(data >> 4); ! 546: *d++ = pcm0; ! 547: *d++ = pcm1; ! 548: } ! 549: write(ofd, dst.data(), dst.size() * sizeof(int16)); ! 550: ! 551: close(ifd); ! 552: close(ofd); ! 553: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.