←Previous | 1 2 3 4 | Next→
    //                                  ④→C2─┘
    channel.ch$fbOutputHandle = port.op$Joint1Box;
    channel.ch$bfOutputHandle = port.op$Joint2Box;  //ゴミが出てくる可能性があるので空いているジョイントに繋いでおく
    channel.ch$c2InputHandle = port.op$Joint4Box;
    channel.ch$c1OutputHandle = channel.ch$m2OutputHandle = channel.ch$OutputBox;
    break;
  case 7:
    //          ①→C1───────────────┐
    //                                              │
    //  ┌─┐                  ③→M2─────┐  │
    //  ↓  │                                  ↓  │
    //  M1→FB────────────────→◯←┘
    //                                          ↑
    //                                  ④→C2─┘
    channel.ch$bfOutputHandle = port.op$Joint2Box;  //ゴミが出てくる可能性があるので空いているジョイントに繋いでおく
    channel.ch$c2InputHandle = port.op$Joint4Box;
    channel.ch$fbOutputHandle = channel.ch$c1OutputHandle = channel.ch$m2OutputHandle = channel.ch$OutputBox;
    break;
  }
  v >>= 3;
  channel.ch$fbScale = v == 0 ? 0 : 1 << (v + 6);  //(1 << (v + 6)) & ~((v - 1) >> 3)
}

//channel.chSetPAN (channel, v)
//  チャンネルのパンを設定する
function chSetPAN (channel, v) {
  v &= 3;
  channel.ch$PAN = v;
  channel.ch$LeftMask = -(v & 1);
  channel.ch$RightMask = -(v >> 1);
}

//channel.chSetKC (channel, v)
//  チャンネルのキーコードを設定する
function chSetKC (channel, v) {
  v &= 127;
  if (channel.ch$KC != v) {
    channel.ch$KC = v;
    //KCが4の倍数のときKC-1と同じ音が出る
    //  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15  v
    //  0  1  2  3  3  4  5  6  6  7  8  9  9 10 11 12  v-(v>>2)
    channel.ch$KeyIndex = 768 + ((v - (v >> 2)) << 6) + channel.ch$KF;
    //エンベロープジェネレータを再設定する
    slRefreshEG (channel.ch$M1);
    slRefreshEG (channel.ch$M2);
    slRefreshEG (channel.ch$C1);
    slRefreshEG (channel.ch$C2);
  }
}

//channel.chSetKF (channel, v)
//  チャンネルのキーフラクションを設定する
function chSetKF (channel, v) {
  v &= 63;
  if (channel.ch$KF != v) {
    channel.ch$KF = v;
    channel.ch$KeyIndex = v |= channel.ch$KeyIndex & ~63;
    //スロットの周波数を再計算する
    channel.ch$M1.sl$Freq = ((opmFreqTable[v + channel.ch$M1.sl$Detune2Depth] + channel.ch$M1.sl$Detune1Freq) * channel.ch$M1.sl$Multiply) >>> 1;
    channel.ch$M2.sl$Freq = ((opmFreqTable[v + channel.ch$M2.sl$Detune2Depth] + channel.ch$M2.sl$Detune1Freq) * channel.ch$M2.sl$Multiply) >>> 1;
    channel.ch$C1.sl$Freq = ((opmFreqTable[v + channel.ch$C1.sl$Detune2Depth] + channel.ch$C1.sl$Detune1Freq) * channel.ch$C1.sl$Multiply) >>> 1;
    channel.ch$C2.sl$Freq = ((opmFreqTable[v + channel.ch$C2.sl$Detune2Depth] + channel.ch$C2.sl$Detune1Freq) * channel.ch$C2.sl$Multiply) >>> 1;
    //エンベロープジェネレータを再設定しない
  }
}

//channel.chSetAMS (channel, v)
//  チャンネルの振幅変調を設定する
function chSetAMS (channel, v) {
  v &= 3;
  channel.ch$AMS = v;
  channel.ch$ShiftAMS = v == 0 ? 0 : 1 << (v - 1);
}

//channel.chSetPMS (channel, v)
//  チャンネルの周波数変調を設定する
function chSetPMS (channel, v) {
  v &= 7;
  channel.ch$PMS = v;
  //  0   1   2   3   4   5   6   7
  //  *0 >>5 >>4 >>3 >>2 >>1 <<1 <<2
  channel.ch$ShiftPMS = v == 0 ? 0 : v < 6 ? 64 >> (6 - v) : 64 << (v - 5);
}

//slot.slKeyOn (slot, mask)
//  スロットをキーONする
//  mask  bit0  0=マニュアルでキーONしない,1=マニュアルでキーONする
//        bit1  0=タイマーでキーONしない,1=タイマーでキーONする
function slKeyOn (slot, mask) {
  let port = slot.sl$Port;
  if (slot.sl$KeyStatus == 0) {  //マニュアルとタイマーのどちらでもキーONされていない
    slot.sl$Phase = 0;
    slot.sl$Stage = OPMStage_ATTACK;  //アタックステージを開始する
    slot.sl$TransitionMask = (4 << slot.sl$AttackShift) - 4;
    slot.sl$Volume += (~slot.sl$Volume * (slot.sl$Attack3 | (slot.sl$Attack4 << (port.op$EGCounter >> slot.sl$AttackShift & 28)) >>> 28)) >> 4;
    if (slot.sl$Volume <= 0) {
      slot.sl$Volume = 0;
      slot.sl$Stage = OPMStage_DECAY;
      slot.sl$TransitionMask = (4 << slot.sl$DecayShift) - 4;
    }
  }
  slot.sl$KeyStatus |= mask;  //マニュアルまたはタイマーでキーONする
}

//slot.sl$KeyOff (slot, mask)
//  スロットをキーOFFする
//  mask  bit0  0=マニュアルでキーOFFする,1=マニュアルでキーOFFしない
//        bit1  0=タイマーでキーOFFする,1=タイマーでキーOFFしない
function slKeyOff (slot, mask) {
  if (slot.sl$KeyStatus != 0) {  //マニュアルまたはタイマーのいずれかでキーONされている
    slot.sl$KeyStatus &= mask;  //マニュアルまたはタイマーでキーOFFする
    if (slot.sl$KeyStatus == 0 &&  //他方でキーONされておらず
        slot.sl$Stage < OPMStage_RELEASE) {  //まだリリースされていない
      slot.sl$Stage = OPMStage_RELEASE;  //リリースステージに移行する
      slot.sl$TransitionMask = (4 << slot.sl$ReleaseShift) - 4;
    }
  }
}

