SET(INC_DIR include)
INCLUDE_DIRECTORIES(${INC_DIR})
-SET(dependents "dlog capi-base-common capi-media-sound-manager libpulse dpm capi-system-info")
-SET(pc_dependents "capi-base-common capi-media-sound-manager")
+SET(dependents "dlog capi-base-common capi-media-sound-manager libpulse dpm capi-system-info libaudio-effect")
+SET(pc_dependents "capi-base-common capi-media-sound-manager libaudio-effect")
INCLUDE(FindPkgConfig)
pkg_check_modules(${fw_name} REQUIRED ${dependents})
SET(ALL_SRC ${SOURCES} ${CPPSOURCES})
+IF(TIZEN_FEATURE_PRODUCT_TV)
+ list(REMOVE_ITEM ALL_SRC "src/audio_io_internal.cpp")
+ENDIF(TIZEN_FEATURE_PRODUCT_TV)
+
ADD_LIBRARY(${fw_name} SHARED ${ALL_SRC})
SET_TARGET_PROPERTIES(${fw_name}
--- /dev/null
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TIZEN_MEDIA_AUDIO_IO_INTERNAL_H__
+#define __TIZEN_MEDIA_AUDIO_IO_INTERNAL_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+ AUDIO_IO_EFFECT_METHOD_ACOUSTIC_ECHO_CANCELLATION = 0x1,
+ AUDIO_IO_EFFECT_METHOD_NOISE_SUPPRESSION = 0x2,
+ AUDIO_IO_EFFECT_METHOD_AUTO_GAIN_CONTROL = 0x4,
+} audio_io_effect_method_e;
+
+typedef struct audio_io_effect_method_s *audio_io_effect_method_h;
+typedef struct audio_io_effect_method_chain_s *audio_io_effect_method_chain_h;
+typedef struct audio_io_effect_method_chain_builder_s *audio_io_effect_method_chain_builder_h;
+
+int audio_io_create_acoustic_echo_canceller(int rate, audio_channel_e channels,
+ audio_sample_type_e sample_type, size_t framesize,
+ audio_io_effect_method_h *audio_io_effect, size_t *adjust_framesize);
+int audio_io_create_noise_suppressor(int rate, audio_channel_e channels,
+ audio_sample_type_e sample_type, size_t framesize,
+ audio_io_effect_method_h *audio_io_effect, size_t *adjust_framesize);
+int audio_io_create_auto_gain_control(int rate, audio_channel_e channels,
+ audio_sample_type_e sample_type, size_t framesize,
+ audio_io_effect_method_h *audio_io_effect, size_t *adjust_framesize);
+int audio_io_destroy_effect_method(audio_io_effect_method_h audio_io_effect);
+
+int audio_io_apply_effect_method(audio_io_effect_method_h audio_io_effect, const char *in, char *out);
+int audio_io_apply_effect_method_with_reference(audio_io_effect_method_h audio_io_effect, const char *in, const char *ref, char *out);
+
+/* effect chain */
+int audio_io_create_effect_method_chain_builder(int rate, audio_channel_e channels,
+ audio_sample_type_e sample_type, size_t request_framesize,
+ audio_io_effect_method_chain_builder_h *builder);
+int audio_io_append_effect_method_chain_builder(audio_io_effect_method_chain_builder_h builder, audio_io_effect_method_e aio_method);
+int audio_io_build_effect_method_chain(audio_io_effect_method_chain_builder_h builder, audio_io_effect_method_chain_h *chain, size_t *adjust_framesize);
+int audio_io_apply_effect_method_chain(audio_io_effect_method_chain_h chain, const char *in, const char *ref, char *out);
+int audio_io_destroy_effect_method_chain_builder(audio_io_effect_method_chain_builder_h builder);
+int audio_io_destroy_effect_method_chain(audio_io_effect_method_chain_h chain);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
Name: capi-media-audio-io
Summary: An Audio Input & Audio Output library in Tizen Native API
-Version: 0.5.68
+Version: 0.5.69
Release: 0
Group: Multimedia/API
License: Apache-2.0
BuildRequires: pkgconfig(libpulse)
BuildRequires: pkgconfig(dpm)
BuildRequires: pkgconfig(capi-system-info)
+BuildRequires: pkgconfig(libaudio-effect)
%description
An Audio Input & Audio Output library in Tizen Native API
%files devel
%manifest %{name}.manifest
%{_includedir}/media/audio_io.h
+%if "%{tizen_profile_name}" == "tv"
+%exclude %{_includedir}/media/audio_io_internal.h
+%else
+%{_includedir}/media/audio_io_internal.h
+%endif
%{_libdir}/pkgconfig/*.pc
%{_libdir}/libcapi-media-audio-io.so
%files tool
%manifest %{name}.manifest
-%{_prefix}/bin/audio_io_test
-%{_prefix}/bin/audio_io_process_test
+%{_prefix}/bin/*
%if 0%{?gcov:1}
%files gcov
--- /dev/null
+/*
+* Copyright (c) 2024 Samsung Electronics Co., Ltd All Rights Reserved
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#include <stdlib.h>
+
+#include "CAudioIODef.h"
+#include "audio_io.h"
+#include "audio_io_internal.h"
+
+#include <audio_effect.h>
+
+#define AUDIO_IO_EFFECT_MAX 3
+
+using namespace tizen_media_audio;
+
+#define _check_param(cond) do { \
+ if (!!!(cond)) { \
+ AUDIO_IO_LOGE("invalid param. condition(\"%s\")", #cond); \
+ return AUDIO_IO_ERROR_INVALID_PARAMETER; \
+ } \
+} while(0)
+
+/* Use libaudio-effect domain in the every structure */
+typedef struct audio_io_effect_method_s {
+ audio_effect_method_e method;
+ audio_effect_s *ae;
+ bool need_reference;
+
+ int rate;
+ int channels;
+ audio_effect_format_e format;
+ size_t framesize;
+} audio_io_effect_method_s;
+
+typedef struct audio_io_effect_method_chain_s {
+ audio_effect_s *ae[AUDIO_IO_EFFECT_MAX];
+ unsigned n_effect;
+
+ int rate;
+ int channels;
+ audio_effect_format_e format;
+ size_t framesize;
+
+ char *buffer[AUDIO_IO_EFFECT_MAX];
+} audio_io_effect_method_chain_s;
+
+typedef struct audio_io_effect_method_chain_builder_s {
+ audio_effect_method_e method[AUDIO_IO_EFFECT_MAX];
+ unsigned method_index;
+
+ int rate;
+ int channels;
+ audio_effect_format_e format;
+ size_t request_framesize;
+} audio_io_effect_method_chain_builder_s;
+
+static int _convert_method(audio_io_effect_method_e from, audio_effect_method_e *to)
+{
+ switch (from) {
+ case AUDIO_IO_EFFECT_METHOD_ACOUSTIC_ECHO_CANCELLATION:
+ *to = AUDIO_EFFECT_METHOD_AEC_WEBRTC;
+ return 0;
+ case AUDIO_IO_EFFECT_METHOD_NOISE_SUPPRESSION:
+ *to = AUDIO_EFFECT_METHOD_NS_RNNOISE;
+ return 0;
+ case AUDIO_IO_EFFECT_METHOD_AUTO_GAIN_CONTROL:
+ *to = AUDIO_EFFECT_METHOD_AGC_SPEEX;
+ return 0;
+ default:
+ return -1;
+ }
+}
+
+static int _convert_format(audio_sample_type_e sample_type, audio_effect_format_e *format)
+{
+ switch (sample_type) {
+ case AUDIO_SAMPLE_TYPE_U8:
+ *format = AUDIO_EFFECT_FORMAT_S8;
+ return 0;
+ case AUDIO_SAMPLE_TYPE_S16_LE:
+ *format = AUDIO_EFFECT_FORMAT_S16;
+ return 0;
+ case AUDIO_SAMPLE_TYPE_S24_LE:
+ *format = AUDIO_EFFECT_FORMAT_S24;
+ return 0;
+ case AUDIO_SAMPLE_TYPE_S24_32_LE:
+ return -1;
+ case AUDIO_SAMPLE_TYPE_S32_LE:
+ *format = AUDIO_EFFECT_FORMAT_S32;
+ return 0;
+ default:
+ break;
+ }
+
+ AUDIO_IO_LOGE("failed to convert audio format. sample_type (%x)", sample_type);
+
+ return -1;
+}
+
+static audio_io_effect_method_s *_create_audio_io_effect_method(audio_effect_method_e method,
+ int rate, int channels,
+ audio_effect_format_e format,
+ size_t framesize, size_t *adjust_framesize)
+{
+ audio_effect_s *ae;
+ audio_io_effect_method_s *aio_effect;
+
+ ae = audio_effect_create(method, rate, channels, format, framesize, adjust_framesize);
+ if (!ae) {
+ AUDIO_IO_LOGE("failed to create audio_effect");
+ return NULL;
+ }
+
+ aio_effect = (audio_io_effect_method_s *)calloc(1, sizeof(audio_io_effect_method_s));
+ if (!aio_effect) {
+ AUDIO_IO_LOGE("failed to allocate audio_io_effect_method");
+ audio_effect_destroy(ae);
+ return NULL;
+ }
+
+ aio_effect->ae = ae;
+ aio_effect->method = method;
+ aio_effect->rate = rate;
+ aio_effect->channels = channels;
+ aio_effect->format = format;
+ aio_effect->framesize = *adjust_framesize;
+
+ return aio_effect;
+}
+
+int audio_io_create_acoustic_echo_canceller(int rate, audio_channel_e channels,
+ audio_sample_type_e sample_type, size_t framesize,
+ audio_io_effect_method_h *audio_io_effect, size_t *adjust_framesize)
+{
+ size_t _adjust_framesize;
+ audio_effect_format_e format;
+ audio_io_effect_method_s *aio_effect;
+ int ch = channels - AUDIO_CHANNEL_MONO + 1;
+
+ _check_param(audio_io_effect);
+ _check_param(_convert_format(sample_type, &format) == 0);
+
+ aio_effect = _create_audio_io_effect_method(AUDIO_EFFECT_METHOD_AEC_WEBRTC, rate, ch, format, framesize, &_adjust_framesize);
+ if (!aio_effect) {
+ AUDIO_IO_LOGE("failed to create audio_io_effect");
+ return AUDIO_IO_ERROR_INVALID_OPERATION;
+ }
+
+ aio_effect->need_reference = true;
+ *audio_io_effect = (audio_io_effect_method_h)aio_effect;
+ *adjust_framesize = _adjust_framesize;
+
+ return AUDIO_IO_ERROR_NONE;
+}
+
+int audio_io_create_noise_suppressor(int rate, audio_channel_e channels,
+ audio_sample_type_e sample_type, size_t framesize,
+ audio_io_effect_method_h *audio_io_effect, size_t *adjust_framesize)
+{
+ size_t _adjust_framesize;
+ audio_effect_format_e format;
+ audio_io_effect_method_s *aio_effect;
+ int ch = channels - AUDIO_CHANNEL_MONO + 1;
+
+ _check_param(audio_io_effect);
+ _check_param(_convert_format(sample_type, &format) == 0);
+
+ aio_effect = _create_audio_io_effect_method(AUDIO_EFFECT_METHOD_NS_RNNOISE, rate, ch, format, framesize, &_adjust_framesize);
+ if (!aio_effect) {
+ AUDIO_IO_LOGE("failed to create audio_io_effect");
+ return AUDIO_IO_ERROR_INVALID_OPERATION;
+ }
+
+ *audio_io_effect = (audio_io_effect_method_h)aio_effect;
+ *adjust_framesize = _adjust_framesize;
+
+ return AUDIO_IO_ERROR_NONE;
+}
+
+int audio_io_create_auto_gain_control(int rate, audio_channel_e channels,
+ audio_sample_type_e sample_type, size_t framesize,
+ audio_io_effect_method_h *audio_io_effect, size_t *adjust_framesize)
+{
+ size_t _adjust_framesize;
+ audio_effect_format_e format;
+ audio_io_effect_method_s *aio_effect;
+ int ch = channels - AUDIO_CHANNEL_MONO + 1;
+
+ _check_param(audio_io_effect);
+ _check_param(_convert_format(sample_type, &format) == 0);
+
+ aio_effect = _create_audio_io_effect_method(AUDIO_EFFECT_METHOD_AGC_SPEEX, rate, ch, format, framesize, &_adjust_framesize);
+ if (!aio_effect) {
+ AUDIO_IO_LOGE("failed to create audio_io_effect");
+ return AUDIO_IO_ERROR_INVALID_OPERATION;
+ }
+
+ *audio_io_effect = (audio_io_effect_method_h)aio_effect;
+ *adjust_framesize = _adjust_framesize;
+
+ return AUDIO_IO_ERROR_NONE;
+}
+
+int audio_io_destroy_effect_method(audio_io_effect_method_h audio_io_effect)
+{
+ audio_io_effect_method_s *aio_effect = audio_io_effect;
+
+ _check_param(aio_effect);
+
+ if (aio_effect->ae)
+ audio_effect_destroy(aio_effect->ae);
+
+ memset(aio_effect, 0x00, sizeof(audio_io_effect_method_s));
+
+ free(aio_effect);
+
+ return AUDIO_IO_ERROR_NONE;
+}
+
+int audio_io_apply_effect_method(audio_io_effect_method_h audio_io_effect, const char *in, char *out)
+{
+ audio_io_effect_method_s *aio_effect = audio_io_effect;
+
+ _check_param(aio_effect);
+ _check_param(aio_effect->need_reference == false);
+ _check_param(in);
+ _check_param(out);
+ _check_param(aio_effect->ae);
+
+ if (audio_effect_process(aio_effect->ae, (const void *)in, out)) {
+ AUDIO_IO_LOGE("failed to process audio audio_io_effect");
+ return AUDIO_IO_ERROR_INVALID_OPERATION;
+ }
+
+ return AUDIO_IO_ERROR_NONE;
+}
+
+int audio_io_apply_effect_method_with_reference(audio_io_effect_method_h audio_io_effect, const char *in, const char *ref, char *out)
+{
+ audio_io_effect_method_s *aio_effect = audio_io_effect;
+
+ _check_param(aio_effect);
+ _check_param(aio_effect->need_reference == true);
+ _check_param(in);
+ _check_param(ref);
+ _check_param(out);
+ _check_param(aio_effect->ae);
+
+ if (audio_effect_process_reference(aio_effect->ae, (const void *)in, (const void *)ref, (void *)out)) {
+ AUDIO_IO_LOGE("failed to process audio audio_io_effect");
+ return AUDIO_IO_ERROR_INVALID_OPERATION;
+ }
+
+ return AUDIO_IO_ERROR_NONE;
+}
+
+int audio_io_create_effect_method_chain_builder(int rate, audio_channel_e channels,
+ audio_sample_type_e sample_type, size_t request_framesize,
+ audio_io_effect_method_chain_builder_h *builder)
+{
+ audio_effect_format_e format;
+ audio_io_effect_method_chain_builder_s *_builder;
+
+ _check_param(builder);
+ _check_param(_convert_format(sample_type, &format) == 0);
+
+ _builder = (audio_io_effect_method_chain_builder_s *)calloc(1, sizeof(audio_io_effect_method_chain_builder_s));
+ if (!_builder) {
+ AUDIO_IO_LOGE("out of memory");
+ return AUDIO_IO_ERROR_OUT_OF_MEMORY;
+ }
+
+ _builder->rate = rate;
+ _builder->channels = channels - AUDIO_CHANNEL_MONO + 1;
+ _builder->format = format;
+ _builder->request_framesize = request_framesize;
+
+ *builder = (audio_io_effect_method_chain_builder_h)_builder;
+
+ return AUDIO_IO_ERROR_NONE;
+}
+
+int audio_io_append_effect_method_chain_builder(audio_io_effect_method_chain_builder_h builder, audio_io_effect_method_e aio_method)
+{
+ audio_effect_method_e method;
+ audio_io_effect_method_chain_builder_s *chain_builder = (audio_io_effect_method_chain_builder_s *)builder;
+
+ _check_param(chain_builder);
+ _check_param(_convert_method(aio_method, &method) == 0);
+
+ if (chain_builder->method_index >= AUDIO_IO_EFFECT_MAX) {
+ AUDIO_IO_LOGE("failed to append audio effect. method_index(%d) must be less than MAX(%d)",
+ chain_builder->method_index, AUDIO_IO_EFFECT_MAX);
+ return AUDIO_IO_ERROR_INVALID_OPERATION;
+ }
+
+ chain_builder->method[chain_builder->method_index] = method;
+ chain_builder->method_index += 1;
+
+ return AUDIO_IO_ERROR_NONE;
+}
+
+int audio_io_build_effect_method_chain(audio_io_effect_method_chain_builder_h builder, audio_io_effect_method_chain_h *chain, size_t *adjust_framesize)
+{
+ size_t _adjust_framesize;
+ size_t framesize_max = 0;
+ audio_io_effect_method_chain_s *_chain;
+ audio_io_effect_method_chain_builder_s *chain_builder = (audio_io_effect_method_chain_builder_s *)builder;
+
+ _check_param(chain);
+ _check_param(chain_builder);
+
+ _chain = (audio_io_effect_method_chain_s *)calloc(1, sizeof(audio_io_effect_method_chain_s));
+ if (!_chain) {
+ AUDIO_IO_LOGE("out of memory");
+ return AUDIO_IO_ERROR_OUT_OF_MEMORY;
+ }
+
+ for (unsigned i=0; i<chain_builder->method_index; i++) {
+ _chain->ae[i] = audio_effect_create(chain_builder->method[i],
+ chain_builder->rate,
+ chain_builder->channels,
+ chain_builder->format,
+ chain_builder->request_framesize,
+ &_adjust_framesize);
+ if (!_chain->ae[i]) {
+ AUDIO_IO_LOGE("failed to create audio effect. %d", builder->method[i]);
+ goto fail;
+ }
+
+ framesize_max = MAX(framesize_max, _adjust_framesize);
+ }
+
+ _chain->rate = chain_builder->rate;
+ _chain->channels = chain_builder->channels;
+ _chain->format = chain_builder->format;
+ _chain->framesize = framesize_max;
+ _chain->n_effect = chain_builder->method_index;
+
+ for (unsigned i=0; i<chain_builder->method_index; i++) {
+ if (i < chain_builder->method_index -1) {
+ _chain->buffer[i] = (char *)malloc(_chain->framesize * _chain->channels * 2);
+ if (!_chain->buffer[i]) {
+ AUDIO_IO_LOGE("out of memory");
+ goto fail;
+ }
+ }
+ }
+
+ /* TODO:limitation */
+ if ((size_t)(_chain->rate / 100) != framesize_max) {
+ AUDIO_IO_LOGE("failed to create audio effect. rate(%d), framesize_max(%zu)", _chain->rate, framesize_max);
+ goto fail;
+ }
+
+ *adjust_framesize = _chain->framesize = framesize_max;
+ *chain = _chain;
+
+ return 0;
+
+fail:
+ for (unsigned i=0; _chain->ae[i]; i++) {
+ if (_chain->ae[i])
+ audio_effect_destroy(_chain->ae[i]);
+ if (_chain->buffer[i])
+ free(_chain->buffer[i]);
+ }
+
+ free(_chain);
+
+ return AUDIO_IO_ERROR_INVALID_OPERATION;
+}
+
+int audio_io_apply_effect_method_chain(audio_io_effect_method_chain_h chain, const char *in, const char *ref, char *out)
+{
+ audio_io_effect_method_chain_s *_chain = (audio_io_effect_method_chain_s *)chain;
+
+ _check_param(chain);
+ _check_param(_chain->n_effect > 1);
+
+ void *_out;
+ const void *_in = (const void *)in;
+ const unsigned last_one = _chain->n_effect - 1;
+
+ for (unsigned i=0; i<_chain->n_effect; i++) {
+ /* check this effect method is the last */
+ if (i == last_one)
+ _out = out;
+ else
+ _out = chain->buffer[i];
+
+ if (audio_effect_process_reference(_chain->ae[i], _in, ref, _out)) {
+ AUDIO_IO_LOGE("failed to process audio audio_io_effect");
+ return AUDIO_IO_ERROR_INVALID_OPERATION;
+ }
+
+ _in = _out;
+ }
+
+ return 0;
+}
+
+int audio_io_destroy_effect_method_chain_builder(audio_io_effect_method_chain_builder_h builder)
+{
+ audio_io_effect_method_chain_builder_s *chain_builder = (audio_io_effect_method_chain_builder_s *)builder;
+
+ _check_param(chain_builder);
+
+ free(chain_builder);
+
+ return 0;
+}
+
+int audio_io_destroy_effect_method_chain(audio_io_effect_method_chain_h chain)
+{
+ audio_io_effect_method_chain_s *_chain = (audio_io_effect_method_chain_s *)chain;
+
+ _check_param(chain);
+
+ for (unsigned i=0; i<_chain->n_effect; i++) {
+ if (_chain->ae[i])
+ audio_effect_destroy(_chain->ae[i]);
+ if (_chain->buffer[i])
+ free(_chain->buffer[i]);
+ }
+
+ free(_chain);
+
+ return 0;
+}
+
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${EXTRA_CXXFLAGS} -fPIC -Wall -Werror -std=c++0x -pie")
aux_source_directory(. sources)
+
+IF(TIZEN_FEATURE_PRODUCT_TV)
+ list(REMOVE_ITEM sources "./audio_io_effect_test.c")
+ENDIF(TIZEN_FEATURE_PRODUCT_TV)
+
FOREACH(src ${sources})
GET_FILENAME_COMPONENT(src_name ${src} NAME_WE)
MESSAGE("${src_name}")
ADD_EXECUTABLE(${src_name} ${src})
TARGET_LINK_LIBRARIES(${src_name} ${fw_name} ${${fw_test}_LDFLAGS} -lm -pthread)
+ INSTALL(TARGETS ${src_name} DESTINATION bin)
ENDFOREACH()
-
-INSTALL(TARGETS audio_io_test DESTINATION bin)
-INSTALL(TARGETS audio_io_process_test DESTINATION bin)
--- /dev/null
+/*
+* Copyright (c) 2024 Samsung Electronics Co., Ltd All Rights Reserved
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <ctype.h>
+#include <string.h>
+#include <audio_io.h>
+#include <audio_io_internal.h>
+
+enum {
+ STREAM_INPUT,
+ STREAM_REFERENCE,
+ STREAM_OUTPUT,
+ STREAM_MAX,
+};
+
+typedef struct wav_header {
+ char riff[4]; // "RIFF"
+ uint32_t overall_size; // File size - 8
+ char wave[4]; // "WAVE"
+ char subchunkID[4]; // 'fmt '
+ uint32_t subchunk2size;
+ uint16_t audioformat;
+ uint16_t numchannels;
+ uint32_t samplerate;
+ uint32_t byterate;
+ uint16_t blockalign;
+ uint16_t bitspersample;
+ char data_chunk_header[4];
+ uint32_t data_size;
+} wav_header_s;
+
+typedef struct arguments {
+ uint32_t rate;
+ uint16_t channels;
+ uint16_t format;
+ uint16_t frame_size;
+ uint32_t data_size;
+} wav_specs_s;
+
+static void _print_usage()
+{
+ fprintf(stderr, "Usage)\n");
+ fprintf(stderr, "\taudio_io_effect test -e [audio_io_effect_method_e] -i [input.wav] -r [reference.wav] -o [output.wav]\n");
+ fprintf(stderr, "\t\texample) audio_io_effect_test -e 1 -i input.wav -r reference.wav -o output.wav\n");
+ fprintf(stderr, "\t\texample) audio_io_effect_test -e 2 -i input.wav -o output.wav\n");
+ fprintf(stderr, "\t\taec:1, ns:2, agc:4\n");
+}
+
+static void _close_all_resources(FILE **fp, char **buffer)
+{
+ int i;
+
+ for (i = 0; i < STREAM_MAX; i++) {
+ if (fp[i])
+ fclose(fp[i]);
+ if (buffer[i])
+ free(buffer[i]);
+ }
+}
+
+static void _make_wav_specs(wav_header_s *wav_header, wav_specs_s *specs)
+{
+ assert(wav_header);
+ assert(specs);
+
+ specs->rate = wav_header->samplerate;
+ specs->channels = wav_header->numchannels;
+ specs->format = wav_header->audioformat;
+ specs->frame_size = specs->channels * (wav_header->bitspersample / 8);
+ specs->data_size = wav_header->data_size;
+}
+
+static void _read_wav_info(const char *filename, wav_header_s *wav_header, wav_specs_s *wav_specs)
+{
+ FILE *fp;
+
+ fp = fopen(filename, "r");
+ assert(fp);
+
+ assert(fread(wav_header, sizeof(wav_header_s), 1, fp) > 0);
+
+ _make_wav_specs(wav_header, wav_specs);
+
+ fclose(fp);
+}
+
+static int _check_args_validation(const char *filename)
+{
+ wav_header_s wav_header;
+ wav_specs_s specs;
+
+ const char *RIFF = "RIFF";
+ const char *WAVE = "WAVE";
+
+ if (!filename) {
+ fprintf(stderr, "[INFO] filename is null\n");
+ return -1;
+ }
+
+ if (access(filename, R_OK) != 0) {
+ fprintf(stderr, "[INFO] file(%s) doesn't exist\n", filename);
+ return -1;
+ }
+
+ _read_wav_info(filename, &wav_header, &specs);
+
+ if (strncmp(wav_header.riff, RIFF, strlen(RIFF))) {
+ fprintf(stderr, "[INFO] file(%s) seemed not wav file.\n", filename);
+ return -1;
+ }
+
+ if (strncmp(wav_header.wave, WAVE, strlen(WAVE))) {
+ fprintf(stderr, "[INFO] file(%s) seemed not wav file.\n", filename);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* move the fp to the pcm start point */
+static FILE *_fopen_at_pcm_start(const char *filename)
+{
+ FILE *fp;
+ long filesize;
+ int32_t position;
+ wav_header_s wav_header;
+
+ assert(filename);
+
+ fp = fopen(filename, "r");
+ assert(fp);
+
+ fseek(fp, 0, SEEK_END);
+ filesize = ftell(fp);
+ fseek(fp, 0, SEEK_SET);
+
+ assert(fread(&wav_header, sizeof(wav_header_s), 1, fp) > 0);
+
+ position = (int32_t)wav_header.data_size;
+ fseek(fp, -position, SEEK_END);
+
+ fprintf(stderr, "[INFO] file(%s), size(%lu), header size(%zu), pcm size(%d)\n",
+ filename, filesize, sizeof(wav_header_s), position);
+
+ return fp;
+}
+
+static int _effect_factory(audio_io_effect_method_e method,
+ int rate,
+ audio_channel_e channels,
+ audio_sample_type_e sample_type,
+ size_t framesize,
+ audio_io_effect_method_h *aio_effect,
+ size_t *adjust_framesize) {
+ int ret = -1;
+ size_t _adjust_framesize;
+ audio_io_effect_method_h _aio_effect;
+
+ switch (method) {
+ case AUDIO_IO_EFFECT_METHOD_ACOUSTIC_ECHO_CANCELLATION:
+ ret = audio_io_create_acoustic_echo_canceller(rate,
+ channels,
+ sample_type,
+ framesize,
+ &_aio_effect,
+ &_adjust_framesize);
+ break;
+ case AUDIO_IO_EFFECT_METHOD_NOISE_SUPPRESSION:
+ ret = audio_io_create_noise_suppressor(rate,
+ channels,
+ sample_type,
+ framesize,
+ &_aio_effect,
+ &_adjust_framesize);
+ break;
+ case AUDIO_IO_EFFECT_METHOD_AUTO_GAIN_CONTROL:
+ ret = audio_io_create_auto_gain_control(rate,
+ channels,
+ sample_type,
+ framesize,
+ &_aio_effect,
+ &_adjust_framesize);
+ break;
+ default:
+ assert(0);
+ break;
+ }
+
+ if (ret != AUDIO_IO_ERROR_NONE) {
+ fprintf(stderr, "failed to create audio effect\n");
+ return -1;
+ }
+
+ *aio_effect = _aio_effect;
+ *adjust_framesize = _adjust_framesize;
+
+ return 0;
+}
+
+int process_effect_aec(const char *input_file, const char *ref_file, char *output_file)
+{
+ int i;
+ size_t adjust_framesize;
+ audio_io_effect_method_h audio_io_effect;
+
+ FILE *fp[STREAM_MAX] = { NULL, };
+ char *buffer[STREAM_MAX] = { NULL, };
+ size_t read_bytes;
+ size_t n_frames = 0, pcm_frames = 0;
+ int channels_base = AUDIO_CHANNEL_MONO - 1;
+ int ret;
+
+ wav_specs_s wav_specs;
+ wav_header_s wav_header;
+
+ _read_wav_info(input_file, &wav_header, &wav_specs);
+
+ fp[STREAM_INPUT] = _fopen_at_pcm_start(input_file);
+ fp[STREAM_REFERENCE] = _fopen_at_pcm_start(ref_file);
+
+ /* input and reference header must be same, anyway, we resuse reference wav header */
+ fp[STREAM_OUTPUT] = fopen(output_file, "w");
+ fwrite(&wav_header, sizeof(wav_header_s), 1, fp[STREAM_OUTPUT]);
+
+ ret = _effect_factory(AUDIO_IO_EFFECT_METHOD_ACOUSTIC_ECHO_CANCELLATION,
+ wav_specs.rate,
+ channels_base + wav_specs.channels,
+ AUDIO_SAMPLE_TYPE_S16_LE,
+ wav_specs.rate / 100,
+ &audio_io_effect,
+ &adjust_framesize);
+ if (ret != 0) {
+ fprintf(stderr, "failed to create aec\n");
+ _close_all_resources(fp, buffer);
+ return -1;
+ }
+
+ read_bytes = adjust_framesize * wav_specs.frame_size;
+ assert(read_bytes > 0);
+
+ fprintf(stderr, "[INFO] read size in bytes: %zu * %d\n", adjust_framesize, wav_specs.frame_size);
+
+ for (i = 0; i < STREAM_MAX; i++)
+ buffer[i] = (char *)malloc(read_bytes);
+
+ while (1) {
+ if ((ret = fread(buffer[STREAM_INPUT], read_bytes, 1, fp[STREAM_INPUT])) <= 0)
+ break;
+
+ if ((ret = fread(buffer[STREAM_REFERENCE], read_bytes, 1, fp[STREAM_REFERENCE])) < 0)
+ break;
+
+ ret = audio_io_apply_effect_method_with_reference(audio_io_effect, buffer[STREAM_INPUT],
+ buffer[STREAM_REFERENCE],
+ buffer[STREAM_OUTPUT]);
+ if (ret != AUDIO_IO_ERROR_NONE) {
+ fprintf(stderr, "failed to process AEC. ret(%x), frames(%zu)\n", ret, n_frames);
+ break;
+ }
+
+ assert(fwrite(buffer[STREAM_OUTPUT], read_bytes, 1, fp[STREAM_OUTPUT]) > 0);
+ n_frames += adjust_framesize;
+ }
+
+ audio_io_destroy_effect_method(audio_io_effect);
+
+ _close_all_resources(fp, buffer);
+
+ pcm_frames = wav_header.data_size / wav_specs.frame_size;
+
+ fprintf(stderr, "[INFO] %zu/%zu frames were applied. remain(%zu)\n",
+ n_frames, pcm_frames, pcm_frames - n_frames);
+
+ return 0;
+}
+
+int process_effect(audio_io_effect_method_e method, const char *input_file, char *output_file)
+{
+ size_t adjust_framesize;
+ audio_io_effect_method_h audio_io_effect;
+
+ FILE *fp[STREAM_MAX] = { NULL, };
+ char *buffer[STREAM_MAX] = { NULL, };
+
+ size_t read_bytes;
+ size_t n_frames = 0, pcm_frames = 0;
+ int channels_base = AUDIO_CHANNEL_MONO - 1;
+ int ret;
+
+ wav_specs_s wav_specs;
+ wav_header_s wav_header;
+
+ _read_wav_info(input_file, &wav_header, &wav_specs);
+
+ fp[STREAM_INPUT] = _fopen_at_pcm_start(input_file);
+ fp[STREAM_OUTPUT] = fopen(output_file, "w");
+
+ ret = _effect_factory(method,
+ wav_specs.rate,
+ channels_base + wav_specs.channels,
+ AUDIO_SAMPLE_TYPE_S16_LE,
+ wav_specs.rate / 100,
+ &audio_io_effect,
+ &adjust_framesize);
+ if (ret != 0) {
+ fprintf(stderr, "failed to create aec\n");
+ _close_all_resources(fp, buffer);
+ return -1;
+ }
+
+ wav_header.data_size = (uint32_t)((wav_header.data_size / adjust_framesize) * adjust_framesize);
+ fwrite(&wav_header, sizeof(wav_header_s), 1, fp[STREAM_OUTPUT]);
+
+ read_bytes = adjust_framesize * wav_specs.frame_size;
+ assert(read_bytes > 0);
+
+ fprintf(stderr, "[INFO] read size in bytes: %zu * %d\n", adjust_framesize, wav_specs.frame_size);
+
+ assert((buffer[STREAM_INPUT] = (char *)malloc(read_bytes)));
+ assert((buffer[STREAM_OUTPUT] = (char *)malloc(read_bytes)));
+
+ while(1) {
+ if ((ret = fread(buffer[STREAM_INPUT], read_bytes, 1, fp[STREAM_INPUT])) <= 0)
+ break;
+
+ ret = audio_io_apply_effect_method(audio_io_effect, buffer[STREAM_INPUT], buffer[STREAM_OUTPUT]);
+ if (ret != AUDIO_IO_ERROR_NONE) {
+ fprintf(stderr, "failed to process. ret(%x), frames(%zu)\n", ret, n_frames);
+ break;
+ }
+
+ assert(fwrite(buffer[STREAM_OUTPUT], read_bytes, 1, fp[STREAM_OUTPUT]) > 0);
+ n_frames += adjust_framesize;
+ }
+
+ audio_io_destroy_effect_method(audio_io_effect);
+
+ _close_all_resources(fp, buffer);
+
+ pcm_frames = wav_header.data_size / wav_specs.frame_size;
+
+ fprintf(stderr, "[INFO] %zu/%zu frames were applied. remain(%zu)\n",
+ n_frames, pcm_frames, pcm_frames - n_frames);
+
+ return 0;
+}
+
+int process_effect_chain(int method, const char *input_file, const char *reference_file, char *output_file)
+{
+ int ret, i;
+ wav_specs_s wav_specs;
+ wav_header_s wav_header;
+ int channels_base = AUDIO_CHANNEL_MONO - 1;
+ audio_io_effect_method_chain_builder_h builder = NULL;
+ audio_io_effect_method_chain_h chain = NULL;
+
+ size_t read_bytes;
+ size_t n_frames = 0, pcm_frames = 0;
+ size_t adjust_framesize;
+
+ FILE *fp[STREAM_MAX] = { NULL, };
+ char *buffer[STREAM_MAX] = { NULL, };
+
+ _read_wav_info(input_file, &wav_header, &wav_specs);
+
+ ret = audio_io_create_effect_method_chain_builder(
+ wav_specs.rate,
+ channels_base + wav_specs.channels,
+ AUDIO_SAMPLE_TYPE_S16_LE,
+ wav_specs.rate / 100,
+ &builder);
+ if (ret != AUDIO_IO_ERROR_NONE) {
+ fprintf(stderr, "failed to create effect method chain builder\n");
+ return -1;
+ }
+
+ if (method & AUDIO_IO_EFFECT_METHOD_AUTO_GAIN_CONTROL) {
+ ret = audio_io_append_effect_method_chain_builder(builder, AUDIO_IO_EFFECT_METHOD_AUTO_GAIN_CONTROL);
+ if (ret != AUDIO_IO_ERROR_NONE) {
+ fprintf(stderr, "failed to append effect method chain builder\n");
+ goto fail;
+ }
+ }
+
+ if (method & AUDIO_IO_EFFECT_METHOD_NOISE_SUPPRESSION) {
+ ret = audio_io_append_effect_method_chain_builder(builder, AUDIO_IO_EFFECT_METHOD_NOISE_SUPPRESSION);
+ if (ret != AUDIO_IO_ERROR_NONE) {
+ fprintf(stderr, "failed to append effect method chain builder\n");
+ goto fail;
+ }
+ }
+
+ if (method & AUDIO_IO_EFFECT_METHOD_ACOUSTIC_ECHO_CANCELLATION) {
+ ret = audio_io_append_effect_method_chain_builder(builder, AUDIO_IO_EFFECT_METHOD_ACOUSTIC_ECHO_CANCELLATION);
+ if (ret != AUDIO_IO_ERROR_NONE) {
+ fprintf(stderr, "failed to append effect method chain builder\n");
+ goto fail;
+ }
+ }
+
+ ret = audio_io_build_effect_method_chain(builder, &chain, &adjust_framesize);
+ if (ret != AUDIO_IO_ERROR_NONE) {
+ fprintf(stderr, "failed to build effect method chain builder\n");
+ goto fail;
+ }
+
+ fp[STREAM_INPUT] = _fopen_at_pcm_start(input_file);
+ fp[STREAM_REFERENCE] = _fopen_at_pcm_start(reference_file);
+ fp[STREAM_OUTPUT] = fopen(output_file, "w");
+
+ wav_header.data_size = (uint32_t)((wav_header.data_size / adjust_framesize) * adjust_framesize);
+ fwrite(&wav_header, sizeof(wav_header_s), 1, fp[STREAM_OUTPUT]);
+
+ read_bytes = adjust_framesize * wav_specs.frame_size;
+ assert(read_bytes > 0);
+
+ for (i = 0; i < STREAM_MAX; i++)
+ buffer[i] = (char *)malloc(read_bytes);
+
+ fprintf(stderr, "[INFO] read size in bytes: %zu * %d\n", adjust_framesize, wav_specs.frame_size);
+
+ while (1) {
+ if ((ret = fread(buffer[STREAM_INPUT], read_bytes, 1, fp[STREAM_INPUT])) <= 0)
+ break;
+
+ if ((ret = fread(buffer[STREAM_REFERENCE], read_bytes, 1, fp[STREAM_REFERENCE])) < 0)
+ break;
+
+ ret = audio_io_apply_effect_method_chain(chain, buffer[STREAM_INPUT],
+ buffer[STREAM_REFERENCE],
+ buffer[STREAM_OUTPUT]);
+ if (ret != AUDIO_IO_ERROR_NONE) {
+ fprintf(stderr, "failed to process AEC. ret(%x), frames(%zu)\n", ret, n_frames);
+ break;
+ }
+
+ assert(fwrite(buffer[STREAM_OUTPUT], read_bytes, 1, fp[STREAM_OUTPUT]) > 0);
+ n_frames += adjust_framesize;
+ }
+
+ audio_io_destroy_effect_method_chain_builder(builder);
+ audio_io_destroy_effect_method_chain(chain);
+ _close_all_resources(fp, buffer);
+
+ pcm_frames = wav_header.data_size / wav_specs.frame_size;
+
+ fprintf(stderr, "[INFO] %zu/%zu frames were applied. remain(%zu)\n",
+ n_frames, pcm_frames, pcm_frames - n_frames);
+
+ return 0;
+
+fail:
+ if (builder)
+ audio_io_destroy_effect_method_chain_builder(builder);
+
+ _close_all_resources(fp, buffer);
+
+ return -1;
+}
+
+int atoi_safe(const char *optarg)
+{
+ for (size_t i = 0; i < strlen(optarg); i++)
+ if (!isdigit(optarg[i]))
+ return -1;
+
+ return atoi(optarg);
+}
+
+int main(int argc, char **argv)
+{
+ int option;
+ const char *optstring = "e:i:r:o:";
+ audio_io_effect_method_e method = 0;
+ char *input_file = NULL;
+ char *reference_file = NULL;
+ char *output_file = NULL;
+ int effect;
+ int ret;
+
+ setbuf(stdout, NULL);
+
+ while (-1 != (option = getopt(argc, argv, optstring))) {
+ switch (option) {
+ case 'e':
+ if ((effect = atoi_safe(optarg)) < 0) {
+ _print_usage();
+ return 0;
+ }
+ method = (audio_io_effect_method_e)effect;
+ break;
+ case 'i':
+ input_file = optarg;
+ break;
+ case 'r':
+ reference_file = optarg;
+ break;
+ case 'o':
+ output_file = optarg;
+ break;
+ default:
+ _print_usage();
+ return 0;
+ }
+ }
+
+ if (_check_args_validation(input_file) < 0) {
+ _print_usage();
+ return -1;
+ }
+
+ fprintf(stderr, "[INFO] input file: %s\n", input_file);
+
+ if (method & AUDIO_IO_EFFECT_METHOD_ACOUSTIC_ECHO_CANCELLATION) {
+ if (_check_args_validation(reference_file) == 0) {
+ fprintf(stderr, "[INFO] reference file: %s\n", reference_file);
+ } else {
+ _print_usage();
+ return -1;
+ }
+ }
+
+ if (!output_file) {
+ fprintf(stderr, "output file is null\n");
+ _print_usage();
+ return -1;
+ }
+
+ fprintf(stderr, "[INFO] output file: %s\n", output_file);
+
+ if (method == AUDIO_IO_EFFECT_METHOD_ACOUSTIC_ECHO_CANCELLATION)
+ ret = process_effect_aec(input_file, reference_file, output_file);
+ else if (method == AUDIO_IO_EFFECT_METHOD_NOISE_SUPPRESSION)
+ ret = process_effect(method, input_file, output_file);
+ else if (method == AUDIO_IO_EFFECT_METHOD_AUTO_GAIN_CONTROL)
+ ret = process_effect(method, input_file, output_file);
+ else
+ ret = process_effect_chain(method, input_file, reference_file, output_file);
+
+ fprintf(stderr, "[INFO] result %d\n", ret);
+
+ return 0;
+}
+