Annotation of nono/vm/adpcm.cpp, revision 1.1.1.8

1.1       root        1: //
                      2: // nono
                      3: // Copyright (C) 2022 nono project
                      4: // Licensed under nono-license.txt
                      5: //
                      6: 
                      7: //
                      8: // ADPCM (MSM6258V)
                      9: //
                     10: 
1.1.1.7   root       11: // ミキサー周波数はこれらがすべて割り切れるよう 62500 [Hz] (= 16 usec)
                     12: // にする。
                     13: //
                     14: //     clk  div        freq            mixerfreq に対する倍率
                     15: //     ---- ----       --------        ---
                     16: //     8MHz 512        15625           4
                     17: //     8MHz 768        10416.67        6
                     18: //     8MHz 1024        7812.5         8
                     19: //     4MHz 512         〃
                     20: //     4MHz 768         5208.33        12
                     21: //     4MHz 1024        3906.25        16
                     22: 
                     23: // ある 16 usec * (freq による倍率) の時間内での書き込みは、このサンプル
                     24: // の代表値。1期間内に1回しか来ないはず。
                     25: // かつ、ブロックは 40 msec で割り切れる時間ごとに区切られているため、
                     26: // 前回の書き込み位置の次、ではなく、現在時刻に応じた位置に書き出す。
                     27: 
1.1       root       28: #include "adpcm.h"
                     29: #include "dmac.h"
1.1.1.6   root       30: #include "event.h"
1.1.1.8 ! root       31: #include "mainbus.h"
1.1.1.7   root       32: #include "monitor.h"
1.1       root       33: #include "scheduler.h"
1.1.1.7   root       34: #include "sound.h"
1.1       root       35: 
                     36: // コンストラクタ
                     37: ADPCMDevice::ADPCMDevice()
1.1.1.2   root       38:        : inherited(OBJ_ADPCM)
1.1       root       39: {
1.1.1.7   root       40:        monitor = gMonitorManager->Regist(ID_MONITOR_ADPCM, this);
1.1.1.8 ! root       41:        monitor->SetCallback(&ADPCMDevice::MonitorScreen);
1.1.1.7   root       42:        monitor->SetSize(40, 7);
1.1       root       43: }
                     44: 
                     45: // デストラクタ
                     46: ADPCMDevice::~ADPCMDevice()
                     47: {
1.1.1.2   root       48: }
                     49: 
                     50: // 初期化
                     51: bool
                     52: ADPCMDevice::Init()
                     53: {
                     54:        dmac = GetDMACDevice();
1.1.1.7   root       55: 
                     56:        sound = GetSoundRenderer();
                     57:        track = sound->RegistTrack(this);
                     58:        if (track == NULL) {
                     59:                // エラーメッセージは表示済み。
                     60:                return false;
                     61:        }
                     62: 
1.1.1.8 ! root       63:        mixer_frame_tsec = 1_sec / sound->GetMixerFreq();
1.1.1.2   root       64: 
1.1.1.6   root       65:        auto evman = GetEventManager();
                     66:        event = evman->Regist(this,
                     67:                ToEventCallback(&ADPCMDevice::EventCallback),
                     68:                "ADPCM");
1.1.1.4   root       69: 
1.1.1.2   root       70:        return true;
1.1       root       71: }
                     72: 
                     73: // リセット
                     74: void
                     75: ADPCMDevice::ResetHard(bool poweron)
                     76: {
1.1.1.7   root       77:        StopPlay();
                     78: 
                     79:        stepidx = 0;
                     80:        xprev = 0;
1.1       root       81: 
                     82:        // ?
1.1.1.7   root       83:        clkMHz = 8;
                     84:        // div は PPI の初期化でセットされるはず。
1.1       root       85:        div = 512;
1.1.1.7   root       86: 
                     87:        // リセットからコマンド受け付けまで 16 msec (MSM6258.pdf)
1.1       root       88: }
                     89: 
