add support for master volume control register

add basic support for per-channel volume controls
This commit is contained in:
Anthony Pesch 2017-05-07 20:44:42 -04:00
parent f606679f1a
commit 80b103760a
1 changed files with 87 additions and 2 deletions

View File

@ -1,3 +1,4 @@
#include <math.h>
#include "hw/aica/aica.h"
#include "core/log.h"
#include "core/option.h"
@ -80,6 +81,82 @@ struct aica {
FILE *recording;
};
static int32_t mvol_scale[16];
static int32_t tl_scale[256];
static void aica_init_tables() {
static int initialized = 0;
if (initialized) {
return;
}
initialized = 1;
/* the MVOL register adjusts the output level based on the table:
MVOL level
----------------
0 -MAX db
1 -42 db
2 -39 db
...
n -42 + (n-1) db
sound pressure level is defined as:
level = 20 * log10(out / in)
out can therefor be calculated as:
out = in * pow(10, level / 20)
this can be approximated using MVOL instead of level as:
out = in / pow(2, (MVOL - i) / 2) */
for (int i = 0; i < 16; i++) {
/* 0 is a special case that mutes the output */
if (i == 0) {
continue;
}
/* a 32-bit int is used for the scale, leaving 15 bits for the fraction */
mvol_scale[i] = (1 << 15) / pow(2.0f, (15 - i) / 2.0f);
}
/* each channel's TL register adjusts the output level based on the table:
TL level
--------------
bit 0 -0.4 db
bit 1 -0.8 db
bit 2 -1.5 db
bit 3 -3.0 db
bit 4 -6.0 db
bit 5 -12.0 db
bit 6 -24.0 db
bit 7 -48.0 db
this can be approximated using TL as:
out = in / pow(2, TL / 16) */
for (int i = 0; i < 256; i++) {
/* a 32-bit int is used for the scale, leaving 15 bits for the fraction */
tl_scale[i] = (1 << 15) / pow(2.0f, i / 16.0f);
}
}
static inline int32_t aica_adjust_master_volume(struct aica *aica, int32_t in) {
int32_t y = mvol_scale[aica->common_data->MVOL];
/* truncate fraction */
return (in * y) >> 15;
}
static inline int32_t aica_adjust_channel_volume(struct aica_channel *ch,
int32_t in) {
int32_t y = tl_scale[ch->data->TL];
/* truncate fraction */
return (in * y) >> 15;
}
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
@ -381,6 +458,9 @@ static int32_t aica_channel_update(struct aica *aica, struct aica_channel *ch) {
uint32_t pos = AICA_OFFSET_POS(ch->offset);
uint32_t frac = AICA_OFFSET_FRAC(ch->offset);
/* the data returned is a signed 16-bit PCM data, but signed 32-bit values are
used intermediately to avoid dealing with overflows until the end */
int32_t next_sample = 0;
int32_t next_quant = 0;
@ -452,10 +532,13 @@ static void aica_generate_frames(struct aica *aica, int num_frames) {
for (int j = 0; j < AICA_NUM_CHANNELS; j++) {
struct aica_channel *ch = &aica->channels[j];
int32_t s = aica_channel_update(aica, ch);
l += s;
r += s;
l += aica_adjust_channel_volume(ch, s);
r += aica_adjust_channel_volume(ch, s);
}
l = aica_adjust_master_volume(aica, l);
r = aica_adjust_master_volume(aica, r);
buffer[i * 2 + 0] = CLAMP(l, INT16_MIN, INT16_MAX);
buffer[i * 2 + 1] = CLAMP(r, INT16_MIN, INT16_MAX);
}
@ -746,6 +829,8 @@ void aica_destroy(struct aica *aica) {
}
struct aica *aica_create(struct dreamcast *dc) {
aica_init_tables();
struct aica *aica = dc_create_device(dc, sizeof(struct aica), "aica",
&aica_init, &aica_debug_menu);
return aica;