bsnes/higan/sfc/dsp/echo.cpp
Tim Allen fdc41611cf Update to v098r14 release.
byuu says:

Changelog:
- improved attenuation of biquad filter by computing butterworth Q
  coefficients correctly (instead of using the same constant)
- adding 1e-25 to each input sample into the biquad filters to try and
  prevent denormalization
- updated normalization from [0.0 to 1.0] to [-1.0 to +1.0]; volume/reverb
  happen in floating-point mode now
- good amount of work to make the base Emulator::Audio support any number
  of output channels
  - so that we don't have to do separate work on left/right channels;
    and can instead share the code for each channel
- Emulator::Interface::audioSample(int16 left, int16 right); changed to:
  - Emulator::Interface::audioSample(double* samples, uint channels);
  - samples are normalized [-1.0 to +1.0]
  - for now at least, channels will be the value given to
    Emulator::Audio::reset()
- fixed GUI crash on startup when audio driver is set to None

I'm probably going to be updating ruby to accept normalized doubles as
well; but I'm not sure if I will try and support anything other 2-channel
audio output. It'll depend on how easy it is to do so; perhaps it'll be
a per-driver setting.

The denormalization thing is fierce. If that happens, it drops the
emulator framerate from 220fps to about 20fps for Game Boy emulation. And
that happens basically whenever audio output is silent. I'm probably
also going to make a nall/denormal.hpp file at some point with
platform-specific functionality to set the CPU state to "denormals as
zero" where applicable. I'll still add the 1e-25 offset (inaudible)
as another fallback.
2016-06-01 21:23:22 +10:00

131 lines
3.2 KiB
C++

auto DSP::calculateFIR(bool channel, int index) -> int {
int sample = state.echoHistory[channel][(uint3)(state.echoHistoryOffset + index + 1)];
return (sample * (int8)REG(FIR + index * 0x10)) >> 6;
}
auto DSP::echoOutput(bool channel) -> int {
int output = (int16)((state._mainOut[channel] * (int8)REG(MVOLL + channel * 0x10)) >> 7)
+ (int16)((state._echoIn [channel] * (int8)REG(EVOLL + channel * 0x10)) >> 7);
return sclamp<16>(output);
}
auto DSP::echoRead(bool channel) -> void {
uint addr = state._echoPointer + channel * 2;
uint8 lo = smp.apuram[(uint16)(addr + 0)];
uint8 hi = smp.apuram[(uint16)(addr + 1)];
int s = (int16)((hi << 8) + lo);
state.echoHistory[channel][state.echoHistoryOffset] = s >> 1;
}
auto DSP::echoWrite(bool channel) -> void {
if(!(state._echoDisabled & 0x20)) {
uint addr = state._echoPointer + channel * 2;
int s = state._echoOut[channel];
smp.apuram[(uint16)(addr + 0)] = s;
smp.apuram[(uint16)(addr + 1)] = s >> 8;
}
state._echoOut[channel] = 0;
}
auto DSP::echo22() -> void {
//history
state.echoHistoryOffset++;
state._echoPointer = (uint16)((state._esa << 8) + state.echoOffset);
echoRead(0);
//FIR
int l = calculateFIR(0, 0);
int r = calculateFIR(1, 0);
state._echoIn[0] = l;
state._echoIn[1] = r;
}
auto DSP::echo23() -> void {
int l = calculateFIR(0, 1) + calculateFIR(0, 2);
int r = calculateFIR(1, 1) + calculateFIR(1, 2);
state._echoIn[0] += l;
state._echoIn[1] += r;
echoRead(1);
}
auto DSP::echo24() -> void {
int l = calculateFIR(0, 3) + calculateFIR(0, 4) + calculateFIR(0, 5);
int r = calculateFIR(1, 3) + calculateFIR(1, 4) + calculateFIR(1, 5);
state._echoIn[0] += l;
state._echoIn[1] += r;
}
auto DSP::echo25() -> void {
int l = state._echoIn[0] + calculateFIR(0, 6);
int r = state._echoIn[1] + calculateFIR(1, 6);
l = (int16)l;
r = (int16)r;
l += (int16)calculateFIR(0, 7);
r += (int16)calculateFIR(1, 7);
state._echoIn[0] = sclamp<16>(l) & ~1;
state._echoIn[1] = sclamp<16>(r) & ~1;
}
auto DSP::echo26() -> void {
//left output volumes
//(save sample for next clock so we can output both together)
state._mainOut[0] = echoOutput(0);
//echo feedback
int l = state._echoOut[0] + (int16)((state._echoIn[0] * (int8)REG(EFB)) >> 7);
int r = state._echoOut[1] + (int16)((state._echoIn[1] * (int8)REG(EFB)) >> 7);
state._echoOut[0] = sclamp<16>(l) & ~1;
state._echoOut[1] = sclamp<16>(r) & ~1;
}
auto DSP::echo27() -> void {
//output
int outl = state._mainOut[0];
int outr = echoOutput(1);
state._mainOut[0] = 0;
state._mainOut[1] = 0;
//todo: global muting isn't this simple
//(turns DAC on and off or something, causing small ~37-sample pulse when first muted)
if(REG(FLG) & 0x40) {
outl = 0;
outr = 0;
}
//output sample to DAC
stream->sample(outl / 32768.0, outr / 32768.0);
}
auto DSP::echo28() -> void {
state._echoDisabled = REG(FLG);
}
auto DSP::echo29() -> void {
state._esa = REG(ESA);
if(!state.echoOffset) state.echoLength = (REG(EDL) & 0x0f) << 11;
state.echoOffset += 4;
if(state.echoOffset >= state.echoLength) state.echoOffset = 0;
//write left echo
echoWrite(0);
state._echoDisabled = REG(FLG);
}
auto DSP::echo30() -> void {
//write right echo
echoWrite(1);
}