1.1.1.3   root       90: busdata
1.1.1.4   root       91: ADPCMDevice::ReadPort(uint32 offset)
1.1       root       92: {
1.1.1.3   root       93:        busdata data;
1.1       root       94: 
                     95:        switch (offset) {
                     96:         case 0:        // STAT
1.1.1.7   root       97:                data = GetSTAT();
1.1.1.3   root       98:                putlog(1, "STAT -> $%02x", data.Data());
1.1       root       99:                break;
                    100:         case 1:        // DATA
1.1.1.8 ! root      101:                putlog(0, "Read $%06x (NOT IMPLEMENTED)",
        !           102:                        GetMainbusDevice()->GetPaddr());
1.1.1.7   root      103:                data = databuf;
1.1       root      104:                break;
                    105:         default:
1.1.1.4   root      106:                VMPANIC("corrupted offset=%u", offset);
1.1       root      107:        }
                    108: 
1.1.1.3   root      109:        // InsideOut p.135
                    110:        const busdata wait = busdata::Wait(18 * 40_nsec);
                    111:        data |= wait;
                    112: 
1.1.1.4   root      113:        data |= BusData::Size1;
1.1       root      114:        return data;
                    115: }
                    116: 
1.1.1.3   root      117: busdata
1.1.1.4   root      118: ADPCMDevice::WritePort(uint32 offset, uint32 data)
1.1       root      119: {
                    120:        switch (offset) {
                    121:         case 0:        // CMD
                    122:                putlog(1, "CMD  <- $%02x", data);
1.1.1.7   root      123:                // XXX 同時に立ってるとどうなる?
1.1       root      124:                if ((data & REC_START)) {
                    125:                        putlog(0, "Start recording (NOT IMPLEMENTED)");
                    126:                }
                    127:                if ((data & PLAY_START)) {
                    128:                        StartPlay();
                    129:                }
                    130:                if ((data & STOP)) {
1.1.1.7   root      131:                        StopPlay();
1.1       root      132:                }
                    133:                break;
                    134:         case 1:        // DATA
1.1.1.7   root      135:                WriteData(data);
1.1       root      136:                break;
                    137:         default:
1.1.1.4   root      138:                VMPANIC("corrupted offset=%u", offset);
1.1       root      139:        }
                    140: 
1.1.1.3   root      141:        // InsideOut p.135
                    142:        const busdata wait = busdata::Wait(22 * 40_nsec);
1.1.1.4   root      143: 
                    144:        busdata r = wait;
                    145:        r |= BusData::Size1;
                    146:        return r;
1.1       root      147: }
                    148: 
1.1.1.3   root      149: busdata
1.1.1.4   root      150: ADPCMDevice::PeekPort(uint32 offset)
1.1       root      151: {
1.1.1.7   root      152:        switch (offset) {
                    153:         case 0:        // STAT
                    154:                return GetSTAT();
                    155: 
                    156:         case 1:        // DATA
                    157:                return databuf;
                    158: 
                    159:         default:
                    160:                VMPANIC("corrupted offset=%d", offset);
                    161:        }
1.1       root      162: }
                    163: 
1.1.1.4   root      164: bool
                    165: ADPCMDevice::PokePort(uint32 offset, uint32 data)
                    166: {
                    167:        return false;
                    168: }
                    169: 
1.1       root      170: void
1.1.1.8 ! root      171: ADPCMDevice::MonitorScreen(Monitor *, TextScreen& screen)
1.1       root      172: {
1.1.1.7   root      173:        int y;
                    174: 
                    175:        // Address: $e92000
                    176:        //
                    177:        // Mode: Recording
                    178:        // Freq: 15625 Hz  (clk=8MHz div=1024)
                    179:        // Pan : L R
                    180: 
                    181:        screen.Clear();
                    182:        y = 0;
                    183: 
                    184:        screen.Puts(0, y++, "Address: $e92000");
                    185:        y++;
                    186:        screen.Puts(0, y, "Mode:");
                    187:        if (playing) {
                    188:                screen.Puts(6, y, "Playing");
                    189:        } else {
                    190:                screen.Puts(6, y, "Stopped");
                    191:        }
                    192:        y++;
                    193: 
                    194:        screen.Print(0, y, "Freq: %u Hz", freq);
                    195:        screen.Print(15, y, "(clk=%uMHz div=%u)", clkMHz, div);
                    196:        y++;
                    197: 
                    198:        screen.Print(0, y, "Pan :");
                    199:        screen.Putc(6, y, 'L' | TA::OnOff(panout_L));
                    200:        screen.Putc(8, y, 'R' | TA::OnOff(panout_R));
                    201:        y++;
                    202:        y++;
                    203: 
                    204:        screen.Print(0, y, "Internal: Xprev=%-5d StepIdx=%d", xprev, stepidx);
                    205: }
                    206: 
                    207: // STAT レジスタの内容を返す。
                    208: uint32
                    209: ADPCMDevice::GetSTAT() const
                    210: {
                    211:        uint32 data;
                    212: 
                    213:        data = 0x40;
                    214:        if (playing) {
                    215:                data |= STAT_PLAY;
1.1       root      216:        }
1.1.1.7   root      217:        return data;
                    218: }
                    219: 
                    220: // 再生開始。
                    221: void
                    222: ADPCMDevice::StartPlay()
                    223: {
                    224:        putlog(1, "Start playing %uHz", freq);
1.1       root      225: 
                    226:        playing = true;
1.1.1.7   root      227:        xprev = 0;
                    228:        sound->StartPlay(track);
                    229: 
1.1.1.2   root      230:        dmac->AssertREQ(3);
1.1       root      231: 
1.1.1.8 ! root      232:        event->time = guest_frame_tsec * 2;
1.1.1.2   root      233:        scheduler->StartEvent(event);
1.1       root      234: }
                    235: 
