xeij/OPM.java
//========================================================================================
// OPM.java
// en:Frequency modulation sound source
// ja:FM音源
// Copyright (C) 2003-2022 Makoto Kamada
//
// This file is part of the XEiJ (X68000 Emulator in Java).
// You can use, modify and redistribute the XEiJ if the conditions are met.
// Read the XEiJ License for more details.
// https://stdkmd.net/xeij/
//========================================================================================
package xeij;
import java.util.*;
public class OPM {
public static final boolean OPM_ON = true; //true=OPMを出力する
public static final int OPM_OSC_FREQ = 4000000; //クロック周波数(Hz)。4000000Hz
public static final int OPM_SAMPLE_FREQ = OPM_OSC_FREQ / 64; //サンプリング周波数(Hz)。62500Hz
public static final long OPM_SAMPLE_TIME = XEiJ.TMR_FREQ / OPM_SAMPLE_FREQ; //1サンプルの時間(XEiJ.TMR_FREQ単位)。16000000ps
public static final int OPM_BLOCK_SAMPLES = OPM_SAMPLE_FREQ / SoundSource.SND_BLOCK_FREQ; //1ブロックのサンプル数。2500
public static int opmOutputMask; //-1=OPMを出力する
//YM2151
public static YM2151 opmYM2151;
public static TickerQueue.Ticker opmTimerATicker;
public static TickerQueue.Ticker opmTimerBTicker;
public static int[] opmRegister; //[address]=data,[256+ch]=slotMask,[264]=AMD,[265]=PMD
public static int[] opmBuffer;
public static int opmPointer;
public static long opmBusyClock;
//opmInit ()
// 初期化
public static void opmInit () {
//opmOutputMask = -1; //OPMを出力する
opmYM2151 = new YM2151 ();
opmTimerATicker = new TickerQueue.Ticker () {
@Override protected void tick () {
opmYM2151.timerAExpired ();
}
};
opmTimerBTicker = new TickerQueue.Ticker () {
@Override protected void tick () {
opmYM2151.timerBExpired ();
}
};
opmRegister = new int[266];
opmYM2151.setListener (new YM2151.Listener () {
@Override public void timerA (int clocks) {
if (clocks != -1) { //始動
TickerQueue.tkqAdd (opmTimerATicker, XEiJ.mpuClockTime + (XEiJ.TMR_FREQ / (long) OPM_OSC_FREQ) * (long) clocks);
} else { //停止
TickerQueue.tkqRemove (opmTimerATicker);
}
}
@Override public void timerB (int clocks) {
if (clocks != -1) { //始動
TickerQueue.tkqAdd (opmTimerBTicker, XEiJ.mpuClockTime + (XEiJ.TMR_FREQ / (long) OPM_OSC_FREQ) * (long) clocks);
} else { //停止
TickerQueue.tkqRemove (opmTimerBTicker);
}
}
@Override public void busy (int clocks) {
opmBusyClock = XEiJ.mpuClockTime + (XEiJ.TMR_FREQ / (long) OPM_OSC_FREQ) * (long) clocks;
}
@Override public boolean isBusy () {
return XEiJ.mpuClockTime < opmBusyClock;
}
@Override public void irq (boolean asserted) {
if (asserted) {
MC68901.mfpOpmirqFall ();
} else {
MC68901.mfpOpmirqRise ();
}
}
@Override public void control (int data) {
FDC.fdcSetEnforcedReady ((data & 1) != 0); //CT2 強制レディ状態の設定。0=通常動作,1=強制レディ状態
ADPCM.pcmOscillator = (data >> 1) & 1; //CT1 ADPCMの原発振周波数の設定。0=8MHz/8MHz,1=4MHz/16MHz
ADPCM.pcmUpdateRepeatInterval ();
}
@Override public void written (int pointer, int address, int data) {
if (address == 0x08) { //KON
opmRegister[256 + (data & 7)] = (data >> 3) & 15;
} else if (address == 0x19) { //AMD/PMD
opmRegister[264 + (data >> 7)] = data & 127;
} else {
opmRegister[address] = data;
}
if (OPMLog.OLG_ON) {
OPMLog.olgSetData (address, data);
}
}
});
opmYM2151.allocate (SoundSource.SND_CHANNELS * (OPM_BLOCK_SAMPLES + 1));
opmReset ();
}
//opmReset ()
// リセット
public static void opmReset () {
opmYM2151.reset ();
opmYM2151.clear ();
opmBuffer = opmYM2151.getBuffer ();
opmPointer = opmYM2151.getPointer ();
opmBusyClock = 0L;
}
//opmSetOutputOn (on)
// OPM出力のON/OFF
public static void opmSetOutputOn (boolean on) {
opmOutputMask = on ? -1 : 0;
opmYM2151.setChannelMask (opmOutputMask & 0xff);
}
}