mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-20 02:47:02 +02:00
audio: mix AudioOut2 objects and apply port gains
Co-authored-by: chenxiao07 <chenxiao07@gmail.com>
This commit is contained in:
@@ -7,6 +7,8 @@
|
||||
#include <array>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
@@ -22,6 +24,7 @@ struct Stream {
|
||||
SDL_AudioSpec spec;
|
||||
uint64_t frames = 0; // Millionths of a frame retain exact simulated consumption.
|
||||
uint64_t updated;
|
||||
std::vector<uint8_t> pcm;
|
||||
};
|
||||
|
||||
uint64_t now = 1000000;
|
||||
@@ -31,6 +34,7 @@ std::vector<std::unique_ptr<Stream>> streams;
|
||||
bool fail_open = false, fail_put = false, stalled = false;
|
||||
bool pad_connected = false, pad_bluetooth = false;
|
||||
uint64_t pad_queue_us = 0;
|
||||
const float* queued_pad_gains = nullptr;
|
||||
int clears = 0;
|
||||
|
||||
Stream& GetStream(SDL_AudioStream* stream) {
|
||||
@@ -61,7 +65,7 @@ SDL_AudioStream* OpenAudioDeviceStream(SDL_AudioDeviceID, const SDL_AudioSpec* s
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
streams.push_back(std::make_unique<Stream>(Stream {*spec, 0, now}));
|
||||
streams.push_back(std::make_unique<Stream>(Stream {*spec, 0, now, {}}));
|
||||
return reinterpret_cast<SDL_AudioStream*>(streams.back().get());
|
||||
}
|
||||
|
||||
@@ -80,12 +84,14 @@ int GetAudioStreamQueued(SDL_AudioStream* stream) {
|
||||
state.spec.channels);
|
||||
}
|
||||
|
||||
bool PutAudioStreamData(SDL_AudioStream* stream, const void*, int bytes) {
|
||||
bool PutAudioStreamData(SDL_AudioStream* stream, const void* data, int bytes) {
|
||||
now += processing;
|
||||
if (fail_put) {
|
||||
return false;
|
||||
}
|
||||
auto& state = GetStream(stream);
|
||||
const auto* pcm = static_cast<const uint8_t*>(data);
|
||||
state.pcm.assign(pcm, pcm + bytes);
|
||||
Drain(state);
|
||||
state.frames += static_cast<uint64_t>(bytes) * 1000000 /
|
||||
(SDL_AUDIO_BYTESIZE(state.spec.format) * state.spec.channels);
|
||||
@@ -148,7 +154,8 @@ void Close(Stream*) {}
|
||||
bool UsesBluetooth(const Stream* stream) {
|
||||
return stream != nullptr && pad_bluetooth;
|
||||
}
|
||||
uint64_t Queue(Stream* stream, int, const void*, uint32_t, uint32_t, bool, const int*, float) {
|
||||
uint64_t Queue(Stream* stream, int, const void*, uint32_t, uint32_t, bool, const int*, float, const float* gains) {
|
||||
queued_pad_gains = gains;
|
||||
return stream != nullptr ? pad_queue_us : 0;
|
||||
}
|
||||
} // namespace DualSenseHaptics
|
||||
@@ -181,6 +188,7 @@ struct Fixture {
|
||||
fail_open = fail_put = stalled = false;
|
||||
pad_connected = pad_bluetooth = false;
|
||||
pad_queue_us = 0;
|
||||
queued_pad_gains = nullptr;
|
||||
sleeps.clear();
|
||||
}
|
||||
|
||||
@@ -386,6 +394,68 @@ void TestZeroOutputFrequency() {
|
||||
"internal output open accepted zero frequency");
|
||||
Check(f.Open().ToInt() == 1, "rejected output port occupied a handle");
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void CheckOutputSamples(std::initializer_list<T> expected, const char* message) {
|
||||
const auto& pcm = streams.back()->pcm;
|
||||
Check(pcm.size() == expected.size() * sizeof(T) &&
|
||||
std::memcmp(pcm.data(), expected.begin(), pcm.size()) == 0, message);
|
||||
}
|
||||
|
||||
void TestChannelGains() {
|
||||
Fixture f;
|
||||
const std::array pcm {0.25f, -0.5f, 0.75f, -1.0f};
|
||||
const auto stereo = f.audio.AudioOutOpen(0, 2, 48000, Audio::Format::FloatStereo);
|
||||
std::array gains {2.0f, 0.5f};
|
||||
Audio::OutputParam output {stereo, pcm.data(), gains.data()};
|
||||
for (int push = 0; push < 2; push++) {
|
||||
f.audio.AudioOutOutputs(&output, 1, false);
|
||||
CheckOutputSamples({0.5f, -0.25f, 1.5f, -0.5f}, "float channel gains were not applied independently");
|
||||
}
|
||||
const int volume[] {16384, 32768};
|
||||
f.audio.AudioOutSetVolume(stereo, 3, volume);
|
||||
f.audio.AudioOutOutputs(&output, 1, false);
|
||||
CheckOutputSamples({0.25f, -0.25f, 0.75f, -0.5f}, "channel gain did not combine with port volume");
|
||||
gains = {0.0f, 0.0f};
|
||||
f.audio.AudioOutOutputs(&output, 1, false);
|
||||
CheckOutputSamples({0.0f, -0.0f, 0.0f, -0.0f}, "zero channel gains did not mute");
|
||||
output.gains = nullptr;
|
||||
f.audio.AudioOutOutputs(&output, 1, false);
|
||||
CheckOutputSamples({0.125f, -0.5f, 0.375f, -1.0f}, "channel scaling changed original float PCM");
|
||||
|
||||
const std::array<int16_t, 2> integer_pcm {20000, -20000};
|
||||
const auto integer = f.audio.AudioOutOpen(0, 1, 48000, Audio::Format::Signed16bitStereo);
|
||||
output = {integer, integer_pcm.data(), gains.data()};
|
||||
for (const auto gain: {2.0f, std::numeric_limits<float>::max()}) {
|
||||
gains = {gain, gain};
|
||||
f.audio.AudioOutOutputs(&output, 1, false);
|
||||
CheckOutputSamples<int16_t>({32767, -32768}, "integer gain overflowed instead of saturating");
|
||||
}
|
||||
gains = {0.5f, 0.25f};
|
||||
f.audio.AudioOutOutputs(&output, 1, false);
|
||||
CheckOutputSamples<int16_t>({10000, -5000}, "integer channel gains changed original PCM");
|
||||
|
||||
const std::array surround_pcm {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f};
|
||||
for (const auto format: {Audio::Format::Float8Ch, Audio::Format::Float12Ch}) {
|
||||
const auto surround = f.audio.AudioOutOpen(0, 1, 48000, format);
|
||||
output = {surround, surround_pcm.data(), surround_pcm.data()};
|
||||
f.audio.AudioOutOutputs(&output, 1, false);
|
||||
if (format == Audio::Format::Float8Ch) {
|
||||
CheckOutputSamples({1.0f, 4.0f, 9.0f, 16.0f, 49.0f, 64.0f, 25.0f, 36.0f},
|
||||
"channel reordering used destination channel gains");
|
||||
} else {
|
||||
CheckOutputSamples({82.0f, 104.0f, 9.0f, 16.0f, 170.0f, 208.0f, 25.0f, 36.0f},
|
||||
"height downmix lost per-channel gains");
|
||||
}
|
||||
}
|
||||
pad_connected = true;
|
||||
const auto speaker = f.Open(2, 4);
|
||||
gains = {0.75f, 0.25f};
|
||||
output = {speaker, pcm.data(), gains.data()};
|
||||
f.audio.AudioOutOutputs(&output, 1, false);
|
||||
Check(queued_pad_gains != nullptr && queued_pad_gains[0] == gains[0] && queued_pad_gains[1] == gains[1],
|
||||
"connected controller did not receive channel gains");
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int main() {
|
||||
@@ -399,6 +469,7 @@ int main() {
|
||||
TestControllerSpeakerPacing();
|
||||
TestInvalidBatchSize();
|
||||
TestZeroOutputFrequency();
|
||||
TestChannelGains();
|
||||
Check(streams.empty(), "output stream leaked");
|
||||
std::puts("AudioOutTimingTests: all cases passed");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user