//slot.slRefreshEG (slot)
//  スロットのエンベロープジェネレータを再設定する
function slRefreshEG (slot) {
  slot.sl$Detune1Freq = opmDT1FreqTable[slot.sl$Detune1Page + (slot.sl$Channel.ch$KC >> 2)];
  slot.sl$Freq = ((opmFreqTable[slot.sl$Channel.ch$KeyIndex + slot.sl$Detune2Depth] + slot.sl$Detune1Freq) * slot.sl$Multiply) >>> 1;
  let u = slot.sl$Channel.ch$KC >> slot.sl$KeyScale;  //(0..127)>>(2..5)=0..31
  let v = slot.sl$AttackRate + u;  //(0..94)+(0..31)=0..125
  if (v < 94) {
    slot.sl$AttackShift = OPM_EG_SHIFT_TABLE[v];
    if (slot.sl$Stage == OPMStage_ATTACK) {
      slot.sl$TransitionMask = (4 << slot.sl$AttackShift) - 4;
    }
    slot.sl$Attack3 = 0;
    slot.sl$Attack4 = OPM_EG_TABLE_X[v];
  } else {
    slot.sl$AttackShift = 0;
    if (slot.sl$Stage == OPMStage_ATTACK) {
      slot.sl$TransitionMask = 0;
    }
    slot.sl$Attack3 = 16;
    slot.sl$Attack4 = 0;
  }
  v = slot.sl$DecayRate + u;  //(0..94)+(0..31)=0..125
  slot.sl$DecayShift = OPM_EG_SHIFT_TABLE[v];
  if (slot.sl$Stage == OPMStage_DECAY) {
    slot.sl$TransitionMask = (4 << slot.sl$DecayShift) - 4;
  }
  slot.sl$Decay3 = 0;
  slot.sl$Decay4 = OPM_EG_TABLE_X[v];
  v = slot.sl$SustainRate + u;  //(0..94)+(0..31)=0..125
  slot.sl$SustainShift = OPM_EG_SHIFT_TABLE[v];
  if (slot.sl$Stage == OPMStage_SUSTAIN) {
    slot.sl$TransitionMask = (4 << slot.sl$SustainShift) - 4;
  }
  slot.sl$Sustain3 = 0;
  slot.sl$Sustain4 = OPM_EG_TABLE_X[v];
  v = slot.sl$ReleaseRate + u;  //(34..94)+(0..31)=34..125
  slot.sl$ReleaseShift = OPM_EG_SHIFT_TABLE[v];
  if (slot.sl$Stage == OPMStage_RELEASE) {
    slot.sl$TransitionMask = (4 << slot.sl$ReleaseShift) - 4;
  }
  slot.sl$Release3 = 0;
  slot.sl$Release4 = OPM_EG_TABLE_X[v];
}

//slot.slSetMUL (slot, v)
//  スロットの周波数の倍率を設定する
function slSetMUL (slot, v) {
  let t = (v & 15) << 1;
  if (t == 0) {
    t = 1;
  }
  if (slot.sl$Multiply != t) {
    slot.sl$Multiply = t;
    slot.sl$Freq = ((opmFreqTable[slot.sl$Channel.ch$KeyIndex + slot.sl$Detune2Depth] + slot.sl$Detune1Freq) * t) >>> 1;
  }
}

//slot.slSetDT1 (slot, v)
//  スロットの小さいデチューンを設定する
function slSetDT1 (slot, v) {
  v = (v & 7) << 5;
  if (slot.sl$Detune1Page != v) {
    slot.sl$Detune1Page = v;  //0..224
    slot.sl$Detune1Freq = opmDT1FreqTable[slot.sl$Detune1Page + (slot.sl$Channel.ch$KC >> 2)];
    slot.sl$Freq = ((opmFreqTable[slot.sl$Channel.ch$KeyIndex + slot.sl$Detune2Depth] + slot.sl$Detune1Freq) * slot.sl$Multiply) >>> 1;
  }
}

//slot.slSetTL (slot, v)
//  スロットのトータルレベルを設定する
function slSetTL (slot, v) {
  slot.sl$TotalLevel = (v & 127) << (OPM_TL_BITS - 7);
}

//slot.slSetAR (slot, v)
//  スロットのアタックレートを設定する
function slSetAR (slot, v) {
  v &= 31;
  //  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
  //  0 34 36 38 40 42 44 46 48 50 52 54 56 58 60 62 64 66 68 70 72 74 76 78 80 82 84 86 88 90 92 94
  v = ((v + 31) & 32) + (v << 1);
  if (slot.sl$AttackRate != v) {
    slot.sl$AttackRate = v;
    v += slot.sl$Channel.ch$KC >> slot.sl$KeyScale;  //(0..94)+((0..127)>>(2..5))=0..125
    if (v < 94) {
      slot.sl$AttackShift = OPM_EG_SHIFT_TABLE[v];
      if (slot.sl$Stage == OPMStage_ATTACK) {
        slot.sl$TransitionMask = (4 << slot.sl$AttackShift) - 4;
      }
      slot.sl$Attack3 = 0;
      slot.sl$Attack4 = OPM_EG_TABLE_X[v];
    } else {
      slot.sl$AttackShift = 0;
      if (slot.sl$Stage == OPMStage_ATTACK) {
        slot.sl$TransitionMask = 0;
      }
      slot.sl$Attack3 = 16;
      slot.sl$Attack4 = 0;
    }
  }
}

//slot.slSetKS (slot, v)
//  スロットのキースケーリングレベルを設定する
function slSetKS (slot, v) {
  v &= 3;
  v = 5 - v;
  //  0  1  2  3
  //  5  4  3  2
  if (slot.sl$KeyScale != v) {
    slot.sl$KeyScale = v;
    let u = slot.sl$Channel.ch$KC >> v;
    v = slot.sl$AttackRate + u;
    if (v < 94) {
      slot.sl$AttackShift = OPM_EG_SHIFT_TABLE[v];
      if (slot.sl$Stage == OPMStage_ATTACK) {
        slot.sl$TransitionMask = (4 << slot.sl$AttackShift) - 4;
      }
      slot.sl$Attack3 = 0;
      slot.sl$Attack4 = OPM_EG_TABLE_X[v];
    } else {
      slot.sl$AttackShift = 0;
      if (slot.sl$Stage == OPMStage_ATTACK) {
        slot.sl$TransitionMask = 0;
      }
      slot.sl$Attack3 = 16;
      slot.sl$Attack4 = 0;
    }
    v = slot.sl$DecayRate + u;
    slot.sl$DecayShift = OPM_EG_SHIFT_TABLE[v];
    if (slot.sl$Stage == OPMStage_DECAY) {
      slot.sl$TransitionMask = (4 << slot.sl$DecayShift) - 4;
    }
    slot.sl$Decay3 = 0;
    slot.sl$Decay4 = OPM_EG_TABLE_X[v];
    v = slot.sl$SustainRate + u;
    slot.sl$SustainShift = OPM_EG_SHIFT_TABLE[v];
    if (slot.sl$Stage == OPMStage_SUSTAIN) {
      slot.sl$TransitionMask = (4 << slot.sl$SustainShift) - 4;
    }
    slot.sl$Sustain3 = 0;
    slot.sl$Sustain4 = OPM_EG_TABLE_X[v];
    v = slot.sl$ReleaseRate + u;
    slot.sl$ReleaseShift = OPM_EG_SHIFT_TABLE[v];
    if (slot.sl$Stage == OPMStage_RELEASE) {
      slot.sl$TransitionMask = (4 << slot.sl$ReleaseShift) - 4;
    }
    slot.sl$Release3 = 0;
    slot.sl$Release4 = OPM_EG_TABLE_X[v];
  }
}

