Fix resampling for movie player

Also clean up some random things.
This commit is contained in:
doop
2026-09-14 22:43:47 +00:00
parent f9be348594
commit bb29b94343
3 changed files with 86 additions and 24 deletions
+2 -4
View File
@@ -46,7 +46,7 @@ DUSK_GAME_DATA u32 JASDriver::sOutputRate;
DUSK_GAME_DATA JASMixMode JASDriver::sMixMode = MIX_MODE_EXTRA;
DUSK_GAME_DATA f32 JASDriver::sDacRate = 32028.5f;
DUSK_GAME_DATA f32 JASDriver::sDacRate = DUSK_IF_ELSE(32000.0f, 32028.5f);
DUSK_GAME_DATA u32 JASDriver::sSubFrames = 0x00000007;
@@ -111,9 +111,7 @@ void JASDriver::setOutputRate(JASOutputRate param_0) {
sDacRate = 48000.0f;
}
#if !TARGET_PC
sDacRate *= 1.0008897f;
#endif
IF_NOT_DUSK(sDacRate *= 1.0008897f);
}
DUSK_GAME_DATA const JASDriver::MixFunc JASDriver::sMixFuncs[4] = {
+72 -8
View File
@@ -191,6 +191,67 @@ static void InterleaveOutputData(const OutputSubframe& data, std::span<f32> targ
}
}
constexpr auto kExtChannels = 2;
constexpr auto kExtSrcInSampleCount = DSP_SUBFRAME_SIZE * 2;
constexpr auto kExtSrcOutSampleCount = DSP_SUBFRAME_SIZE * 3;
// simple 3:2 resampler
struct LinearResampler {
f32 prev = 0.0f;
int phase = 0;
void resample(std::span<const s16> in, std::span<f32> out, int skip = 1, int offset = 0) {
const auto inSize = in.size() / skip;
const auto outSize = out.size() / skip;
assert(inSize * 3 == outSize * 2);
int idx = -1;
int phi = phase;
int n = 0;
while (n < outSize) {
f32 s0 = (idx < 0) ? prev : in[idx * skip + offset] / 32768.0f;
f32 s1 = in[idx * skip + skip + offset] / 32768.0f;
f32 frac = phi * (1.0f / 3.0f);
out[n++ * skip + offset] = s0 + frac * (s1 - s0);
phi += 2;
if (phi >= 3) {
phi -= 3;
idx++;
}
}
prev = in[(inSize - 1) * skip + offset] / 32768.0f;
phase = phi;
}
};
static struct {
std::array<f32, kExtSrcOutSampleCount * kExtChannels> buf;
int available = 0;
LinearResampler leftSrc;
LinearResampler rightSrc;
bool hungry() const { return available <= 0; }
void feed(std::span<const s16> in) {
const auto out = std::span{buf};
leftSrc.resample(in, out, kExtChannels, 0);
rightSrc.resample(in, out, kExtChannels, 1);
available = buf.size();
}
std::span<const f32> drain_subframe() {
constexpr auto kDrainSize = DSP_SUBFRAME_SIZE * kExtChannels;
assert(available >= kDrainSize);
const auto rv = std::span{&buf[buf.size() - available], kDrainSize};
available -= kDrainSize;
return rv;
}
} extResampler;
int RenderAudioSubframe() {
ZoneScoped;
OutBuffer = {};
@@ -202,16 +263,19 @@ int RenderAudioSubframe() {
InterleaveOutputData(OutBuffer, OutInterleaveBuffer);
if (JASDriver::extMixCallback != nullptr && JASDriver::sMixMode == MIX_MODE_INTERLEAVE) {
// NOTE: In the real game, this gets called on the entire audio frame, rather than the subframe.
// That's probably more efficient, but I didn't wanna change the code to calculate the
// entire audio buffers at once.
// This is only used for the movie player, and it seems to work fine with the smaller calls.
const auto mixData = JASDriver::extMixCallback(DSP_SUBFRAME_SIZE);
if (mixData) {
if (extResampler.hungry()) {
const auto mixData = JASDriver::extMixCallback(kExtSrcInSampleCount);
if (mixData) {
extResampler.feed(std::span{mixData, kExtSrcInSampleCount * kExtChannels});
}
}
if (!extResampler.hungry()) {
const auto inBuf = extResampler.drain_subframe();
for (int i = 0; i < DSP_SUBFRAME_SIZE; i++) {
const auto oi = i * OutChannelCount;
OutInterleaveBuffer[oi] += static_cast<f32>(mixData[i * 2]) / 32767.0f;
OutInterleaveBuffer[oi + 1] += static_cast<f32>(mixData[i * 2 + 1]) / 32767.0f;
OutInterleaveBuffer[oi] += inBuf[i * kExtChannels];
OutInterleaveBuffer[oi + 1] += inBuf[i * kExtChannels + 1];
}
}
}
+12 -12
View File
@@ -393,9 +393,9 @@ static void FillDecodeBuf(JASDsp::TChannel& channel, ChannelAuxData& aux, int ne
static BiquadCoeffs RemapBiquad32to48(float b1, float b2, float a1, float a2) {
using cplx = std::complex<f64>;
constexpr f64 kOldRate = 32000.0;
constexpr f64 kNewRate = f64(SampleRate);
constexpr f64 kGamma = kOldRate / kNewRate;
constexpr auto kOldRate = 32000.0;
constexpr auto kNewRate = f64(SampleRate);
constexpr auto kGamma = kOldRate / kNewRate;
if (b1 == 0 && b2 == 0 && a1 == 0 && a2 == 0) {
return {0, 0, 0, 0};
@@ -416,12 +416,12 @@ static BiquadCoeffs RemapBiquad32to48(float b1, float b2, float a1, float a2) {
cplx p1, p2;
if (disc >= 0.0) {
f64 sq = std::sqrt(disc);
p1 = { (a1 + sq) * 0.5, 0.0 };
p2 = { (a1 - sq) * 0.5, 0.0 };
p1 = {(a1 + sq) * 0.5, 0.0};
p2 = {(a1 - sq) * 0.5, 0.0};
} else {
f64 sq = std::sqrt(-disc);
p1 = { a1 * 0.5, sq * 0.5 };
p2 = { a1 * 0.5, -sq * 0.5 };
p1 = {a1 * 0.5, sq * 0.5};
p2 = {a1 * 0.5, -sq * 0.5};
}
cplx p1n = mapPoint(p1);
cplx p2n = mapPoint(p2);
@@ -445,7 +445,7 @@ static BiquadCoeffs RemapBiquad32to48(float b1, float b2, float a1, float a2) {
f64 K = (hOldRef / shapeNewRef).real();
return { float(K), float(-K * z0n), float(a1n), float(a2n) };
return {float(K), float(-K * z0n), float(a1n), float(a2n)};
}
/**
@@ -504,10 +504,10 @@ static void RenderChannel(
std::span newCoefs{channel.iir_filter_params, 4};
if (!std::ranges::equal(newCoefs, aux.curBiquadCoefs)) {
aux.remappedBiquadCoefs = RemapBiquad32to48(
newCoefs[0] / 32768.0,
newCoefs[1] / 32768.0,
newCoefs[2] / 32768.0,
newCoefs[3] / 32768.0
newCoefs[0] / 32768.0f,
newCoefs[1] / 32768.0f,
newCoefs[2] / 32768.0f,
newCoefs[3] / 32768.0f
);
std::ranges::copy(newCoefs, aux.curBiquadCoefs.begin());
}