Path: blob/master/tests/scene/test_audio_stream_wav.cpp
45993 views
/**************************************************************************/1/* test_audio_stream_wav.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "tests/test_macros.h"3132TEST_FORCE_LINK(test_audio_stream_wav)3334#include "core/io/file_access.h"35#include "core/io/marshalls.h"36#include "core/math/math_defs.h"37#include "core/math/math_funcs.h"38#include "scene/resources/audio_stream_wav.h"39#include "tests/test_utils.h"4041namespace TestAudioStreamWAV {4243// Default wav rate for test cases.44constexpr float WAV_RATE = 44100;45/* Default wav count for test cases. 1 second of audio is used so that the file can be listened46to manually if needed. */47constexpr int WAV_COUNT = WAV_RATE;4849float gen_wav(float frequency, float wav_rate, int wav_number) {50// formula for generating a sin wave with given frequency.51return Math::sin((Math::TAU * frequency / wav_rate) * wav_number);52}5354/* Generates a 440Hz sin wave in channel 0 (mono channel or left stereo channel)55* and a 261.63Hz wave in channel 1 (right stereo channel).56* These waves correspond to the music notes A4 and C4 respectively.57*/58Vector<uint8_t> gen_pcm8_test(float wav_rate, int wav_count, bool stereo) {59Vector<uint8_t> buffer;60buffer.resize(stereo ? wav_count * 2 : wav_count);6162uint8_t *write_ptr = buffer.ptrw();63for (int i = 0; i < buffer.size(); i++) {64float wav;65if (stereo) {66if (i % 2 == 0) {67wav = gen_wav(440, wav_rate, i / 2);68} else {69wav = gen_wav(261.63, wav_rate, i / 2);70}71} else {72wav = gen_wav(440, wav_rate, i);73}7475// Map sin wave to full range of 8-bit values.76uint8_t wav_8bit = Math::fast_ftoi(((wav + 1) / 2) * UINT8_MAX);77// Unlike the .wav format, AudioStreamWAV expects signed 8-bit wavs.78uint8_t wav_8bit_signed = wav_8bit - (INT8_MAX + 1);79write_ptr[i] = wav_8bit_signed;80}8182return buffer;83}8485// Same as gen_pcm8_test but with 16-bit wavs.86Vector<uint8_t> gen_pcm16_test(float wav_rate, int wav_count, bool stereo) {87Vector<uint8_t> buffer;88buffer.resize(stereo ? wav_count * 4 : wav_count * 2);8990uint8_t *write_ptr = buffer.ptrw();91for (int i = 0; i < buffer.size() / 2; i++) {92float wav;93if (stereo) {94if (i % 2 == 0) {95wav = gen_wav(440, wav_rate, i / 2);96} else {97wav = gen_wav(261.63, wav_rate, i / 2);98}99} else {100wav = gen_wav(440, wav_rate, i);101}102103// Map sin wave to full range of 16-bit values.104uint16_t wav_16bit = Math::fast_ftoi(((wav + 1) / 2) * UINT16_MAX);105// The .wav format expects wavs larger than 8 bits to be signed.106uint16_t wav_16bit_signed = wav_16bit - (INT16_MAX + 1);107encode_uint16(wav_16bit_signed, write_ptr + (i * 2));108}109110return buffer;111}112113void run_test(String file_name, AudioStreamWAV::Format data_format, bool stereo, float wav_rate, float wav_count) {114String save_path = TestUtils::get_temp_path(file_name);115116Vector<uint8_t> test_data;117if (data_format == AudioStreamWAV::FORMAT_8_BITS) {118test_data = gen_pcm8_test(wav_rate, wav_count, stereo);119} else {120test_data = gen_pcm16_test(wav_rate, wav_count, stereo);121}122123Ref<AudioStreamWAV> stream = memnew(AudioStreamWAV);124stream->set_mix_rate(wav_rate);125CHECK(stream->get_mix_rate() == wav_rate);126127stream->set_format(data_format);128CHECK(stream->get_format() == data_format);129130stream->set_stereo(stereo);131CHECK(stream->is_stereo() == stereo);132133stream->set_data(test_data);134CHECK(stream->get_data() == test_data);135136SUBCASE("Stream length is computed properly") {137CHECK(stream->get_length() == doctest::Approx(double(wav_count / wav_rate)));138}139140SUBCASE("Stream can be saved as .wav") {141REQUIRE(stream->save_to_wav(save_path) == OK);142143Error error;144Ref<FileAccess> wav_file = FileAccess::open(save_path, FileAccess::READ, &error);145REQUIRE(error == OK);146147Dictionary options;148Ref<AudioStreamWAV> loaded_stream = AudioStreamWAV::load_from_file(save_path, options);149150CHECK(loaded_stream->get_format() == stream->get_format());151CHECK(loaded_stream->get_loop_mode() == stream->get_loop_mode());152CHECK(loaded_stream->get_loop_begin() == stream->get_loop_begin());153CHECK(loaded_stream->get_loop_end() == stream->get_loop_end());154CHECK(loaded_stream->get_mix_rate() == stream->get_mix_rate());155CHECK(loaded_stream->is_stereo() == stream->is_stereo());156CHECK(loaded_stream->get_length() == stream->get_length());157CHECK(loaded_stream->is_monophonic() == stream->is_monophonic());158CHECK(loaded_stream->get_data() == stream->get_data());159}160}161162TEST_CASE("[Audio][AudioStreamWAV] Mono PCM8 format") {163run_test("test_pcm8_mono.wav", AudioStreamWAV::FORMAT_8_BITS, false, WAV_RATE, WAV_COUNT);164}165166TEST_CASE("[Audio][AudioStreamWAV] Mono PCM16 format") {167run_test("test_pcm16_mono.wav", AudioStreamWAV::FORMAT_16_BITS, false, WAV_RATE, WAV_COUNT);168}169170TEST_CASE("[Audio][AudioStreamWAV] Stereo PCM8 format") {171run_test("test_pcm8_stereo.wav", AudioStreamWAV::FORMAT_8_BITS, true, WAV_RATE, WAV_COUNT);172}173174TEST_CASE("[Audio][AudioStreamWAV] Stereo PCM16 format") {175run_test("test_pcm16_stereo.wav", AudioStreamWAV::FORMAT_16_BITS, true, WAV_RATE, WAV_COUNT);176}177178TEST_CASE("[Audio][AudioStreamWAV] Alternate mix rate") {179run_test("test_pcm16_stereo_38000Hz.wav", AudioStreamWAV::FORMAT_16_BITS, true, 38000, 38000);180}181182TEST_CASE("[Audio][AudioStreamWAV] save_to_wav() adds '.wav' file extension automatically") {183String save_path = TestUtils::get_temp_path("test_wav_extension");184Vector<uint8_t> test_data = gen_pcm8_test(WAV_RATE, WAV_COUNT, false);185Ref<AudioStreamWAV> stream = memnew(AudioStreamWAV);186stream->set_data(test_data);187188REQUIRE(stream->save_to_wav(save_path) == OK);189Error error;190Ref<FileAccess> wav_file = FileAccess::open(save_path + ".wav", FileAccess::READ, &error);191CHECK(error == OK);192}193194TEST_CASE("[Audio][AudioStreamWAV] Default values") {195Ref<AudioStreamWAV> stream = memnew(AudioStreamWAV);196CHECK(stream->get_format() == AudioStreamWAV::FORMAT_8_BITS);197CHECK(stream->get_loop_mode() == AudioStreamWAV::LOOP_DISABLED);198CHECK(stream->get_loop_begin() == 0);199CHECK(stream->get_loop_end() == 0);200CHECK(stream->get_mix_rate() == 44100);201CHECK(stream->is_stereo() == false);202CHECK(stream->get_length() == 0);203CHECK(stream->is_monophonic() == false);204CHECK(stream->get_data() == Vector<uint8_t>{});205CHECK(stream->get_stream_name() == "");206}207208TEST_CASE("[Audio][AudioStreamWAV] Save empty file") {209run_test("test_empty.wav", AudioStreamWAV::FORMAT_8_BITS, false, WAV_RATE, 0);210}211212TEST_CASE("[Audio][AudioStreamWAV] Saving IMA ADPCM is not supported") {213String save_path = TestUtils::get_temp_path("test_adpcm.wav");214Ref<AudioStreamWAV> stream = memnew(AudioStreamWAV);215stream->set_format(AudioStreamWAV::FORMAT_IMA_ADPCM);216ERR_PRINT_OFF;217CHECK(stream->save_to_wav(save_path) == ERR_UNAVAILABLE);218ERR_PRINT_ON;219}220221} // namespace TestAudioStreamWAV222223224