//slot.slSetD1R (slot, v)
//  スロットのファーストディケイレートを設定する
//  KC=OCT<<4|NOTE
//  KS=0のときKCの上位5bit、
//  KS=1のときKCの上位4bit、
//  KS=2のときKCの上位3bit、
//  KS=3のときKCの上位2bitをD1RKSとする
//  D1RKS=KC>>5-KS
//  D1RATE=min(63,(D1R==0?0:32)+2*D1R+D1RKS)
function slSetD1R (slot, v) {
  v &= 31;
  //  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
  //  0 34 36 38 40 42 44 46 48 50 52 54 56 58 60 62 64 66 68 70 72 74 76 78 80 82 84 86 88 90 92 94
  v = ((v + 31) & 32) + (v << 1);  //2*D1R。D1R==0でないとき32下駄履き
  if (slot.sl$DecayRate != v) {
    slot.sl$DecayRate = v;
    v += slot.sl$Channel.ch$KC >> slot.sl$KeyScale;  //D1RATE=2*D1R+D1RKS。D1R==0でないとき32下駄履き
    slot.sl$DecayShift = OPM_EG_SHIFT_TABLE[v];  //max(0,11-(D1RATE>>2))。D1R==0のとき0
    if (slot.sl$Stage == OPMStage_DECAY) {
      slot.sl$TransitionMask = (4 << slot.sl$DecayShift) - 4;
    }
    slot.sl$Decay3 = 0;
    slot.sl$Decay4 = OPM_EG_TABLE_X[v];
  }
}

//slot.slSetAMSEN (slot, v)
//  スロットの振幅変調を有効にする
function slSetAMSEN (slot, v) {
  v &= 1;
  slot.sl$AMSMask = -v;
}

//slot.slSetD2R (slot, v)
//  スロットのセカンドディケイレートを設定する
//  RATE=min(63,2*D2R+(KC>>5-KS))
function slSetD2R (slot, v) {
  v &= 31;
  //  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
  //  0 34 36 38 40 42 44 46 48 50 52 54 56 58 60 62 64 66 68 70 72 74 76 78 80 82 84 86 88 90 92 94
  v = ((v + 31) & 32) + (v << 1);
  if (slot.sl$SustainRate != v) {
    slot.sl$SustainRate = v;
    v += slot.sl$Channel.ch$KC >> slot.sl$KeyScale;
    slot.sl$SustainShift = OPM_EG_SHIFT_TABLE[v];
    if (slot.sl$Stage == OPMStage_SUSTAIN) {
      slot.sl$TransitionMask = (4 << slot.sl$SustainShift) - 4;
    }
    slot.sl$Sustain3 = 0;
    slot.sl$Sustain4 = OPM_EG_TABLE_X[v];
  }
}

//slot.slSetDT2 (slot, v)
//  スロットの大きいデチューンを設定する
//  DT2  cent              倍率                sl$Detune2Depth
//   1    600  (2^(1/12))^6.00=1.41421356237  600*64/100=384.00
//   2    781  (2^(1/12))^7.81=1.57007484471  781*64/100=499.84
//   3    950  (2^(1/12))^9.50=1.73107312201  950*64/100=608.00
function slSetDT2 (slot, v) {
  v &= 3;
  //  0   1   2   3
  //  0 384 500 608
  v = ((384 << 20 | 500 << 10 | 608) >>> (30 - v * 10)) & 1023;
  if (slot.sl$Detune2Depth != v) {
    slot.sl$Detune2Depth = v;
    slot.sl$Freq = ((opmFreqTable[slot.sl$Channel.ch$KeyIndex + v] + slot.sl$Detune1Freq) * slot.sl$Multiply) >>> 1;
  }
}

//slot.slSetRR (slot, v)
//  スロットのリリースレートを設定する
//  RATE=min(63,2+4*RR+(KC>>5-KS))
function slSetRR (slot, v) {
  v &= 15;
  //   0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15
  //  34 38 42 46 50 54 58 62 66 70 74 78 82 86 90 94
  v = 32 + 2 + (v << 2);
  if (slot.sl$ReleaseRate != v) {
    slot.sl$ReleaseRate = v;
    v += slot.sl$Channel.ch$KC >> slot.sl$KeyScale;  //(34..94)+((0..127)>>(2..5))=34..125
    slot.sl$ReleaseShift = OPM_EG_SHIFT_TABLE[v];
    if (slot.sl$Stage == OPMStage_RELEASE) {
      slot.sl$TransitionMask = (4 << slot.sl$ReleaseShift) - 4;
    }
    slot.sl$Release3 = 0;
    slot.sl$Release4 = OPM_EG_TABLE_X[v];
  }
}

//slSetD1L (slot, v)
//  スロットのファーストディケイレベルを設定する
//  D1L   0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15
//   db   0  3  6  9 12 15 18 21 24 27 30 33 36 39 42 93
function slSetD1L (slot, v) {
  v &= 15;
  slot.sl$DecayLevel = (((v + 1) & 16) + v) << (OPM_TL_BITS - 5);
  v &= 15;
}

