From: Seungbae Shin Date: Tue, 20 Jul 2021 09:47:19 +0000 (+0900) Subject: audio_io_test_ec: apply command design pattern X-Git-Tag: accepted/tizen/unified/20210923.133037^0 X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fapi%2Faudio-io.git;a=commitdiff_plain;h=524af5c59b2596981364542a8704e7dd758825ee audio_io_test_ec: apply command design pattern [Version] 0.5.42 [Issue Type] Refactoring Change-Id: I2615757da7e51ecd338fbe5d39c64ee9f08ab762 --- diff --git a/packaging/capi-media-audio-io.spec b/packaging/capi-media-audio-io.spec index d954082..aaaa3b2 100644 --- a/packaging/capi-media-audio-io.spec +++ b/packaging/capi-media-audio-io.spec @@ -1,6 +1,6 @@ Name: capi-media-audio-io Summary: An Audio Input & Audio Output library in Tizen Native API -Version: 0.5.41 +Version: 0.5.42 Release: 0 Group: Multimedia/API License: Apache-2.0 diff --git a/test/audio_io_test_ec.c b/test/audio_io_test_ec.c deleted file mode 100644 index 1559cb3..0000000 --- a/test/audio_io_test_ec.c +++ /dev/null @@ -1,275 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#ifndef TIZEN_FEATURE_TV_PROD -static double _get_rand_double_range(double min, double max) -{ - double ret; - int r; - - r = rand(); - ret = (max / RAND_MAX) * r * (r & 0x1 ? 1 : -1); - - return ret; -} - -static void _white_noise_stream_write_cb(audio_out_h handle, size_t nbytes, void *user_data) -{ - int i; - short *ptr; - char *buffer; - double amp = 0.3 * 32767.0; - - buffer = (char *)malloc(nbytes); - if (!buffer) - return; - - ptr = (short *)buffer; - for (i = 0; i < nbytes; i += 2, ptr++) - ptr[0] = (short)(amp * _get_rand_double_range(-1.0, 1.0)); - - audio_out_write(handle, buffer, nbytes); - - free(buffer); -} - -int play_white_noise_async(audio_out_h *output) -{ - audio_out_h _output = NULL; - int ret; - - srand(time(NULL)); - - ret = audio_out_create_new(48000, AUDIO_CHANNEL_STEREO, AUDIO_SAMPLE_TYPE_S16_LE, &_output); - if (ret != AUDIO_IO_ERROR_NONE) { - printf("failed to create audio out\n"); - return -1; - } - - ret = audio_out_set_stream_cb(_output, _white_noise_stream_write_cb, NULL); - if (ret != AUDIO_IO_ERROR_NONE) { - printf("failed to set stream callback\n"); - goto fail; - } - - ret = audio_out_prepare(_output); - if (ret != AUDIO_IO_ERROR_NONE) { - printf("failed to prepare \n"); - goto fail; - } - - *output = _output; - - return 0; - -fail: - audio_out_destroy(_output); - - return -1; -} - -int stop_white_noise_async(audio_out_h output) -{ - int ret; - - ret = audio_out_unprepare(output); - if (ret != AUDIO_IO_ERROR_NONE) - printf("failed to prepare\n"); - - ret = audio_out_destroy(output); - if (ret != AUDIO_IO_ERROR_NONE) { - printf("failed to destroy output\n"); - return -1; - } - - return 0; -} - -int capture_sound(char **buffer, int *length, int sec) -{ - int ret; - char *_buffer = NULL; - audio_in_h input = NULL; - int _length = 16000 * 1 * 2 * sec; // 16Khz, mono, S16LE - - if (!buffer) { - printf("buffer is null\n"); - return -1; - } - - ret = audio_in_create(16000, AUDIO_CHANNEL_MONO, AUDIO_SAMPLE_TYPE_S16_LE, &input); - if (ret != AUDIO_IO_ERROR_NONE) { - printf("failed to create audio_input\n"); - return -1; - } - - ret = audio_in_prepare(input); - if (ret != AUDIO_IO_ERROR_NONE) { - printf("failed to prepare %x0x\n", ret); - goto fail; - } - - _buffer = malloc(_length); - if (!_buffer) { - printf("_buffer is null\n"); - goto fail; - } - - ret = audio_in_read(input, _buffer, _length); - if (ret <= 0) { - printf("failed to read ret(0x%x), length(%d)\n", ret, _length); - goto fail; - } - ret = audio_in_unprepare(input); - if (ret != AUDIO_IO_ERROR_NONE) { - printf("failed to unprepare 0x%x", ret); - goto fail; - } - - *buffer = _buffer; - *length = _length; - - return 0; - -fail: - if (input) - audio_in_destroy(input); - if (buffer) - free(buffer); - - return -1; -} - -int enable_echo_cancellation(bool enable) -{ - int ret; - - if (enable) - ret = sound_manager_start_aec(); - else - ret = sound_manager_stop_aec(); - - if (ret != 0) { - printf("failed to %s echo cancellation. ret(0x%x)\n", - enable ? "start" : "stop", ret); - return -1; - } - - return 0; -} - -int test_echo_cancellation(char **buffer, bool enable_ec, bool enable_ref, int time_sec) -{ - audio_out_h output = NULL; - char *_buffer = NULL; - int length; - - if (enable_ec && enable_echo_cancellation(true)) - goto exit; - - if (enable_ref && play_white_noise_async(&output)) - goto exit; - - printf("Say something for %d seconds.. enable_ec(%s) enable_ref(%s)\n", - time_sec, - enable_ec ? "on" : "off", - enable_ref? "on" : "off"); - - if (capture_sound(&_buffer, &length, time_sec)) - goto exit; - - if (output && stop_white_noise_async(output)) - goto exit; - - output = NULL; - - if (enable_ec && enable_echo_cancellation(false)) - goto exit; - - *buffer = _buffer; - - return length; - -exit: - if (output) - stop_white_noise_async(output); - - if (enable_ec) - enable_echo_cancellation(false); - - if (_buffer) - free(_buffer); - - return -1; -} - -int dump_to_file(const char *filename, char *buffer, int length) -{ - int fd, ret; - - fd = open(filename, O_CREAT|O_TRUNC|O_RDWR, 0644); - if (fd < 0) { - printf("failed to open file\n"); - return -1; - } - - ret = write(fd, buffer, length); - if (ret < 0) - printf("failed to write\n"); - - close(fd); - - return 0; -} -#endif - -int main(int argc, char **argv) -{ -#ifndef TIZEN_FEATURE_TV_PROD - bool enable_ec; - bool enable_ref; - int time_sec; - char *filename; - - char *buffer; - int size; - - if (argc != 5) { - printf("- Usages :\n"); - printf("- # audio_io_test_ec [filename] [on/off 0:1] [noise 0:1] [seconds]\n"); - return -1; - } - - filename = argv[1]; - enable_ec = !!atoi(argv[2]); - enable_ref = !!atoi(argv[3]); - time_sec = atoi(argv[4]); - - size = test_echo_cancellation(&buffer, enable_ec, enable_ref, time_sec); - if (size <= 0) { - printf("failed to test aec\n"); - return -1; - } - - if (dump_to_file(filename, buffer, size)) - printf("failed to save file %s\n", filename); - - if (buffer) - free(buffer); -#else - printf("not supported yet\n"); -#endif - - return 0; -} - diff --git a/test/audio_io_test_ec.cpp b/test/audio_io_test_ec.cpp new file mode 100644 index 0000000..88f7d8b --- /dev/null +++ b/test/audio_io_test_ec.cpp @@ -0,0 +1,360 @@ +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#ifndef TIZEN_FEATURE_TV_PROD + +enum Receiver +{ + ECHO_CANCELLER = 0, + NOISE_GENERATOR, +}; + +class Command +{ +public: + virtual void execute() const = 0; +}; + +class EchoCanceller +{ +public: + EchoCanceller() = default; + virtual ~EchoCanceller() = default; + + void on() { + int ret = sound_manager_start_aec(); + if (ret != 0) + std::cerr << "failed to start echo cancellation, " << ret << std::endl; + else + std::cout << "[ON] Echo Canceller" << std::endl; + } + void off() { + int ret = sound_manager_stop_aec(); + if (ret != 0) + std::cerr << "failed to stop echo cancellation, " << ret << std::endl; + else + std::cout << "[OFF] Echo Canceller" << std::endl; + } +}; + +class NoiseGenerator +{ +public: + NoiseGenerator() = default; + virtual ~NoiseGenerator() = default; + + void on() { + std::cout << "[ON] Noise Generator" << std::endl; + playWhiteNoise(); + } + void off() { + std::cout << "[OFF] Noise Generator" << std::endl; + stopWhiteNoise(); + } + +private: + void playWhiteNoise(); + void stopWhiteNoise(); + + static void _streamWriteCb(audio_out_h handle, size_t nbytes, void *user_data); + static double _getRandDoubleRange(double min, double max); + + audio_out_h output{}; +}; + +void NoiseGenerator::playWhiteNoise() +{ + audio_out_h _output = nullptr; + + srand(time(NULL)); + + int ret = audio_out_create_new(48000, AUDIO_CHANNEL_STEREO, AUDIO_SAMPLE_TYPE_S16_LE, &_output); + if (ret != AUDIO_IO_ERROR_NONE) { + std::cerr << "failed to create audio out" << std::endl; + return; + } + + ret = audio_out_set_stream_cb(_output, _streamWriteCb, NULL); + if (ret != AUDIO_IO_ERROR_NONE) { + std::cerr << "failed to set stream callback" << std::endl; + audio_out_destroy(_output); + return; + } + + ret = audio_out_prepare(_output); + if (ret != AUDIO_IO_ERROR_NONE) { + std::cerr << "failed to prepare " << std::endl; + audio_out_destroy(_output); + return; + } + + output = _output; +} + +void NoiseGenerator::stopWhiteNoise() +{ + if (!output) + return; + + int ret = audio_out_unprepare(output); + if (ret != AUDIO_IO_ERROR_NONE) + std::cerr << "failed to prepare" << std::endl; + + ret = audio_out_destroy(output); + if (ret != AUDIO_IO_ERROR_NONE) + std::cerr << "failed to destroy output" << std::endl; + + output = nullptr; +} + +double NoiseGenerator::_getRandDoubleRange(double min, double max) +{ + static unsigned int seedp; + int r = rand_r(&seedp); + return (max / RAND_MAX) * r * (r & 0x1 ? 1 : -1); +} + +void NoiseGenerator::_streamWriteCb(audio_out_h handle, size_t nbytes, void *user_data) +{ + constexpr double amp = 0.3 * 32767.0; + + auto buffer = static_cast(malloc(nbytes)); + if (!buffer) + return; + + auto ptr = buffer; + for (size_t i = 0; i < nbytes; i += 2, ptr++) + ptr[0] = (short)(amp * _getRandDoubleRange(-1.0, 1.0)); + + audio_out_write(handle, static_cast(buffer), nbytes); + + free(buffer); +} + +class EchoCancellerOnCommand : public Command +{ +public: + explicit EchoCancellerOnCommand(std::shared_ptr& ec) : ec(ec) {} + virtual ~EchoCancellerOnCommand() = default; + void execute() const override { ec->on(); } + +private: + std::shared_ptr ec; +}; + +class EchoCancellerOffCommand : public Command +{ +public: + explicit EchoCancellerOffCommand(std::shared_ptr& ec) : ec(ec) {} + virtual ~EchoCancellerOffCommand() = default; + void execute() const override { ec->off(); } + +private: + std::shared_ptr ec; +}; + +class NoiseGeneratorOnCommand : public Command +{ +public: + explicit NoiseGeneratorOnCommand(std::shared_ptr& ng) : ng(ng) {} + virtual ~NoiseGeneratorOnCommand() = default; + void execute() const override { ng->on(); } + +private: + std::shared_ptr ng; +}; + +class NoiseGeneratorOffCommand : public Command +{ +public: + explicit NoiseGeneratorOffCommand(std::shared_ptr& ng) : ng(ng) {} + virtual ~NoiseGeneratorOffCommand() = default; + void execute() const override { ng->off(); } + +private: + std::shared_ptr ng; +}; + + +class RemoteControl +{ +public: + RemoteControl() = default; + + void addCommand(Receiver id, std::pair, std::shared_ptr> Commands) { + commands.insert({id, Commands}); + } + + void onButtonPressed(Receiver id) { + if (commands.find(id) != commands.end()) + commands[id].first->execute(); + } + + void offButtonPressed(Receiver id) { + if (commands.find(id) != commands.end()) + commands[id].second->execute(); + } + +private: + std::map, std::shared_ptr>> commands{}; +}; + +int capture_sound(char **buffer, int *length, int sec) +{ + int ret; + char *_buffer = NULL; + audio_in_h input = NULL; + int _length = 16000 * 1 * 2 * sec; // 16Khz, mono, S16LE + + if (!buffer) { + std::cout << "buffer is null" << std::endl; + return -1; + } + + ret = audio_in_create(16000, AUDIO_CHANNEL_MONO, AUDIO_SAMPLE_TYPE_S16_LE, &input); + if (ret != AUDIO_IO_ERROR_NONE) { + std::cerr << "failed to create audio_input" << std::endl; + return -1; + } + + ret = audio_in_prepare(input); + if (ret != AUDIO_IO_ERROR_NONE) { + std::cerr << "failed to prepare, " << ret << std::endl; + goto fail; + } + + _buffer = static_cast(malloc(_length)); + if (!_buffer) { + std::cerr << "_buffer is null" << std::endl; + goto fail; + } + + ret = audio_in_read(input, _buffer, _length); + if (ret <= 0) { + std::cerr << "failed to read, " << ret << ", " << _length << std::endl; + goto fail; + } + ret = audio_in_unprepare(input); + if (ret != AUDIO_IO_ERROR_NONE) { + std::cerr << "failed to unprepare, " << ret << std::endl; + goto fail; + } + + *buffer = _buffer; + *length = _length; + + return 0; + +fail: + if (input) + audio_in_destroy(input); + if (buffer) + free(buffer); + + return -1; +} + +int test_echo_cancellation(char **buffer, bool enable_ec, bool enable_ref, int time_sec) +{ + int ret; + char *_buffer = NULL; + int length = -1; + + auto ec = std::make_shared(); + auto ng = std::make_shared(); + + RemoteControl control; + control.addCommand(Receiver::ECHO_CANCELLER, + { std::make_shared(ec), + std::make_shared(ec) }); + control.addCommand(Receiver::NOISE_GENERATOR, + { std::make_shared(ng), + std::make_shared(ng) }); + + if (enable_ec) + control.onButtonPressed(Receiver::ECHO_CANCELLER); + if (enable_ref) + control.onButtonPressed(Receiver::NOISE_GENERATOR); + + std::cout << "Say something for " << time_sec << " seconds.. ec " << enable_ec << ", ref " << enable_ref << std::endl; + + ret = capture_sound(&_buffer, &length, time_sec); + + if (enable_ref) + control.offButtonPressed(Receiver::NOISE_GENERATOR); + if (enable_ec) + control.offButtonPressed(Receiver::ECHO_CANCELLER); + + if (ret == 0) + *buffer = _buffer; + else + free(_buffer); + + return length; +} + +int dump_to_file(const char *filename, char *buffer, int length) +{ + int fd = open(filename, O_CREAT|O_TRUNC|O_RDWR, 0644); + if (fd < 0) { + std::cerr << "failed to open file" << std::endl; + return -1; + } + + ssize_t ret = write(fd, buffer, length); + if (ret < 0) + std::cerr << "failed to write" << std::endl; + + close(fd); + + return 0; +} +#endif + +int main(int argc, char **argv) +{ +#ifndef TIZEN_FEATURE_TV_PROD + bool enable_ec; + bool enable_ref; + int time_sec; + char *filename; + + char *buffer = NULL; + int size; + + if (argc != 5) { + std::cout << "- Usages :" << std::endl; + std::cout << "- # audio_io_test_ec [filename] [on/off 0:1] [noise 0:1] [seconds]" << std::endl; + return -1; + } + + filename = argv[1]; + enable_ec = !!atoi(argv[2]); + enable_ref = !!atoi(argv[3]); + time_sec = atoi(argv[4]); + + size = test_echo_cancellation(&buffer, enable_ec, enable_ref, time_sec); + if (size <= 0) { + std::cerr << "failed to test aec" << std::endl; + return -1; + } + + if (dump_to_file(filename, buffer, size)) + std::cerr << "failed to save file, " << filename << std::endl; + if (buffer) + free(buffer); +#else + std::cout << "not supported yet" << std::endl; +#endif + + return 0; +} \ No newline at end of file