OPM.java
     1: //========================================================================================
     2: //  OPM.java
     3: //    en:Frequency modulation sound source
     4: //    ja:FM音源
     5: //  Copyright (C) 2003-2022 Makoto Kamada
     6: //
     7: //  This file is part of the XEiJ (X68000 Emulator in Java).
     8: //  You can use, modify and redistribute the XEiJ if the conditions are met.
     9: //  Read the XEiJ License for more details.
    10: //  https://stdkmd.net/xeij/
    11: //========================================================================================
    12: 
    13: package xeij;
    14: 
    15: import java.util.*;
    16: 
    17: public class OPM {
    18: 
    19:   public static final boolean OPM_ON = true;  //true=OPMを出力する
    20: 
    21:   public static final int OPM_OSC_FREQ = 4000000;  //クロック周波数(Hz)。4000000Hz
    22:   public static final int OPM_SAMPLE_FREQ = OPM_OSC_FREQ / 64;  //サンプリング周波数(Hz)。62500Hz
    23:   public static final long OPM_SAMPLE_TIME = XEiJ.TMR_FREQ / OPM_SAMPLE_FREQ;  //1サンプルの時間(XEiJ.TMR_FREQ単位)。16000000ps
    24:   public static final int OPM_BLOCK_SAMPLES = OPM_SAMPLE_FREQ / SoundSource.SND_BLOCK_FREQ;  //1ブロックのサンプル数。2500
    25: 
    26:   public static int opmOutputMask;  //-1=OPMを出力する
    27: 
    28:   //YM2151
    29:   public static YM2151 opmYM2151;
    30:   public static TickerQueue.Ticker opmTimerATicker;
    31:   public static TickerQueue.Ticker opmTimerBTicker;
    32:   public static int[] opmRegister;  //[address]=data,[256+ch]=slotMask,[264]=AMD,[265]=PMD
    33:   public static int[] opmBuffer;
    34:   public static int opmPointer;
    35:   public static long opmBusyClock;
    36: 
    37:   //opmInit ()
    38:   //  初期化
    39:   public static void opmInit () {
    40:     //opmOutputMask = -1;  //OPMを出力する
    41:     opmYM2151 = new YM2151 ();
    42:     opmTimerATicker = new TickerQueue.Ticker () {
    43:       @Override protected void tick () {
    44:         opmYM2151.timerAExpired ();
    45:       }
    46:     };
    47:     opmTimerBTicker = new TickerQueue.Ticker () {
    48:       @Override protected void tick () {
    49:         opmYM2151.timerBExpired ();
    50:       }
    51:     };
    52:     opmRegister = new int[266];
    53:     opmYM2151.setListener (new YM2151.Listener () {
    54:       @Override public void timerA (int clocks) {
    55:         if (clocks != -1) {  //始動
    56:           TickerQueue.tkqAdd (opmTimerATicker, XEiJ.mpuClockTime + (XEiJ.TMR_FREQ / (long) OPM_OSC_FREQ) * (long) clocks);
    57:         } else {  //停止
    58:           TickerQueue.tkqRemove (opmTimerATicker);
    59:         }
    60:       }
    61:       @Override public void timerB (int clocks) {
    62:         if (clocks != -1) {  //始動
    63:           TickerQueue.tkqAdd (opmTimerBTicker, XEiJ.mpuClockTime + (XEiJ.TMR_FREQ / (long) OPM_OSC_FREQ) * (long) clocks);
    64:         } else {  //停止
    65:           TickerQueue.tkqRemove (opmTimerBTicker);
    66:         }
    67:       }
    68:       @Override public void busy (int clocks) {
    69:         opmBusyClock = XEiJ.mpuClockTime + (XEiJ.TMR_FREQ / (long) OPM_OSC_FREQ) * (long) clocks;
    70:       }
    71:       @Override public boolean isBusy () {
    72:         return XEiJ.mpuClockTime < opmBusyClock;
    73:       }
    74:       @Override public void irq (boolean asserted) {
    75:         if (asserted) {
    76:           MC68901.mfpOpmirqFall ();
    77:         } else {
    78:           MC68901.mfpOpmirqRise ();
    79:         }
    80:       }
    81:       @Override public void control (int data) {
    82:         FDC.fdcSetEnforcedReady ((data & 1) != 0);  //CT2 強制レディ状態の設定。0=通常動作,1=強制レディ状態
    83:         ADPCM.pcmOscillator = (data >> 1) & 1;  //CT1 ADPCMの原発振周波数の設定。0=8MHz/8MHz,1=4MHz/16MHz
    84:         ADPCM.pcmUpdateRepeatInterval ();
    85:       }
    86:       @Override public void written (int pointer, int address, int data) {
    87:         if (address == 0x08) {  //KON
    88:           opmRegister[256 + (data & 7)] = (data >> 3) & 15;
    89:         } else if (address == 0x19) {  //AMD/PMD
    90:           opmRegister[264 + (data >> 7)] = data & 127;
    91:         } else {
    92:           opmRegister[address] = data;
    93:         }
    94:         if (OPMLog.OLG_ON) {
    95:           OPMLog.olgSetData (address, data);
    96:         }
    97:       }
    98:     });
    99:     opmYM2151.allocate (SoundSource.SND_CHANNELS * (OPM_BLOCK_SAMPLES + 1));
   100:     opmReset ();
   101:   }
   102: 
   103:   //opmReset ()
   104:   //  リセット
   105:   public static void opmReset () {
   106:     opmYM2151.reset ();
   107:     opmYM2151.clear ();
   108:     opmBuffer = opmYM2151.getBuffer ();
   109:     opmPointer = opmYM2151.getPointer ();
   110:     opmBusyClock = 0L;
   111:   }
   112: 
   113:   //opmSetOutputOn (on)
   114:   //  OPM出力のON/OFF
   115:   public static void opmSetOutputOn (boolean on) {
   116:     opmOutputMask = on ? -1 : 0;
   117:     opmYM2151.setChannelMask (opmOutputMask & 0xff);
   118:   }
   119: 
   120: }