//opmAdvance (from, to)
//  内部バッファにOPMデータを書き込む
//  レジスタを更新する直前とライン出力に転送する直前とCSMでキーONする直前に呼び出す
//  音声出力がOFFのときも、いつでもONにできるように、EG、PG、フィードバック回路などは常に動作している
//  飽和処理はミキサで行うのでここでは行わない
//  そのためshortに収まらない値が出力されることがある
function opmAdvance (from, to) {
  for (let pn = 0; pn < OPM_PORT_COUNT; pn++) {
    let port = opmPortArray[pn];
    for (let k = from; k < to; ) {
      //EG
      if (--port.op$EGTimer == 0) {
        port.op$EGTimer = 3;
        port.op$EGCounter += 4;
        for (let i = 0; i < OPM_SLOT_COUNT; i++) {
          let slot = port.op$Slot[i];
          if ((port.op$EGCounter & slot.sl$TransitionMask) == 0) {
            switch (slot.sl$Stage) {
              //アタック
            case OPMStage_ATTACK:
              slot.sl$Volume += (~slot.sl$Volume * (slot.sl$Attack3 | (slot.sl$Attack4 << (port.op$EGCounter >> slot.sl$AttackShift & 28)) >>> 28)) >> 4;
              if (slot.sl$Volume <= 0) {
                slot.sl$Volume = 0;
                slot.sl$Stage = OPMStage_DECAY;
                slot.sl$TransitionMask = (4 << slot.sl$DecayShift) - 4;
              }
              break;
              //ファーストディケイ
            case OPMStage_DECAY:
              slot.sl$Volume += slot.sl$Decay3 | (slot.sl$Decay4 << (port.op$EGCounter >> slot.sl$DecayShift & 28)) >>> 28;
              if (slot.sl$Volume >= slot.sl$DecayLevel) {
                slot.sl$Stage = OPMStage_SUSTAIN;
                slot.sl$TransitionMask = (4 << slot.sl$SustainShift) - 4;
              }
              break;
              //セカンドディケイ
            case OPMStage_SUSTAIN:
              slot.sl$Volume += slot.sl$Sustain3 | (slot.sl$Sustain4 << (port.op$EGCounter >> slot.sl$SustainShift & 28)) >>> 28;
              if (slot.sl$Volume >= OPM_TL_SIZE - 1) {
                slot.sl$Volume = OPM_TL_SIZE - 1;
                slot.sl$Stage = OPMStage_SILENCE;
                slot.sl$TransitionMask = 1;
              }
              break;
              //リリース
            case OPMStage_RELEASE:
              slot.sl$Volume += slot.sl$Release3 | (slot.sl$Release4 << (port.op$EGCounter >> slot.sl$ReleaseShift & 28)) >>> 28;
              if (slot.sl$Volume >= OPM_TL_SIZE - 1) {
                slot.sl$Volume = OPM_TL_SIZE - 1;
                slot.sl$Stage = OPMStage_SILENCE;
                slot.sl$TransitionMask = 1;
              }
              break;
              //停止
            case OPMStage_SILENCE:
              break;
            }
          }
        }
      }
      //音色生成
      for (let i = 0; i < OPM_CHANNEL_COUNT; i++) {
        let channel = port.op$Channel[i];
        port.op$Joint1Box[0] = port.op$Joint2Box[0] = port.op$Joint3Box[0] = port.op$Joint4Box[0] = channel.ch$OutputBox[0] = 0;
        channel.ch$bfOutputHandle[0] = channel.ch$bfInputValue;  //前回バッファに入力した値→今回バッファから出力する値
        channel.ch$fbOutputHandle[0] = channel.ch$fbInputValue;  //前回フィードバック回路に入力した値→今回フィードバック回路から出力する値
        let amsValue = port.op$LFOAMOutput * channel.ch$ShiftAMS;  //振幅変調回路の出力
        let env = channel.ch$M1.sl$TotalLevel + channel.ch$M1.sl$Volume + (amsValue & channel.ch$M1.sl$AMSMask);
        let fbValue = (channel.ch$fbPreviousValue + channel.ch$fbInputValue) * channel.ch$fbScale;  //今回フィードバックする値。前々回の入力と前回の入力の和をスケーリングしたもの
        channel.ch$fbPreviousValue = channel.ch$fbInputValue;  //前回フィードバック回路に入力した値
        if (env < OPM_ENV_QUIET) {  //0..831
          let p = (env << 3) + opmSinTable[((channel.ch$M1.sl$Phase & ~OPM_PHASE_MASK) + fbValue) >> OPM_PHASE_SHIFT & OPM_SIN_MASK];  //(0..831)*8+(0..4275)=0..10923
          channel.ch$fbInputValue = p < OPM_TL_TABLE_SIZE ? opmTLTable[p] : 0;  //今回フィードバック回路に入力する値
        } else {
          channel.ch$fbInputValue = 0;
        }
        env = channel.ch$M2.sl$TotalLevel + channel.ch$M2.sl$Volume + (amsValue & channel.ch$M2.sl$AMSMask);
        if (env < OPM_ENV_QUIET) {
          let p = (env << 3) + opmSinTable[((channel.ch$M2.sl$Phase >> OPM_PHASE_SHIFT) + (channel.ch$m2InputHandle[0] >> (OPM_PHASE_SHIFT - 15))) & OPM_SIN_MASK];  //(0..831)*8+(0..4275)=0..10923
          if (p < OPM_TL_TABLE_SIZE) {
            channel.ch$m2OutputHandle[0] += opmTLTable[p];
          }
        }
        env = channel.ch$C1.sl$TotalLevel + channel.ch$C1.sl$Volume + (amsValue & channel.ch$C1.sl$AMSMask);
        if (env < OPM_ENV_QUIET) {
          let p = (env << 3) + opmSinTable[((channel.ch$C1.sl$Phase >> OPM_PHASE_SHIFT) + (channel.ch$c1InputHandle[0] >> (OPM_PHASE_SHIFT - 15))) & OPM_SIN_MASK];  //(0..831)*8+(0..4275)=0..10923
          if (p < OPM_TL_TABLE_SIZE) {
            channel.ch$c1OutputHandle[0] += opmTLTable[p];
          }
        }
        env = channel.ch$C2.sl$TotalLevel + channel.ch$C2.sl$Volume + (amsValue & channel.ch$C2.sl$AMSMask);
        if (channel.ch$NoiseOn) {
          channel.ch$OutputBox[0] += ((port.op$NoiseRegister >> 15 & 2) - 1) * (env < 0x3ff ? (env ^ 0x3ff) << 1 : 0);
        } else {
          if (env < OPM_ENV_QUIET) {
            let p = (env << 3) + opmSinTable[((channel.ch$C2.sl$Phase >> OPM_PHASE_SHIFT) + (channel.ch$c2InputHandle[0] >> (OPM_PHASE_SHIFT - 15))) & OPM_SIN_MASK];  //(0..831)*8+(0..4275)=0..10923
            if (p < OPM_TL_TABLE_SIZE) {
              channel.ch$OutputBox[0] += opmTLTable[p];
            }
          }
        }
        channel.ch$bfInputValue = channel.ch$bfInputHandle[0];  //今回バッファに入力する値
      }
      let l = 0;
      let r = 0;
      for (let i = 0; i < OPM_CHANNEL_COUNT; i++) {
        let channel = port.op$Channel[i];
        l += channel.ch$OutputBox[0] & channel.ch$LeftMask;
        r += channel.ch$OutputBox[0] & channel.ch$RightMask;
      }
      internalBufferLeft[k] += l * (1 / 8192);
      internalBufferRight[k] += r * (1 / 8192);
      k++;
      //LFO
      if (port.op$LFOActive && --port.op$LFOCounterMinor == 0) {
        port.op$LFOCounterMinor = port.op$LFOPeriodMinor;
        let t = port.op$LFOCounterMajor + port.op$LFOPeriodMajor;
        port.op$LFOCounterMajor = t & 15;
        port.op$LFOWaveIndex = (port.op$LFOWaveIndex + (t >>> 4)) & 255;
        port.op$LFOAMValue = opmLFOAMTable[port.op$WAVE256 + port.op$LFOWaveIndex];
        port.op$LFOPMValue = opmLFOPMTable[port.op$WAVE256 + port.op$LFOWaveIndex];
      }
      port.op$LFOAMOutput = (port.op$LFOAMValue * port.op$AMD) >>> 7;
      let u = port.op$LFOPMValue * port.op$PMD;
      port.op$LFOPMOutput = u >= 0 ? u >> 7 : -(-u >> 7);  //opmLFOPMValue*opmPMD/128。xがintのときx/128とx>>7は異なることに注意。-64/128は0で-64>>7は-1
      //NOISE
      port.op$NoisePhase += port.op$NoiseFrequency;
      if (port.op$NoisePhase >>> 16 != 0) {  //port.op$NoisePhase >= 65536
        port.op$NoiseRegister = (((port.op$NoiseRegister ^ port.op$NoiseRegister >>> 3) & 1) ^ 1) << 16 | port.op$NoiseRegister >>> 1;
        port.op$NoisePhase &= 65535;
      }
      //PG
      for (let i = 0; i < OPM_CHANNEL_COUNT; i++) {
        let channel = port.op$Channel[i];
        let m1 = channel.ch$M1;
        let m2 = channel.ch$M2;
        let c1 = channel.ch$C1;
        let c2 = channel.ch$C2;
        let pmDepth = port.op$LFOPMOutput * channel.ch$ShiftPMS >> 6;
        if (pmDepth != 0) {
          let keyIndex = channel.ch$KeyIndex + pmDepth;
          m1.sl$Phase += ((opmFreqTable[keyIndex + m1.sl$Detune2Depth] + m1.sl$Detune1Freq) * m1.sl$Multiply) >>> 1;
          m2.sl$Phase += ((opmFreqTable[keyIndex + m2.sl$Detune2Depth] + m2.sl$Detune1Freq) * m2.sl$Multiply) >>> 1;
          c1.sl$Phase += ((opmFreqTable[keyIndex + c1.sl$Detune2Depth] + c1.sl$Detune1Freq) * c1.sl$Multiply) >>> 1;
          c2.sl$Phase += ((opmFreqTable[keyIndex + c2.sl$Detune2Depth] + c2.sl$Detune1Freq) * c2.sl$Multiply) >>> 1;
        } else {
          m1.sl$Phase += m1.sl$Freq;
          m2.sl$Phase += m2.sl$Freq;
          c1.sl$Phase += c1.sl$Freq;
          c2.sl$Phase += c2.sl$Freq;
        }
      }
      //CSM
      if (port.op$CSMRequest != 0) {
        if (port.op$CSMRequest > 0) {
          for (let i = 0; i < OPM_CHANNEL_COUNT; i++) {
            let channel = port.op$Channel[i];
            slKeyOn (channel.ch$M1, 2);
            slKeyOn (channel.ch$M2, 2);
            slKeyOn (channel.ch$C1, 2);
            slKeyOn (channel.ch$C2, 2);
          }
          port.op$CSMRequest = -1;
        } else {
          for (let i = 0; i < OPM_CHANNEL_COUNT; i++) {
            let channel = port.op$Channel[i];
            slKeyOff (channel.ch$M1, ~2);
            slKeyOff (channel.ch$M2, ~2);
            slKeyOff (channel.ch$C1, ~2);
            slKeyOff (channel.ch$C2, ~2);
          }
          port.op$CSMRequest = 0;
        }
      }
    }  //for k
  }  //for pn
}  //opmAdvance



