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

309 lines
11 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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();
}
}
}
}