mirror of https://github.com/inolen/redream.git
set channel key state to off when a non-looping channel is stopped
This commit is contained in:
parent
04a42d7b07
commit
294392a64e
|
@ -24,7 +24,11 @@ DEFINE_AGGREGATE_COUNTER(frames);
|
||||||
#define FRAME_WIDTH 640
|
#define FRAME_WIDTH 640
|
||||||
#define FRAME_HEIGHT 480
|
#define FRAME_HEIGHT 480
|
||||||
|
|
||||||
enum { FRAME_WAITING, FRAME_RENDERING, FRAME_RENDERED, };
|
enum {
|
||||||
|
FRAME_WAITING,
|
||||||
|
FRAME_RENDERING,
|
||||||
|
FRAME_RENDERED,
|
||||||
|
};
|
||||||
|
|
||||||
struct emu {
|
struct emu {
|
||||||
int multi_threaded;
|
int multi_threaded;
|
||||||
|
|
|
@ -22,7 +22,7 @@ DEFINE_AGGREGATE_COUNTER(aica_samples);
|
||||||
#define LOG_AICA(...)
|
#define LOG_AICA(...)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define AICA_SAMPLE_BATCH 10
|
#define AICA_BATCH_SIZE 10
|
||||||
#define AICA_NUM_CHANNELS 64
|
#define AICA_NUM_CHANNELS 64
|
||||||
|
|
||||||
#define AICA_FNS_BITS 10
|
#define AICA_FNS_BITS 10
|
||||||
|
@ -35,14 +35,22 @@ DEFINE_AGGREGATE_COUNTER(aica_samples);
|
||||||
#define ADPCM_QUANT_MIN 0x7f
|
#define ADPCM_QUANT_MIN 0x7f
|
||||||
#define ADPCM_QUANT_MAX 0x6000
|
#define ADPCM_QUANT_MAX 0x6000
|
||||||
|
|
||||||
|
/* amplitude / frequency envelope generator state */
|
||||||
|
struct aica_eg_state {
|
||||||
|
int state;
|
||||||
|
|
||||||
|
int attack_rate;
|
||||||
|
int decay1_rate;
|
||||||
|
int decay2_rate;
|
||||||
|
int release_rate;
|
||||||
|
int decay_level;
|
||||||
|
};
|
||||||
|
|
||||||
struct aica_channel {
|
struct aica_channel {
|
||||||
struct channel_data *data;
|
struct channel_data *data;
|
||||||
|
|
||||||
int active;
|
int active;
|
||||||
|
|
||||||
/* signals the the current channel has looped */
|
|
||||||
int looped;
|
|
||||||
|
|
||||||
/* base address in host memory of sound data */
|
/* base address in host memory of sound data */
|
||||||
uint8_t *base;
|
uint8_t *base;
|
||||||
|
|
||||||
|
@ -52,9 +60,15 @@ struct aica_channel {
|
||||||
/* current position in the sound source */
|
/* current position in the sound source */
|
||||||
uint32_t offset;
|
uint32_t offset;
|
||||||
|
|
||||||
|
/* signals the the current channel has looped */
|
||||||
|
int looped;
|
||||||
|
|
||||||
/* previous sample state */
|
/* previous sample state */
|
||||||
int32_t prev_sample;
|
int32_t prev_sample;
|
||||||
int32_t prev_quant;
|
int32_t prev_quant;
|
||||||
|
|
||||||
|
/*struct aica_eg_state aeg;
|
||||||
|
struct aica_eg_state feg;*/
|
||||||
};
|
};
|
||||||
|
|
||||||
struct aica {
|
struct aica {
|
||||||
|
@ -73,23 +87,29 @@ struct aica {
|
||||||
int rtc_write;
|
int rtc_write;
|
||||||
uint32_t rtc;
|
uint32_t rtc;
|
||||||
|
|
||||||
/* channels */
|
/* there are 64 channels, each with 32 x 16-bit registers arranged on 32-bit
|
||||||
struct common_data *common_data;
|
boundaries. the arm7 will perform either 32-bit or 8-bit accesses to the
|
||||||
|
registers, while the sh4 will only perform 32-bit accesses as they must
|
||||||
|
go through the g2 bus's fifo buffer */
|
||||||
struct aica_channel channels[AICA_NUM_CHANNELS];
|
struct aica_channel channels[AICA_NUM_CHANNELS];
|
||||||
|
struct common_data *common_data;
|
||||||
struct timer *sample_timer;
|
struct timer *sample_timer;
|
||||||
|
|
||||||
/* raw audio recording */
|
/* raw audio recording */
|
||||||
FILE *recording;
|
FILE *recording;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* approximated lookup tables for MVOL / TL scaling */
|
||||||
static int32_t mvol_scale[16];
|
static int32_t mvol_scale[16];
|
||||||
static int32_t tl_scale[256];
|
static int32_t tl_scale[256];
|
||||||
|
|
||||||
static char *aica_fmt_names[] = {
|
static char *aica_fmt_names[] = {
|
||||||
"PCMS16", /* AICA_FMT_PCMS16 */
|
"PCMS16", /* AICA_FMT_PCMS16 */
|
||||||
"PCMS8", /* AICA_FMT_PCMS8 */
|
"PCMS8", /* AICA_FMT_PCMS8 */
|
||||||
"ADPCM", /* AICA_FMT_ADPCM */
|
"ADPCM", /* AICA_FMT_ADPCM */
|
||||||
"ADPCM_STREAM", /* AICA_FMT_ADPCM_STREAM */
|
"ADPCM_STREAM", /* AICA_FMT_ADPCM_STREAM */
|
||||||
};
|
};
|
||||||
|
|
||||||
static char *aica_loop_names[] = {
|
static char *aica_loop_names[] = {
|
||||||
"LOOP_NONE", /* AICA_LOOP_NONE */
|
"LOOP_NONE", /* AICA_LOOP_NONE */
|
||||||
"LOOP_FORWARD", /* AICA_LOOP_FORWARD */
|
"LOOP_FORWARD", /* AICA_LOOP_FORWARD */
|
||||||
|
@ -420,6 +440,10 @@ static void aica_channel_stop(struct aica *aica, struct aica_channel *ch) {
|
||||||
|
|
||||||
ch->active = 0;
|
ch->active = 0;
|
||||||
|
|
||||||
|
/* this will already be cleared if the channel is stopped due to a key event.
|
||||||
|
however, it will not be set when a non-looping channel is stopped */
|
||||||
|
ch->data->KYONB = 0;
|
||||||
|
|
||||||
LOG_AICA("aica_channel_stop [%d]", ch - aica->channels);
|
LOG_AICA("aica_channel_stop [%d]", ch - aica->channels);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -430,16 +454,17 @@ static void aica_channel_start(struct aica *aica, struct aica_channel *ch) {
|
||||||
|
|
||||||
uint32_t start_addr = (ch->data->SA_hi << 16) | ch->data->SA_lo;
|
uint32_t start_addr = (ch->data->SA_hi << 16) | ch->data->SA_lo;
|
||||||
ch->active = 1;
|
ch->active = 1;
|
||||||
ch->looped = 0;
|
|
||||||
ch->base = &aica->wave_ram[start_addr];
|
ch->base = &aica->wave_ram[start_addr];
|
||||||
ch->step = aica_channel_step(ch);
|
ch->step = aica_channel_step(ch);
|
||||||
ch->offset = 0;
|
ch->offset = 0;
|
||||||
|
ch->looped = 0;
|
||||||
|
|
||||||
ch->prev_sample = 0;
|
ch->prev_sample = 0;
|
||||||
ch->prev_quant = ADPCM_QUANT_MIN;
|
ch->prev_quant = ADPCM_QUANT_MIN;
|
||||||
|
|
||||||
float hz = (AICA_SAMPLE_FREQ * ch->step) / (float)AICA_STEP_SAMPLE;
|
float hz = (AICA_SAMPLE_FREQ * ch->step) / (float)AICA_STEP_SAMPLE;
|
||||||
float duration = ch->data->LEA / hz;
|
float duration = ch->data->LEA / hz;
|
||||||
|
|
||||||
LOG_AICA("aica_channel_start [%d] %s, %s, %.2f hz, %.2f sec",
|
LOG_AICA("aica_channel_start [%d] %s, %s, %.2f hz, %.2f sec",
|
||||||
ch - aica->channels, aica_fmt_names[ch->data->PCMS],
|
ch - aica->channels, aica_fmt_names[ch->data->PCMS],
|
||||||
aica_loop_names[ch->data->LPCTL], hz, duration);
|
aica_loop_names[ch->data->LPCTL], hz, duration);
|
||||||
|
@ -524,6 +549,10 @@ static int32_t aica_channel_update(struct aica *aica, struct aica_channel *ch) {
|
||||||
/* check if the current position in the sound source has passed the loop end
|
/* check if the current position in the sound source has passed the loop end
|
||||||
position */
|
position */
|
||||||
if (next_pos > ch->data->LEA) {
|
if (next_pos > ch->data->LEA) {
|
||||||
|
ch->looped = 1;
|
||||||
|
|
||||||
|
LOG_AICA("aica_channel_update [%d] looped", ch - aica->channels);
|
||||||
|
|
||||||
switch (ch->data->LPCTL) {
|
switch (ch->data->LPCTL) {
|
||||||
case AICA_LOOP_NONE: {
|
case AICA_LOOP_NONE: {
|
||||||
aica_channel_stop(aica, ch);
|
aica_channel_stop(aica, ch);
|
||||||
|
@ -531,7 +560,6 @@ static int32_t aica_channel_update(struct aica *aica, struct aica_channel *ch) {
|
||||||
|
|
||||||
case AICA_LOOP_FORWARD: {
|
case AICA_LOOP_FORWARD: {
|
||||||
/* restart channel at loop start address */
|
/* restart channel at loop start address */
|
||||||
ch->looped = 1;
|
|
||||||
ch->offset = ch->data->LSA << AICA_FNS_BITS;
|
ch->offset = ch->data->LSA << AICA_FNS_BITS;
|
||||||
ch->prev_sample = 0;
|
ch->prev_sample = 0;
|
||||||
ch->prev_quant = ADPCM_QUANT_MIN;
|
ch->prev_quant = ADPCM_QUANT_MIN;
|
||||||
|
@ -542,42 +570,36 @@ static int32_t aica_channel_update(struct aica *aica, struct aica_channel *ch) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void aica_generate_frames(struct aica *aica, int num_frames) {
|
static void aica_generate_frames(struct aica *aica) {
|
||||||
#define MAX_FRAMES 10
|
|
||||||
struct dreamcast *dc = aica->dc;
|
struct dreamcast *dc = aica->dc;
|
||||||
int16_t buffer[MAX_FRAMES * 2];
|
int16_t buffer[AICA_BATCH_SIZE * 2];
|
||||||
|
|
||||||
for (int frame = 0; frame < num_frames;) {
|
for (int frame = 0; frame < AICA_BATCH_SIZE; frame++) {
|
||||||
int n = MIN(num_frames - frame, MAX_FRAMES);
|
int32_t l = 0;
|
||||||
|
int32_t r = 0;
|
||||||
|
|
||||||
for (int i = 0; i < n; i++) {
|
for (int i = 0; i < AICA_NUM_CHANNELS; i++) {
|
||||||
int32_t l = 0;
|
struct aica_channel *ch = &aica->channels[i];
|
||||||
int32_t r = 0;
|
int32_t s = aica_channel_update(aica, ch);
|
||||||
|
l += aica_adjust_channel_volume(ch, s);
|
||||||
for (int j = 0; j < AICA_NUM_CHANNELS; j++) {
|
r += aica_adjust_channel_volume(ch, s);
|
||||||
struct aica_channel *ch = &aica->channels[j];
|
|
||||||
int32_t s = aica_channel_update(aica, ch);
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dc_push_audio(dc, buffer, n);
|
l = aica_adjust_master_volume(aica, l);
|
||||||
frame += n;
|
r = aica_adjust_master_volume(aica, r);
|
||||||
|
|
||||||
/* save raw audio out while recording */
|
buffer[frame * 2 + 0] = CLAMP(l, INT16_MIN, INT16_MAX);
|
||||||
if (aica->recording) {
|
buffer[frame * 2 + 1] = CLAMP(r, INT16_MIN, INT16_MAX);
|
||||||
fwrite(buffer, 4, n, aica->recording);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
prof_counter_add(COUNTER_aica_samples, num_frames);
|
dc_push_audio(dc, buffer, AICA_BATCH_SIZE);
|
||||||
|
|
||||||
|
/* save raw audio out while recording */
|
||||||
|
if (aica->recording) {
|
||||||
|
fwrite(buffer, 4, AICA_BATCH_SIZE, aica->recording);
|
||||||
|
}
|
||||||
|
|
||||||
|
prof_counter_add(COUNTER_aica_samples, AICA_BATCH_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t aica_channel_reg_read(struct aica *aica, uint32_t addr,
|
static uint32_t aica_channel_reg_read(struct aica *aica, uint32_t addr,
|
||||||
|
@ -611,13 +633,18 @@ static uint32_t aica_common_reg_read(struct aica *aica, uint32_t addr,
|
||||||
switch (addr) {
|
switch (addr) {
|
||||||
case 0x10:
|
case 0x10:
|
||||||
case 0x11: { /* EG, SGC, LP */
|
case 0x11: { /* EG, SGC, LP */
|
||||||
|
/* reads the current EG / SGC / LP params from the stream specified by
|
||||||
|
MSLC */
|
||||||
struct aica_channel *ch = &aica->channels[aica->common_data->MSLC];
|
struct aica_channel *ch = &aica->channels[aica->common_data->MSLC];
|
||||||
|
|
||||||
if ((addr == 0x10 && DATA_SIZE() == 2) ||
|
if (aica->common_data->AFSEL) {
|
||||||
(addr == 0x11 && DATA_SIZE() == 1)) {
|
/* read FEG status */
|
||||||
aica->common_data->LP = ch->looped;
|
} else {
|
||||||
ch->looped = 0;
|
/* read AEG status */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
aica->common_data->LP = ch->looped;
|
||||||
|
ch->looped = 0;
|
||||||
} break;
|
} break;
|
||||||
case 0x14: { /* CA */
|
case 0x14: { /* CA */
|
||||||
struct aica_channel *ch = &aica->channels[aica->common_data->MSLC];
|
struct aica_channel *ch = &aica->channels[aica->common_data->MSLC];
|
||||||
|
@ -741,7 +768,7 @@ void aica_reg_write(struct aica *aica, uint32_t addr, uint32_t data,
|
||||||
static void aica_next_sample(void *data) {
|
static void aica_next_sample(void *data) {
|
||||||
struct aica *aica = data;
|
struct aica *aica = data;
|
||||||
|
|
||||||
aica_generate_frames(aica, AICA_SAMPLE_BATCH);
|
aica_generate_frames(aica);
|
||||||
aica_raise_interrupt(aica, AICA_INT_SAMPLE);
|
aica_raise_interrupt(aica, AICA_INT_SAMPLE);
|
||||||
aica_update_arm(aica);
|
aica_update_arm(aica);
|
||||||
aica_update_sh(aica);
|
aica_update_sh(aica);
|
||||||
|
@ -749,7 +776,7 @@ static void aica_next_sample(void *data) {
|
||||||
/* reschedule */
|
/* reschedule */
|
||||||
aica->sample_timer =
|
aica->sample_timer =
|
||||||
scheduler_start_timer(aica->scheduler, &aica_next_sample, aica,
|
scheduler_start_timer(aica->scheduler, &aica_next_sample, aica,
|
||||||
HZ_TO_NANO(AICA_SAMPLE_FREQ / AICA_SAMPLE_BATCH));
|
HZ_TO_NANO(AICA_SAMPLE_FREQ / AICA_BATCH_SIZE));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void aica_toggle_recording(struct aica *aica) {
|
static void aica_toggle_recording(struct aica *aica) {
|
||||||
|
@ -799,7 +826,7 @@ static int aica_init(struct device *dev) {
|
||||||
aica->common_data = (struct common_data *)(aica->reg + 0x2800);
|
aica->common_data = (struct common_data *)(aica->reg + 0x2800);
|
||||||
aica->sample_timer =
|
aica->sample_timer =
|
||||||
scheduler_start_timer(aica->scheduler, &aica_next_sample, aica,
|
scheduler_start_timer(aica->scheduler, &aica_next_sample, aica,
|
||||||
HZ_TO_NANO(AICA_SAMPLE_FREQ / AICA_SAMPLE_BATCH));
|
HZ_TO_NANO(AICA_SAMPLE_FREQ / AICA_BATCH_SIZE));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* init timers */
|
/* init timers */
|
||||||
|
|
|
@ -31,6 +31,13 @@ enum {
|
||||||
AICA_LOOP_FORWARD,
|
AICA_LOOP_FORWARD,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
AICA_EG_ATTACK,
|
||||||
|
AICA_EG_DECAY1,
|
||||||
|
AICA_EG_DECAY2,
|
||||||
|
AICA_EG_RELEASE,
|
||||||
|
};
|
||||||
|
|
||||||
struct channel_data {
|
struct channel_data {
|
||||||
/* 0x0 */
|
/* 0x0 */
|
||||||
uint32_t SA_hi : 7;
|
uint32_t SA_hi : 7;
|
||||||
|
|
Loading…
Reference in New Issue