//------------------------------------------------------------------------
//PCM

let pcmData = new Uint8Array (0);
let pcmRate = 4;
let pcmLeft = true;
let pcmRight = true;

const PCM_PORT_COUNT = 1;  //int  PCMポートの数
let pcmPortArray;  //Array  PCMポートの配列

//ADPCMデコーダ
let adpcmLowerTable;  //Float32Array  下位4ビットに対応する差分のテーブル
let adpcmUpperTable;  //Float32Array  上位4ビットに対応する差分のテーブル
let adpcmNextTable;  //Int32Array  次の予測指標<<8のテーブル

//pcmInit ()
//  ADPCMデコーダを初期化する
function pcmInit () {
  //4bitデータのADPCM→PCM変換テーブルを作る
  //  d4[予測指標<<4|4bitデータ] = 4bitデータに対応する差分
  //  p4[予測指標<<4|4bitデータ] = 次の予測指標
  let d4 = new Float32Array (16 * 49);
  let p4 = new Int32Array (16 * 49);
  for (let p = 0; p < 49; p++) {  //予測指標
    let x = floor (16 * pow (1.1, p));  //PCM8.X,PCMLIB.a,MAMEなど
    //let x = floor (32768 / pow (1.1, 80 - p));  //PCM8A.X
    for (let n = 0; n < 16; n++) {  //4bitデータ
      let i = p << 4 | n;
      d4[i] = (1 - (n >> 2 & 2)) * ((n >> 2 & 1) * x + (n >> 1 & 1) * (x >> 1) + (n & 1) * (x >> 2) + (x >> 3)) * (1 / 2048);  //4bitデータに対応する差分
      p4[i] = max2 (0, min2 (48, p + ((n & 7) - 4 >> 3 | (n & 3) + 1 << 1)));  //次の予測指標。p+={-1,-1,-1,-1,2,4,6,8}[n&7]
    }
  }
  //8bitデータのADPCM→PCM変換テーブルを作る
  //  adpcmLowerTable[予測指標<<8|8bitデータ] = 下位4bitに対応する差分
  //  adpcmUpperTable[予測指標<<8|8bitデータ] = 上位4bitに対応する差分
  //  adpcmNextTable[予測指標<<8|8bitデータ] = 次の予測指標<<8
  adpcmLowerTable = new Float32Array (256 * 49);
  adpcmUpperTable = new Float32Array (256 * 49);
  adpcmNextTable = new Int32Array (256 * 49);
  for (let p = 0; p < 49; p++) {  //予測指標
    for (let n = 0; n < 256; n++) {  //8bitデータ
      let i = p << 8 | n;
      let j = p << 4 | (n & 15);
      adpcmLowerTable[i] = d4[j];  //下位4ビットに対応する差分
      j = p4[j] << 4 | n >> 4;
      adpcmUpperTable[i] = d4[j];  //上位4ビットに対応する差分
      adpcmNextTable[i] = p4[j] << 8;  //次の予測指標<<8
    }
  }
  //ポート
  pcmPortArray = [];
  for (let pn = 0; pn < PCM_PORT_COUNT; pn++) {
    pcmPortArray[pn] = {

    pp$RateNumber: 4,  //int  ADPCMデータのサンプリング周波数の番号。0=3906Hz,1=5208Hz,2=7812Hz,3=10416Hz,4=15625Hz,5=20833Hz,6=31250Hz
    pp$PanLeft: true,  //boolean  true=左ON
    pp$PanRight: true,  //boolean  true=右ON

    pp$Stage: 0,  //int  現在の予測指標<<8

    pp$Buffer: new Uint8Array (0),  //Uint8Array  ADPCMデータのバッファ
    pp$Pointer: 0,  //int  pp$Bufferから次に読み出す位置
    pp$Length: 0,  //int  ADPCMデータの長さ
    pp$Running: false,  //boolean  true=再生中

    pp$Data: 0,  //float  デコードされたPCMデータ。-1<=pp$Data<=1
    pp$Data1: 0,  //float  前回のデコードされたPCMデータ
    pp$Data2: 0,  //float  前々回のデコードされたPCMデータ
    pp$Data3: 0,  //float  前々々回のデコードされたPCMデータ。4個使って補間する

    pp$TempBuffer: new Float32Array (32),  //Uint8Array  周波数変換されたPCMデータのバッファ
    pp$TempPointer: 0,  //int  pp$TempBufferから次に読み出す位置
    pp$TempLength: 0  //int  周波数変換されたPCMデータの長さ

    };
  }
}

//pcmSet (data, rate, left, right)
//  PCMデータを設定する
function pcmSet (data, rate, left, right) {
  if (data !== undefined) {
    pcmData = data;
  }
  if (rate !== undefined) {
    pcmRate = rate;
  }
  if (left !== undefined) {
    pcmLeft = left;
  }
  if (right !== undefined) {
    pcmRight = right;
  }
}

