Path: blob/master/servers/audio/effects/audio_effect_record.cpp
10278 views
/**************************************************************************/1/* audio_effect_record.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 "audio_effect_record.h"3132#include "core/io/marshalls.h"3334void AudioEffectRecordInstance::process(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) {35if (!is_recording) {36for (int i = 0; i < p_frame_count; i++) {37p_dst_frames[i] = p_src_frames[i];38}39return;40}4142//Add incoming audio frames to the IO ring buffer43const AudioFrame *src = p_src_frames;44AudioFrame *rb_buf = ring_buffer.ptrw();45for (int i = 0; i < p_frame_count; i++) {46p_dst_frames[i] = p_src_frames[i];47rb_buf[ring_buffer_pos & ring_buffer_mask] = src[i];48ring_buffer_pos++;49}50}5152void AudioEffectRecordInstance::_update_buffer() {53//Case: Frames are remaining in the buffer54while (ring_buffer_read_pos < ring_buffer_pos) {55//Read from the buffer into recording_data56_io_store_buffer();57}58}5960void AudioEffectRecordInstance::_update(void *userdata) {61AudioEffectRecordInstance *ins = static_cast<AudioEffectRecordInstance *>(userdata);62ins->_update_buffer();63}6465bool AudioEffectRecordInstance::process_silence() const {66return true;67}6869void AudioEffectRecordInstance::_io_thread_process() {70while (is_recording) {71_update_buffer();72if (is_recording) {73//Wait to avoid too much busy-wait74OS::get_singleton()->delay_usec(500);75}76}77}7879void AudioEffectRecordInstance::_io_store_buffer() {80int to_read = ring_buffer_pos - ring_buffer_read_pos;8182AudioFrame *rb_buf = ring_buffer.ptrw();8384while (to_read) {85AudioFrame buffered_frame = rb_buf[ring_buffer_read_pos & ring_buffer_mask];86recording_data.push_back(buffered_frame.left);87recording_data.push_back(buffered_frame.right);8889ring_buffer_read_pos++;90to_read--;91}92}9394void AudioEffectRecordInstance::_thread_callback(void *_instance) {95AudioEffectRecordInstance *aeri = reinterpret_cast<AudioEffectRecordInstance *>(_instance);9697aeri->_io_thread_process();98}99100void AudioEffectRecordInstance::init() {101//Reset recorder status102ring_buffer_pos = 0;103ring_buffer_read_pos = 0;104105//We start a new recording106recording_data.clear(); //Clear data completely and reset length107is_recording = true;108109io_thread.start(_thread_callback, this);110}111112void AudioEffectRecordInstance::finish() {113is_recording = false;114if (io_thread.is_started()) {115io_thread.wait_to_finish();116}117}118119Ref<AudioEffectInstance> AudioEffectRecord::instantiate() {120Ref<AudioEffectRecordInstance> ins;121ins.instantiate();122ins->is_recording = false;123124// Reusing the buffer size calculations from audio_effect_delay.cpp.125float ring_buffer_max_size = IO_BUFFER_SIZE_MS;126ring_buffer_max_size /= 1000.0; //convert to seconds127ring_buffer_max_size *= AudioServer::get_singleton()->get_mix_rate();128129int ringbuff_size = ring_buffer_max_size;130131int bits = 0;132133while (ringbuff_size > 0) {134bits++;135ringbuff_size /= 2;136}137138ringbuff_size = 1 << bits;139ins->ring_buffer_mask = ringbuff_size - 1;140ins->ring_buffer_pos = 0;141142ins->ring_buffer.resize(ringbuff_size);143144ins->ring_buffer_read_pos = 0;145146ensure_thread_stopped();147bool is_currently_recording = false;148if (current_instance.is_valid()) {149is_currently_recording = current_instance->is_recording;150}151if (is_currently_recording) {152ins->init();153}154current_instance = ins;155156return ins;157}158159void AudioEffectRecord::ensure_thread_stopped() {160if (current_instance.is_valid()) {161current_instance->finish();162}163}164165void AudioEffectRecord::set_recording_active(bool p_record) {166if (p_record) {167if (current_instance.is_null()) {168WARN_PRINT("Recording should not be set as active before Godot has initialized.");169return;170}171ensure_thread_stopped();172current_instance->init();173} else {174if (current_instance.is_valid()) {175current_instance->is_recording = false;176}177}178}179180bool AudioEffectRecord::is_recording_active() const {181if (current_instance.is_null()) {182return false;183} else {184return current_instance->is_recording;185}186}187188void AudioEffectRecord::set_format(AudioStreamWAV::Format p_format) {189format = p_format;190}191192AudioStreamWAV::Format AudioEffectRecord::get_format() const {193return format;194}195196Ref<AudioStreamWAV> AudioEffectRecord::get_recording() const {197AudioStreamWAV::Format dst_format = format;198bool stereo = true; //forcing mono is not implemented199200Vector<uint8_t> dst_data;201202ERR_FAIL_COND_V(current_instance.is_null(), nullptr);203ERR_FAIL_COND_V(current_instance->recording_data.is_empty(), nullptr);204205if (dst_format == AudioStreamWAV::FORMAT_8_BITS) {206int data_size = current_instance->recording_data.size();207dst_data.resize(data_size);208uint8_t *w = dst_data.ptrw();209210for (int i = 0; i < data_size; i++) {211int8_t v = CLAMP(current_instance->recording_data[i] * 128, -128, 127);212w[i] = v;213}214} else if (dst_format == AudioStreamWAV::FORMAT_16_BITS) {215int data_size = current_instance->recording_data.size();216dst_data.resize(data_size * 2);217uint8_t *w = dst_data.ptrw();218219for (int i = 0; i < data_size; i++) {220int16_t v = CLAMP(current_instance->recording_data[i] * 32768, -32768, 32767);221encode_uint16(v, &w[i * 2]);222}223} else if (dst_format == AudioStreamWAV::FORMAT_IMA_ADPCM) {224//byte interleave225Vector<float> left;226Vector<float> right;227228int tframes = current_instance->recording_data.size() / 2;229left.resize(tframes);230right.resize(tframes);231232for (int i = 0; i < tframes; i++) {233left.set(i, current_instance->recording_data[i * 2 + 0]);234right.set(i, current_instance->recording_data[i * 2 + 1]);235}236237Vector<uint8_t> bleft;238Vector<uint8_t> bright;239240AudioStreamWAV::_compress_ima_adpcm(left, bleft);241AudioStreamWAV::_compress_ima_adpcm(right, bright);242243int dl = bleft.size();244dst_data.resize(dl * 2);245246uint8_t *w = dst_data.ptrw();247const uint8_t *rl = bleft.ptr();248const uint8_t *rr = bright.ptr();249250for (int i = 0; i < dl; i++) {251w[i * 2 + 0] = rl[i];252w[i * 2 + 1] = rr[i];253}254} else if (dst_format == AudioStreamWAV::FORMAT_QOA) {255qoa_desc desc = {};256desc.samples = current_instance->recording_data.size() / 2;257desc.samplerate = AudioServer::get_singleton()->get_mix_rate();258desc.channels = 2;259AudioStreamWAV::_compress_qoa(current_instance->recording_data, dst_data, &desc);260} else {261ERR_PRINT("Format not implemented.");262}263264Ref<AudioStreamWAV> sample;265sample.instantiate();266sample->set_data(dst_data);267sample->set_format(dst_format);268sample->set_mix_rate(AudioServer::get_singleton()->get_mix_rate());269sample->set_loop_mode(AudioStreamWAV::LOOP_DISABLED);270sample->set_loop_begin(0);271sample->set_loop_end(0);272sample->set_stereo(stereo);273274return sample;275}276277void AudioEffectRecord::_bind_methods() {278ClassDB::bind_method(D_METHOD("set_recording_active", "record"), &AudioEffectRecord::set_recording_active);279ClassDB::bind_method(D_METHOD("is_recording_active"), &AudioEffectRecord::is_recording_active);280ClassDB::bind_method(D_METHOD("set_format", "format"), &AudioEffectRecord::set_format);281ClassDB::bind_method(D_METHOD("get_format"), &AudioEffectRecord::get_format);282ClassDB::bind_method(D_METHOD("get_recording"), &AudioEffectRecord::get_recording);283284ADD_PROPERTY(PropertyInfo(Variant::INT, "format", PROPERTY_HINT_ENUM, "8-Bit,16-Bit,IMA ADPCM,Quite OK Audio"), "set_format", "get_format");285}286287AudioEffectRecord::AudioEffectRecord() {288format = AudioStreamWAV::FORMAT_16_BITS;289}290291AudioEffectRecord::~AudioEffectRecord() {292ensure_thread_stopped();293}294295296