blob: 9c291a6eb4adedb445c2b1b6fe2d9d1534b39da2 [file] [log] [blame]
// Copyright 2019 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 "src/media/audio/effects/test_effects/test_effects_v1.h"
#include <memory>
#include <string>
#include <string_view>
extern fuchsia_audio_effects_module_v1 fuchsia_audio_effects_module_v1_instance;
namespace {
static constexpr uint32_t TEST_EFFECTS_MAX = 255;
static constexpr uint32_t TEST_EFFECTS_DEFAULT_MAX_FRAMES_PER_BATCH = 512;
test_effect_v1_spec g_effects[TEST_EFFECTS_MAX] = {};
uint32_t g_instance_count = 0;
class TestEffect {
public:
TestEffect(uint32_t effect_id, uint32_t frame_rate, uint16_t chans_in, uint16_t chans_out,
std::string_view config)
: effect_id_(effect_id),
frame_rate_(frame_rate),
channels_in_(chans_in),
channels_out_(chans_out),
config_(config) {
// If this is an out-of-place effect, allocate our output buffer.
if (channels_in() != channels_out()) {
out_of_place_buffer_frames_ = max_batch_size();
if (out_of_place_buffer_frames_ == FUCHSIA_AUDIO_EFFECTS_FRAMES_PER_BUFFER_ANY) {
out_of_place_buffer_frames_ = TEST_EFFECTS_DEFAULT_MAX_FRAMES_PER_BATCH;
}
out_of_place_buffer_ =
std::make_unique<float[]>(out_of_place_buffer_frames_ * channels_out());
}
}
uint32_t effect_id() const { return effect_id_; }
uint32_t frame_rate() const { return frame_rate_; }
uint16_t channels_in() const { return channels_in_; }
uint16_t channels_out() const {
return channels_out_ == FUCHSIA_AUDIO_EFFECTS_CHANNELS_SAME_AS_IN ? channels_in_
: channels_out_;
}
uint32_t block_size_frames() const { return g_effects[effect_id()].block_size_frames; }
uint32_t max_batch_size() const { return g_effects[effect_id()].max_batch_size; }
uint32_t latency_frames() const { return g_effects[effect_id()].signal_latency_frames; }
uint32_t ring_out_frames() const { return g_effects[effect_id()].ring_out_frames; }
std::string_view config() const { return config_; }
size_t flush_count() const { return flush_count_; }
bool UpdateConfiguration(std::string_view new_config) {
config_ = new_config;
return true;
}
bool GetParameters(fuchsia_audio_effects_parameters* params) const {
memset(params, 0, sizeof(*params));
params->frame_rate = frame_rate();
params->channels_in = channels_in();
params->channels_out = channels_out();
params->block_size_frames = block_size_frames();
params->max_frames_per_buffer = max_batch_size();
params->signal_latency_frames = latency_frames();
params->ring_out_frames = ring_out_frames();
return true;
}
bool ProcessInPlace(uint32_t num_frames, float* audio_buff_in_out) {
if (channels_in() != channels_out()) {
return false;
}
if (block_size_frames() && num_frames % block_size_frames() != 0) {
fprintf(stderr, "Cannot mix %u frames; block size is %u\n", num_frames, block_size_frames());
return false;
}
auto& effect = g_effects[effect_id()];
for (uint32_t i = 0; i < num_frames * channels_in(); ++i) {
if (effect.action == TEST_EFFECTS_ACTION_ADD) {
audio_buff_in_out[i] = audio_buff_in_out[i] + effect.value;
} else if (effect.action == TEST_EFFECTS_ACTION_ASSIGN) {
audio_buff_in_out[i] = effect.value;
} else if (effect.action == TEST_EFFECTS_ACTION_ASSIGN_CONFIG_SIZE) {
audio_buff_in_out[i] = static_cast<float>(config_.size());
} else {
return false;
}
}
return true;
}
bool Process(uint32_t num_frames, const float* audio_buff_in, float** audio_buff_out) {
// If channels are equal, ProcessInPlace should be used.
if (channels_in() == channels_out()) {
return false;
}
if (!out_of_place_buffer_) {
return false;
}
if (num_frames > out_of_place_buffer_frames_) {
return false;
}
*audio_buff_out = out_of_place_buffer_.get();
auto& effect = g_effects[effect_id()];
for (uint32_t i = 0; i < num_frames; ++i) {
for (int32_t j = 0; j < channels_out(); ++j) {
if (effect.action == TEST_EFFECTS_ACTION_ADD) {
if (j < channels_in()) {
(*audio_buff_out)[i * channels_out() + j] =
audio_buff_in[i * channels_in() + j] + effect.value;
} else {
(*audio_buff_out)[i * channels_out() + j] = 0.0;
}
} else if (effect.action == TEST_EFFECTS_ACTION_ASSIGN) {
(*audio_buff_out)[i * channels_out() + j] = effect.value;
} else {
return false;
}
}
}
return true;
}
bool Flush() {
flush_count_++;
return true;
}
void SetStreamInfo(const fuchsia_audio_effects_stream_info* stream_info) {
stream_info_ = *stream_info;
}
zx_status_t Inspect(test_effects_v1_inspect_state* state) {
state->config = config_.data();
state->config_length = config_.size();
state->effect_id = effect_id();
state->flush_count = flush_count();
state->stream_info = stream_info_;
return ZX_OK;
}
private:
uint32_t effect_id_;
uint32_t frame_rate_;
uint16_t channels_in_;
uint16_t channels_out_;
std::string config_;
fuchsia_audio_effects_stream_info stream_info_ = {};
size_t flush_count_ = 0;
std::unique_ptr<float[]> out_of_place_buffer_;
size_t out_of_place_buffer_frames_;
};
bool get_info(uint32_t effect_id, fuchsia_audio_effects_description* desc) {
if (effect_id >= fuchsia_audio_effects_module_v1_instance.num_effects) {
return false;
}
*desc = g_effects[effect_id].description;
return true;
}
fuchsia_audio_effects_handle_t create_effect(uint32_t effect_id, uint32_t frame_rate,
uint16_t channels_in, uint16_t channels_out,
const char* config, size_t config_length) {
if (effect_id > TEST_EFFECTS_MAX || channels_in > FUCHSIA_AUDIO_EFFECTS_CHANNELS_MAX ||
channels_out > FUCHSIA_AUDIO_EFFECTS_CHANNELS_MAX) {
return FUCHSIA_AUDIO_EFFECTS_INVALID_HANDLE;
}
g_instance_count++;
return reinterpret_cast<fuchsia_audio_effects_handle_t>(
new TestEffect(effect_id, frame_rate, channels_in, channels_out, {config, config_length}));
}
bool delete_effect(fuchsia_audio_effects_handle_t effects_handle) {
if (effects_handle == FUCHSIA_AUDIO_EFFECTS_INVALID_HANDLE) {
return false;
}
--g_instance_count;
delete reinterpret_cast<TestEffect*>(effects_handle);
return true;
}
bool update_effect_configuration(fuchsia_audio_effects_handle_t effects_handle, const char* config,
size_t config_length) {
if (effects_handle == FUCHSIA_AUDIO_EFFECTS_INVALID_HANDLE) {
return false;
}
return reinterpret_cast<TestEffect*>(effects_handle)
->UpdateConfiguration({config, config_length});
}
bool get_parameters(fuchsia_audio_effects_handle_t effects_handle,
fuchsia_audio_effects_parameters* params) {
if (effects_handle == FUCHSIA_AUDIO_EFFECTS_INVALID_HANDLE || params == nullptr) {
return false;
}
return reinterpret_cast<TestEffect*>(effects_handle)->GetParameters(params);
}
bool process_inplace(fuchsia_audio_effects_handle_t effects_handle, uint32_t num_frames,
float* audio_buff_in_out) {
if (effects_handle == FUCHSIA_AUDIO_EFFECTS_INVALID_HANDLE || audio_buff_in_out == nullptr) {
return false;
}
return reinterpret_cast<TestEffect*>(effects_handle)
->ProcessInPlace(num_frames, audio_buff_in_out);
}
bool process(fuchsia_audio_effects_handle_t effects_handle, uint32_t num_frames,
const float* audio_buff_in, float** audio_buff_out) {
if (effects_handle == FUCHSIA_AUDIO_EFFECTS_INVALID_HANDLE || !audio_buff_in || !audio_buff_out) {
return false;
}
return reinterpret_cast<TestEffect*>(effects_handle)
->Process(num_frames, audio_buff_in, audio_buff_out);
}
bool flush(fuchsia_audio_effects_handle_t effects_handle) {
if (effects_handle == FUCHSIA_AUDIO_EFFECTS_INVALID_HANDLE) {
return false;
}
return reinterpret_cast<TestEffect*>(effects_handle)->Flush();
}
void set_stream_info(fuchsia_audio_effects_handle_t effects_handle,
const fuchsia_audio_effects_stream_info* stream_info) {
if (effects_handle == FUCHSIA_AUDIO_EFFECTS_INVALID_HANDLE) {
return;
}
return reinterpret_cast<TestEffect*>(effects_handle)->SetStreamInfo(stream_info);
}
zx_status_t ext_add_effect(test_effect_v1_spec effect) {
if (g_instance_count > 0) {
return ZX_ERR_BAD_STATE;
}
auto id = fuchsia_audio_effects_module_v1_instance.num_effects;
if (id > TEST_EFFECTS_MAX) {
return ZX_ERR_OUT_OF_RANGE;
}
g_effects[id] = effect;
fuchsia_audio_effects_module_v1_instance.num_effects++;
return ZX_OK;
}
zx_status_t ext_clear_effects() {
if (g_instance_count > 0) {
return ZX_ERR_BAD_STATE;
}
fuchsia_audio_effects_module_v1_instance.num_effects = 0;
return ZX_OK;
}
zx_status_t ext_inspect_instance(fuchsia_audio_effects_handle_t effects_handle,
test_effects_v1_inspect_state* state) {
if (effects_handle == FUCHSIA_AUDIO_EFFECTS_INVALID_HANDLE) {
return ZX_ERR_INVALID_ARGS;
}
return reinterpret_cast<TestEffect*>(effects_handle)->Inspect(state);
}
uint32_t ext_num_instances() { return g_instance_count; }
} // namespace
DECLARE_FUCHSIA_AUDIO_EFFECTS_MODULE_V1{
0,
&get_info,
&create_effect,
&update_effect_configuration,
&delete_effect,
&get_parameters,
&process_inplace,
&process,
&flush,
&set_stream_info,
};
DECLARE_TEST_EFFECTS_V1_EXT{
&ext_add_effect,
&ext_clear_effects,
&ext_num_instances,
&ext_inspect_instance,
};