//pcmStart ()
//  PCMデータの再生を開始する
function pcmStart () {
  let data = pcmData;
  let rate = pcmRate;
  let left = pcmLeft;
  let right = pcmRight;
  let idlePort = null;  //再生中ではないポート
  let nearlyIdlePort = null;  //再生中だが残りのデータが少ないポート
  for (let pn = 0; pn < PCM_PORT_COUNT; pn++) {
    let port = pcmPortArray[pn];
    if (!port.pp$Running) {  //再生中ではない
      idlePort = port;  //再生中ではないポート
      break;
    }
    if (nearlyIdlePort == null ||
        port.pp$Length - port.pp$Pointer <
        nearlyIdlePort.pp$Length - nearlyIdlePort.pp$Pointer) {  //残りのデータが少ない
      nearlyIdlePort = port;  //再生中だが残りのデータが少ないポート
    }
  }
  if (idlePort == null) {  //再生中ではないポートが見付からなかった
    idlePort = nearlyIdlePort;  //再生中だが残りのデータが少ないポート
    idlePort.pp$Running = false;  //止めて使う
  }
  idlePort.pp$RateNumber = rate;  //ADPCMデータのサンプリング周波数の番号
  idlePort.pp$PanLeft = left;  //左ON
  idlePort.pp$PanRight = right;  //右ON
  idlePort.pp$Stage = 0;  //予測指標
  idlePort.pp$Buffer = data;  //ADPCMデータ
  idlePort.pp$Pointer = 0;
  idlePort.pp$Length = data.length;
  idlePort.pp$Running = true;  //再生開始
}

//  PCMデータの再生を停止する
function pcmStop () {
  for (let pn = 0; pn < PCM_PORT_COUNT; pn++) {
    let port = pcmPortArray[pn];
    port.pp$Running = false;  //再生停止
  }
}