1.1.1.7   root      236: // 再生停止。
                    237: void
                    238: ADPCMDevice::StopPlay()
                    239: {
                    240:        if (playing) {
                    241:                putlog(1, "Stop playing");
                    242:        }
                    243:        playing = false;
                    244: 
                    245:        // REQ はどうなる?
                    246: 
                    247:        scheduler->StopEvent(event);
                    248: 
                    249:        sound->StopPlay(track);
                    250: }
                    251: 
                    252: // データ書き込み。
                    253: void
                    254: ADPCMDevice::WriteData(uint32 data)
                    255: {
                    256:        if (__predict_false(playing == false)) {
                    257:                // 非再生中の書き込み?
                    258:                putlog(1, "DATA <- $%02x (Not Playing)", data);
                    259:        } else {
                    260:                putlog(2, "DATA <- $%02x", data);
                    261:        }
                    262:        databuf = data;
                    263: }
                    264: 
1.1       root      265: // イベント
                    266: void
1.1.1.6   root      267: ADPCMDevice::EventCallback(Event *ev)
1.1       root      268: {
                    269:        if (playing) {
1.1.1.7   root      270:                DecodeADPCM(databuf);
                    271: 
                    272:                // 空いたので REQ を上げる。
1.1.1.2   root      273:                dmac->AssertREQ(3);
1.1       root      274:        }
1.1.1.7   root      275: 
1.1.1.8 ! root      276:        event->time = guest_frame_tsec * 2;
1.1.1.7   root      277:        scheduler->StartEvent(event);
                    278: }
                    279: 
                    280: // DACK 信号をアサートする (DMAC から呼ばれる)。
                    281: // 実際には PEDEC がやってるはず?。
                    282: // ここではデータの受け渡しが完了したはずなのでその REQ を下げるだけ。
                    283: void
                    284: ADPCMDevice::AssertDACK()
                    285: {
                    286:        dmac->NegateREQ(3);
1.1       root      287: }
                    288: 
1.1.1.7   root      289: // パンアウト L を設定する。PPI から呼ばれる。
1.1       root      290: void
1.1.1.7   root      291: ADPCMDevice::SetPanOutL(bool on)
1.1       root      292: {
1.1.1.7   root      293:        panout_L = on;
1.1       root      294: }
                    295: 
