AD1848: Use the correct algorithm for CS423x ADPCM

Still has level and DC offset issues, but actual use cases are lacking (all validation was done with ALSA's adpcm conversion plugin)
This commit is contained in:
RichardG867
2025-09-27 16:31:11 -03:00
parent bc41f8bbb6
commit 37eb2cb359
2 changed files with 48 additions and 24 deletions

View File

@@ -66,10 +66,10 @@ typedef struct ad1848_t {
uint8_t enable : 1;
uint8_t irq : 4;
uint8_t dma : 3;
uint8_t adpcm_ref;
int8_t adpcm_step;
int adpcm_predictor[2];
int16_t adpcm_step_index[2];
int freq;
int adpcm_data;
uint8_t adpcm_data;
int adpcm_pos;
uint8_t dma_ff;

View File

@@ -56,9 +56,22 @@ ad1848_log(const char *fmt, ...)
static int ad1848_vols_7bits[128];
static double ad1848_vols_5bits_aux_gain[32];
/* Borrowed from snd_sb_dsp */
extern int8_t scaleMap4[64];
extern uint8_t adjustMap4[64];
/* Borrowed from ffmpeg. */
static const int8_t adpcm_index_table[16] = {
-1, -1, -1, -1, 2, 4, 6, 8,
-1, -1, -1, -1, 2, 4, 6, 8
};
static const int16_t adpcm_step_table[89] = {
7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
};
void
ad1848_setirq(ad1848_t *ad1848, int irq)
@@ -311,6 +324,8 @@ ad1848_write(uint16_t addr, uint8_t val, void *priv)
case 9:
if (!ad1848->enable && (val & 0x41) == 0x01) {
ad1848->adpcm_pos = 0;
ad1848->adpcm_predictor[0] = ad1848->adpcm_predictor[1] = 0;
ad1848->adpcm_step_index[0] = ad1848->adpcm_step_index[1] = 0;
ad1848->dma_ff = 0;
if (ad1848->timer_latch)
timer_set_delay_u64(&ad1848->timer_count, ad1848->timer_latch);
@@ -358,6 +373,11 @@ ad1848_write(uint16_t addr, uint8_t val, void *priv)
}
break;
case 17:
if (val & 0x08)
ad1848->adpcm_predictor[0] = ad1848->adpcm_predictor[1] = 0;
break;
case 18 ... 19:
if (ad1848->type >= AD1848_TYPE_CS4236B) {
if (ad1848->type >= AD1848_TYPE_CS4235) {
@@ -622,29 +642,33 @@ ad1848_dma_channel_read(ad1848_t *ad1848, int channel)
}
static int16_t
ad1848_process_adpcm(ad1848_t *ad1848)
ad1848_process_adpcm(ad1848_t *ad1848, int channel)
{
int temp;
if (ad1848->adpcm_pos++ & 1) {
temp = (ad1848->adpcm_data & 0x0f) + ad1848->adpcm_step;
temp = ad1848->adpcm_data >> 4;
} else {
ad1848->adpcm_data = (int) (ad1848_dma_channel_read(ad1848, ad1848->dma) & 0xffff);
temp = (ad1848->adpcm_data >> 4) + ad1848->adpcm_step;
ad1848->adpcm_data = ad1848_dma_channel_read(ad1848, ad1848->dma);
temp = ad1848->adpcm_data & 0x0f;
}
if (temp < 0)
temp = 0;
else if (temp > 63)
temp = 63;
ad1848->adpcm_ref += scaleMap4[temp];
if (ad1848->adpcm_ref > 0xff)
ad1848->adpcm_ref = 0xff;
else if (ad1848->adpcm_ref < 0x00)
ad1848->adpcm_ref = 0x00;
int step = adpcm_step_table[ad1848->adpcm_step_index[channel]];
int step_index = ad1848->adpcm_step_index[channel] + adpcm_index_table[temp];
if (step_index < 0)
step_index = 0;
else if (step_index > 88)
step_index = 88;
ad1848->adpcm_step = (int8_t) ((ad1848->adpcm_step + adjustMap4[temp]) & 0xff);
int diff = ((2 * (temp & 7) + 1) * step) >> 3;
int predictor = ad1848->adpcm_predictor[channel] + ((temp & 8) ? -diff : diff);
if (predictor < -32768)
predictor = -32768;
else if (predictor > 32767)
predictor = 32767;
ad1848->adpcm_predictor[channel] = predictor;
ad1848->adpcm_step_index[channel] = step_index;
return (int16_t) ((ad1848->adpcm_ref ^ 0x80) << 8);
return (int16_t) predictor;
}
static void
@@ -705,12 +729,12 @@ ad1848_poll(void *priv)
/* 0x80 and 0x90 reserved */
case 0xa0: /* Mono, 4-bit ADPCM */
ad1848->out_l = ad1848->out_r = ad1848_process_adpcm(ad1848);
ad1848->out_l = ad1848->out_r = ad1848_process_adpcm(ad1848, 0);
break;
case 0xb0: /* Stereo, 4-bit ADPCM */
ad1848->out_l = ad1848_process_adpcm(ad1848);
ad1848->out_r = ad1848_process_adpcm(ad1848);
ad1848->out_l = ad1848_process_adpcm(ad1848, 0);
ad1848->out_r = ad1848_process_adpcm(ad1848, 1);
break;
case 0xc0: /* Mono, 16-bit PCM big endian */