AxibugEmuOnline/AxibugEmuOnline.Client/Assets/Plugins/Mame.Core/sound/OKI6295.cs

309 lines
11 KiB
C#
Raw Normal View History

using System;
using System.IO;
namespace MAME.Core
{
public class OKI6295
{
public struct adpcm_state
{
public int signal;
public int step;
};
public struct ADPCMVoice
{
public bool playing;
public uint base_offset;
public uint sample;
public uint count;
public uint volume;
};
public struct okim6295Struct
{
public ADPCMVoice[] voice;
public int command;
public int bank_offset;
public uint master_clock;
};
public static byte[] okirom;
public static okim6295Struct OKI;
public static adpcm_state[] adpcm;
private static int[] index_shift = new int[8] { -1, -1, -1, -1, 2, 4, 6, 8 };
private static int[] diff_lookup = new int[49 * 16];
private static uint[] volume_table = new uint[16];
private static int tables_computed = 0;
private static void compute_tables()
{
int[,] nbl2bit = new int[16, 4]
{
{ 1, 0, 0, 0}, { 1, 0, 0, 1}, { 1, 0, 1, 0}, { 1, 0, 1, 1},
{ 1, 1, 0, 0}, { 1, 1, 0, 1}, { 1, 1, 1, 0}, { 1, 1, 1, 1},
{-1, 0, 0, 0}, {-1, 0, 0, 1}, {-1, 0, 1, 0}, {-1, 0, 1, 1},
{-1, 1, 0, 0}, {-1, 1, 0, 1}, {-1, 1, 1, 0}, {-1, 1, 1, 1}
};
int step, nib;
for (step = 0; step <= 48; step++)
{
int stepval = (int)Math.Floor(16.0 * Math.Pow(11.0 / 10.0, (double)step));
for (nib = 0; nib < 16; nib++)
{
diff_lookup[step * 16 + nib] = nbl2bit[nib, 0] *
(stepval * nbl2bit[nib, 1] +
stepval / 2 * nbl2bit[nib, 2] +
stepval / 4 * nbl2bit[nib, 3] +
stepval / 8);
}
}
for (step = 0; step < 16; step++)
{
double dout = 256.0;
int vol = step;
while (vol-- > 0)
dout /= 1.412537545;
volume_table[step] = (uint)dout;
}
tables_computed = 1;
}
private static void reset_adpcm(int i)
{
if (tables_computed == 0)
{
compute_tables();
}
adpcm[i].signal = -2;
adpcm[i].step = 0;
}
private static short clock_adpcm(int i, byte nibble)
{
adpcm[i].signal += diff_lookup[adpcm[i].step * 16 + (nibble & 15)];
if (adpcm[i].signal > 2047)
adpcm[i].signal = 2047;
else if (adpcm[i].signal < -2048)
adpcm[i].signal = -2048;
adpcm[i].step += index_shift[nibble & 7];
if (adpcm[i].step > 48)
adpcm[i].step = 48;
else if (adpcm[i].step < 0)
adpcm[i].step = 0;
return (short)(adpcm[i].signal << 4);
}
private static void generate_adpcm(int i, short[] buffer, int samples)
{
int i1 = 0;
if (OKI.voice[i].playing)
{
int bbase = (int)(OKI.bank_offset + OKI.voice[i].base_offset);
int sample = (int)OKI.voice[i].sample;
int count = (int)OKI.voice[i].count;
while (samples != 0)
{
int nibble = okirom[bbase + sample / 2] >> (((sample & 1) << 2) ^ 4);
buffer[i1] = (short)(clock_adpcm(i, (byte)nibble) * OKI.voice[i].volume / 256);
i1++;
samples--;
if (++sample >= count)
{
OKI.voice[i].playing = false;
break;
}
}
OKI.voice[i].sample = (uint)sample;
}
while ((samples--) != 0)
{
buffer[i1] = 0;
i1++;
}
}
//TODO 移动到这里,但是大小,还需要考虑
static short[] sample_data = new short[10000];
public unsafe static void okim6295_update(int offset, int length)
{
int i;
for (i = 0; i < length; i++)
{
Sound.okistream.streamoutput_Ptrs[0][offset + i] = 0;
}
for (i = 0; i < 4; i++)
{
//不每次new避免GC排除问题。待验证影响
//short[] sample_data = new short[10000];
int remaining = length;
while (remaining != 0)
{
int samples1 = (remaining > 10000) ? 10000 : remaining;
int samp;
generate_adpcm(i, sample_data, samples1);
for (samp = 0; samp < length; samp++)
{
Sound.okistream.streamoutput_Ptrs[0][offset + samp] += sample_data[samp];
}
remaining -= samples1;
}
}
}
public static void okim6295_start()
{
int voice;
compute_tables();
OKI.command = -1;
OKI.bank_offset = 0;
OKI.master_clock = 1000000;
OKI.voice = new ADPCMVoice[4];
adpcm = new adpcm_state[4];
for (voice = 0; voice < 4; voice++)
{
OKI.voice[voice].volume = 255;
reset_adpcm(voice);
}
}
public static void okim6295_reset()
{
int i;
Sound.okistream.stream_update();
for (i = 0; i < 4; i++)
{
OKI.voice[i].playing = false;
}
}
public static void okim6295_set_bank_base(int base1)
{
Sound.okistream.stream_update();
OKI.bank_offset = base1;
}
public static void okim6295_set_pin7(int pin7)
{
int divisor = pin7 != 0 ? 132 : 165;
//stream_set_sample_rate(info->stream, info->master_clock / divisor);
}
public static int okim6295_status_r()
{
int i, result;
result = 0xf0;
Sound.okistream.stream_update();
for (i = 0; i < 4; i++)
{
ADPCMVoice voice = OKI.voice[i];
if (voice.playing)
result |= 1 << i;
}
return result;
}
private static void okim6295_data_w(int num, int data)
{
if (OKI.command != -1)
{
int temp = data >> 4, i, start, stop;
int baseoffset;
Sound.okistream.stream_update();
for (i = 0; i < 4; i++, temp >>= 1)
{
if ((temp & 1) != 0)
{
baseoffset = OKI.bank_offset + OKI.command * 8;
start = ((okirom[baseoffset + 0] << 16) + (okirom[baseoffset + 1] << 8) + okirom[baseoffset + 2]) & 0x3ffff;
stop = ((okirom[baseoffset + 3] << 16) + (okirom[baseoffset + 4] << 8) + okirom[baseoffset + 5]) & 0x3ffff;
if (start < stop)
{
if (!OKI.voice[i].playing)
{
OKI.voice[i].playing = true;
OKI.voice[i].base_offset = (uint)start;
OKI.voice[i].sample = 0;
OKI.voice[i].count = (uint)(2 * (stop - start + 1));
reset_adpcm(i);
OKI.voice[i].volume = volume_table[data & 0x0f];
}
else
{
}
}
else
{
OKI.voice[i].playing = false;
}
}
}
OKI.command = -1;
}
else if ((data & 0x80) != 0)
{
OKI.command = data & 0x7f;
}
else
{
int temp = data >> 3, i;
Sound.okistream.stream_update();
for (i = 0; i < 4; i++, temp >>= 1)
{
if ((temp & 1) != 0)
{
OKI.voice[i].playing = false;
}
}
}
}
public static byte okim6295_status_0_r()
{
int i;
byte result;
result = 0xf0;
Sound.okistream.stream_update();
for (i = 0; i < 4; i++)
{
if (OKI.voice[i].playing)
{
result |= (byte)(1 << i);
}
}
return result;
}
public static int okim6295_status_0_lsb_r()
{
return okim6295_status_r();
}
public static void okim6295_data_0_w(byte data)
{
okim6295_data_w(0, data);
}
public static void okim6295_data_0_lsb_w(byte data)
{
//if (ACCESSING_BITS_0_7)
okim6295_data_w(0, data & 0xff);
}
public static void SaveStateBinary(BinaryWriter writer)
{
int i;
writer.Write(OKI.command);
writer.Write(OKI.bank_offset);
for (i = 0; i < 4; i++)
{
writer.Write(OKI.voice[i].playing);
writer.Write(OKI.voice[i].sample);
writer.Write(OKI.voice[i].count);
writer.Write(OKI.voice[i].volume);
writer.Write(OKI.voice[i].base_offset);
writer.Write(adpcm[i].signal);
writer.Write(adpcm[i].step);
}
}
public static void LoadStateBinary(BinaryReader reader)
{
int i;
OKI.command = reader.ReadInt32();
OKI.bank_offset = reader.ReadInt32();
for (i = 0; i < 4; i++)
{
OKI.voice[i].playing = reader.ReadBoolean();
OKI.voice[i].sample = reader.ReadUInt32();
OKI.voice[i].count = reader.ReadUInt32();
OKI.voice[i].volume = reader.ReadUInt32();
OKI.voice[i].base_offset = reader.ReadUInt32();
adpcm[i].signal = reader.ReadInt32();
adpcm[i].step = reader.ReadInt32();
}
}
}
}