mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-26 21:06:53 +01:00
audio: mix AudioOut2 objects and apply port gains
Co-authored-by: chenxiao07 <chenxiao07@gmail.com>
This commit is contained in:
+219
-100
@@ -11,6 +11,7 @@
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
@@ -170,12 +171,14 @@ static std::atomic_uint64_t g_audioout2_next_port {1};
|
||||
static AudioOut2UserHandle g_audioout2_next_user = 1;
|
||||
|
||||
struct AudioOut2ContextState {
|
||||
bool used = false;
|
||||
AudioOut2ContextHandle handle = 0;
|
||||
uint32_t queue_depth = 4;
|
||||
uint32_t queued = 0;
|
||||
uint32_t num_grains = 512;
|
||||
uint64_t last_update = 0;
|
||||
bool used = false;
|
||||
AudioOut2ContextHandle handle = 0;
|
||||
uint32_t queue_depth = 4;
|
||||
uint32_t queued = 0;
|
||||
uint32_t num_grains = 512;
|
||||
uint64_t last_update = 0;
|
||||
int object_audio_handle = 0;
|
||||
std::vector<float> object_mix;
|
||||
};
|
||||
|
||||
struct AudioOut2PortStateEntry {
|
||||
@@ -189,9 +192,11 @@ struct AudioOut2PortStateEntry {
|
||||
uint32_t samples_num = 512;
|
||||
AudioInternal::Format audio_format = AudioInternal::Format::Unknown;
|
||||
int audio_handle = 0;
|
||||
std::array<float, 16> gains {};
|
||||
uint32_t ambisonics = UINT32_MAX;
|
||||
bool attributes_locked = false;
|
||||
std::vector<uint8_t> pcm_data;
|
||||
};
|
||||
|
||||
struct AudioOut2SpeakerArrayState {
|
||||
bool used = false;
|
||||
uint32_t num_speakers = 0;
|
||||
@@ -207,9 +212,8 @@ struct AudioOut2LatencyState {
|
||||
uint32_t latency_us = 0;
|
||||
};
|
||||
|
||||
static Common::Mutex g_audioout2_context_mutex;
|
||||
static Common::Mutex g_audioout2_state_mutex;
|
||||
static std::array<AudioOut2ContextState, 16> g_audioout2_contexts;
|
||||
static Common::Mutex g_audioout2_port_mutex;
|
||||
static std::array<AudioOut2PortStateEntry, 256> g_audioout2_ports;
|
||||
static std::vector<AudioOut2UserHandle> g_audioout2_users;
|
||||
static Common::Mutex g_audioout2_speaker_array_mutex;
|
||||
@@ -223,7 +227,11 @@ static constexpr int AUDIO_OUT2_ERROR_INVALID_PARAM = -2144960511;
|
||||
static constexpr int AUDIO_OUT2_ERROR_BUSY = -2144960505; /* 0x80268007 */
|
||||
static constexpr int AUDIO_OUT2_ERROR_MASTERING_INVALID_API_PARAM = -2144959999; /* 0x80268201 */
|
||||
static constexpr int AUDIO_OUT2_ERROR_MASTERING_INVALID_STATES_ID = -2144959996; /* 0x80268204 */
|
||||
static constexpr int AUDIO_OUT2_ERROR_OUT_OF_RESOURCE = -2144960510; /* 0x80268002 */
|
||||
static constexpr int AUDIO_OUT2_ERROR_INVALID_PORT = -2144960503; /* 0x80268009 */
|
||||
static constexpr uint32_t AUDIO_OUT2_PORT_ATTRIBUTE_ID_PCM = 0;
|
||||
static constexpr uint32_t AUDIO_OUT2_PORT_ATTRIBUTE_ID_GAIN = 1;
|
||||
static constexpr uint32_t AUDIO_OUT2_PORT_ATTRIBUTE_ID_AMBISONICS = 8;
|
||||
static constexpr uint32_t AUDIO_OUT2_MASTERING_OUTPUT_RECORDING = 2;
|
||||
static constexpr uint32_t AUDIO_OUT2_MASTERING_STATES_ID_DEFAULT = 1;
|
||||
static constexpr uint32_t AUDIO_OUT2_MASTERING_STATES_ID_V2 = 2;
|
||||
@@ -350,37 +358,80 @@ static size_t audioout2_pcm_size(const AudioOut2PortStateEntry& state) {
|
||||
audioout2_data_format_channels(state.data_format) * bytes_per_sample;
|
||||
}
|
||||
|
||||
static bool audioout2_context_has_queueable_device(AudioOut2ContextHandle ctx) {
|
||||
Common::LockGuard lock(g_audioout2_port_mutex);
|
||||
static bool audioout2_context_has_queueable_device(const AudioOut2ContextState& context) {
|
||||
for (const auto& state: g_audioout2_ports) {
|
||||
if (state.used && state.context == ctx && state.audio_handle > 0 &&
|
||||
!state.pcm_data.empty() && AudioInternal::AudioOutHasDevice(state.audio_handle)) {
|
||||
return true;
|
||||
if (state.used && state.context == context.handle && !state.pcm_data.empty()) {
|
||||
const int handle = audioout2_port_type_is_object(state.port_type)
|
||||
? context.object_audio_handle
|
||||
: state.audio_handle;
|
||||
if (AudioInternal::AudioOutHasDevice(handle)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void audioout2_mix_object(AudioOut2ContextState& context,
|
||||
const AudioOut2PortStateEntry& object) {
|
||||
auto& mix = context.object_mix;
|
||||
if (mix.empty()) {
|
||||
mix.resize(static_cast<size_t>(context.num_grains) * 2, 0.0f);
|
||||
}
|
||||
|
||||
// Center ordinary objects. First-order stereo ambisonics uses left/right-facing
|
||||
// cardioids; FuMa W has a different normalization from ACN/SN3D W.
|
||||
constexpr float half_power = 0.70710678f;
|
||||
float left = half_power, right = half_power;
|
||||
switch (object.ambisonics) {
|
||||
case UINT32_MAX:
|
||||
case 64: break;
|
||||
case 0: left = right = 1.0f; break;
|
||||
case 2:
|
||||
case 65: right = -half_power; break;
|
||||
default: return;
|
||||
}
|
||||
left *= object.gains[0];
|
||||
right *= object.gains[0];
|
||||
for (uint32_t frame = 0; frame < context.num_grains; frame++) {
|
||||
const float sample =
|
||||
object.audio_format == AudioInternal::Format::FloatMono
|
||||
? reinterpret_cast<const float*>(object.pcm_data.data())[frame]
|
||||
: reinterpret_cast<const int16_t*>(object.pcm_data.data())[frame] / 32768.0f;
|
||||
mix[frame * 2] += sample * left;
|
||||
mix[frame * 2 + 1] += sample * right;
|
||||
}
|
||||
}
|
||||
|
||||
static void audioout2_queue_context_audio(AudioOut2ContextHandle ctx, bool blocking) {
|
||||
// The backend consumes the buffers synchronously, but can block while pacing SDL's queue.
|
||||
// Keep both PCM storage and port handles alive until it returns.
|
||||
Common::LockGuard lock(g_audioout2_state_mutex);
|
||||
auto* context = audioout2_find_context_locked(ctx);
|
||||
if (context == nullptr) {
|
||||
return;
|
||||
}
|
||||
context->object_mix.clear();
|
||||
std::vector<AudioInternal::OutputParam> params;
|
||||
params.reserve(AudioInternal::OUT_PORTS_MAX);
|
||||
|
||||
Common::LockGuard lock(g_audioout2_port_mutex);
|
||||
for (const auto& state: g_audioout2_ports) {
|
||||
if (state.used && state.context == ctx && state.audio_handle > 0 &&
|
||||
!state.pcm_data.empty() && params.size() < AudioInternal::OUT_PORTS_MAX) {
|
||||
params.push_back(AudioInternal::OutputParam {state.audio_handle, state.pcm_data.data()});
|
||||
if (!state.used || state.context != ctx || state.pcm_data.empty()) {
|
||||
continue;
|
||||
}
|
||||
if (audioout2_port_type_is_object(state.port_type)) {
|
||||
audioout2_mix_object(*context, state);
|
||||
} else if (state.audio_handle > 0 && params.size() < AudioInternal::OUT_PORTS_MAX) {
|
||||
params.push_back({state.audio_handle, state.pcm_data.data(), state.gains.data()});
|
||||
}
|
||||
}
|
||||
|
||||
if (!context->object_mix.empty() && params.size() < AudioInternal::OUT_PORTS_MAX) {
|
||||
params.push_back({context->object_audio_handle, context->object_mix.data()});
|
||||
}
|
||||
if (!params.empty()) {
|
||||
(void)AudioInternal::AudioOutOutputs(params.data(), static_cast<uint32_t>(params.size()),
|
||||
blocking);
|
||||
}
|
||||
}
|
||||
|
||||
static void audioout2_close_audio_handle(int audio_handle) {
|
||||
if (audio_handle > 0) {
|
||||
AudioInternal::AudioOutClose(audio_handle);
|
||||
@@ -431,52 +482,58 @@ int KYTY_SYSV_ABI AudioOut2ContextCreate(const AudioOut2ContextParam* params, vo
|
||||
EXIT_NOT_IMPLEMENTED(params == nullptr);
|
||||
EXIT_NOT_IMPLEMENTED(ctx == nullptr);
|
||||
|
||||
*ctx = g_audioout2_next_context.fetch_add(1, std::memory_order_relaxed);
|
||||
|
||||
g_audioout2_context_mutex.Lock();
|
||||
auto* state = audioout2_find_context_locked(0);
|
||||
if (state == nullptr) {
|
||||
for (auto& candidate: g_audioout2_contexts) {
|
||||
if (!candidate.used) {
|
||||
state = &candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
const auto num_grains = params->num_grains == 0 ? 512u : params->num_grains;
|
||||
const int object_audio_handle =
|
||||
params->max_object_ports == 0
|
||||
? 0
|
||||
: AudioInternal::AudioOutOpen(AUDIO_OUT_PORT_TYPE_MAIN, num_grains, 48000,
|
||||
AudioInternal::Format::FloatStereo);
|
||||
if (params->max_object_ports != 0 && object_audio_handle == 0) {
|
||||
return AUDIO_OUT2_ERROR_OUT_OF_RESOURCE;
|
||||
}
|
||||
EXIT_NOT_IMPLEMENTED(state == nullptr);
|
||||
*state = AudioOut2ContextState {};
|
||||
state->used = true;
|
||||
state->handle = *ctx;
|
||||
state->queue_depth = (params->queue_depth == 0 ? 4u : params->queue_depth);
|
||||
state->queued = 0;
|
||||
state->num_grains = (params->num_grains == 0 ? 512u : params->num_grains);
|
||||
state->last_update = LibKernel::KernelGetProcessTime();
|
||||
g_audioout2_context_mutex.Unlock();
|
||||
|
||||
g_audioout2_state_mutex.Lock();
|
||||
auto it = std::find_if(g_audioout2_contexts.begin(), g_audioout2_contexts.end(),
|
||||
[](const auto& state) { return !state.used; });
|
||||
if (it == g_audioout2_contexts.end()) {
|
||||
g_audioout2_state_mutex.Unlock();
|
||||
audioout2_close_audio_handle(object_audio_handle);
|
||||
return AUDIO_OUT2_ERROR_OUT_OF_RESOURCE;
|
||||
}
|
||||
*ctx = g_audioout2_next_context.fetch_add(1, std::memory_order_relaxed);
|
||||
auto* state = &*it;
|
||||
*state = AudioOut2ContextState {};
|
||||
state->used = true;
|
||||
state->handle = *ctx;
|
||||
state->queue_depth = params->queue_depth == 0 ? 4u : params->queue_depth;
|
||||
state->num_grains = num_grains;
|
||||
state->last_update = LibKernel::KernelGetProcessTime();
|
||||
state->object_audio_handle = object_audio_handle;
|
||||
g_audioout2_state_mutex.Unlock();
|
||||
|
||||
LOGF("\t buffer = 0x%016" PRIx64 "\n"
|
||||
"\t buffer_size = 0x%016" PRIx64 "\n"
|
||||
"\t ctx = 0x%016" PRIx64 "\n"
|
||||
"\t queue_depth = %" PRIu32 ", num_grains = %" PRIu32 "\n",
|
||||
reinterpret_cast<uint64_t>(buffer), static_cast<uint64_t>(buffer_size), *ctx,
|
||||
state->queue_depth, state->num_grains);
|
||||
(params->queue_depth == 0 ? 4u : params->queue_depth), num_grains);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI AudioOut2ContextDestroy(AudioOut2ContextHandle ctx) {
|
||||
PRINT_NAME();
|
||||
LOGF("\t ctx = 0x%016" PRIx64 "\n", ctx);
|
||||
|
||||
g_audioout2_context_mutex.Lock();
|
||||
std::array<int, 257> audio_handles {};
|
||||
size_t audio_handles_num = 0;
|
||||
|
||||
g_audioout2_state_mutex.Lock();
|
||||
if (auto* state = audioout2_find_context_locked(ctx); state != nullptr) {
|
||||
if (state->object_audio_handle > 0) {
|
||||
audio_handles[audio_handles_num++] = state->object_audio_handle;
|
||||
}
|
||||
*state = AudioOut2ContextState {};
|
||||
}
|
||||
g_audioout2_context_mutex.Unlock();
|
||||
|
||||
std::array<int, 256> audio_handles {};
|
||||
size_t audio_handles_num = 0;
|
||||
|
||||
g_audioout2_port_mutex.Lock();
|
||||
for (auto& port_state: g_audioout2_ports) {
|
||||
if (port_state.used && port_state.context == ctx) {
|
||||
if (port_state.audio_handle > 0 && audio_handles_num < audio_handles.size()) {
|
||||
@@ -485,7 +542,7 @@ int KYTY_SYSV_ABI AudioOut2ContextDestroy(AudioOut2ContextHandle ctx) {
|
||||
port_state = AudioOut2PortStateEntry {};
|
||||
}
|
||||
}
|
||||
g_audioout2_port_mutex.Unlock();
|
||||
g_audioout2_state_mutex.Unlock();
|
||||
|
||||
for (size_t i = 0; i < audio_handles_num; i++) {
|
||||
audioout2_close_audio_handle(audio_handles[i]);
|
||||
@@ -504,11 +561,16 @@ int KYTY_SYSV_ABI AudioOut2ContextSetAttributes(AudioOut2ContextHandle ctx,
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI AudioOut2ContextAdvance(AudioOut2ContextHandle ctx) {
|
||||
g_audioout2_context_mutex.Lock();
|
||||
g_audioout2_state_mutex.Lock();
|
||||
if (auto* state = audioout2_find_context_locked(ctx); state != nullptr) {
|
||||
audioout2_update_context_locked(state);
|
||||
}
|
||||
g_audioout2_context_mutex.Unlock();
|
||||
for (auto& port: g_audioout2_ports) {
|
||||
if (port.used && port.context == ctx) {
|
||||
port.attributes_locked = false;
|
||||
}
|
||||
}
|
||||
g_audioout2_state_mutex.Unlock();
|
||||
|
||||
return OK;
|
||||
}
|
||||
@@ -520,11 +582,10 @@ int KYTY_SYSV_ABI AudioOut2ContextPush(AudioOut2ContextHandle ctx, uint32_t bloc
|
||||
// Only a synchronous submission carrying PCM to a real device can rely on the SDL queue for
|
||||
// pacing. Async pushes must retain queue-depth backpressure, and a handle without PCM (or a
|
||||
// vibration/failed-open handle) has no downstream operation that can block this call.
|
||||
const bool use_device_clock =
|
||||
blocking != 0 && audioout2_context_has_queueable_device(ctx);
|
||||
|
||||
g_audioout2_context_mutex.Lock();
|
||||
g_audioout2_state_mutex.Lock();
|
||||
if (auto* state = audioout2_find_context_locked(ctx); state != nullptr) {
|
||||
const bool use_device_clock =
|
||||
blocking != 0 && audioout2_context_has_queueable_device(*state);
|
||||
audioout2_update_context_locked(state);
|
||||
sleep_micros = audioout2_grain_micros(state->num_grains);
|
||||
if (state->queued < state->queue_depth || use_device_clock) {
|
||||
@@ -534,12 +595,12 @@ int KYTY_SYSV_ABI AudioOut2ContextPush(AudioOut2ContextHandle ctx, uint32_t bloc
|
||||
if (state->queued < state->queue_depth) {
|
||||
state->queued++;
|
||||
}
|
||||
g_audioout2_context_mutex.Unlock();
|
||||
g_audioout2_state_mutex.Unlock();
|
||||
audioout2_queue_context_audio(ctx, blocking != 0);
|
||||
return OK;
|
||||
}
|
||||
}
|
||||
g_audioout2_context_mutex.Unlock();
|
||||
g_audioout2_state_mutex.Unlock();
|
||||
|
||||
if (blocking == 0) {
|
||||
return AUDIO_OUT2_ERROR_NOT_READY;
|
||||
@@ -558,7 +619,7 @@ int KYTY_SYSV_ABI AudioOut2ContextGetQueueLevel(AudioOut2ContextHandle ctx, uint
|
||||
*available_queues = 4;
|
||||
}
|
||||
|
||||
g_audioout2_context_mutex.Lock();
|
||||
g_audioout2_state_mutex.Lock();
|
||||
if (auto* state = audioout2_find_context_locked(ctx); state != nullptr) {
|
||||
audioout2_update_context_locked(state);
|
||||
if (queue_level != nullptr) {
|
||||
@@ -569,7 +630,7 @@ int KYTY_SYSV_ABI AudioOut2ContextGetQueueLevel(AudioOut2ContextHandle ctx, uint
|
||||
(state->queued < state->queue_depth ? state->queue_depth - state->queued : 0);
|
||||
}
|
||||
}
|
||||
g_audioout2_context_mutex.Unlock();
|
||||
g_audioout2_state_mutex.Unlock();
|
||||
|
||||
return OK;
|
||||
}
|
||||
@@ -583,19 +644,25 @@ int KYTY_SYSV_ABI AudioOut2PortCreate(AudioOut2ContextHandle ctx, const AudioOut
|
||||
const auto audio_format = audioout2_data_format_to_audio_format(params->data_format);
|
||||
const auto audio_type = audioout2_port_type_to_audio_out_type(params->port_type);
|
||||
|
||||
g_audioout2_context_mutex.Lock();
|
||||
g_audioout2_state_mutex.Lock();
|
||||
const auto* context_state = audioout2_find_context_locked(ctx);
|
||||
if (context_state == nullptr) {
|
||||
g_audioout2_context_mutex.Unlock();
|
||||
g_audioout2_state_mutex.Unlock();
|
||||
return AUDIO_OUT2_ERROR_INVALID_PARAM;
|
||||
}
|
||||
const auto samples_num = context_state->num_grains == 0 ? 512u : context_state->num_grains;
|
||||
if (audioout2_port_type_is_object(params->port_type) &&
|
||||
(params->port_type != 0x100 || context_state->object_audio_handle == 0 ||
|
||||
params->sampling_freq != 48000 ||
|
||||
(audio_format != AudioInternal::Format::FloatMono &&
|
||||
audio_format != AudioInternal::Format::Signed16bitMono))) {
|
||||
g_audioout2_state_mutex.Unlock();
|
||||
return AUDIO_OUT2_ERROR_INVALID_PARAM;
|
||||
}
|
||||
const auto samples_num = context_state->num_grains;
|
||||
|
||||
g_audioout2_port_mutex.Lock();
|
||||
if (std::find(g_audioout2_users.begin(), g_audioout2_users.end(), params->user_handle) ==
|
||||
g_audioout2_users.end()) {
|
||||
g_audioout2_port_mutex.Unlock();
|
||||
g_audioout2_context_mutex.Unlock();
|
||||
g_audioout2_state_mutex.Unlock();
|
||||
return AUDIO_OUT2_ERROR_INVALID_PARAM;
|
||||
}
|
||||
AudioOut2PortStateEntry* port_state = nullptr;
|
||||
@@ -616,9 +683,9 @@ int KYTY_SYSV_ABI AudioOut2PortCreate(AudioOut2ContextHandle ctx, const AudioOut
|
||||
port_state->sampling_freq = params->sampling_freq;
|
||||
port_state->samples_num = samples_num;
|
||||
port_state->audio_format = audio_format;
|
||||
port_state->gains.fill(1.0f);
|
||||
}
|
||||
g_audioout2_port_mutex.Unlock();
|
||||
g_audioout2_context_mutex.Unlock();
|
||||
g_audioout2_state_mutex.Unlock();
|
||||
|
||||
if (port_state == nullptr) {
|
||||
return AUDIO_OUT2_ERROR_PORT_FULL;
|
||||
@@ -632,12 +699,12 @@ int KYTY_SYSV_ABI AudioOut2PortCreate(AudioOut2ContextHandle ctx, const AudioOut
|
||||
audio_format);
|
||||
}
|
||||
|
||||
g_audioout2_port_mutex.Lock();
|
||||
g_audioout2_state_mutex.Lock();
|
||||
const bool reserved = port_state->used && port_state->handle == next_port;
|
||||
if (reserved) {
|
||||
port_state->audio_handle = audio_handle;
|
||||
}
|
||||
g_audioout2_port_mutex.Unlock();
|
||||
g_audioout2_state_mutex.Unlock();
|
||||
if (!reserved) {
|
||||
audioout2_close_audio_handle(audio_handle);
|
||||
return AUDIO_OUT2_ERROR_INVALID_PARAM;
|
||||
@@ -663,12 +730,12 @@ int KYTY_SYSV_ABI AudioOut2PortDestroy(AudioOut2PortHandle port) {
|
||||
LOGF("\t port = 0x%016" PRIx64 "\n", port);
|
||||
|
||||
int audio_handle = 0;
|
||||
g_audioout2_port_mutex.Lock();
|
||||
g_audioout2_state_mutex.Lock();
|
||||
if (auto* state = audioout2_find_port_locked(port); state != nullptr) {
|
||||
audio_handle = state->audio_handle;
|
||||
*state = AudioOut2PortStateEntry {};
|
||||
}
|
||||
g_audioout2_port_mutex.Unlock();
|
||||
g_audioout2_state_mutex.Unlock();
|
||||
|
||||
audioout2_close_audio_handle(audio_handle);
|
||||
|
||||
@@ -677,36 +744,86 @@ int KYTY_SYSV_ABI AudioOut2PortDestroy(AudioOut2PortHandle port) {
|
||||
|
||||
int KYTY_SYSV_ABI AudioOut2PortSetAttributes(AudioOut2PortHandle port,
|
||||
const AudioOut2Attribute* attributes, uint32_t num) {
|
||||
EXIT_NOT_IMPLEMENTED(num != 0 && attributes == nullptr);
|
||||
if (num != 0 && attributes == nullptr) {
|
||||
return AUDIO_OUT2_ERROR_INVALID_PARAM;
|
||||
}
|
||||
Common::LockGuard lock(g_audioout2_state_mutex);
|
||||
auto* state = audioout2_find_port_locked(port);
|
||||
if (state == nullptr) {
|
||||
return AUDIO_OUT2_ERROR_INVALID_PORT;
|
||||
}
|
||||
|
||||
const void* pcm_data = nullptr;
|
||||
bool has_pcm = false;
|
||||
const void* pcm_data = nullptr;
|
||||
auto gains = state->gains;
|
||||
auto ambisonics = state->ambisonics;
|
||||
uint32_t seen = 0;
|
||||
for (uint32_t i = 0; i < num; i++) {
|
||||
if (attributes[i].attribute_id == AUDIO_OUT2_PORT_ATTRIBUTE_ID_PCM &&
|
||||
attributes[i].value != nullptr && attributes[i].value_size >= sizeof(AudioOut2Pcm)) {
|
||||
AudioOut2Pcm pcm {};
|
||||
std::memcpy(&pcm, attributes[i].value, sizeof(AudioOut2Pcm));
|
||||
pcm_data = pcm.data;
|
||||
has_pcm = true;
|
||||
const auto& attribute = attributes[i];
|
||||
const auto id = attribute.attribute_id;
|
||||
if (id != AUDIO_OUT2_PORT_ATTRIBUTE_ID_PCM && id != AUDIO_OUT2_PORT_ATTRIBUTE_ID_GAIN &&
|
||||
id != AUDIO_OUT2_PORT_ATTRIBUTE_ID_AMBISONICS) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (has_pcm) {
|
||||
g_audioout2_port_mutex.Lock();
|
||||
if (auto* state = audioout2_find_port_locked(port); state != nullptr) {
|
||||
if (pcm_data != nullptr && state->audio_format != AudioInternal::Format::Unknown) {
|
||||
const auto* bytes = static_cast<const uint8_t*>(pcm_data);
|
||||
state->pcm_data.assign(bytes, bytes + audioout2_pcm_size(*state));
|
||||
} else {
|
||||
state->pcm_data.clear();
|
||||
if ((seen & (1u << id)) != 0) {
|
||||
return AUDIO_OUT2_ERROR_INVALID_PARAM;
|
||||
}
|
||||
seen |= 1u << id;
|
||||
if (id != AUDIO_OUT2_PORT_ATTRIBUTE_ID_PCM && state->attributes_locked) {
|
||||
continue;
|
||||
}
|
||||
if (attribute.value == nullptr) {
|
||||
return AUDIO_OUT2_ERROR_INVALID_PARAM;
|
||||
}
|
||||
switch (id) {
|
||||
case AUDIO_OUT2_PORT_ATTRIBUTE_ID_PCM: {
|
||||
if (attribute.value_size < sizeof(AudioOut2Pcm)) {
|
||||
return AUDIO_OUT2_ERROR_INVALID_PARAM;
|
||||
}
|
||||
AudioOut2Pcm pcm {};
|
||||
std::memcpy(&pcm, attribute.value, sizeof(pcm));
|
||||
pcm_data = pcm.data;
|
||||
break;
|
||||
}
|
||||
case AUDIO_OUT2_PORT_ATTRIBUTE_ID_GAIN: {
|
||||
const auto channels = audioout2_data_format_channels(state->data_format);
|
||||
if (attribute.value_size != channels * sizeof(float)) {
|
||||
return AUDIO_OUT2_ERROR_INVALID_PARAM;
|
||||
}
|
||||
std::memcpy(gains.data(), attribute.value, attribute.value_size);
|
||||
for (uint32_t ch = 0; ch < channels; ch++) {
|
||||
if (!std::isfinite(gains[ch]) || gains[ch] < 0.0f) {
|
||||
return AUDIO_OUT2_ERROR_INVALID_PARAM;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AUDIO_OUT2_PORT_ATTRIBUTE_ID_AMBISONICS:
|
||||
if (!audioout2_port_type_is_object(state->port_type) ||
|
||||
attribute.value_size != sizeof(ambisonics)) {
|
||||
return AUDIO_OUT2_ERROR_INVALID_PARAM;
|
||||
}
|
||||
std::memcpy(&ambisonics, attribute.value, sizeof(ambisonics));
|
||||
if (ambisonics != UINT32_MAX && ambisonics > 15 &&
|
||||
(ambisonics < 64 || ambisonics > 99)) {
|
||||
return AUDIO_OUT2_ERROR_INVALID_PARAM;
|
||||
}
|
||||
break;
|
||||
}
|
||||
g_audioout2_port_mutex.Unlock();
|
||||
}
|
||||
|
||||
state->gains = gains;
|
||||
state->ambisonics = ambisonics;
|
||||
if ((seen & (1u << AUDIO_OUT2_PORT_ATTRIBUTE_ID_PCM)) != 0) {
|
||||
if (pcm_data != nullptr && state->audio_format != AudioInternal::Format::Unknown) {
|
||||
const auto* bytes = static_cast<const uint8_t*>(pcm_data);
|
||||
state->pcm_data.assign(bytes, bytes + audioout2_pcm_size(*state));
|
||||
} else {
|
||||
state->pcm_data.clear();
|
||||
}
|
||||
state->attributes_locked = audioout2_port_type_is_object(state->port_type);
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI AudioOut2PortGetState(AudioOut2PortHandle port, AudioOut2PortState* state) {
|
||||
PRINT_NAME();
|
||||
|
||||
@@ -719,11 +836,11 @@ int KYTY_SYSV_ABI AudioOut2PortGetState(AudioOut2PortHandle port, AudioOut2PortS
|
||||
state->reroute_counter = 0;
|
||||
state->flags = 0;
|
||||
|
||||
g_audioout2_port_mutex.Lock();
|
||||
g_audioout2_state_mutex.Lock();
|
||||
if (auto* port_state = audioout2_find_port_locked(port); port_state != nullptr) {
|
||||
state->num_channels = audioout2_data_format_channels(port_state->data_format);
|
||||
}
|
||||
g_audioout2_port_mutex.Unlock();
|
||||
g_audioout2_state_mutex.Unlock();
|
||||
|
||||
// LOGF("\t port = 0x%016" PRIx64 "\n", port);
|
||||
// LOGF("\t num_channels = %" PRIu8 "\n", state->num_channels);
|
||||
@@ -743,7 +860,7 @@ int KYTY_SYSV_ABI AudioOut2UserCreate(uint32_t user_id, AudioOut2UserHandle* han
|
||||
if (handle == nullptr) {
|
||||
return AUDIO_OUT2_ERROR_INVALID_PARAM;
|
||||
}
|
||||
Common::LockGuard lock(g_audioout2_port_mutex);
|
||||
Common::LockGuard lock(g_audioout2_state_mutex);
|
||||
*handle = g_audioout2_next_user++;
|
||||
g_audioout2_users.push_back(*handle);
|
||||
LOGF("\t user_id = %" PRIu32 ", handle = 0x%016" PRIx64 "\n", user_id,
|
||||
@@ -754,7 +871,7 @@ int KYTY_SYSV_ABI AudioOut2UserCreate(uint32_t user_id, AudioOut2UserHandle* han
|
||||
int KYTY_SYSV_ABI AudioOut2UserDestroy(AudioOut2UserHandle handle) {
|
||||
PRINT_NAME();
|
||||
LOGF("\t handle = 0x%016" PRIx64 "\n", static_cast<uint64_t>(handle));
|
||||
Common::LockGuard lock(g_audioout2_port_mutex);
|
||||
Common::LockGuard lock(g_audioout2_state_mutex);
|
||||
const auto it = std::find(g_audioout2_users.begin(), g_audioout2_users.end(), handle);
|
||||
if (it == g_audioout2_users.end()) {
|
||||
return AUDIO_OUT2_ERROR_INVALID_PARAM;
|
||||
@@ -773,13 +890,15 @@ int KYTY_SYSV_ABI AudioOut2UserGetSupportedAttributes(AudioOut2UserHandle handle
|
||||
if (context_attributes == nullptr || port_attributes == nullptr) {
|
||||
return AUDIO_OUT2_ERROR_INVALID_PARAM;
|
||||
}
|
||||
Common::LockGuard lock(g_audioout2_port_mutex);
|
||||
Common::LockGuard lock(g_audioout2_state_mutex);
|
||||
if (std::find(g_audioout2_users.begin(), g_audioout2_users.end(), handle) ==
|
||||
g_audioout2_users.end()) {
|
||||
return AUDIO_OUT2_ERROR_INVALID_PARAM;
|
||||
}
|
||||
*context_attributes = 0;
|
||||
*port_attributes = 1u << AUDIO_OUT2_PORT_ATTRIBUTE_ID_PCM;
|
||||
*port_attributes = (1u << AUDIO_OUT2_PORT_ATTRIBUTE_ID_PCM) |
|
||||
(1u << AUDIO_OUT2_PORT_ATTRIBUTE_ID_GAIN) |
|
||||
(1u << AUDIO_OUT2_PORT_ATTRIBUTE_ID_AMBISONICS);
|
||||
return OK;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user