blob: d5c3d7831f62783e70133e4560d94b8bee2b991c [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "garnet/bin/media/audio_core/mixer/fx_loader.h"
#include <dlfcn.h>
#include "lib/fxl/logging.h"
namespace media::audio {
// Private internal method
bool FxLoader::TryLoad(void* lib, const char* export_name,
void** export_func_ptr) {
FXL_DCHECK(lib != nullptr);
FXL_DCHECK(export_name != nullptr);
FXL_DCHECK(export_func_ptr != nullptr);
*export_func_ptr = dlsym(lib, export_name);
if (*export_func_ptr == nullptr) {
FXL_LOG(ERROR) << "Failed to load .SO export [" << export_name << "]";
return false;
}
return true;
}
void FxLoader::ClearExports() {
exports_loaded_ = false;
fn_get_num_fx_ = nullptr;
fn_get_info_ = nullptr;
fn_get_ctrl_info_ = nullptr;
fn_create_ = nullptr;
fn_delete_ = nullptr;
fn_get_params_ = nullptr;
fn_get_ctrl_val_ = nullptr;
fn_set_ctrl_val_ = nullptr;
fn_reset_ = nullptr;
fn_process_inplace_ = nullptr;
fn_process_ = nullptr;
fn_flush_ = nullptr;
}
//
// Protected methods
//
// Virtual, can be overridden by children (test fixtures)
void* FxLoader::OpenLoadableModuleBinary() {
auto module = dlopen("audiofx.so", RTLD_LAZY | RTLD_GLOBAL);
if (module == nullptr) {
FXL_LOG(ERROR) << "audiofx.so did not load";
}
return module;
}
//
// Public methods
//
// TODO(mpuryear): Consider moving to a single export symbol, which in turn
// returns the function pointers that I currently load/check individually.
zx_status_t FxLoader::LoadLibrary() {
if (fx_lib_ != nullptr) {
return ZX_ERR_ALREADY_EXISTS;
}
fx_lib_ = OpenLoadableModuleBinary();
if (fx_lib_ == nullptr) {
return ZX_ERR_UNAVAILABLE;
}
bool load_success = true;
// Don't stop at the first failure; try to load ALL the exports. The &&=
// operator "short-circuits" (exits early), so we use bitwise &= instead.
load_success &= TryLoad(fx_lib_, "fuchsia_audio_dfx_get_num_effects",
reinterpret_cast<void**>(&fn_get_num_fx_));
load_success &= TryLoad(fx_lib_, "fuchsia_audio_dfx_get_info",
reinterpret_cast<void**>(&fn_get_info_));
load_success &= TryLoad(fx_lib_, "fuchsia_audio_dfx_get_control_info",
reinterpret_cast<void**>(&fn_get_ctrl_info_));
load_success &= TryLoad(fx_lib_, "fuchsia_audio_dfx_create",
reinterpret_cast<void**>(&fn_create_));
load_success &= TryLoad(fx_lib_, "fuchsia_audio_dfx_delete",
reinterpret_cast<void**>(&fn_delete_));
load_success &= TryLoad(fx_lib_, "fuchsia_audio_dfx_get_parameters",
reinterpret_cast<void**>(&fn_get_params_));
load_success &= TryLoad(fx_lib_, "fuchsia_audio_dfx_get_control_value",
reinterpret_cast<void**>(&fn_get_ctrl_val_));
load_success &= TryLoad(fx_lib_, "fuchsia_audio_dfx_set_control_value",
reinterpret_cast<void**>(&fn_set_ctrl_val_));
load_success &= TryLoad(fx_lib_, "fuchsia_audio_dfx_reset",
reinterpret_cast<void**>(&fn_reset_));
load_success &= TryLoad(fx_lib_, "fuchsia_audio_dfx_process_inplace",
reinterpret_cast<void**>(&fn_process_inplace_));
load_success &= TryLoad(fx_lib_, "fuchsia_audio_dfx_process",
reinterpret_cast<void**>(&fn_process_));
load_success &= TryLoad(fx_lib_, "fuchsia_audio_dfx_flush",
reinterpret_cast<void**>(&fn_flush_));
if (!load_success) {
ClearExports();
return ZX_ERR_NOT_FOUND;
}
exports_loaded_ = true;
// Pre-fetch this lib's number of effects. This can be 0, but shouldn't fail.
if (fn_get_num_fx_(&num_fx_) == false) {
num_fx_ = 0;
return ZX_ERR_NOT_SUPPORTED;
}
return ZX_OK;
}
// TODO(mpuryear): dlfcn doesn't actually unload anything currently. Should we
// consider adding additional .SO entry points for Initialize and Deinitialize,
// so we can better control when the library does its resource allocation?
//
// Related: once we add FxProcessor, we must make sure to release any remaining
// FxProcessor instances here, before calling dlclose.
zx_status_t FxLoader::UnloadLibrary() {
zx_status_t ret_val = ZX_OK;
if (fx_lib_ == nullptr || dlclose(fx_lib_) != 0) {
ret_val = ZX_ERR_UNAVAILABLE;
}
ClearExports();
fx_lib_ = nullptr;
return ret_val;
}
zx_status_t FxLoader::GetNumFx(uint32_t* num_fx_out) {
if (!exports_loaded_) {
return ZX_ERR_NOT_FOUND;
}
if (num_fx_out == nullptr) {
return ZX_ERR_INVALID_ARGS;
}
*num_fx_out = num_fx_;
return ZX_OK;
}
zx_status_t FxLoader::GetFxInfo(uint32_t effect_id,
fuchsia_audio_dfx_description* fx_desc) {
if (!exports_loaded_) {
return ZX_ERR_NOT_FOUND;
}
if (fx_desc == nullptr) {
return ZX_ERR_INVALID_ARGS;
}
if (effect_id >= num_fx_) {
return ZX_ERR_OUT_OF_RANGE;
}
if (fn_get_info_(effect_id, fx_desc) == false) {
return ZX_ERR_NOT_SUPPORTED;
}
return ZX_OK;
}
zx_status_t FxLoader::GetFxControlInfo(
uint32_t effect_id, uint16_t ctrl_num,
fuchsia_audio_dfx_control_description* fx_ctrl_desc) {
if (!exports_loaded_) {
return ZX_ERR_NOT_FOUND;
}
if (fx_ctrl_desc == nullptr) {
return ZX_ERR_INVALID_ARGS;
}
if (effect_id >= num_fx_) {
return ZX_ERR_OUT_OF_RANGE;
}
if (fn_get_ctrl_info_(effect_id, ctrl_num, fx_ctrl_desc) == false) {
return ZX_ERR_NOT_SUPPORTED;
}
return ZX_OK;
}
fx_token_t FxLoader::CreateFx(uint32_t effect_id, uint32_t frame_rate,
uint16_t channels_in, uint16_t channels_out) {
if (!exports_loaded_) {
return FUCHSIA_AUDIO_DFX_INVALID_TOKEN;
}
if (effect_id >= num_fx_) {
return FUCHSIA_AUDIO_DFX_INVALID_TOKEN;
}
return fn_create_(effect_id, frame_rate, channels_in, channels_out);
}
zx_status_t FxLoader::DeleteFx(fx_token_t fx_token) {
if (!exports_loaded_) {
return ZX_ERR_NOT_FOUND;
}
if (fx_token == FUCHSIA_AUDIO_DFX_INVALID_TOKEN) {
return ZX_ERR_INVALID_ARGS;
}
if (fn_delete_(fx_token) == false) {
return ZX_ERR_NOT_SUPPORTED;
}
return ZX_OK;
}
zx_status_t FxLoader::FxGetParameters(fx_token_t fx_token,
fuchsia_audio_dfx_parameters* fx_params) {
if (!exports_loaded_) {
return ZX_ERR_NOT_FOUND;
}
if (fx_token == FUCHSIA_AUDIO_DFX_INVALID_TOKEN || fx_params == nullptr) {
return ZX_ERR_INVALID_ARGS;
}
if (fn_get_params_(fx_token, fx_params) == false) {
return ZX_ERR_NOT_SUPPORTED;
}
return ZX_OK;
}
zx_status_t FxLoader::FxGetControlValue(fx_token_t fx_token, uint16_t ctrl_num,
float* ctrl_val_out) {
if (!exports_loaded_) {
return ZX_ERR_NOT_FOUND;
}
if (fx_token == FUCHSIA_AUDIO_DFX_INVALID_TOKEN || ctrl_val_out == nullptr) {
return ZX_ERR_INVALID_ARGS;
}
if (fn_get_ctrl_val_(fx_token, ctrl_num, ctrl_val_out) == false) {
return ZX_ERR_NOT_SUPPORTED;
}
return ZX_OK;
}
zx_status_t FxLoader::FxSetControlValue(fx_token_t fx_token, uint16_t ctrl_num,
float ctrl_val) {
if (!exports_loaded_) {
return ZX_ERR_NOT_FOUND;
}
if (fx_token == FUCHSIA_AUDIO_DFX_INVALID_TOKEN) {
return ZX_ERR_INVALID_ARGS;
}
if (fn_set_ctrl_val_(fx_token, ctrl_num, ctrl_val) == false) {
return ZX_ERR_NOT_SUPPORTED;
}
return ZX_OK;
}
zx_status_t FxLoader::FxReset(fx_token_t fx_token) {
if (!exports_loaded_) {
return ZX_ERR_NOT_FOUND;
}
if (fx_token == FUCHSIA_AUDIO_DFX_INVALID_TOKEN) {
return ZX_ERR_INVALID_ARGS;
}
if (fn_reset_(fx_token) == false) {
return ZX_ERR_NOT_SUPPORTED;
}
return ZX_OK;
}
zx_status_t FxLoader::FxProcessInPlace(fx_token_t fx_token, uint32_t num_frames,
float* audio_buff_in_out) {
if (!exports_loaded_) {
return ZX_ERR_NOT_FOUND;
}
if (fx_token == FUCHSIA_AUDIO_DFX_INVALID_TOKEN ||
audio_buff_in_out == nullptr) {
return ZX_ERR_INVALID_ARGS;
}
if (fn_process_inplace_(fx_token, num_frames, audio_buff_in_out) == false) {
return ZX_ERR_NOT_SUPPORTED;
}
return ZX_OK;
}
zx_status_t FxLoader::FxProcess(fx_token_t fx_token, uint32_t num_frames,
const float* audio_buff_in,
float* audio_buff_out) {
if (!exports_loaded_) {
return ZX_ERR_NOT_FOUND;
}
if (fx_token == FUCHSIA_AUDIO_DFX_INVALID_TOKEN || audio_buff_in == nullptr ||
audio_buff_out == nullptr) {
return ZX_ERR_INVALID_ARGS;
}
if (fn_process_(fx_token, num_frames, audio_buff_in, audio_buff_out) ==
false) {
return ZX_ERR_NOT_SUPPORTED;
}
return ZX_OK;
}
zx_status_t FxLoader::FxFlush(fx_token_t fx_token) {
if (!exports_loaded_) {
return ZX_ERR_NOT_FOUND;
}
if (fx_token == FUCHSIA_AUDIO_DFX_INVALID_TOKEN) {
return ZX_ERR_INVALID_ARGS;
}
if (fn_flush_(fx_token) == false) {
return ZX_ERR_NOT_SUPPORTED;
}
return ZX_OK;
}
} // namespace media::audio