1.1.1.7   root      296: // パンアウト R を設定する。PPI から呼ばれる。
1.1       root      297: void
1.1.1.7   root      298: ADPCMDevice::SetPanOutR(bool on)
1.1       root      299: {
1.1.1.7   root      300:        panout_R = on;
                    301: }
                    302: 
                    303: // サンプリングレートを設定する。PPI から呼ばれる。
                    304: void
                    305: ADPCMDevice::SetRate(uint32 rate)
                    306: {
                    307:        assert(rate < 4);
                    308: 
                    309:        // %11 は %01 と同じになる (Inside p295)
                    310:        if (rate == 3) {
                    311:                rate = 1;
                    312:        }
                    313: 
                    314:        div = 1024 - rate * 256;
                    315:        CalcRate();
1.1       root      316: }
                    317: 
                    318: // クロック切り替え。OPM から呼ばれる。
                    319: // CT1 = %0 で 8MHz、%1 で 4MHz。
                    320: void
                    321: ADPCMDevice::SetClock(bool ct1)
                    322: {
                    323:        if (ct1) {
1.1.1.7   root      324:                clkMHz = 4;
1.1       root      325:        } else {
1.1.1.7   root      326:                clkMHz = 8;
                    327:        }
                    328:        CalcRate();
                    329: }
                    330: 
                    331: // div と clk から必要なパラメータを計算する。
                    332: void
                    333: ADPCMDevice::CalcRate()
                    334: {
                    335:        // 1フレームにかかる時間。15625 Hz なら 64 usec。
1.1.1.8 ! root      336:        guest_frame_tsec = usec_to_tsec(div / clkMHz);
1.1.1.7   root      337: 
                    338:        // 周波数。主に表示用。
1.1.1.8 ! root      339:        freq = clkMHz * 1000'000U / div;
1.1.1.7   root      340: 
                    341:        // アップスケール用の倍率。15625Hz なら 4倍。
1.1.1.8 ! root      342:        scale_factor = guest_frame_tsec / mixer_frame_tsec;
1.1.1.7   root      343: 
                    344:        // イベント名に載せておく。
                    345:        event->SetName(string_format("ADPCM %u Hz", freq));
                    346: }
                    347: 
                    348: // ADPCM ストリームの1バイトをデコードする。(再生)
                    349: void
                    350: ADPCMDevice::DecodeADPCM(uint32 data)
                    351: {
                    352:        int16 s1 = adpcm2pcm_step(data & 0x0f);
                    353:        int16 s2 = adpcm2pcm_step(data >> 4);
                    354: 
                    355:        uint64 vtime = scheduler->GetVirtTime();
                    356: 
                    357:        // 1サンプル目。
                    358:        WritePCM(vtime, s1);
                    359:        // 2サンプル目。
1.1.1.8 ! root      360:        WritePCM(vtime + guest_frame_tsec, s2);
1.1.1.7   root      361: }
                    362: 
                    363: // vtime に相当する位置に PCM データを書き込む。
                    364: // その際、値は pcm0 から target までを線形補間し、
                    365: // target を次の pcm0 とする。
                    366: void
                    367: ADPCMDevice::WritePCM(uint64 vtime, int16 target)
                    368: {
1.1.1.8 ! root      369:        uint64 vseq = vtime / Sound::BLK_TSEC;
        !           370:        uint64 voff = vtime % Sound::BLK_TSEC;
1.1.1.7   root      371: 
                    372:        // 40msec バッファでは 1バイト (2サンプル) がバッファをまたぐ事があるため、
                    373:        // サンプルごとに行う必要がある。
                    374:        int16 *blkbuf = track->AllocSeq(vseq);
                    375:        assert(blkbuf);
                    376: 
                    377:        // このフレームがミキサーバッファのどの位置か。
                    378:        uint32 mixer_frame_idx =
1.1.1.8 ! root      379:                voff / guest_frame_tsec * Sound::NCHAN * scale_factor;
1.1.1.7   root      380:        int16 *d = blkbuf + mixer_frame_idx;
                    381: 
                    382:        // ここから scale_factor 個で pcm0 から target になるよう線形補間する。
                    383:        int16 delta = (target - pcm0) / scale_factor;
                    384:        int16 val;
                    385: 
                    386:        val = pcm0;
                    387:        for (uint i = 0; i < scale_factor; i++) {
                    388:                val += delta;
                    389:                *d++ = __predict_true(panout_L) ? val : 0;
                    390:                *d++ = __predict_true(panout_R) ? val : 0;
                    391:        }
                    392:        pcm0 = target;
                    393: }
                    394: 
                    395: // エンコード (PCM -> ADPCM) 側は録音方向なので使ってない。
                    396: //
                    397: // let Bit3 = Bit2 = Bit1 = Bit0 = 0
                    398: // if (d(n) < 0)
                    399: //   Bit3 = 1
                    400: // d(n) = ABS(d(n))
                    401: // if (d(n) >= ss(n))
                    402: //   Bit2 = 1; d(n) = d(n) - ss(n)
                    403: // if (d(n) >= ss(n) / 2)
                    404: //   Bit1 = 1; d(n) = d(n) - ss(n) / 2
                    405: // if (d(n) >= ss(n) / 4)
                    406: //   Bit0 = 1
                    407: // L(n) = (0b1000 * Bit3) + (0b100 * Bit2) + (0b10 + Bit1) + Bit0
                    408: inline uint32
                    409: ADPCMDevice::adpcm_encode(int32 dn)
                    410: {
                    411:        uint32 Ln = 0;
                    412: 
                    413:        int ss = adpcm_stepsize[stepidx];
                    414: 
                    415:        if (dn < 0) {
                    416:                Ln = 0x8;
                    417:                dn = -dn;
                    418:        }
                    419:        if (dn >= ss) {
                    420:                Ln |= 0x4;
                    421:                dn -= ss;
                    422:        }
                    423:        ss /= 2;
                    424:        if (dn >= ss) {
                    425:                Ln |= 0x2;
                    426:                dn -= ss;
                    427:        }
                    428:        ss /= 2;
                    429:        if (dn >= ss) {
                    430:                Ln |= 0x1;
1.1       root      431:        }
1.1.1.7   root      432:        return Ln;
1.1       root      433: }
1.1.1.7   root      434: 
                    435: // d(n) = (ss(n) * Bit2) + (ss(n) / 2 * Bit1) + (ss(n) / 4 * Bit0) + (ss(n) / 8)
                    436: // if (Bit3 = 1)
                    437: //   d(n) = d(n) * (-1)
                    438: // X(n) = X(n-1) + d(n)
                    439: inline int32
                    440: ADPCMDevice::adpcm_decode(uint32 data, int32 xn)
                    441: {
                    442:        int b3 = (data & 8) ? 1 : 0;
                    443:        int b2 = (data & 4) ? 1 : 0;
                    444:        int b1 = (data & 2) ? 1 : 0;
                    445:        int b0 = (data & 1);
                    446: 
                    447:        int32 ss = adpcm_stepsize[stepidx];
                    448:        int32 dn = (ss * b2) + (ss / 2 * b1) + (ss / 4 * b0) + ss / 8;
                    449:        if (b3) {
                    450:                dn = -dn;
                    451:        }
                    452: 
                    453:        xn += dn;
                    454: 
                    455:        // Saturate in 12 bits.
                    456:        if (__predict_false(xn > 2047)) {
                    457:                xn = 2047;
                    458:        } else if (__predict_false(xn < -2047)) {
                    459:                xn = -2047;
                    460:        }
                    461: 
                    462:        return xn;
                    463: }
                    464: 
                    465: inline void
                    466: ADPCMDevice::adpcm_calcstep(uint32 data)
                    467: {
                    468:        stepidx += adpcm_stepadj[data];
                    469:        if (__predict_false(stepidx < 0)) {
                    470:                stepidx = 0;
                    471:        } else if (__predict_false(stepidx > 48)) {
                    472:                stepidx = 48;
                    473:        }
                    474: }
                    475: 
                    476: uint32
                    477: ADPCMDevice::pcm2adpcm_step(int16 pcm)
                    478: {
                    479:        // Input of this algorithm is 12 bit.
                    480:        int32 xn = pcm / 16;
                    481:        int32 dn = xn - xprev;
                    482:        uint32 Ln = adpcm_encode(dn);
                    483: 
                    484:        // next はこの Ln をデコードして PCM にした値
                    485:        xprev = adpcm_decode(Ln, xprev);
                    486:        adpcm_calcstep(Ln);
                    487: 
                    488:        return Ln;
                    489: }
                    490: 
                    491: int16
                    492: ADPCMDevice::adpcm2pcm_step(uint32 data)
                    493: {
                    494:        int32 xn = adpcm_decode(data, xprev);
                    495: 
                    496:        // next
                    497:        xprev = xn;
                    498:        adpcm_calcstep(data);
                    499: 
                    500:        // このアルゴリズムの出力は(下詰めで) 12 bit。
                    501:        // ただし MSM6258 の出力 DAC は 10 bit 分しかない。
                    502:        xn /= 4;
                    503:        // その 10 bit を 16 bit PCM にする。
                    504:        xn *= 64;
                    505:        return xn;
                    506: }
                    507: 
                    508: /*static*/ const int
                    509: ADPCMDevice::adpcm_stepadj[16] = {
                    510:        -1, -1, -1, -1, 2, 4, 6, 8,
                    511:        -1, -1, -1, -1, 2, 4, 6, 8,
                    512: };
                    513: 
                    514: /*static*/ const int32
                    515: ADPCMDevice::adpcm_stepsize[49] = {
                    516:         16,  17,  19,  21,  23,  25,  28,  31,  34,  37,
                    517:         41,  45,  50,  55,  60,  66,  73,  80,  88,  97,
                    518:        107, 118, 130, 143, 157, 173, 190, 209, 230, 253,
                    519:        279, 307, 337, 371, 408, 449, 494, 544, 598, 658,
                    520:        724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552,
                    521: };

unix.superglobalmegacorp.com

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