Permalink
Browse files

initial ADPCM decoding support

  • Loading branch information...
inolen committed Apr 19, 2017
1 parent 1ba84de commit b95d67b39f6a19e6a57ba06d8290fe710cd87561
Showing with 89 additions and 18 deletions.
  1. +1 −0 src/core/math.h
  2. +87 −18 src/hw/aica/aica.c
  3. +1 −0 src/hw/maple/controller.c
View
@@ -5,6 +5,7 @@
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#define CLAMP(x, lo, hi) MAX((lo), MIN((hi), (x)))
#define ABS(x) ((x) < 0 ? -(x) : (x))
#define align_up(v, alignment) (((v) + (alignment)-1) & ~((alignment)-1))
View
@@ -31,6 +31,9 @@ DEFINE_AGGREGATE_COUNTER(aica_samples);
#define AICA_TIMER_PERIOD 0xff
#define ADPCM_QUANT_MIN 0x7f
#define ADPCM_QUANT_MAX 0x6000
struct aica_channel {
struct channel_data *data;
@@ -47,6 +50,10 @@ struct aica_channel {
/* current position in the sound source */
uint32_t offset;
/* previous sample state */
int32_t prev_sample;
int32_t prev_quant;
};
struct aica {
@@ -75,6 +82,56 @@ struct aica {
FILE *recording;
};
static void aica_decode_adpcm(int32_t data, int32_t prev, int32_t prev_quant,
int32_t *next, int32_t *next_quant) {
/* the decoded value (n) = (1 - 2 * l4) * (l3 + l2/2 + l1/4 + 1/8) * quantized
width (n) + decoded value (n - 1)
a lookup table is used to compute the second part of the above expression:
l3 l2 l1 f
--------------
0 0 0 1
0 0 1 3
0 1 0 5
0 1 1 7
1 0 0 9
1 0 1 11
1 1 0 13
1 1 1 15
the final value is a signed 16-bit value and must be clamped as such */
static int32_t adpcm_scale[8] = {1, 3, 5, 7, 9, 11, 13, 15};
int l4 = data >> 3;
int l321 = data & 0x7;
int sign = 1 - 2 * l4;
*next = sign * ((adpcm_scale[l321] * prev_quant) >> 3) + prev;
*next = CLAMP(*next, INT16_MIN, INT16_MAX);
/* the quantized width (n+1) = f(l3, l2, l1) * quantized width (n).
f(l3, l2, l1) is the rate of change in the quantized width found
from the table:
l3 l2 l1 f
----------------------
0 0 0 0.8984375 (230 / 256)
0 0 1 0.8984375 (230 / 256)
0 1 0 0.8984375 (230 / 256)
0 1 1 0.8984375 (230 / 256)
1 0 0 1.19921875 (307 / 256)
1 0 1 1.59765625 (409 / 256)
1 1 0 2.0 (512 / 256)
1 1 1 2.3984375 (614 / 256)
the quantized width's min value is 127, and its max value is 24576 */
static int32_t adpcm_rate[8] = {230, 230, 230, 230, 307, 409, 512, 614};
*next_quant = (prev_quant * adpcm_rate[l321]) >> 8;
*next_quant = CLAMP(*next_quant, ADPCM_QUANT_MIN, ADPCM_QUANT_MAX);
}
static void aica_raise_interrupt(struct aica *aica, int intr) {
aica->common_data->MCIPD |= (1 << intr);
aica->common_data->SCIPD |= (1 << intr);
@@ -290,6 +347,8 @@ static void aica_channel_start(struct aica *aica, struct aica_channel *ch) {
ch->base = &aica->wave_ram[start_addr];
ch->step = aica_channel_step(ch);
ch->offset = 0;
ch->prev_sample = 0;
ch->prev_quant = ADPCM_QUANT_MIN;
LOG_AICA("aica_channel_start %d", ch - aica->channels);
}
@@ -322,37 +381,45 @@ static int32_t aica_channel_update(struct aica *aica, struct aica_channel *ch) {
CHECK_NOTNULL(ch->base);
uint32_t pos = AICA_OFFSET_POS(ch->offset);
uint32_t frac = AICA_OFFSET_FRAC(ch->offset);
/* get prev and next sample from sound source */
int32_t prev = 0;
int32_t next = 0;
int pos = AICA_OFFSET_POS(ch->offset);
int frac = AICA_OFFSET_FRAC(ch->offset);
int32_t next_sample = 0;
int32_t next_quant = 0;
switch (ch->data->PCMS) {
/* 16-bit signed PCM */
case 0: {
int16_t *samples = (int16_t *)ch->base;
prev = samples[pos];
next = samples[pos + 1];
next_sample = *(int16_t *)&ch->base[pos << 1];
} break;
/* 8-bit signed PCM */
case 1: {
int8_t *samples = (int8_t *)ch->base;
prev = samples[pos] << 8;
next = samples[pos + 1] << 8;
next_sample = *(int8_t *)&ch->base[pos] << 8;
} break;
/* 4-bit ADPCM */
case 2:
case 3: {
int shift = (pos & 1) << 2;
int32_t data = (ch->base[pos >> 1] >> shift) & 0xf;
aica_decode_adpcm(data, ch->prev_sample, ch->prev_quant, &next_sample,
&next_quant);
} break;
default:
/* LOG_WARNING("Unsupported PCMS %d", ch->data->PCMS); */
LOG_WARNING("Unsupported PCMS %d", ch->data->PCMS);
break;
}
/* interpolate sample */
int32_t sample = (prev * ((1 << AICA_FNS_BITS) - frac)) + (next * frac);
sample >>= AICA_FNS_BITS;
int32_t result = ch->prev_sample * ((1 << AICA_FNS_BITS) - frac);
result += next_sample * frac;
result >>= AICA_FNS_BITS;
/* step forward */
ch->offset += ch->step;
ch->prev_sample = next_sample;
ch->prev_quant = next_quant;
/* check if the current position in the sound source has passed the loop end
position */
@@ -361,13 +428,15 @@ static int32_t aica_channel_update(struct aica *aica, struct aica_channel *ch) {
/* restart channel at LSA */
LOG_AICA("aica_channel_step %d restart", ch - aica->channels);
ch->offset = ch->data->LSA << AICA_FNS_BITS;
ch->prev_sample = 0;
ch->prev_quant = ADPCM_QUANT_MIN;
ch->looped = 1;
} else {
aica_channel_stop(aica, ch);
}
}
return sample;
return result;
}
static void aica_clear_frames(struct aica *aica) {
@@ -424,8 +493,8 @@ static void aica_generate_frames(struct aica *aica, int num_frames) {
r += s;
}
l = MIN(INT16_MAX, MAX(INT16_MIN, l));
r = MIN(INT16_MAX, MAX(INT16_MIN, r));
l = CLAMP(l, INT16_MIN, INT16_MAX);
r = CLAMP(r, INT16_MIN, INT16_MAX);
frames[i] = (r << 16) | l;
}
@@ -107,6 +107,7 @@ static int controller_input(struct maple_device *dev, enum keycode key,
uint8_t scaled = ((int32_t)value - INT16_MIN) >> 8;
if (!button) {
LOG_DEBUG("Unhandled key %s", get_name_by_key(key));
return 0;
}

0 comments on commit b95d67b

Please sign in to comment.