139 lines
5.2 KiB
C
139 lines
5.2 KiB
C
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <wchar.h>
|
|
#include <86box/86box.h>
|
|
#include <86box/snd_ym7128.h>
|
|
|
|
|
|
static int attenuation[32];
|
|
static int tap_position[32];
|
|
|
|
|
|
void ym7128_init(ym7128_t *ym7128)
|
|
{
|
|
int c;
|
|
double out = 65536.0;
|
|
|
|
for (c = 0; c < 32; c++)
|
|
tap_position[c] = c * (2400 / 31);
|
|
|
|
for (c = 31; c >= 1; c--)
|
|
{
|
|
attenuation[c] = (int)out;
|
|
out /= 1.25963; /*2 dB steps*/
|
|
}
|
|
attenuation[0] = 0;
|
|
}
|
|
|
|
#define GET_ATTENUATION(val) (val & 0x20) ? -attenuation[val & 0x1f] : attenuation[val & 0x1f]
|
|
|
|
void ym7128_write(ym7128_t *ym7128, uint8_t val)
|
|
{
|
|
int new_dat = val & 1;
|
|
int new_sci = val & 2;
|
|
int new_a0 = val & 4;
|
|
if (!ym7128->sci && new_sci)
|
|
ym7128->dat = (ym7128->dat << 1) | new_dat;
|
|
|
|
if (ym7128->a0 != new_a0)
|
|
{
|
|
if (!ym7128->a0)
|
|
ym7128->reg_sel = ym7128->dat & 0x1f;
|
|
else
|
|
{
|
|
switch (ym7128->reg_sel)
|
|
{
|
|
case 0x00: case 0x01: case 0x02: case 0x03:
|
|
case 0x04: case 0x05: case 0x06: case 0x07:
|
|
ym7128->gl[ym7128->reg_sel & 7] = GET_ATTENUATION(ym7128->dat);
|
|
break;
|
|
case 0x08: case 0x09: case 0x0a: case 0x0b:
|
|
case 0x0c: case 0x0d: case 0x0e: case 0x0f:
|
|
ym7128->gr[ym7128->reg_sel & 7] = GET_ATTENUATION(ym7128->dat);
|
|
break;
|
|
|
|
case 0x10:
|
|
ym7128->vm = GET_ATTENUATION(ym7128->dat);
|
|
break;
|
|
case 0x11:
|
|
ym7128->vc = GET_ATTENUATION(ym7128->dat);
|
|
break;
|
|
case 0x12:
|
|
ym7128->vl = GET_ATTENUATION(ym7128->dat);
|
|
break;
|
|
case 0x13:
|
|
ym7128->vr = GET_ATTENUATION(ym7128->dat);
|
|
break;
|
|
|
|
case 0x14:
|
|
ym7128->c0 = (ym7128->dat & 0x3f) << 6;
|
|
if (ym7128->dat & 0x20)
|
|
ym7128->c0 |= 0xfffff000;
|
|
break;
|
|
case 0x15:
|
|
ym7128->c1 = (ym7128->dat & 0x3f) << 6;
|
|
if (ym7128->dat & 0x20)
|
|
ym7128->c1 |= 0xfffff000;
|
|
break;
|
|
|
|
case 0x16: case 0x17: case 0x18: case 0x19:
|
|
case 0x1a: case 0x1b: case 0x1c: case 0x1d: case 0x1e:
|
|
ym7128->t[ym7128->reg_sel - 0x16] = tap_position[ym7128->dat & 0x1f];
|
|
break;
|
|
}
|
|
ym7128->regs[ym7128->reg_sel] = ym7128->dat;
|
|
}
|
|
ym7128->dat = 0;
|
|
}
|
|
|
|
ym7128->sci = new_sci;
|
|
ym7128->a0 = new_a0;
|
|
}
|
|
|
|
#define GET_DELAY_SAMPLE(ym7128, offset) (((ym7128->delay_pos - offset) < 0) ? ym7128->delay_buffer[(ym7128->delay_pos - offset) + 2400] : ym7128->delay_buffer[ym7128->delay_pos - offset])
|
|
|
|
void ym7128_apply(ym7128_t *ym7128, int16_t *buffer, int len)
|
|
{
|
|
int c, d;
|
|
|
|
for (c = 0; c < len*2; c += 4)
|
|
{
|
|
/*YM7128 samples a mono stream at ~24 kHz, so downsample*/
|
|
int32_t samp = ((int32_t)buffer[c] + (int32_t)buffer[c+1] + (int32_t)buffer[c+2] + (int32_t)buffer[c+3]) / 4;
|
|
int32_t filter_temp, filter_out;
|
|
int32_t samp_l = 0, samp_r = 0;
|
|
|
|
filter_temp = GET_DELAY_SAMPLE(ym7128, ym7128->t[0]);
|
|
filter_out = ((filter_temp * ym7128->c0) >> 11) + ((ym7128->filter_dat * ym7128->c1) >> 11);
|
|
filter_out = (filter_out * ym7128->vc) >> 16;
|
|
|
|
samp = (samp * ym7128->vm) >> 16;
|
|
samp += filter_out;
|
|
|
|
ym7128->delay_buffer[ym7128->delay_pos] = samp;
|
|
|
|
for (d = 0; d < 8; d++)
|
|
{
|
|
samp_l += (GET_DELAY_SAMPLE(ym7128, ym7128->t[d+1]) * ym7128->gl[d]) >> 16;
|
|
samp_r += (GET_DELAY_SAMPLE(ym7128, ym7128->t[d+1]) * ym7128->gr[d]) >> 16;
|
|
}
|
|
|
|
samp_l = (samp_l * ym7128->vl*2) >> 16;
|
|
samp_r = (samp_r * ym7128->vr*2) >> 16;
|
|
|
|
buffer[c] += ((int32_t)samp_l + (int32_t)ym7128->prev_l) / 2;
|
|
buffer[c+1] += ((int32_t)samp_r + (int32_t)ym7128->prev_r) / 2;
|
|
buffer[c+2] += samp_l;
|
|
buffer[c+3] += samp_r;
|
|
|
|
ym7128->delay_pos++;
|
|
if (ym7128->delay_pos >= 2400)
|
|
ym7128->delay_pos = 0;
|
|
|
|
ym7128->filter_dat = filter_temp;
|
|
ym7128->prev_l = samp_l;
|
|
ym7128->prev_r = samp_r;
|
|
}
|
|
}
|