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