mirror of
https://github.com/ZDoom/ZMusic.git
synced 2024-12-11 21:11:00 +00:00
524 lines
13 KiB
C
524 lines
13 KiB
C
|
// ---------------------------------------------------------------------------
|
|||
|
// OPN/A/B interface with ADPCM support
|
|||
|
// Copyright (C) cisc 1998, 2003.
|
|||
|
// ---------------------------------------------------------------------------
|
|||
|
// $Id: opna.h,v 1.33 2003/06/12 13:14:37 cisc Exp $
|
|||
|
|
|||
|
#ifndef FM_OPNA_H
|
|||
|
#define FM_OPNA_H
|
|||
|
|
|||
|
#include "fmgen_fmgen.h"
|
|||
|
#include "fmgen_fmtimer.h"
|
|||
|
#include "fmgen_psg.h"
|
|||
|
|
|||
|
// ---------------------------------------------------------------------------
|
|||
|
// class OPN/OPNA
|
|||
|
// OPN/OPNA に良く似た音を生成する音源ユニット
|
|||
|
//
|
|||
|
// interface:
|
|||
|
// bool Init(uint clock, uint rate, bool, const char* path);
|
|||
|
// 初期化.このクラスを使用する前にかならず呼んでおくこと.
|
|||
|
// OPNA の場合はこの関数でリズムサンプルを読み込む
|
|||
|
//
|
|||
|
// clock: OPN/OPNA/OPNB のクロック周波数(Hz)
|
|||
|
//
|
|||
|
// rate: 生成する PCM の標本周波数(Hz)
|
|||
|
//
|
|||
|
// path: リズムサンプルのパス(OPNA のみ有効)
|
|||
|
// 省略時はカレントディレクトリから読み込む
|
|||
|
// 文字列の末尾には '¥' や '/' などをつけること
|
|||
|
//
|
|||
|
// 返り値 初期化に成功すれば true
|
|||
|
//
|
|||
|
// bool LoadRhythmSample(const char* path)
|
|||
|
// (OPNA ONLY)
|
|||
|
// Rhythm サンプルを読み直す.
|
|||
|
// path は Init の path と同じ.
|
|||
|
//
|
|||
|
// bool SetRate(uint clock, uint rate, bool)
|
|||
|
// クロックや PCM レートを変更する
|
|||
|
// 引数等は Init を参照のこと.
|
|||
|
//
|
|||
|
// void Mix(FM_SAMPLETYPE* dest, int nsamples)
|
|||
|
// Stereo PCM データを nsamples 分合成し, dest で始まる配列に
|
|||
|
// 加える(加算する)
|
|||
|
// ・dest には sample*2 個分の領域が必要
|
|||
|
// ・格納形式は L, R, L, R... となる.
|
|||
|
// ・あくまで加算なので,あらかじめ配列をゼロクリアする必要がある
|
|||
|
// ・FM_SAMPLETYPE が short 型の場合クリッピングが行われる.
|
|||
|
// ・この関数は音源内部のタイマーとは独立している.
|
|||
|
// Timer は Count と GetNextEvent で操作する必要がある.
|
|||
|
//
|
|||
|
// void Reset()
|
|||
|
// 音源をリセット(初期化)する
|
|||
|
//
|
|||
|
// void SetReg(uint reg, uint data)
|
|||
|
// 音源のレジスタ reg に data を書き込む
|
|||
|
//
|
|||
|
// uint GetReg(uint reg)
|
|||
|
// 音源のレジスタ reg の内容を読み出す
|
|||
|
// 読み込むことが出来るレジスタは PSG, ADPCM の一部,ID(0xff) とか
|
|||
|
//
|
|||
|
// uint ReadStatus()/ReadStatusEx()
|
|||
|
// 音源のステータスレジスタを読み出す
|
|||
|
// ReadStatusEx は拡張ステータスレジスタの読み出し(OPNA)
|
|||
|
// busy フラグは常に 0
|
|||
|
//
|
|||
|
// bool Count(uint32 t)
|
|||
|
// 音源のタイマーを t [μ秒] 進める.
|
|||
|
// 音源の内部状態に変化があった時(timer オーバーフロー)
|
|||
|
// true を返す
|
|||
|
//
|
|||
|
// uint32 GetNextEvent()
|
|||
|
// 音源のタイマーのどちらかがオーバーフローするまでに必要な
|
|||
|
// 時間[μ秒]を返す
|
|||
|
// タイマーが停止している場合は ULONG_MAX を返す… と思う
|
|||
|
//
|
|||
|
// void SetVolumeFM(int db)/SetVolumePSG(int db) ...
|
|||
|
// 各音源の音量を+−方向に調節する.標準値は 0.
|
|||
|
// 単位は約 1/2 dB,有効範囲の上限は 20 (10dB)
|
|||
|
//
|
|||
|
namespace FM
|
|||
|
{
|
|||
|
// OPN Base -------------------------------------------------------
|
|||
|
struct OPNBaseData {
|
|||
|
struct TimerData timer;
|
|||
|
int fmvolume;
|
|||
|
uint clock;
|
|||
|
uint rate;
|
|||
|
uint psgrate;
|
|||
|
uint status;
|
|||
|
uint8 prescale;
|
|||
|
struct ChipData chip;
|
|||
|
struct PSGData psg;
|
|||
|
};
|
|||
|
|
|||
|
class OPNBase : public Timer
|
|||
|
{
|
|||
|
public:
|
|||
|
OPNBase();
|
|||
|
|
|||
|
bool Init(uint c, uint r);
|
|||
|
virtual void Reset();
|
|||
|
|
|||
|
void SetVolumeFM(int db);
|
|||
|
void SetVolumePSG(int db);
|
|||
|
|
|||
|
void DataSave(struct OPNBaseData* data);
|
|||
|
void DataLoad(struct OPNBaseData* data);
|
|||
|
|
|||
|
protected:
|
|||
|
void SetParameter(Channel4* ch, uint addr, uint data);
|
|||
|
void SetPrescaler(uint p);
|
|||
|
void RebuildTimeTable();
|
|||
|
|
|||
|
int fmvolume;
|
|||
|
|
|||
|
uint clock; // OPN クロック
|
|||
|
uint rate; // FM 音源合成レート
|
|||
|
uint psgrate; // FMGen 出力レート
|
|||
|
uint status;
|
|||
|
Channel4* csmch;
|
|||
|
|
|||
|
|
|||
|
static uint32 lfotable[8];
|
|||
|
|
|||
|
private:
|
|||
|
void TimerA();
|
|||
|
uint8 prescale;
|
|||
|
|
|||
|
protected:
|
|||
|
Chip chip;
|
|||
|
PSG psg;
|
|||
|
};
|
|||
|
|
|||
|
// OPN2 Base ------------------------------------------------------
|
|||
|
struct OPNABaseData {
|
|||
|
struct OPNBaseData opnbase;
|
|||
|
uint8 pan[6];
|
|||
|
uint16 panvolume_l[6]; // libOPNMIDI: soft panning
|
|||
|
uint16 panvolume_r[6]; // libOPNMIDI: soft panning
|
|||
|
uint8 fnum2[9];
|
|||
|
uint8 reg22;
|
|||
|
uint reg29;
|
|||
|
uint stmask;
|
|||
|
uint statusnext;
|
|||
|
uint32 lfocount;
|
|||
|
uint32 lfodcount;
|
|||
|
uint fnum[6];
|
|||
|
uint fnum3[3];
|
|||
|
bool is_adpcmbuf;
|
|||
|
uint8 adpcmbuf[0x40000];
|
|||
|
uint adpcmmask;
|
|||
|
uint adpcmnotice;
|
|||
|
uint startaddr;
|
|||
|
uint stopaddr;
|
|||
|
uint memaddr;
|
|||
|
uint limitaddr;
|
|||
|
int adpcmlevel;
|
|||
|
int adpcmvolume;
|
|||
|
int adpcmvol;
|
|||
|
uint deltan;
|
|||
|
int adplc;
|
|||
|
int adpld;
|
|||
|
uint adplbase;
|
|||
|
int adpcmx;
|
|||
|
int adpcmd;
|
|||
|
int adpcmout;
|
|||
|
int apout0;
|
|||
|
int apout1;
|
|||
|
uint adpcmreadbuf;
|
|||
|
bool adpcmplay;
|
|||
|
int8 granuality;
|
|||
|
bool adpcmmask_;
|
|||
|
uint8 control1;
|
|||
|
uint8 control2;
|
|||
|
uint8 adpcmreg[8];
|
|||
|
int rhythmmask_;
|
|||
|
struct Channel4Data ch[6];
|
|||
|
};
|
|||
|
|
|||
|
class OPNABase : public OPNBase
|
|||
|
{
|
|||
|
public:
|
|||
|
OPNABase();
|
|||
|
~OPNABase();
|
|||
|
|
|||
|
uint ReadStatus() { return status & 0x03; }
|
|||
|
uint ReadStatusEx();
|
|||
|
void SetChannelMask(uint mask);
|
|||
|
|
|||
|
// libOPNMIDI: soft panning
|
|||
|
void SetPan(uint c, uint8 p);
|
|||
|
|
|||
|
void DataSave(struct OPNABaseData* data);
|
|||
|
void DataLoad(struct OPNABaseData* data);
|
|||
|
|
|||
|
private:
|
|||
|
virtual void Intr(bool) {}
|
|||
|
|
|||
|
void MakeTable2();
|
|||
|
|
|||
|
protected:
|
|||
|
bool Init(uint c, uint r, bool);
|
|||
|
bool SetRate(uint c, uint r, bool);
|
|||
|
|
|||
|
void Reset();
|
|||
|
void SetReg(uint addr, uint data);
|
|||
|
void SetADPCMBReg(uint reg, uint data);
|
|||
|
uint GetReg(uint addr);
|
|||
|
|
|||
|
protected:
|
|||
|
void FMMix(Sample* buffer, int nsamples);
|
|||
|
void Mix6(Sample* buffer, int nsamples, int activech);
|
|||
|
|
|||
|
void MixSubS(int activech, ISample**);
|
|||
|
void MixSubSL(int activech, ISample**);
|
|||
|
|
|||
|
void SetStatus(uint bit);
|
|||
|
void ResetStatus(uint bit);
|
|||
|
void UpdateStatus();
|
|||
|
void LFO();
|
|||
|
|
|||
|
void DecodeADPCMB();
|
|||
|
void ADPCMBMix(Sample* dest, uint count);
|
|||
|
|
|||
|
void WriteRAM(uint data);
|
|||
|
uint ReadRAM();
|
|||
|
int ReadRAMN();
|
|||
|
int DecodeADPCMBSample(uint);
|
|||
|
|
|||
|
// FM 音源関係
|
|||
|
uint8 pan[6];
|
|||
|
uint16 panvolume_l[6]; // libOPNMIDI: soft panning
|
|||
|
uint16 panvolume_r[6]; // libOPNMIDI: soft panning
|
|||
|
uint8 fnum2[9];
|
|||
|
|
|||
|
uint8 reg22;
|
|||
|
uint reg29; // OPNA only?
|
|||
|
|
|||
|
uint stmask;
|
|||
|
uint statusnext;
|
|||
|
|
|||
|
uint32 lfocount;
|
|||
|
uint32 lfodcount;
|
|||
|
|
|||
|
uint fnum[6];
|
|||
|
uint fnum3[3];
|
|||
|
|
|||
|
// ADPCM 関係
|
|||
|
uint8* adpcmbuf; // ADPCM RAM
|
|||
|
uint adpcmmask; // メモリアドレスに対するビットマスク
|
|||
|
uint adpcmnotice; // ADPCM 再生終了時にたつビット
|
|||
|
uint startaddr; // Start address
|
|||
|
uint stopaddr; // Stop address
|
|||
|
uint memaddr; // 再生中アドレス
|
|||
|
uint limitaddr; // Limit address/mask
|
|||
|
int adpcmlevel; // ADPCM 音量
|
|||
|
int adpcmvolume;
|
|||
|
int adpcmvol;
|
|||
|
uint deltan; // N
|
|||
|
int adplc; // 周波数変換用変数
|
|||
|
int adpld; // 周波数変換用変数差分値
|
|||
|
uint adplbase; // adpld の元
|
|||
|
int adpcmx; // ADPCM 合成用 x
|
|||
|
int adpcmd; // ADPCM 合成用
|
|||
|
int adpcmout; // ADPCM 合成後の出力
|
|||
|
int apout0; // out(t-2)+out(t-1)
|
|||
|
int apout1; // out(t-1)+out(t)
|
|||
|
|
|||
|
uint adpcmreadbuf; // ADPCM リード用バッファ
|
|||
|
bool adpcmplay; // ADPCM 再生中
|
|||
|
int8 granuality;
|
|||
|
bool adpcmmask_;
|
|||
|
|
|||
|
uint8 control1; // ADPCM コントロールレジスタ1
|
|||
|
uint8 control2; // ADPCM コントロールレジスタ2
|
|||
|
uint8 adpcmreg[8]; // ADPCM レジスタの一部分
|
|||
|
|
|||
|
int rhythmmask_;
|
|||
|
|
|||
|
Channel4 ch[6];
|
|||
|
|
|||
|
static void BuildLFOTable();
|
|||
|
static int amtable[FM_LFOENTS];
|
|||
|
static int pmtable[FM_LFOENTS];
|
|||
|
static int32 tltable[FM_TLENTS+FM_TLPOS];
|
|||
|
static bool tablehasmade;
|
|||
|
};
|
|||
|
|
|||
|
// YM2203(OPN) ----------------------------------------------------
|
|||
|
struct OPNData {
|
|||
|
struct OPNBaseData opnbase;
|
|||
|
uint fnum[3];
|
|||
|
uint fnum3[3];
|
|||
|
uint8 fnum2[6];
|
|||
|
struct Channel4Data ch[3];
|
|||
|
};
|
|||
|
|
|||
|
class OPN : public OPNBase
|
|||
|
{
|
|||
|
public:
|
|||
|
OPN();
|
|||
|
virtual ~OPN() {}
|
|||
|
|
|||
|
bool Init(uint c, uint r, bool=false, const char* =0);
|
|||
|
bool SetRate(uint c, uint r, bool=false);
|
|||
|
|
|||
|
void Reset();
|
|||
|
void Mix(Sample* buffer, int nsamples);
|
|||
|
void SetReg(uint addr, uint data);
|
|||
|
uint GetReg(uint addr);
|
|||
|
uint ReadStatus() { return status & 0x03; }
|
|||
|
uint ReadStatusEx() { return 0xff; }
|
|||
|
|
|||
|
void SetChannelMask(uint mask);
|
|||
|
|
|||
|
int dbgGetOpOut(int c, int s) { return ch[c].op[s].dbgopout_; }
|
|||
|
int dbgGetPGOut(int c, int s) { return ch[c].op[s].dbgpgout_; }
|
|||
|
Channel4* dbgGetCh(int c) { return &ch[c]; }
|
|||
|
|
|||
|
void DataSave(struct OPNData* data);
|
|||
|
void DataLoad(struct OPNData* data);
|
|||
|
|
|||
|
private:
|
|||
|
virtual void Intr(bool) {}
|
|||
|
|
|||
|
void SetStatus(uint bit);
|
|||
|
void ResetStatus(uint bit);
|
|||
|
|
|||
|
uint fnum[3];
|
|||
|
uint fnum3[3];
|
|||
|
uint8 fnum2[6];
|
|||
|
|
|||
|
Channel4 ch[3];
|
|||
|
};
|
|||
|
|
|||
|
// YM2608(OPNA) ---------------------------------------------------
|
|||
|
struct Rhythm
|
|||
|
{
|
|||
|
uint8 pan; // ぱん
|
|||
|
int8 level; // おんりょう
|
|||
|
int volume; // おんりょうせってい
|
|||
|
int16* sample; // さんぷる
|
|||
|
uint size; // さいず
|
|||
|
uint pos; // いち
|
|||
|
uint step; // すてっぷち
|
|||
|
uint rate; // さんぷるのれーと
|
|||
|
};
|
|||
|
|
|||
|
struct OPNAData {
|
|||
|
struct OPNABaseData opnabase;
|
|||
|
Rhythm rhythm[6];
|
|||
|
int8 rhythmtl;
|
|||
|
int rhythmtvol;
|
|||
|
uint8 rhythmkey;
|
|||
|
};
|
|||
|
|
|||
|
class OPNA : public OPNABase
|
|||
|
{
|
|||
|
public:
|
|||
|
OPNA();
|
|||
|
virtual ~OPNA();
|
|||
|
|
|||
|
bool Init(uint c, uint r, bool = false, const char* rhythmpath=0);
|
|||
|
bool LoadRhythmSample(const char*);
|
|||
|
|
|||
|
bool SetRate(uint c, uint r, bool = false);
|
|||
|
void Mix(Sample* buffer, int nsamples);
|
|||
|
|
|||
|
void Reset();
|
|||
|
void SetReg(uint addr, uint data);
|
|||
|
uint GetReg(uint addr);
|
|||
|
|
|||
|
void SetVolumeADPCM(int db);
|
|||
|
void SetVolumeRhythmTotal(int db);
|
|||
|
void SetVolumeRhythm(int index, int db);
|
|||
|
|
|||
|
uint8* GetADPCMBuffer() { return adpcmbuf; }
|
|||
|
|
|||
|
int dbgGetOpOut(int c, int s) { return ch[c].op[s].dbgopout_; }
|
|||
|
int dbgGetPGOut(int c, int s) { return ch[c].op[s].dbgpgout_; }
|
|||
|
Channel4* dbgGetCh(int c) { return &ch[c]; }
|
|||
|
|
|||
|
void DataSave(struct OPNAData* data);
|
|||
|
void DataLoad(struct OPNAData* data);
|
|||
|
|
|||
|
private:
|
|||
|
void RhythmMix(Sample* buffer, uint count);
|
|||
|
|
|||
|
// リズム音源関係
|
|||
|
Rhythm rhythm[6];
|
|||
|
int8 rhythmtl; // リズム全体の音量
|
|||
|
int rhythmtvol;
|
|||
|
uint8 rhythmkey; // リズムのキー
|
|||
|
};
|
|||
|
|
|||
|
// YM2610/B(OPNB) ---------------------------------------------------
|
|||
|
struct ADPCMA
|
|||
|
{
|
|||
|
uint8 pan; // ぱん
|
|||
|
int8 level; // おんりょう
|
|||
|
int volume; // おんりょうせってい
|
|||
|
uint pos; // いち
|
|||
|
uint step; // すてっぷち
|
|||
|
|
|||
|
uint start; // 開始
|
|||
|
uint stop; // 終了
|
|||
|
uint nibble; // 次の 4 bit
|
|||
|
int adpcmx; // 変換用
|
|||
|
int adpcmd; // 変換用
|
|||
|
};
|
|||
|
|
|||
|
struct OPNBData {
|
|||
|
struct OPNABaseData opnabase;
|
|||
|
// uint8* adpcmabuf;
|
|||
|
int adpcmasize;
|
|||
|
ADPCMA adpcma[6];
|
|||
|
int8 adpcmatl;
|
|||
|
int adpcmatvol;
|
|||
|
uint8 adpcmakey;
|
|||
|
int adpcmastep;
|
|||
|
uint8 adpcmareg[32];
|
|||
|
struct Channel4Data ch[6];
|
|||
|
};
|
|||
|
|
|||
|
class OPNB : public OPNABase
|
|||
|
{
|
|||
|
public:
|
|||
|
OPNB();
|
|||
|
virtual ~OPNB();
|
|||
|
|
|||
|
bool Init(uint c, uint r, bool = false,
|
|||
|
uint8 *_adpcma = 0, int _adpcma_size = 0,
|
|||
|
uint8 *_adpcmb = 0, int _adpcmb_size = 0);
|
|||
|
|
|||
|
bool SetRate(uint c, uint r, bool = false);
|
|||
|
void Mix(Sample* buffer, int nsamples);
|
|||
|
|
|||
|
void Reset();
|
|||
|
void SetReg(uint addr, uint data);
|
|||
|
uint GetReg(uint addr);
|
|||
|
uint ReadStatusEx();
|
|||
|
|
|||
|
void SetVolumeADPCMATotal(int db);
|
|||
|
void SetVolumeADPCMA(int index, int db);
|
|||
|
void SetVolumeADPCMB(int db);
|
|||
|
|
|||
|
// void SetChannelMask(uint mask);
|
|||
|
|
|||
|
void DataSave(struct OPNBData* data, void* adpcmdata);
|
|||
|
void DataLoad(struct OPNBData* data, void* adpcmdata);
|
|||
|
|
|||
|
private:
|
|||
|
int DecodeADPCMASample(uint);
|
|||
|
void ADPCMAMix(Sample* buffer, uint count);
|
|||
|
static void InitADPCMATable();
|
|||
|
|
|||
|
// ADPCMA 関係
|
|||
|
uint8* adpcmabuf; // ADPCMA ROM
|
|||
|
int adpcmasize;
|
|||
|
ADPCMA adpcma[6];
|
|||
|
int8 adpcmatl; // ADPCMA 全体の音量
|
|||
|
int adpcmatvol;
|
|||
|
uint8 adpcmakey; // ADPCMA のキー
|
|||
|
int adpcmastep;
|
|||
|
uint8 adpcmareg[32];
|
|||
|
|
|||
|
static int jedi_table[(48+1)*16];
|
|||
|
|
|||
|
Channel4 ch[6];
|
|||
|
};
|
|||
|
|
|||
|
// YM2612/3438(OPN2) ----------------------------------------------------
|
|||
|
class OPN2 : public OPNBase
|
|||
|
{
|
|||
|
public:
|
|||
|
OPN2();
|
|||
|
virtual ~OPN2() {}
|
|||
|
|
|||
|
bool Init(uint c, uint r, bool=false, const char* =0);
|
|||
|
bool SetRate(uint c, uint r, bool);
|
|||
|
|
|||
|
void Reset();
|
|||
|
void Mix(Sample* buffer, int nsamples);
|
|||
|
void SetReg(uint addr, uint data);
|
|||
|
uint GetReg(uint addr);
|
|||
|
uint ReadStatus() { return status & 0x03; }
|
|||
|
uint ReadStatusEx() { return 0xff; }
|
|||
|
|
|||
|
void SetChannelMask(uint mask);
|
|||
|
|
|||
|
private:
|
|||
|
virtual void Intr(bool) {}
|
|||
|
|
|||
|
void SetStatus(uint bit);
|
|||
|
void ResetStatus(uint bit);
|
|||
|
|
|||
|
uint fnum[3];
|
|||
|
uint fnum3[3];
|
|||
|
uint8 fnum2[6];
|
|||
|
|
|||
|
// 線形補間用ワーク
|
|||
|
int32 mixc, mixc1;
|
|||
|
|
|||
|
Channel4 ch[3];
|
|||
|
};
|
|||
|
}
|
|||
|
|
|||
|
// ---------------------------------------------------------------------------
|
|||
|
|
|||
|
inline void FM::OPNBase::RebuildTimeTable()
|
|||
|
{
|
|||
|
int p = prescale;
|
|||
|
prescale = -1;
|
|||
|
SetPrescaler(p);
|
|||
|
}
|
|||
|
|
|||
|
inline void FM::OPNBase::SetVolumePSG(int db)
|
|||
|
{
|
|||
|
psg.SetVolume(db);
|
|||
|
}
|
|||
|
|
|||
|
#endif // FM_OPNA_H
|