309 lines
11 KiB
C#
309 lines
11 KiB
C#
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();
|
||
}
|
||
}
|
||
}
|
||
}
|