mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2026-01-30 11:04:35 +00:00
some openal work in audio3d
This commit is contained in:
parent
f503e5a287
commit
fe31cd5b19
@ -1,9 +1,12 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <SDL3/SDL_audio.h>
|
||||
#include <AL/al.h>
|
||||
#include <AL/alc.h>
|
||||
#include <magic_enum/magic_enum.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <alext.h>
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/libraries/audio/audioout.h"
|
||||
@ -17,7 +20,6 @@
|
||||
namespace Libraries::Audio3d {
|
||||
|
||||
static constexpr u32 AUDIO3D_SAMPLE_RATE = 48000;
|
||||
|
||||
static constexpr AudioOut::OrbisAudioOutParamFormat AUDIO3D_OUTPUT_FORMAT =
|
||||
AudioOut::OrbisAudioOutParamFormat::S16Stereo;
|
||||
static constexpr u32 AUDIO3D_OUTPUT_NUM_CHANNELS = 2;
|
||||
@ -30,6 +32,280 @@ static bool Audio3dHRTFActive() {
|
||||
return al.IsInitialized() && al.IsHRTFEnabled();
|
||||
}
|
||||
|
||||
// Convert PS4 3D coordinate system to OpenAL
|
||||
static void ConvertPositionToOpenAL(const float* ps4_pos, float* al_pos) {
|
||||
al_pos[0] = ps4_pos[0]; // X same
|
||||
al_pos[1] = ps4_pos[1]; // Y same
|
||||
al_pos[2] = -ps4_pos[2]; // Z inverted
|
||||
}
|
||||
|
||||
// Helper function to downmix 8-channel audio to stereo S16
|
||||
static std::vector<std::byte> Downmix8ChannelToStereoS16(const OrbisAudio3dPcm& pcm) {
|
||||
std::vector<std::byte> converted_data;
|
||||
|
||||
const bool is_float = (pcm.format == OrbisAudio3dFormat::ORBIS_AUDIO3D_FORMAT_FLOAT);
|
||||
const u32 input_channels = 8;
|
||||
|
||||
// Output will be stereo S16
|
||||
const size_t output_size = pcm.num_samples * 2 * sizeof(int16_t);
|
||||
converted_data.resize(output_size);
|
||||
|
||||
// Initialize with zeros
|
||||
std::memset(converted_data.data(), 0, output_size);
|
||||
|
||||
if (!pcm.sample_buffer || pcm.num_samples == 0) {
|
||||
LOG_WARNING(Lib_Audio3d, "Empty audio buffer for downmixing");
|
||||
return converted_data;
|
||||
}
|
||||
|
||||
int16_t* output = reinterpret_cast<int16_t*>(converted_data.data());
|
||||
|
||||
if (is_float) {
|
||||
const float* input = static_cast<const float*>(pcm.sample_buffer);
|
||||
|
||||
for (u32 i = 0; i < pcm.num_samples; i++) {
|
||||
size_t base_idx = i * input_channels;
|
||||
|
||||
float fl = input[base_idx + 0];
|
||||
float fr = input[base_idx + 1];
|
||||
float fc = input[base_idx + 2];
|
||||
float lfe = input[base_idx + 3];
|
||||
float bl = input[base_idx + 4];
|
||||
float br = input[base_idx + 5];
|
||||
float sl = input[base_idx + 6];
|
||||
float sr = input[base_idx + 7];
|
||||
|
||||
// 7.1 to stereo downmix with volume reduction
|
||||
float left = fl * 0.7f + fc * 0.5f + sl * 0.5f + bl * 0.3f + lfe * 0.1f;
|
||||
float right = fr * 0.7f + fc * 0.5f + sr * 0.5f + br * 0.3f + lfe * 0.1f;
|
||||
|
||||
// Clamp and convert to int16
|
||||
left = std::clamp(left, -1.0f, 1.0f);
|
||||
right = std::clamp(right, -1.0f, 1.0f);
|
||||
|
||||
output[i * 2 + 0] = static_cast<int16_t>(left * 32767.0f);
|
||||
output[i * 2 + 1] = static_cast<int16_t>(right * 32767.0f);
|
||||
}
|
||||
} else {
|
||||
const int16_t* input = static_cast<const int16_t*>(pcm.sample_buffer);
|
||||
|
||||
for (u32 i = 0; i < pcm.num_samples; i++) {
|
||||
size_t base_idx = i * input_channels;
|
||||
|
||||
// Convert to float for processing
|
||||
float fl = input[base_idx + 0] / 32768.0f;
|
||||
float fr = input[base_idx + 1] / 32768.0f;
|
||||
float fc = input[base_idx + 2] / 32768.0f;
|
||||
float lfe = input[base_idx + 3] / 32768.0f;
|
||||
float bl = input[base_idx + 4] / 32768.0f;
|
||||
float br = input[base_idx + 5] / 32768.0f;
|
||||
float sl = input[base_idx + 6] / 32768.0f;
|
||||
float sr = input[base_idx + 7] / 32768.0f;
|
||||
|
||||
// Downmix with volume reduction
|
||||
float left = fl * 0.7f + fc * 0.5f + sl * 0.5f + bl * 0.3f + lfe * 0.1f;
|
||||
float right = fr * 0.7f + fc * 0.5f + sr * 0.5f + br * 0.3f + lfe * 0.1f;
|
||||
|
||||
// Clamp and convert back to int16
|
||||
left = std::clamp(left, -1.0f, 1.0f);
|
||||
right = std::clamp(right, -1.0f, 1.0f);
|
||||
|
||||
output[i * 2 + 0] = static_cast<int16_t>(left * 32767.0f);
|
||||
output[i * 2 + 1] = static_cast<int16_t>(right * 32767.0f);
|
||||
}
|
||||
}
|
||||
|
||||
LOG_DEBUG(Lib_Audio3d, "Downmixed 8-channel to stereo S16: {} samples", pcm.num_samples);
|
||||
return converted_data;
|
||||
}
|
||||
|
||||
// Convert any input to stereo S16 format
|
||||
static std::vector<std::byte> ConvertToStereoS16(const OrbisAudio3dPcm& pcm, u32 num_channels) {
|
||||
std::vector<std::byte> converted_data;
|
||||
|
||||
if (!pcm.sample_buffer || pcm.num_samples == 0) {
|
||||
LOG_ERROR(Lib_Audio3d, "Empty or invalid PCM data");
|
||||
return converted_data;
|
||||
}
|
||||
|
||||
// We only support S16 format
|
||||
if (pcm.format != OrbisAudio3dFormat::ORBIS_AUDIO3D_FORMAT_S16) {
|
||||
LOG_ERROR(Lib_Audio3d, "Only S16 format supported, got: {}",
|
||||
magic_enum::enum_name(pcm.format));
|
||||
return converted_data;
|
||||
}
|
||||
|
||||
if (num_channels == 2) {
|
||||
// Direct copy for stereo S16
|
||||
const size_t total_size = pcm.num_samples * 2 * sizeof(int16_t);
|
||||
converted_data.resize(total_size);
|
||||
std::memcpy(converted_data.data(), pcm.sample_buffer, total_size);
|
||||
} else if (num_channels == 8) {
|
||||
// Downmix 8-channel to stereo S16
|
||||
converted_data = Downmix8ChannelToStereoS16(pcm);
|
||||
} else {
|
||||
LOG_ERROR(Lib_Audio3d, "Unsupported channel count: {} (only 2 or 8 supported)",
|
||||
num_channels);
|
||||
return converted_data;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Lib_Audio3d, "Converted to stereo S16: {} bytes, {} samples", converted_data.size(),
|
||||
pcm.num_samples);
|
||||
|
||||
return converted_data;
|
||||
}
|
||||
|
||||
// Queue audio data (always converted to stereo S16)
|
||||
static s32 PortQueueAudio(Port& port, const OrbisAudio3dPcm& pcm, const u32 num_channels) {
|
||||
LOG_DEBUG(Lib_Audio3d, "PortQueueAudio: channels={}, samples={}, format={}", num_channels,
|
||||
pcm.num_samples, magic_enum::enum_name(pcm.format));
|
||||
|
||||
if (!pcm.sample_buffer || pcm.num_samples == 0) {
|
||||
LOG_ERROR(Lib_Audio3d, "Invalid PCM data for queue");
|
||||
return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
// Convert to stereo S16
|
||||
auto converted_data = ConvertToStereoS16(pcm, num_channels);
|
||||
|
||||
if (converted_data.empty()) {
|
||||
LOG_ERROR(Lib_Audio3d, "Failed to convert audio to stereo S16");
|
||||
return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
// Verify size matches expected stereo S16
|
||||
const size_t expected_size = pcm.num_samples * 2 * sizeof(int16_t);
|
||||
if (converted_data.size() != expected_size) {
|
||||
LOG_ERROR(Lib_Audio3d, "Converted size mismatch: got {} bytes, expected {} bytes",
|
||||
converted_data.size(), expected_size);
|
||||
return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
AudioData audio_data{
|
||||
.sample_buffer = std::move(converted_data),
|
||||
.num_samples = pcm.num_samples,
|
||||
};
|
||||
|
||||
std::lock_guard lock(port.lock);
|
||||
port.queue.emplace_back(std::move(audio_data));
|
||||
|
||||
LOG_DEBUG(Lib_Audio3d, "Queued stereo S16 audio: {} bytes, {} samples",
|
||||
audio_data.sample_buffer.size(), audio_data.num_samples);
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
// Clean up processed OpenAL buffers
|
||||
static void CleanupProcessedBuffers(Audio3dObject& obj) {
|
||||
if (obj.al_source.source_id == 0)
|
||||
return;
|
||||
|
||||
Libraries::AudioOut::OpenALManager::Instance().MakeContextCurrent();
|
||||
|
||||
ALint processed = 0;
|
||||
alGetSourcei(obj.al_source.source_id, AL_BUFFERS_PROCESSED, &processed);
|
||||
|
||||
if (processed > 0) {
|
||||
std::vector<ALuint> buffers(processed);
|
||||
alSourceUnqueueBuffers(obj.al_source.source_id, processed, buffers.data());
|
||||
alDeleteBuffers(processed, buffers.data());
|
||||
}
|
||||
}
|
||||
|
||||
// Stream object audio (always stereo S16)
|
||||
static void StreamObjectAudio(Audio3dObject& obj) {
|
||||
if (!obj.in_use || !obj.active)
|
||||
return;
|
||||
|
||||
// Clean up processed buffers first
|
||||
CleanupProcessedBuffers(obj);
|
||||
|
||||
// Ensure OpenAL source exists
|
||||
if (obj.al_source.source_id == 0) {
|
||||
alGenSources(1, &obj.al_source.source_id);
|
||||
if (obj.al_source.source_id == 0) {
|
||||
LOG_ERROR(Lib_Audio3d, "Failed to generate OpenAL source");
|
||||
return;
|
||||
}
|
||||
|
||||
// Set default OpenAL source properties
|
||||
Libraries::AudioOut::OpenALManager::Instance().MakeContextCurrent();
|
||||
alSourcef(obj.al_source.source_id, AL_PITCH, obj.al_source.pitch);
|
||||
alSourcef(obj.al_source.source_id, AL_GAIN, obj.al_source.gain);
|
||||
alSource3f(obj.al_source.source_id, AL_POSITION, obj.al_source.position[0],
|
||||
obj.al_source.position[1], obj.al_source.position[2]);
|
||||
alSource3f(obj.al_source.source_id, AL_VELOCITY, obj.al_source.velocity[0],
|
||||
obj.al_source.velocity[1], obj.al_source.velocity[2]);
|
||||
alSourcef(obj.al_source.source_id, AL_REFERENCE_DISTANCE, obj.al_source.reference_distance);
|
||||
alSourcef(obj.al_source.source_id, AL_MAX_DISTANCE, obj.al_source.max_distance);
|
||||
alSourcef(obj.al_source.source_id, AL_ROLLOFF_FACTOR, obj.al_source.rolloff_factor);
|
||||
alSourcei(obj.al_source.source_id, AL_LOOPING, obj.al_source.looping ? AL_TRUE : AL_FALSE);
|
||||
alSourcei(obj.al_source.source_id, AL_SOURCE_RELATIVE, AL_FALSE);
|
||||
}
|
||||
|
||||
// Check if there is audio to stream
|
||||
if (obj.pcm_queue.empty())
|
||||
return;
|
||||
|
||||
auto& data = obj.pcm_queue.front();
|
||||
|
||||
if (data.sample_buffer.empty()) {
|
||||
LOG_WARNING(Lib_Audio3d, "Empty audio buffer in queue");
|
||||
obj.pcm_queue.pop_front();
|
||||
return;
|
||||
}
|
||||
|
||||
// Generate OpenAL buffer
|
||||
ALuint buffer = 0;
|
||||
alGenBuffers(1, &buffer);
|
||||
|
||||
if (buffer == 0) {
|
||||
LOG_ERROR(Lib_Audio3d, "Failed to generate OpenAL buffer");
|
||||
obj.pcm_queue.pop_front();
|
||||
return;
|
||||
}
|
||||
|
||||
// Always use stereo S16 format (AL_FORMAT_STEREO16 = 0x1103)
|
||||
ALenum al_format = AL_FORMAT_STEREO16;
|
||||
|
||||
// Upload data to buffer
|
||||
alBufferData(buffer, al_format, data.sample_buffer.data(),
|
||||
static_cast<ALsizei>(data.sample_buffer.size()), AUDIO3D_SAMPLE_RATE);
|
||||
|
||||
ALenum error = alGetError();
|
||||
if (error != AL_NO_ERROR) {
|
||||
LOG_ERROR(Lib_Audio3d, "Failed to upload buffer data: {}", error);
|
||||
alDeleteBuffers(1, &buffer);
|
||||
obj.pcm_queue.pop_front();
|
||||
return;
|
||||
}
|
||||
|
||||
// Queue buffer to source
|
||||
alSourceQueueBuffers(obj.al_source.source_id, 1, &buffer);
|
||||
|
||||
error = alGetError();
|
||||
if (error != AL_NO_ERROR) {
|
||||
LOG_ERROR(Lib_Audio3d, "Failed to queue buffer: {}", error);
|
||||
alDeleteBuffers(1, &buffer);
|
||||
obj.pcm_queue.pop_front();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if source is playing
|
||||
ALint state;
|
||||
alGetSourcei(obj.al_source.source_id, AL_SOURCE_STATE, &state);
|
||||
if (state != AL_PLAYING) {
|
||||
alSourcePlay(obj.al_source.source_id);
|
||||
error = alGetError();
|
||||
if (error != AL_NO_ERROR) {
|
||||
LOG_ERROR(Lib_Audio3d, "Failed to play source: {}", error);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove from queue
|
||||
obj.pcm_queue.pop_front();
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceAudio3dAudioOutOutput(const s32 handle, void* ptr) {
|
||||
LOG_DEBUG(Lib_Audio3d, "called, handle = {}, ptr = {}", handle, ptr);
|
||||
|
||||
@ -58,38 +334,6 @@ s32 PS4_SYSV_ABI sceAudio3dAudioOutOutputs(AudioOut::OrbisAudioOutOutputParam* p
|
||||
return AudioOut::sceAudioOutOutputs(param, num);
|
||||
}
|
||||
|
||||
static s32 PortQueueAudio(Port& port, const OrbisAudio3dPcm& pcm, const u32 num_channels) {
|
||||
// Audio3d output is configured for stereo signed 16-bit PCM. Convert the data to match.
|
||||
const SDL_AudioSpec src_spec = {
|
||||
.format = pcm.format == OrbisAudio3dFormat::ORBIS_AUDIO3D_FORMAT_S16 ? SDL_AUDIO_S16LE
|
||||
: SDL_AUDIO_F32LE,
|
||||
.channels = static_cast<int>(num_channels),
|
||||
.freq = AUDIO3D_SAMPLE_RATE,
|
||||
};
|
||||
constexpr SDL_AudioSpec dst_spec = {
|
||||
.format = SDL_AUDIO_S16LE,
|
||||
.channels = AUDIO3D_OUTPUT_NUM_CHANNELS,
|
||||
.freq = AUDIO3D_SAMPLE_RATE,
|
||||
};
|
||||
const auto src_size = pcm.num_samples *
|
||||
(pcm.format == OrbisAudio3dFormat::ORBIS_AUDIO3D_FORMAT_S16 ? 2 : 4) *
|
||||
num_channels;
|
||||
|
||||
u8* dst_data;
|
||||
int dst_len;
|
||||
if (!SDL_ConvertAudioSamples(&src_spec, static_cast<u8*>(pcm.sample_buffer),
|
||||
static_cast<int>(src_size), &dst_spec, &dst_data, &dst_len)) {
|
||||
LOG_ERROR(Lib_Audio3d, "SDL_ConvertAudioSamples failed: {}", SDL_GetError());
|
||||
return ORBIS_AUDIO3D_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
port.queue.emplace_back(AudioData{
|
||||
.sample_buffer = dst_data,
|
||||
.num_samples = pcm.num_samples,
|
||||
});
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceAudio3dBedWrite(const OrbisAudio3dPortId port_id, const u32 num_channels,
|
||||
const OrbisAudio3dFormat format, void* buffer,
|
||||
const u32 num_samples) {
|
||||
@ -114,18 +358,14 @@ s32 PS4_SYSV_ABI sceAudio3dBedWrite2(const OrbisAudio3dPortId port_id, const u32
|
||||
return ORBIS_AUDIO3D_ERROR_INVALID_PORT;
|
||||
}
|
||||
|
||||
if (output_route > OrbisAudio3dOutputRoute::ORBIS_AUDIO3D_OUTPUT_BOTH) {
|
||||
LOG_ERROR(Lib_Audio3d, "output_route > ORBIS_AUDIO3D_OUTPUT_BOTH");
|
||||
return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (format > OrbisAudio3dFormat::ORBIS_AUDIO3D_FORMAT_FLOAT) {
|
||||
LOG_ERROR(Lib_Audio3d, "format > ORBIS_AUDIO3D_FORMAT_FLOAT");
|
||||
// We only support stereo (2 channels) or 8-channel (for downmixing) S16 format
|
||||
if (format != OrbisAudio3dFormat::ORBIS_AUDIO3D_FORMAT_S16) {
|
||||
LOG_ERROR(Lib_Audio3d, "Only S16 format supported, got: {}", magic_enum::enum_name(format));
|
||||
return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (num_channels != 2 && num_channels != 8) {
|
||||
LOG_ERROR(Lib_Audio3d, "num_channels != 2 && num_channels != 8");
|
||||
LOG_ERROR(Lib_Audio3d, "Only 2 or 8 channels supported, got: {}", num_channels);
|
||||
return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
@ -134,16 +374,10 @@ s32 PS4_SYSV_ABI sceAudio3dBedWrite2(const OrbisAudio3dPortId port_id, const u32
|
||||
return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (format == OrbisAudio3dFormat::ORBIS_AUDIO3D_FORMAT_FLOAT) {
|
||||
if ((reinterpret_cast<uintptr_t>(buffer) & 3) != 0) {
|
||||
LOG_ERROR(Lib_Audio3d, "buffer & 3 != 0");
|
||||
return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
} else if (format == OrbisAudio3dFormat::ORBIS_AUDIO3D_FORMAT_S16) {
|
||||
if ((reinterpret_cast<uintptr_t>(buffer) & 1) != 0) {
|
||||
LOG_ERROR(Lib_Audio3d, "buffer & 1 != 0");
|
||||
return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
// Verify buffer alignment for S16 (2-byte alignment)
|
||||
if ((reinterpret_cast<uintptr_t>(buffer) & 1) != 0) {
|
||||
LOG_ERROR(Lib_Audio3d, "s16 buffer & 1 != 0 (not 2-byte aligned)");
|
||||
return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
return PortQueueAudio(state->ports[port_id],
|
||||
@ -175,16 +409,64 @@ s32 PS4_SYSV_ABI sceAudio3dObjectSetAttributes(const OrbisAudio3dPortId port_id,
|
||||
|
||||
switch (attribute.attribute_id) {
|
||||
case OrbisAudio3dAttributeId::ORBIS_AUDIO3D_OBJECT_ATTRIBUTE_PCM: {
|
||||
const auto pcm = static_cast<OrbisAudio3dPcm*>(attribute.value);
|
||||
// Object audio has 1 channel.
|
||||
if (const auto ret = PortQueueAudio(port, *pcm, 1); ret != ORBIS_OK) {
|
||||
return ret;
|
||||
if (object_id >= port.objects.size())
|
||||
return ORBIS_AUDIO3D_ERROR_INVALID_OBJECT;
|
||||
|
||||
auto& obj = port.objects[object_id];
|
||||
const auto* pcm = static_cast<OrbisAudio3dPcm*>(attribute.value);
|
||||
|
||||
// Convert to stereo S16 (objects are mono, but we'll handle as stereo)
|
||||
auto converted_data = ConvertToStereoS16(*pcm, 1);
|
||||
|
||||
if (converted_data.empty()) {
|
||||
return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
AudioData audio_data{
|
||||
.sample_buffer = std::move(converted_data),
|
||||
.num_samples = pcm->num_samples,
|
||||
};
|
||||
|
||||
obj.pcm_queue.push_back(std::move(audio_data));
|
||||
break;
|
||||
}
|
||||
case OrbisAudio3dAttributeId::ORBIS_AUDIO3D_OBJECT_ATTRIBUTE_POSITION: {
|
||||
if (object_id >= port.objects.size()) {
|
||||
return ORBIS_AUDIO3D_ERROR_INVALID_OBJECT;
|
||||
}
|
||||
|
||||
auto& obj = port.objects[object_id];
|
||||
if (attribute.value_size >= sizeof(float) * 3) {
|
||||
const float* position = static_cast<const float*>(attribute.value);
|
||||
ConvertPositionToOpenAL(position, obj.al_source.position);
|
||||
|
||||
if (obj.al_source.source_id != 0) {
|
||||
Libraries::AudioOut::OpenALManager::Instance().MakeContextCurrent();
|
||||
alSource3f(obj.al_source.source_id, AL_POSITION, obj.al_source.position[0],
|
||||
obj.al_source.position[1], obj.al_source.position[2]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OrbisAudio3dAttributeId::ORBIS_AUDIO3D_OBJECT_ATTRIBUTE_GAIN: {
|
||||
if (object_id >= port.objects.size()) {
|
||||
return ORBIS_AUDIO3D_ERROR_INVALID_OBJECT;
|
||||
}
|
||||
|
||||
auto& obj = port.objects[object_id];
|
||||
if (attribute.value_size >= sizeof(float)) {
|
||||
obj.al_source.gain = *static_cast<const float*>(attribute.value);
|
||||
|
||||
if (obj.al_source.source_id != 0) {
|
||||
Libraries::AudioOut::OpenALManager::Instance().MakeContextCurrent();
|
||||
alSourcef(obj.al_source.source_id, AL_GAIN, obj.al_source.gain);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOG_ERROR(Lib_Audio3d, "Unsupported attribute ID: {:#x}",
|
||||
static_cast<u32>(attribute.attribute_id));
|
||||
LOG_INFO(Lib_Audio3d, "Processing attribute ID: {:#x}",
|
||||
static_cast<u32>(attribute.attribute_id));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -193,32 +475,23 @@ s32 PS4_SYSV_ABI sceAudio3dObjectSetAttributes(const OrbisAudio3dPortId port_id,
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceAudio3dPortAdvance(const OrbisAudio3dPortId port_id) {
|
||||
LOG_DEBUG(Lib_Audio3d, "called, port_id = {}", port_id);
|
||||
LOG_DEBUG(Lib_Audio3d, "sceAudio3dPortAdvance(port_id={})", port_id);
|
||||
|
||||
if (!state->ports.contains(port_id)) {
|
||||
LOG_ERROR(Lib_Audio3d, "!state->ports.contains(port_id)");
|
||||
auto it = state->ports.find(port_id);
|
||||
if (it == state->ports.end())
|
||||
return ORBIS_AUDIO3D_ERROR_INVALID_PORT;
|
||||
}
|
||||
|
||||
if (state->ports[port_id].parameters.buffer_mode ==
|
||||
OrbisAudio3dBufferMode::ORBIS_AUDIO3D_BUFFER_NO_ADVANCE) {
|
||||
LOG_ERROR(Lib_Audio3d, "port doesn't have advance capability");
|
||||
Port& port = it->second;
|
||||
|
||||
if (port.parameters.buffer_mode == OrbisAudio3dBufferMode::ORBIS_AUDIO3D_BUFFER_NO_ADVANCE) {
|
||||
return ORBIS_AUDIO3D_ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
auto& port = state->ports[port_id];
|
||||
if (port.current_buffer.has_value()) {
|
||||
// Free existing buffer before replacing.
|
||||
SDL_free(port.current_buffer->sample_buffer);
|
||||
}
|
||||
|
||||
if (!port.queue.empty()) {
|
||||
port.current_buffer = port.queue.front();
|
||||
port.current_buffer = std::move(port.queue.front());
|
||||
port.queue.pop_front();
|
||||
} else {
|
||||
// Nothing to advance to.
|
||||
LOG_DEBUG(Lib_Audio3d, "Port advance with no buffer queued");
|
||||
port.current_buffer = std::nullopt;
|
||||
port.current_buffer.reset();
|
||||
}
|
||||
|
||||
return ORBIS_OK;
|
||||
@ -226,28 +499,22 @@ s32 PS4_SYSV_ABI sceAudio3dPortAdvance(const OrbisAudio3dPortId port_id) {
|
||||
|
||||
s32 PS4_SYSV_ABI sceAudio3dPortGetQueueLevel(const OrbisAudio3dPortId port_id, u32* queue_level,
|
||||
u32* queue_available) {
|
||||
LOG_DEBUG(Lib_Audio3d, "called, port_id = {}, queue_level = {}, queue_available = {}", port_id,
|
||||
static_cast<void*>(queue_level), static_cast<void*>(queue_available));
|
||||
|
||||
if (!state->ports.contains(port_id)) {
|
||||
LOG_ERROR(Lib_Audio3d, "!state->ports.contains(port_id)");
|
||||
auto it = state->ports.find(port_id);
|
||||
if (it == state->ports.end())
|
||||
return ORBIS_AUDIO3D_ERROR_INVALID_PORT;
|
||||
}
|
||||
|
||||
if (!queue_level && !queue_available) {
|
||||
if (!queue_level && !queue_available)
|
||||
return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
auto& port = state->ports[port_id];
|
||||
const size_t size = port.queue.size();
|
||||
const Port& port = it->second;
|
||||
const u32 depth = port.parameters.queue_depth;
|
||||
const u32 level = static_cast<u32>(port.queue.size());
|
||||
|
||||
if (queue_level) {
|
||||
*queue_level = size;
|
||||
}
|
||||
if (queue_level)
|
||||
*queue_level = level;
|
||||
|
||||
if (queue_available) {
|
||||
*queue_available = port.parameters.queue_depth - size;
|
||||
}
|
||||
if (queue_available)
|
||||
*queue_available = (level >= depth) ? 0 : (depth - level);
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
@ -262,7 +529,9 @@ s32 PS4_SYSV_ABI sceAudio3dPortPush(const OrbisAudio3dPortId port_id,
|
||||
return ORBIS_AUDIO3D_ERROR_INVALID_PORT;
|
||||
}
|
||||
|
||||
const auto& port = state->ports[port_id];
|
||||
auto& port = state->ports[port_id];
|
||||
std::lock_guard lock(port.lock);
|
||||
|
||||
if (port.parameters.buffer_mode !=
|
||||
OrbisAudio3dBufferMode::ORBIS_AUDIO3D_BUFFER_ADVANCE_AND_PUSH) {
|
||||
LOG_ERROR(Lib_Audio3d, "port doesn't have push capability");
|
||||
@ -270,14 +539,37 @@ s32 PS4_SYSV_ABI sceAudio3dPortPush(const OrbisAudio3dPortId port_id,
|
||||
}
|
||||
|
||||
if (!port.current_buffer.has_value()) {
|
||||
// Nothing to push.
|
||||
LOG_DEBUG(Lib_Audio3d, "Port push with no buffer ready");
|
||||
return ORBIS_OK;
|
||||
// Generate silent audio if nothing to push
|
||||
LOG_DEBUG(Lib_Audio3d, "Port push with no buffer ready - generating silence");
|
||||
|
||||
// Create silent buffer (stereo S16, granularity samples)
|
||||
const size_t silent_size = port.parameters.granularity * 2 * sizeof(int16_t);
|
||||
std::vector<std::byte> silent_buffer(silent_size, std::byte{0});
|
||||
|
||||
return AudioOut::sceAudioOutOutput(state->audio_out_handle, silent_buffer.data());
|
||||
}
|
||||
|
||||
// TODO: Implement asynchronous blocking mode.
|
||||
const auto& [sample_buffer, num_samples] = port.current_buffer.value();
|
||||
return AudioOut::sceAudioOutOutput(state->audio_out_handle, sample_buffer);
|
||||
auto& audio_data = port.current_buffer.value();
|
||||
|
||||
// Stream object audio
|
||||
for (auto& obj : port.objects) {
|
||||
StreamObjectAudio(obj);
|
||||
}
|
||||
|
||||
// Output bed audio
|
||||
if (!audio_data.sample_buffer.empty()) {
|
||||
LOG_DEBUG(Lib_Audio3d, "Pushing bed audio: {} bytes", audio_data.sample_buffer.size());
|
||||
|
||||
return AudioOut::sceAudioOutOutput(
|
||||
state->audio_out_handle,
|
||||
const_cast<void*>(static_cast<const void*>(audio_data.sample_buffer.data())));
|
||||
} else {
|
||||
// Generate silent audio
|
||||
const size_t silent_size = port.parameters.granularity * 2 * sizeof(int16_t);
|
||||
std::vector<std::byte> silent_buffer(silent_size, std::byte{0});
|
||||
|
||||
return AudioOut::sceAudioOutOutput(state->audio_out_handle, silent_buffer.data());
|
||||
}
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceAudio3dPortSetAttribute(const OrbisAudio3dPortId port_id,
|
||||
@ -297,103 +589,128 @@ s32 PS4_SYSV_ABI sceAudio3dPortSetAttribute(const OrbisAudio3dPortId port_id,
|
||||
return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
// TODO
|
||||
auto& port = state->ports[port_id];
|
||||
|
||||
switch (attribute_id) {
|
||||
case OrbisAudio3dAttributeId::ORBIS_AUDIO3D_PORT_ATTRIBUTE_LATE_REVERB_LEVEL:
|
||||
LOG_INFO(Lib_Audio3d, "Setting late reverb level (not implemented)");
|
||||
break;
|
||||
|
||||
case OrbisAudio3dAttributeId::ORBIS_AUDIO3D_OBJECT_ATTRIBUTE_POSITION:
|
||||
// Update listener position
|
||||
if (attribute_size >= sizeof(float) * 3) {
|
||||
const float* position = static_cast<const float*>(attribute);
|
||||
ConvertPositionToOpenAL(position, port.listener_position);
|
||||
|
||||
if (Libraries::AudioOut::OpenALManager::Instance().IsInitialized()) {
|
||||
Libraries::AudioOut::OpenALManager::Instance().MakeContextCurrent();
|
||||
alListener3f(AL_POSITION, port.listener_position[0], port.listener_position[1],
|
||||
port.listener_position[2]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG_INFO(Lib_Audio3d, "Setting port attribute {:#x} (not fully implemented)",
|
||||
static_cast<u32>(attribute_id));
|
||||
break;
|
||||
}
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceAudio3dCreateSpeakerArray() {
|
||||
LOG_ERROR(Lib_Audio3d, "(STUBBED) called");
|
||||
LOG_INFO(Lib_Audio3d, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceAudio3dDeleteSpeakerArray() {
|
||||
LOG_ERROR(Lib_Audio3d, "(STUBBED) called");
|
||||
LOG_INFO(Lib_Audio3d, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceAudio3dGetSpeakerArrayMemorySize() {
|
||||
LOG_ERROR(Lib_Audio3d, "(STUBBED) called");
|
||||
LOG_INFO(Lib_Audio3d, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceAudio3dGetSpeakerArrayMixCoefficients() {
|
||||
LOG_ERROR(Lib_Audio3d, "(STUBBED) called");
|
||||
LOG_INFO(Lib_Audio3d, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceAudio3dGetSpeakerArrayMixCoefficients2() {
|
||||
LOG_ERROR(Lib_Audio3d, "(STUBBED) called");
|
||||
LOG_INFO(Lib_Audio3d, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceAudio3dPortClose() {
|
||||
LOG_ERROR(Lib_Audio3d, "(STUBBED) called");
|
||||
LOG_INFO(Lib_Audio3d, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceAudio3dPortCreate() {
|
||||
LOG_ERROR(Lib_Audio3d, "(STUBBED) called");
|
||||
LOG_INFO(Lib_Audio3d, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceAudio3dPortDestroy() {
|
||||
LOG_ERROR(Lib_Audio3d, "(STUBBED) called");
|
||||
LOG_INFO(Lib_Audio3d, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceAudio3dPortFlush() {
|
||||
LOG_ERROR(Lib_Audio3d, "(STUBBED) called");
|
||||
LOG_INFO(Lib_Audio3d, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceAudio3dPortFreeState() {
|
||||
LOG_ERROR(Lib_Audio3d, "(STUBBED) called");
|
||||
LOG_INFO(Lib_Audio3d, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceAudio3dPortGetList() {
|
||||
LOG_ERROR(Lib_Audio3d, "(STUBBED) called");
|
||||
LOG_INFO(Lib_Audio3d, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceAudio3dPortGetParameters() {
|
||||
LOG_ERROR(Lib_Audio3d, "(STUBBED) called");
|
||||
LOG_INFO(Lib_Audio3d, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceAudio3dPortGetState() {
|
||||
LOG_ERROR(Lib_Audio3d, "(STUBBED) called");
|
||||
LOG_INFO(Lib_Audio3d, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceAudio3dPortGetStatus() {
|
||||
LOG_ERROR(Lib_Audio3d, "(STUBBED) called");
|
||||
LOG_INFO(Lib_Audio3d, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceAudio3dPortQueryDebug() {
|
||||
LOG_ERROR(Lib_Audio3d, "(STUBBED) called");
|
||||
LOG_INFO(Lib_Audio3d, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceAudio3dReportRegisterHandler() {
|
||||
LOG_ERROR(Lib_Audio3d, "(STUBBED) called");
|
||||
LOG_INFO(Lib_Audio3d, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceAudio3dReportUnregisterHandler() {
|
||||
LOG_ERROR(Lib_Audio3d, "(STUBBED) called");
|
||||
LOG_INFO(Lib_Audio3d, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceAudio3dSetGpuRenderer() {
|
||||
LOG_ERROR(Lib_Audio3d, "(STUBBED) called");
|
||||
LOG_INFO(Lib_Audio3d, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceAudio3dStrError() {
|
||||
LOG_ERROR(Lib_Audio3d, "(STUBBED) called");
|
||||
LOG_INFO(Lib_Audio3d, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
@ -433,6 +750,24 @@ s32 PS4_SYSV_ABI sceAudio3dInitialize(const s64 reserved) {
|
||||
return init_ret;
|
||||
}
|
||||
|
||||
// Initialize OpenAL for 3D audio
|
||||
if (!Libraries::AudioOut::OpenALManager::Instance().Initialize(AUDIO3D_SAMPLE_RATE)) {
|
||||
LOG_ERROR(Lib_Audio3d, "Failed to initialize OpenAL for 3D audio");
|
||||
return ORBIS_AUDIO3D_ERROR_NOT_READY;
|
||||
}
|
||||
|
||||
// Enable HRTF if available
|
||||
if (Libraries::AudioOut::OpenALManager::Instance().HasHRTF()) {
|
||||
LOG_INFO(Lib_Audio3d, "HRTF is available");
|
||||
}
|
||||
|
||||
// Set default OpenAL listener parameters
|
||||
Libraries::AudioOut::OpenALManager::Instance().MakeContextCurrent();
|
||||
alListener3f(AL_POSITION, 0.0f, 0.0f, 0.0f);
|
||||
alListener3f(AL_VELOCITY, 0.0f, 0.0f, 0.0f);
|
||||
float listener_orientation[] = {0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f};
|
||||
alListenerfv(AL_ORIENTATION, listener_orientation);
|
||||
|
||||
AudioOut::OrbisAudioOutParamExtendedInformation ext_info{};
|
||||
ext_info.data_format.Assign(AUDIO3D_OUTPUT_FORMAT);
|
||||
state->audio_out_handle =
|
||||
@ -451,9 +786,32 @@ s32 PS4_SYSV_ABI sceAudio3dTerminate() {
|
||||
return ORBIS_AUDIO3D_ERROR_NOT_READY;
|
||||
}
|
||||
|
||||
// Clean up OpenAL resources for each port
|
||||
for (auto& [port_id, port] : state->ports) {
|
||||
std::lock_guard lock(port.lock);
|
||||
|
||||
// Clean up OpenAL sources for objects
|
||||
for (auto& obj : port.objects) {
|
||||
if (obj.al_source.source_id != 0) {
|
||||
Libraries::AudioOut::OpenALManager::Instance().MakeContextCurrent();
|
||||
if (obj.al_source.buffer_id != 0) {
|
||||
alDeleteBuffers(1, &obj.al_source.buffer_id);
|
||||
}
|
||||
alDeleteSources(1, &obj.al_source.source_id);
|
||||
obj.al_source.source_id = 0;
|
||||
obj.al_source.buffer_id = 0;
|
||||
}
|
||||
}
|
||||
port.objects.clear();
|
||||
port.queue.clear();
|
||||
port.current_buffer.reset();
|
||||
}
|
||||
state->ports.clear();
|
||||
|
||||
AudioOut::sceAudioOutOutput(state->audio_out_handle, nullptr);
|
||||
AudioOut::sceAudioOutClose(state->audio_out_handle);
|
||||
state.release();
|
||||
state.reset();
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
@ -613,7 +971,7 @@ s32 PS4_SYSV_ABI sceAudio3dAudioOutOpen(
|
||||
return ORBIS_AUDIO3D_ERROR_NOT_READY;
|
||||
}
|
||||
|
||||
return audio_out_handle; // TODO missing more stuff but let's keep it simple for now
|
||||
return audio_out_handle;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceAudio3dAudioOutClose(const s32 handle) {
|
||||
@ -643,6 +1001,36 @@ s32 PS4_SYSV_ABI sceAudio3dObjectReserve(const OrbisAudio3dPortId port_id,
|
||||
auto& obj = port.objects[i];
|
||||
if (!obj.in_use) {
|
||||
obj = {}; // reset
|
||||
|
||||
// Create OpenAL source for this object
|
||||
if (Libraries::AudioOut::OpenALManager::Instance().IsInitialized()) {
|
||||
Libraries::AudioOut::OpenALManager::Instance().MakeContextCurrent();
|
||||
|
||||
alGenSources(1, &obj.al_source.source_id);
|
||||
ALenum error = alGetError();
|
||||
if (error != AL_NO_ERROR) {
|
||||
LOG_ERROR(Lib_Audio3d, "Failed to generate OpenAL source: {}", error);
|
||||
return ORBIS_AUDIO3D_ERROR_OUT_OF_RESOURCES;
|
||||
}
|
||||
|
||||
// Set default OpenAL source properties for stereo S16
|
||||
alSourcef(obj.al_source.source_id, AL_PITCH, obj.al_source.pitch);
|
||||
alSourcef(obj.al_source.source_id, AL_GAIN, obj.al_source.gain);
|
||||
alSource3f(obj.al_source.source_id, AL_POSITION, obj.al_source.position[0],
|
||||
obj.al_source.position[1], obj.al_source.position[2]);
|
||||
alSource3f(obj.al_source.source_id, AL_VELOCITY, obj.al_source.velocity[0],
|
||||
obj.al_source.velocity[1], obj.al_source.velocity[2]);
|
||||
alSourcef(obj.al_source.source_id, AL_REFERENCE_DISTANCE,
|
||||
obj.al_source.reference_distance);
|
||||
alSourcef(obj.al_source.source_id, AL_MAX_DISTANCE, obj.al_source.max_distance);
|
||||
alSourcef(obj.al_source.source_id, AL_ROLLOFF_FACTOR, obj.al_source.rolloff_factor);
|
||||
alSourcei(obj.al_source.source_id, AL_LOOPING,
|
||||
obj.al_source.looping ? AL_TRUE : AL_FALSE);
|
||||
|
||||
// Enable 3D positioning
|
||||
alSourcei(obj.al_source.source_id, AL_SOURCE_RELATIVE, AL_FALSE);
|
||||
}
|
||||
|
||||
obj.in_use = true;
|
||||
obj.active = true;
|
||||
*object_id = i;
|
||||
@ -676,9 +1064,19 @@ s32 PS4_SYSV_ABI sceAudio3dObjectUnreserve(OrbisAudio3dPortId port_id,
|
||||
if (!obj.in_use)
|
||||
return ORBIS_AUDIO3D_ERROR_INVALID_OBJECT;
|
||||
|
||||
// Clean up OpenAL resources
|
||||
if (obj.al_source.source_id != 0) {
|
||||
Libraries::AudioOut::OpenALManager::Instance().MakeContextCurrent();
|
||||
if (obj.al_source.buffer_id != 0) {
|
||||
alDeleteBuffers(1, &obj.al_source.buffer_id);
|
||||
}
|
||||
alDeleteSources(1, &obj.al_source.source_id);
|
||||
}
|
||||
|
||||
// Mark free
|
||||
obj.in_use = false;
|
||||
obj.active = false;
|
||||
obj.al_source = {}; // Reset OpenAL source
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
@ -700,7 +1098,7 @@ s32 sceAudio3dPortGetAttributesSupported(OrbisAudio3dPortId portId, OrbisAudio3d
|
||||
|
||||
std::vector<OrbisAudio3dAttributeId> supported;
|
||||
|
||||
// Always supported
|
||||
// Always supported for S16 stereo
|
||||
supported.push_back(OrbisAudio3dAttributeId::ORBIS_AUDIO3D_OBJECT_ATTRIBUTE_PCM);
|
||||
supported.push_back(OrbisAudio3dAttributeId::ORBIS_AUDIO3D_OBJECT_ATTRIBUTE_GAIN);
|
||||
supported.push_back(OrbisAudio3dAttributeId::ORBIS_AUDIO3D_OBJECT_ATTRIBUTE_PRIORITY);
|
||||
@ -710,7 +1108,7 @@ s32 sceAudio3dPortGetAttributesSupported(OrbisAudio3dPortId portId, OrbisAudio3d
|
||||
supported.push_back(OrbisAudio3dAttributeId::ORBIS_AUDIO3D_OBJECT_ATTRIBUTE_RESTRICTED);
|
||||
|
||||
if (hrtf) {
|
||||
// Object-based renderer
|
||||
// Object-based renderer with HRTF
|
||||
supported.push_back(OrbisAudio3dAttributeId::ORBIS_AUDIO3D_OBJECT_ATTRIBUTE_POSITION);
|
||||
supported.push_back(OrbisAudio3dAttributeId::ORBIS_AUDIO3D_OBJECT_ATTRIBUTE_SPREAD);
|
||||
supported.push_back(OrbisAudio3dAttributeId::ORBIS_AUDIO3D_OBJECT_ATTRIBUTE_OUTPUT_ROUTE);
|
||||
@ -787,4 +1185,4 @@ void RegisterLib(Core::Loader::SymbolsResolver* sym) {
|
||||
LIB_FUNCTION("WW1TS2iz5yc", "libSceAudio3d", 1, "libSceAudio3d", sceAudio3dTerminate);
|
||||
};
|
||||
|
||||
} // namespace Libraries::Audio3d
|
||||
} // namespace Libraries::Audio3d
|
||||
@ -3,9 +3,14 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <deque>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
|
||||
#include <al.h>
|
||||
#include "common/types.h"
|
||||
#include "core/libraries/audio/audioout.h"
|
||||
|
||||
@ -87,22 +92,48 @@ struct OrbisAudio3dAttribute {
|
||||
u64 value_size;
|
||||
};
|
||||
|
||||
// OpenAL-specific structures for 3D audio
|
||||
struct OpenAL3dSource {
|
||||
ALuint source_id = 0;
|
||||
ALuint buffer_id = 0;
|
||||
bool active = false;
|
||||
float position[3] = {0.0f, 0.0f, 0.0f};
|
||||
float velocity[3] = {0.0f, 0.0f, 0.0f};
|
||||
float gain = 1.0f;
|
||||
float pitch = 1.0f;
|
||||
float reference_distance = 1.0f;
|
||||
float max_distance = 100.0f;
|
||||
float rolloff_factor = 1.0f;
|
||||
float cone_inner_angle = 360.0f;
|
||||
float cone_outer_angle = 360.0f;
|
||||
float cone_outer_gain = 0.0f;
|
||||
bool looping = false;
|
||||
};
|
||||
|
||||
// Simplified audio data - always stereo S16
|
||||
struct AudioData {
|
||||
u8* sample_buffer;
|
||||
u32 num_samples;
|
||||
std::vector<std::byte> sample_buffer; // Always stereo S16 format
|
||||
u32 num_samples; // Number of stereo samples
|
||||
};
|
||||
|
||||
struct Audio3dObject {
|
||||
bool in_use = false;
|
||||
bool active = false;
|
||||
OpenAL3dSource al_source;
|
||||
std::deque<AudioData> pcm_queue; // Stereo S16 audio data
|
||||
};
|
||||
|
||||
struct Port {
|
||||
OrbisAudio3dOpenParameters parameters{};
|
||||
std::deque<AudioData> queue; // Only stores PCM buffers for now
|
||||
std::deque<AudioData> queue; // Stereo S16 audio data
|
||||
std::optional<AudioData> current_buffer{};
|
||||
std::vector<Audio3dObject> objects;
|
||||
std::mutex lock;
|
||||
|
||||
// OpenAL listener for this port
|
||||
float listener_position[3] = {0.0f, 0.0f, 0.0f};
|
||||
float listener_velocity[3] = {0.0f, 0.0f, 0.0f};
|
||||
float listener_orientation[6] = {0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f};
|
||||
};
|
||||
|
||||
struct Audio3dState {
|
||||
@ -110,6 +141,7 @@ struct Audio3dState {
|
||||
s32 audio_out_handle;
|
||||
};
|
||||
|
||||
// Function declarations
|
||||
s32 PS4_SYSV_ABI sceAudio3dAudioOutClose(s32 handle);
|
||||
s32 PS4_SYSV_ABI sceAudio3dAudioOutOpen(OrbisAudio3dPortId port_id,
|
||||
Libraries::UserService::OrbisUserServiceUserId user_id,
|
||||
@ -165,4 +197,4 @@ s32 PS4_SYSV_ABI sceAudio3dStrError();
|
||||
s32 PS4_SYSV_ABI sceAudio3dTerminate();
|
||||
|
||||
void RegisterLib(Core::Loader::SymbolsResolver* sym);
|
||||
} // namespace Libraries::Audio3d
|
||||
} // namespace Libraries::Audio3d
|
||||
Loading…
Reference in New Issue
Block a user