mirror of https://github.com/bsnes-emu/bsnes.git
175 lines
4.0 KiB
C++
175 lines
4.0 KiB
C++
|
#ifdef DSP_CPP
|
||
|
|
||
|
inline void DSP::voice_output(voice_t &v, bool channel) {
|
||
|
//apply left/right volume
|
||
|
int amp = (state.t_output * (int8)VREG(voll + channel)) >> 7;
|
||
|
|
||
|
//add to output total
|
||
|
state.t_main_out[channel] += amp;
|
||
|
state.t_main_out[channel] = sclamp<16>(state.t_main_out[channel]);
|
||
|
|
||
|
//optionally add to echo total
|
||
|
if(state.t_eon & v.vbit) {
|
||
|
state.t_echo_out[channel] += amp;
|
||
|
state.t_echo_out[channel] = sclamp<16>(state.t_echo_out[channel]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void DSP::voice_1(voice_t &v) {
|
||
|
state.t_dir_addr = (state.t_dir << 8) + (state.t_srcn << 2);
|
||
|
state.t_srcn = VREG(srcn);
|
||
|
}
|
||
|
|
||
|
void DSP::voice_2(voice_t &v) {
|
||
|
//read sample pointer (ignored if not needed)
|
||
|
uint16 addr = state.t_dir_addr;
|
||
|
if(!v.kon_delay) addr += 2;
|
||
|
uint8 lo = memory::apuram[(uint16)(addr + 0)];
|
||
|
uint8 hi = memory::apuram[(uint16)(addr + 1)];
|
||
|
state.t_brr_next_addr = ((hi << 8) + lo);
|
||
|
|
||
|
state.t_adsr0 = VREG(adsr0);
|
||
|
|
||
|
//read pitch, spread over two clocks
|
||
|
state.t_pitch = VREG(pitchl);
|
||
|
}
|
||
|
|
||
|
void DSP::voice_3(voice_t &v) {
|
||
|
voice_3a(v);
|
||
|
voice_3b(v);
|
||
|
voice_3c(v);
|
||
|
}
|
||
|
|
||
|
void DSP::voice_3a(voice_t &v) {
|
||
|
state.t_pitch += (VREG(pitchh) & 0x3f) << 8;
|
||
|
}
|
||
|
|
||
|
void DSP::voice_3b(voice_t &v) {
|
||
|
state.t_brr_byte = memory::apuram[(uint16)(v.brr_addr + v.brr_offset)];
|
||
|
state.t_brr_header = memory::apuram[(uint16)(v.brr_addr)];
|
||
|
}
|
||
|
|
||
|
void DSP::voice_3c(voice_t &v) {
|
||
|
//pitch modulation using previous voice's output
|
||
|
|
||
|
if(state.t_pmon & v.vbit) {
|
||
|
state.t_pitch += ((state.t_output >> 5) * state.t_pitch) >> 10;
|
||
|
}
|
||
|
|
||
|
if(v.kon_delay) {
|
||
|
//get ready to start BRR decoding on next sample
|
||
|
if(v.kon_delay == 5) {
|
||
|
v.brr_addr = state.t_brr_next_addr;
|
||
|
v.brr_offset = 1;
|
||
|
v.buf_pos = 0;
|
||
|
state.t_brr_header = 0; //header is ignored on this sample
|
||
|
}
|
||
|
|
||
|
//envelope is never run during KON
|
||
|
v.env = 0;
|
||
|
v.hidden_env = 0;
|
||
|
|
||
|
//disable BRR decoding until last three samples
|
||
|
v.interp_pos = 0;
|
||
|
v.kon_delay--;
|
||
|
if(v.kon_delay & 3) v.interp_pos = 0x4000;
|
||
|
|
||
|
//pitch is never added during KON
|
||
|
state.t_pitch = 0;
|
||
|
}
|
||
|
|
||
|
//gaussian interpolation
|
||
|
int output = gaussian_interpolate(v);
|
||
|
|
||
|
//noise
|
||
|
if(state.t_non & v.vbit) {
|
||
|
output = (int16)(state.noise << 1);
|
||
|
}
|
||
|
|
||
|
//apply envelope
|
||
|
state.t_output = ((output * v.env) >> 11) & ~1;
|
||
|
v.t_envx_out = v.env >> 4;
|
||
|
|
||
|
//immediate silence due to end of sample or soft reset
|
||
|
if(REG(flg) & 0x80 || (state.t_brr_header & 3) == 1) {
|
||
|
v.env_mode = env_release;
|
||
|
v.env = 0;
|
||
|
}
|
||
|
|
||
|
if(state.every_other_sample) {
|
||
|
//KOFF
|
||
|
if(state.t_koff & v.vbit) {
|
||
|
v.env_mode = env_release;
|
||
|
}
|
||
|
|
||
|
//KON
|
||
|
if(state.kon & v.vbit) {
|
||
|
v.kon_delay = 5;
|
||
|
v.env_mode = env_attack;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//run envelope for next sample
|
||
|
if(!v.kon_delay) envelope_run(v);
|
||
|
}
|
||
|
|
||
|
void DSP::voice_4(voice_t &v) {
|
||
|
//decode BRR
|
||
|
state.t_looped = 0;
|
||
|
if(v.interp_pos >= 0x4000) {
|
||
|
brr_decode(v);
|
||
|
v.brr_offset += 2;
|
||
|
if(v.brr_offset >= 9) {
|
||
|
//start decoding next BRR block
|
||
|
v.brr_addr = (uint16)(v.brr_addr + 9);
|
||
|
if(state.t_brr_header & 1) {
|
||
|
v.brr_addr = state.t_brr_next_addr;
|
||
|
state.t_looped = v.vbit;
|
||
|
}
|
||
|
v.brr_offset = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//apply pitch
|
||
|
v.interp_pos = (v.interp_pos & 0x3fff) + state.t_pitch;
|
||
|
|
||
|
//keep from getting too far ahead (when using pitch modulation)
|
||
|
if(v.interp_pos > 0x7fff) v.interp_pos = 0x7fff;
|
||
|
|
||
|
//output left
|
||
|
voice_output(v, 0);
|
||
|
}
|
||
|
|
||
|
void DSP::voice_5(voice_t &v) {
|
||
|
//output right
|
||
|
voice_output(v, 1);
|
||
|
|
||
|
//ENDX, OUTX and ENVX won't update if you wrote to them 1-2 clocks earlier
|
||
|
state.endx_buf = REG(endx) | state.t_looped;
|
||
|
|
||
|
//clear bit in ENDX if KON just began
|
||
|
if(v.kon_delay == 5) state.endx_buf &= ~v.vbit;
|
||
|
}
|
||
|
|
||
|
void DSP::voice_6(voice_t &v) {
|
||
|
state.outx_buf = state.t_output >> 8;
|
||
|
}
|
||
|
|
||
|
void DSP::voice_7(voice_t &v) {
|
||
|
//update ENDX
|
||
|
REG(endx) = (uint8)state.endx_buf;
|
||
|
state.envx_buf = v.t_envx_out;
|
||
|
}
|
||
|
|
||
|
void DSP::voice_8(voice_t &v) {
|
||
|
//update OUTX
|
||
|
VREG(outx) = (uint8)state.outx_buf;
|
||
|
}
|
||
|
|
||
|
void DSP::voice_9(voice_t &v) {
|
||
|
//update ENVX
|
||
|
VREG(envx) = (uint8)state.envx_buf;
|
||
|
}
|
||
|
|
||
|
#endif
|