//pcmAdvance (from, to)
//  内部バッファにPCMデータを加える
function pcmAdvance (from, to) {
  for (let pn = 0; pn < PCM_PORT_COUNT; pn++) {
    let port = pcmPortArray[pn];
    if (!port.pp$Running) {  //再生中ではない
      continue;
    }
    let rateNumber = port.pp$RateNumber;
    let panLeft = port.pp$PanLeft;
    let panRight = port.pp$PanRight;
    let buffer = port.pp$Buffer;
    let length = port.pp$Length;
    let tempBuffer = port.pp$TempBuffer;
    let pointer = port.pp$Pointer;
    let data = port.pp$Data;
    let data1 = port.pp$Data1;
    let data2 = port.pp$Data2;
    let data3 = port.pp$Data3;
    let stage = port.pp$Stage;
    let tempLength = port.pp$TempLength;
    let tempPointer = port.pp$TempPointer;
    for (let p = from; p < to; ) {
      if (tempLength <= tempPointer) {  //内部サンプリング周波数に変換されたPCMデータがない
        let mm = data3;
        let m0 = data2;
        let m1 = data1;
        let m2, m3;
        //ADPCMデータを1バイトデコードしてPCMデータを2個取り出す
        if (pointer < length) {  //ADPCMデータがある
          let i = stage + buffer[pointer++];
          m2 = data = max2 (-0.5, min2 (0.5, data + adpcmLowerTable[i]));
          m3 = data = max2 (-0.5, min2 (0.5, data + adpcmUpperTable[i]));
          stage = adpcmNextTable[i];
        } else if (data != 0) {  //ADPCMデータがないが無音でない
          m2 = m3 = data;
        } else {  //ADPCMデータがなくて無音である
          port.pp$Running = false;  //再生終了
          break;
        }
        //減衰させる
        if (data < 0) {
          data += 1 / 8192;
        } else if (0 < data) {
          data -= 1 / 8192;
        }
        //PCMデータを補間して内部サンプリング周波数に変換する
        switch (rateNumber) {
          //<<<<---- automatically generated code ----
          //  echo read("hermite2.gp");hermite_jscode() | gp -q
        case 0:  //3906Hz
          tempBuffer[ 0] = -0.02746582 * mm + 0.99060059 * m0 + 0.03869629 * m1 - 0.00183105 * m2;
          tempBuffer[ 1] = -0.04785156 * mm + 0.96386719 * m0 + 0.09082031 * m1 - 0.00683594 * m2;
          tempBuffer[ 2] = -0.06188965 * mm + 0.92199707 * m0 + 0.15417480 * m1 - 0.01428223 * m2;
          tempBuffer[ 3] = -0.07031250 * mm + 0.86718750 * m0 + 0.22656250 * m1 - 0.02343750 * m2;
          tempBuffer[ 4] = -0.07385254 * mm + 0.80163574 * m0 + 0.30578613 * m1 - 0.03356934 * m2;
          tempBuffer[ 5] = -0.07324219 * mm + 0.72753906 * m0 + 0.38964844 * m1 - 0.04394531 * m2;
          tempBuffer[ 6] = -0.06921387 * mm + 0.64709473 * m0 + 0.47595215 * m1 - 0.05383301 * m2;
          tempBuffer[ 7] = -0.06250000 * mm + 0.56250000 * m0 + 0.56250000 * m1 - 0.06250000 * m2;
          tempBuffer[ 8] = -0.05383301 * mm + 0.47595215 * m0 + 0.64709473 * m1 - 0.06921387 * m2;
          tempBuffer[ 9] = -0.04394531 * mm + 0.38964844 * m0 + 0.72753906 * m1 - 0.07324219 * m2;
          tempBuffer[10] = -0.03356934 * mm + 0.30578613 * m0 + 0.80163574 * m1 - 0.07385254 * m2;
          tempBuffer[11] = -0.02343750 * mm + 0.22656250 * m0 + 0.86718750 * m1 - 0.07031250 * m2;
          tempBuffer[12] = -0.01428223 * mm + 0.15417480 * m0 + 0.92199707 * m1 - 0.06188965 * m2;
          tempBuffer[13] = -0.00683594 * mm + 0.09082031 * m0 + 0.96386719 * m1 - 0.04785156 * m2;
          tempBuffer[14] = -0.00183105 * mm + 0.03869629 * m0 + 0.99060059 * m1 - 0.02746582 * m2;
          tempBuffer[15] = m1;
          tempBuffer[16] = -0.02746582 * m0 + 0.99060059 * m1 + 0.03869629 * m2 - 0.00183105 * m3;
          tempBuffer[17] = -0.04785156 * m0 + 0.96386719 * m1 + 0.09082031 * m2 - 0.00683594 * m3;
          tempBuffer[18] = -0.06188965 * m0 + 0.92199707 * m1 + 0.15417480 * m2 - 0.01428223 * m3;
          tempBuffer[19] = -0.07031250 * m0 + 0.86718750 * m1 + 0.22656250 * m2 - 0.02343750 * m3;
          tempBuffer[20] = -0.07385254 * m0 + 0.80163574 * m1 + 0.30578613 * m2 - 0.03356934 * m3;
          tempBuffer[21] = -0.07324219 * m0 + 0.72753906 * m1 + 0.38964844 * m2 - 0.04394531 * m3;
          tempBuffer[22] = -0.06921387 * m0 + 0.64709473 * m1 + 0.47595215 * m2 - 0.05383301 * m3;
          tempBuffer[23] = -0.06250000 * m0 + 0.56250000 * m1 + 0.56250000 * m2 - 0.06250000 * m3;
          tempBuffer[24] = -0.05383301 * m0 + 0.47595215 * m1 + 0.64709473 * m2 - 0.06921387 * m3;
          tempBuffer[25] = -0.04394531 * m0 + 0.38964844 * m1 + 0.72753906 * m2 - 0.07324219 * m3;
          tempBuffer[26] = -0.03356934 * m0 + 0.30578613 * m1 + 0.80163574 * m2 - 0.07385254 * m3;
          tempBuffer[27] = -0.02343750 * m0 + 0.22656250 * m1 + 0.86718750 * m2 - 0.07031250 * m3;
          tempBuffer[28] = -0.01428223 * m0 + 0.15417480 * m1 + 0.92199707 * m2 - 0.06188965 * m3;
          tempBuffer[29] = -0.00683594 * m0 + 0.09082031 * m1 + 0.96386719 * m2 - 0.04785156 * m3;
          tempBuffer[30] = -0.00183105 * m0 + 0.03869629 * m1 + 0.99060059 * m2 - 0.02746582 * m3;
          tempBuffer[31] = m2;
          tempLength = 32;
          break;
        case 1:  //5208Hz
          tempBuffer[ 0] = -0.03501157 * mm + 0.98350694 * m0 + 0.05468750 * m1 - 0.00318287 * m2;
          tempBuffer[ 1] = -0.05787037 * mm + 0.93750000 * m0 + 0.13194444 * m1 - 0.01157407 * m2;
          tempBuffer[ 2] = -0.07031250 * mm + 0.86718750 * m0 + 0.22656250 * m1 - 0.02343750 * m2;
          tempBuffer[ 3] = -0.07407407 * mm + 0.77777778 * m0 + 0.33333333 * m1 - 0.03703704 * m2;
          tempBuffer[ 4] = -0.07089120 * mm + 0.67447917 * m0 + 0.44704861 * m1 - 0.05063657 * m2;
          tempBuffer[ 5] = -0.06250000 * mm + 0.56250000 * m0 + 0.56250000 * m1 - 0.06250000 * m2;
          tempBuffer[ 6] = -0.05063657 * mm + 0.44704861 * m0 + 0.67447917 * m1 - 0.07089120 * m2;
          tempBuffer[ 7] = -0.03703704 * mm + 0.33333333 * m0 + 0.77777778 * m1 - 0.07407407 * m2;
          tempBuffer[ 8] = -0.02343750 * mm + 0.22656250 * m0 + 0.86718750 * m1 - 0.07031250 * m2;
          tempBuffer[ 9] = -0.01157407 * mm + 0.13194444 * m0 + 0.93750000 * m1 - 0.05787037 * m2;
          tempBuffer[10] = -0.00318287 * mm + 0.05468750 * m0 + 0.98350694 * m1 - 0.03501157 * m2;
          tempBuffer[11] = m1;
          tempBuffer[12] = -0.03501157 * m0 + 0.98350694 * m1 + 0.05468750 * m2 - 0.00318287 * m3;
          tempBuffer[13] = -0.05787037 * m0 + 0.93750000 * m1 + 0.13194444 * m2 - 0.01157407 * m3;
          tempBuffer[14] = -0.07031250 * m0 + 0.86718750 * m1 + 0.22656250 * m2 - 0.02343750 * m3;
          tempBuffer[15] = -0.07407407 * m0 + 0.77777778 * m1 + 0.33333333 * m2 - 0.03703704 * m3;
          tempBuffer[16] = -0.07089120 * m0 + 0.67447917 * m1 + 0.44704861 * m2 - 0.05063657 * m3;
          tempBuffer[17] = -0.06250000 * m0 + 0.56250000 * m1 + 0.56250000 * m2 - 0.06250000 * m3;
          tempBuffer[18] = -0.05063657 * m0 + 0.44704861 * m1 + 0.67447917 * m2 - 0.07089120 * m3;
          tempBuffer[19] = -0.03703704 * m0 + 0.33333333 * m1 + 0.77777778 * m2 - 0.07407407 * m3;
          tempBuffer[20] = -0.02343750 * m0 + 0.22656250 * m1 + 0.86718750 * m2 - 0.07031250 * m3;
          tempBuffer[21] = -0.01157407 * m0 + 0.13194444 * m1 + 0.93750000 * m2 - 0.05787037 * m3;
          tempBuffer[22] = -0.00318287 * m0 + 0.05468750 * m1 + 0.98350694 * m2 - 0.03501157 * m3;
          tempBuffer[23] = m2;
          tempLength = 24;
          break;
        case 2:  //7812Hz
          tempBuffer[ 0] = -0.04785156 * mm + 0.96386719 * m0 + 0.09082031 * m1 - 0.00683594 * m2;
          tempBuffer[ 1] = -0.07031250 * mm + 0.86718750 * m0 + 0.22656250 * m1 - 0.02343750 * m2;
          tempBuffer[ 2] = -0.07324219 * mm + 0.72753906 * m0 + 0.38964844 * m1 - 0.04394531 * m2;
          tempBuffer[ 3] = -0.06250000 * mm + 0.56250000 * m0 + 0.56250000 * m1 - 0.06250000 * m2;
          tempBuffer[ 4] = -0.04394531 * mm + 0.38964844 * m0 + 0.72753906 * m1 - 0.07324219 * m2;
          tempBuffer[ 5] = -0.02343750 * mm + 0.22656250 * m0 + 0.86718750 * m1 - 0.07031250 * m2;
          tempBuffer[ 6] = -0.00683594 * mm + 0.09082031 * m0 + 0.96386719 * m1 - 0.04785156 * m2;
          tempBuffer[ 7] = m1;
          tempBuffer[ 8] = -0.04785156 * m0 + 0.96386719 * m1 + 0.09082031 * m2 - 0.00683594 * m3;
          tempBuffer[ 9] = -0.07031250 * m0 + 0.86718750 * m1 + 0.22656250 * m2 - 0.02343750 * m3;
          tempBuffer[10] = -0.07324219 * m0 + 0.72753906 * m1 + 0.38964844 * m2 - 0.04394531 * m3;
          tempBuffer[11] = -0.06250000 * m0 + 0.56250000 * m1 + 0.56250000 * m2 - 0.06250000 * m3;
          tempBuffer[12] = -0.04394531 * m0 + 0.38964844 * m1 + 0.72753906 * m2 - 0.07324219 * m3;
          tempBuffer[13] = -0.02343750 * m0 + 0.22656250 * m1 + 0.86718750 * m2 - 0.07031250 * m3;
          tempBuffer[14] = -0.00683594 * m0 + 0.09082031 * m1 + 0.96386719 * m2 - 0.04785156 * m3;
          tempBuffer[15] = m2;
          tempLength = 16;
          break;
        case 3:  //10416Hz
          tempBuffer[ 0] = -0.05787037 * mm + 0.93750000 * m0 + 0.13194444 * m1 - 0.01157407 * m2;
          tempBuffer[ 1] = -0.07407407 * mm + 0.77777778 * m0 + 0.33333333 * m1 - 0.03703704 * m2;
          tempBuffer[ 2] = -0.06250000 * mm + 0.56250000 * m0 + 0.56250000 * m1 - 0.06250000 * m2;
          tempBuffer[ 3] = -0.03703704 * mm + 0.33333333 * m0 + 0.77777778 * m1 - 0.07407407 * m2;
          tempBuffer[ 4] = -0.01157407 * mm + 0.13194444 * m0 + 0.93750000 * m1 - 0.05787037 * m2;
          tempBuffer[ 5] = m1;
          tempBuffer[ 6] = -0.05787037 * m0 + 0.93750000 * m1 + 0.13194444 * m2 - 0.01157407 * m3;
          tempBuffer[ 7] = -0.07407407 * m0 + 0.77777778 * m1 + 0.33333333 * m2 - 0.03703704 * m3;
          tempBuffer[ 8] = -0.06250000 * m0 + 0.56250000 * m1 + 0.56250000 * m2 - 0.06250000 * m3;
          tempBuffer[ 9] = -0.03703704 * m0 + 0.33333333 * m1 + 0.77777778 * m2 - 0.07407407 * m3;
          tempBuffer[10] = -0.01157407 * m0 + 0.13194444 * m1 + 0.93750000 * m2 - 0.05787037 * m3;
          tempBuffer[11] = m2;
          tempLength = 12;
          break;
        case 4:  //15625Hz
          tempBuffer[ 0] = -0.07031250 * mm + 0.86718750 * m0 + 0.22656250 * m1 - 0.02343750 * m2;
          tempBuffer[ 1] = -0.06250000 * mm + 0.56250000 * m0 + 0.56250000 * m1 - 0.06250000 * m2;
          tempBuffer[ 2] = -0.02343750 * mm + 0.22656250 * m0 + 0.86718750 * m1 - 0.07031250 * m2;
          tempBuffer[ 3] = m1;
          tempBuffer[ 4] = -0.07031250 * m0 + 0.86718750 * m1 + 0.22656250 * m2 - 0.02343750 * m3;
          tempBuffer[ 5] = -0.06250000 * m0 + 0.56250000 * m1 + 0.56250000 * m2 - 0.06250000 * m3;
          tempBuffer[ 6] = -0.02343750 * m0 + 0.22656250 * m1 + 0.86718750 * m2 - 0.07031250 * m3;
          tempBuffer[ 7] = m2;
          tempLength = 8;
          break;
        case 5:  //20833Hz
          tempBuffer[ 0] = -0.07407407 * mm + 0.77777778 * m0 + 0.33333333 * m1 - 0.03703704 * m2;
          tempBuffer[ 1] = -0.03703704 * mm + 0.33333333 * m0 + 0.77777778 * m1 - 0.07407407 * m2;
          tempBuffer[ 2] = m1;
          tempBuffer[ 3] = -0.07407407 * m0 + 0.77777778 * m1 + 0.33333333 * m2 - 0.03703704 * m3;
          tempBuffer[ 4] = -0.03703704 * m0 + 0.33333333 * m1 + 0.77777778 * m2 - 0.07407407 * m3;
          tempBuffer[ 5] = m2;
          tempLength = 6;
          break;
        case 6:  //31250Hz
          tempBuffer[ 0] = -0.06250000 * mm + 0.56250000 * m0 + 0.56250000 * m1 - 0.06250000 * m2;
          tempBuffer[ 1] = m1;
          tempBuffer[ 2] = -0.06250000 * m0 + 0.56250000 * m1 + 0.56250000 * m2 - 0.06250000 * m3;
          tempBuffer[ 3] = m2;
          tempLength = 4;
          break;
          //---- automatically generated code ---->>>>
        }
        data3 = m1;
        data2 = m2;
        data1 = m3;
        tempPointer = 0;
      }
      //内部バッファに内部サンプリング周波数に変換されたPCMデータを加える
      let tempPointerTo = tempPointer + min2 (tempLength, to - p);
      if (panLeft && panRight) {  //左と右
        for (let q = tempPointer; q < tempPointerTo; q++) {
          let t = tempBuffer[q];
          internalBufferLeft[p] += t;
          internalBufferRight[p++] += t;
        }
      } else if (panLeft) {  //左
        for (let q = tempPointer; q < tempPointerTo; q++) {
          internalBufferLeft[p++] += tempBuffer[q];
        }
      } else if (panRight) {  //右
        for (let q = tempPointer; q < tempPointerTo; q++) {
          internalBufferRight[p++] = tempBuffer[q];
        }
      } else {  //なし
        p += tempPointerTo - tempPointer;
      }
      tempPointer = tempPointerTo;
    }  //for p
    port.pp$Pointer = pointer;
    port.pp$Data = data;
    port.pp$Data1 = data1;
    port.pp$Data2 = data2;
    port.pp$Data3 = data3;
    port.pp$Stage = stage;
    port.pp$TempLength = tempLength;
    port.pp$TempPointer = tempPointer;
  }  //for de
}



