Annotation of nono/util/adpcmconv/adpcmconv.cpp, revision 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.