Annotation of nono/util/adpcmconv/adpcmconv.cpp, revision 1.1.1.1

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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.