//audioAdvance (from, to)
//  内部バッファをfromからtoまで構築する
function audioAdvance (from, to) {
  //内部バッファをゼロクリアする
  internalBufferLeft.fill (0, from, to);
  internalBufferRight.fill (0, from, to);
  //内部バッファにOPMデータを加える
  opmAdvance (from, to);
  //内部バッファにPCMデータを加える
  pcmAdvance (from, to);
}



//コマンドキュー
let commandQueue;  //Array  コマンドキュー。command.tの昇順にソートされている。command.tまで内部バッファを構築してからcommand.cを実行する
let commandTime;  //最後に実行したコマンドの時刻

//s = commandComparator (x, y)
//  コマンドコンパレータ
function commandComparator (x, y) {
  return x.t - y.t;
}

//  コマンドを追加する
//  時刻が同じときは先に追加されたコマンドを先に実行する
function addCommand (command) {
  let l = 0;
  let r = commandQueue.length;
  while (l < r) {
    let m = (l + r) >> 1;
    if (commandComparator (commandQueue[m], command) <= 0) {
      l = m + 1;
    } else {
      r = m;
    }
  }
  commandQueue.splice (l, 0, command);
}

//  コマンドを削除する
//  なければ何もしない
function removeCommand (c, p) {
  for (let i = 0; i < commandQueue.length; i++) {
    if (commandQueue[i].c == c &&
        commandQueue[i].p == p) {
      commandQueue.splice (i, 1);
      break;
    }
  }
}

//  コマンドを実行する
//  opmset
//    OPMレジスタに書き込む
//    t  時刻
//    c  "opmset"
//    p  ポート
//    a  アドレス
//    d  データ
//  opminta
//    OPMタイマA割り込み
//    t  時刻
//    c  "opminta"
//    p  ポート
//  opmintb
//    OPMタイマB割り込み
//    t  時刻
//    c  "opmintb"
//    p  ポート
//  pcmset
//    PCMデータを設定する
//    t  時刻
//    c  "pcmset"
//    d  データ
//    f  ADPCMデータのサンプリング周波数の番号。0=3906Hz,1=5208Hz,2=7812Hz,3=10416Hz,4=15625Hz,5=20833Hz,6=31250Hz
//    l  true=左ON
//    r  true=右ON
//  pcmstart
//    PCMデータの再生を開始する
//    t  時刻
//    c  "pcmstart"
//  pcmstop
//    PCMデータの再生を停止する
//    t  時刻
//    c  "pcmstop"
function execCommand (command) {
  commandTime = command.t;
  switch (command.c) {
  case "opmset":
    opmSet (opmPortArray[command.p], command.a, command.d);
    break;
  case "opminta":
    opmIntA (opmPortArray[command.p]);
    break;
←Previous | 1 2 3 4 | Next→