Add audio effect APIs 11/315611/9 accepted/tizen_unified accepted/tizen_unified_toolchain accepted/tizen_unified_x accepted/tizen_unified_x_asan accepted/tizen/unified/20240924.153231 accepted/tizen/unified/toolchain/20241004.102016 accepted/tizen/unified/x/20240925.015936 accepted/tizen/unified/x/asan/20241014.000416
authorJaechul Lee <jcsing.lee@samsung.com>
Mon, 5 Aug 2024 07:42:29 +0000 (16:42 +0900)
committerJaechul Lee <jcsing.lee@samsung.com>
Tue, 24 Sep 2024 01:44:03 +0000 (10:44 +0900)
in case of VD, the files related to audio_io_effect aren't installed
because libaudio-effect is not up-to-date. it will make build breaks.

[Version] 0.5.69
[Issue Type] New Feature

Change-Id: I2b0af49067deab33716dbc428a5eb52f61858044
Signed-off-by: Jaechul Lee <jcsing.lee@samsung.com>
CMakeLists.txt
include/audio_io_internal.h [new file with mode: 0644]
packaging/capi-media-audio-io.spec
src/audio_io_internal.cpp [new file with mode: 0644]
test/CMakeLists.txt
test/audio_io_effect_test.c [new file with mode: 0644]

index af1abcb..3be14cb 100644 (file)
@@ -10,8 +10,8 @@ SET(PREFIX ${CMAKE_INSTALL_PREFIX})
 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})
@@ -49,6 +49,10 @@ aux_source_directory(src SOURCES)
 
 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}
diff --git a/include/audio_io_internal.h b/include/audio_io_internal.h
new file mode 100644 (file)
index 0000000..3dccb0e
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * 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
+
index 80df104..5bcd293 100644 (file)
@@ -1,6 +1,6 @@
 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
@@ -13,6 +13,7 @@ BuildRequires:  pkgconfig(capi-base-common)
 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
@@ -85,13 +86,17 @@ find . -name '*.gcno' -exec cp --parents '{}' "$gcno_obj_dir" ';'
 %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
diff --git a/src/audio_io_internal.cpp b/src/audio_io_internal.cpp
new file mode 100644 (file)
index 0000000..5b4ecd9
--- /dev/null
@@ -0,0 +1,444 @@
+/*
+* 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;
+}
+
index 9684f82..2a5a1c6 100644 (file)
@@ -9,12 +9,15 @@ SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -Wall -Werror -pie")
 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)
diff --git a/test/audio_io_effect_test.c b/test/audio_io_effect_test.c
new file mode 100644 (file)
index 0000000..6f3c14d
--- /dev/null
@@ -0,0 +1,562 @@
+/*
+* 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;
+}
+