1/**************************************************************************/
2/* audio_effect_record.cpp */
3/**************************************************************************/
4/* This file is part of: */
5/* GODOT ENGINE */
6/* https://godotengine.org */
7/**************************************************************************/
8/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10/* */
11/* Permission is hereby granted, free of charge, to any person obtaining */
12/* a copy of this software and associated documentation files (the */
13/* "Software"), to deal in the Software without restriction, including */
14/* without limitation the rights to use, copy, modify, merge, publish, */
15/* distribute, sublicense, and/or sell copies of the Software, and to */
16/* permit persons to whom the Software is furnished to do so, subject to */
17/* the following conditions: */
18/* */
19/* The above copyright notice and this permission notice shall be */
20/* included in all copies or substantial portions of the Software. */
21/* */
22/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29/**************************************************************************/
30
31#include "audio_effect_record.h"
32
33#ifdef TOOLS_ENABLED
34// FIXME: This file shouldn't depend on editor stuff.
35#include "editor/import/resource_importer_wav.h"
36#endif
37
38void AudioEffectRecordInstance::process(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) {
39 if (!is_recording) {
40 for (int i = 0; i < p_frame_count; i++) {
41 p_dst_frames[i] = p_src_frames[i];
42 }
43 return;
44 }
45
46 //Add incoming audio frames to the IO ring buffer
47 const AudioFrame *src = p_src_frames;
48 AudioFrame *rb_buf = ring_buffer.ptrw();
49 for (int i = 0; i < p_frame_count; i++) {
50 p_dst_frames[i] = p_src_frames[i];
51 rb_buf[ring_buffer_pos & ring_buffer_mask] = src[i];
52 ring_buffer_pos++;
53 }
54}
55
56void AudioEffectRecordInstance::_update_buffer() {
57 //Case: Frames are remaining in the buffer
58 while (ring_buffer_read_pos < ring_buffer_pos) {
59 //Read from the buffer into recording_data
60 _io_store_buffer();
61 }
62}
63
64void AudioEffectRecordInstance::_update(void *userdata) {
65 AudioEffectRecordInstance *ins = static_cast<AudioEffectRecordInstance *>(userdata);
66 ins->_update_buffer();
67}
68
69bool AudioEffectRecordInstance::process_silence() const {
70 return true;
71}
72
73void AudioEffectRecordInstance::_io_thread_process() {
74 while (is_recording) {
75 _update_buffer();
76 if (is_recording) {
77 //Wait to avoid too much busy-wait
78 OS::get_singleton()->delay_usec(500);
79 }
80 }
81}
82
83void AudioEffectRecordInstance::_io_store_buffer() {
84 int to_read = ring_buffer_pos - ring_buffer_read_pos;
85
86 AudioFrame *rb_buf = ring_buffer.ptrw();
87
88 while (to_read) {
89 AudioFrame buffered_frame = rb_buf[ring_buffer_read_pos & ring_buffer_mask];
90 recording_data.push_back(buffered_frame.l);
91 recording_data.push_back(buffered_frame.r);
92
93 ring_buffer_read_pos++;
94 to_read--;
95 }
96}
97
98void AudioEffectRecordInstance::_thread_callback(void *_instance) {
99 AudioEffectRecordInstance *aeri = reinterpret_cast<AudioEffectRecordInstance *>(_instance);
100
101 aeri->_io_thread_process();
102}
103
104void AudioEffectRecordInstance::init() {
105 //Reset recorder status
106 ring_buffer_pos = 0;
107 ring_buffer_read_pos = 0;
108
109 //We start a new recording
110 recording_data.clear(); //Clear data completely and reset length
111 is_recording = true;
112
113 io_thread.start(_thread_callback, this);
114}
115
116void AudioEffectRecordInstance::finish() {
117 is_recording = false;
118 if (io_thread.is_started()) {
119 io_thread.wait_to_finish();
120 }
121}
122
123Ref<AudioEffectInstance> AudioEffectRecord::instantiate() {
124 Ref<AudioEffectRecordInstance> ins;
125 ins.instantiate();
126 ins->is_recording = false;
127
128 //Re-using the buffer size calculations from audio_effect_delay.cpp
129 float ring_buffer_max_size = IO_BUFFER_SIZE_MS;
130 ring_buffer_max_size /= 1000.0; //convert to seconds
131 ring_buffer_max_size *= AudioServer::get_singleton()->get_mix_rate();
132
133 int ringbuff_size = ring_buffer_max_size;
134
135 int bits = 0;
136
137 while (ringbuff_size > 0) {
138 bits++;
139 ringbuff_size /= 2;
140 }
141
142 ringbuff_size = 1 << bits;
143 ins->ring_buffer_mask = ringbuff_size - 1;
144 ins->ring_buffer_pos = 0;
145
146 ins->ring_buffer.resize(ringbuff_size);
147
148 ins->ring_buffer_read_pos = 0;
149
150 ensure_thread_stopped();
151 bool is_currently_recording = false;
152 if (current_instance != nullptr) {
153 is_currently_recording = current_instance->is_recording;
154 }
155 if (is_currently_recording) {
156 ins->init();
157 }
158 current_instance = ins;
159
160 return ins;
161}
162
163void AudioEffectRecord::ensure_thread_stopped() {
164 if (current_instance != nullptr) {
165 current_instance->finish();
166 }
167}
168
169void AudioEffectRecord::set_recording_active(bool p_record) {
170 if (p_record) {
171 if (current_instance == nullptr) {
172 WARN_PRINT("Recording should not be set as active before Godot has initialized.");
173 return;
174 }
175 ensure_thread_stopped();
176 current_instance->init();
177 } else {
178 if (current_instance != nullptr) {
179 current_instance->is_recording = false;
180 }
181 }
182}
183
184bool AudioEffectRecord::is_recording_active() const {
185 if (current_instance == nullptr) {
186 return false;
187 } else {
188 return current_instance->is_recording;
189 }
190}
191
192void AudioEffectRecord::set_format(AudioStreamWAV::Format p_format) {
193 format = p_format;
194}
195
196AudioStreamWAV::Format AudioEffectRecord::get_format() const {
197 return format;
198}
199
200Ref<AudioStreamWAV> AudioEffectRecord::get_recording() const {
201 AudioStreamWAV::Format dst_format = format;
202 bool stereo = true; //forcing mono is not implemented
203
204 Vector<uint8_t> dst_data;
205
206 ERR_FAIL_COND_V(current_instance.is_null(), nullptr);
207 ERR_FAIL_COND_V(current_instance->recording_data.size() == 0, nullptr);
208
209 if (dst_format == AudioStreamWAV::FORMAT_8_BITS) {
210 int data_size = current_instance->recording_data.size();
211 dst_data.resize(data_size);
212 uint8_t *w = dst_data.ptrw();
213
214 for (int i = 0; i < data_size; i++) {
215 int8_t v = CLAMP(current_instance->recording_data[i] * 128, -128, 127);
216 w[i] = v;
217 }
218 } else if (dst_format == AudioStreamWAV::FORMAT_16_BITS) {
219 int data_size = current_instance->recording_data.size();
220 dst_data.resize(data_size * 2);
221 uint8_t *w = dst_data.ptrw();
222
223 for (int i = 0; i < data_size; i++) {
224 int16_t v = CLAMP(current_instance->recording_data[i] * 32768, -32768, 32767);
225 encode_uint16(v, &w[i * 2]);
226 }
227 } else if (dst_format == AudioStreamWAV::FORMAT_IMA_ADPCM) {
228 //byte interleave
229 Vector<float> left;
230 Vector<float> right;
231
232 int tframes = current_instance->recording_data.size() / 2;
233 left.resize(tframes);
234 right.resize(tframes);
235
236 for (int i = 0; i < tframes; i++) {
237 left.set(i, current_instance->recording_data[i * 2 + 0]);
238 right.set(i, current_instance->recording_data[i * 2 + 1]);
239 }
240
241 Vector<uint8_t> bleft;
242 Vector<uint8_t> bright;
243
244#ifdef TOOLS_ENABLED
245 ResourceImporterWAV::_compress_ima_adpcm(left, bleft);
246 ResourceImporterWAV::_compress_ima_adpcm(right, bright);
247#else
248 ERR_PRINT("AudioEffectRecord cannot do IMA ADPCM compression at runtime.");
249#endif
250
251 int dl = bleft.size();
252 dst_data.resize(dl * 2);
253
254 uint8_t *w = dst_data.ptrw();
255 const uint8_t *rl = bleft.ptr();
256 const uint8_t *rr = bright.ptr();
257
258 for (int i = 0; i < dl; i++) {
259 w[i * 2 + 0] = rl[i];
260 w[i * 2 + 1] = rr[i];
261 }
262 } else {
263 ERR_PRINT("Format not implemented.");
264 }
265
266 Ref<AudioStreamWAV> sample;
267 sample.instantiate();
268 sample->set_data(dst_data);
269 sample->set_format(dst_format);
270 sample->set_mix_rate(AudioServer::get_singleton()->get_mix_rate());
271 sample->set_loop_mode(AudioStreamWAV::LOOP_DISABLED);
272 sample->set_loop_begin(0);
273 sample->set_loop_end(0);
274 sample->set_stereo(stereo);
275
276 return sample;
277}
278
279void AudioEffectRecord::_bind_methods() {
280 ClassDB::bind_method(D_METHOD("set_recording_active", "record"), &AudioEffectRecord::set_recording_active);
281 ClassDB::bind_method(D_METHOD("is_recording_active"), &AudioEffectRecord::is_recording_active);
282 ClassDB::bind_method(D_METHOD("set_format", "format"), &AudioEffectRecord::set_format);
283 ClassDB::bind_method(D_METHOD("get_format"), &AudioEffectRecord::get_format);
284 ClassDB::bind_method(D_METHOD("get_recording"), &AudioEffectRecord::get_recording);
285
286 ADD_PROPERTY(PropertyInfo(Variant::INT, "format", PROPERTY_HINT_ENUM, "8-Bit,16-Bit,IMA-ADPCM"), "set_format", "get_format");
287}
288
289AudioEffectRecord::AudioEffectRecord() {
290 format = AudioStreamWAV::FORMAT_16_BITS;
291}
292
293AudioEffectRecord::~AudioEffectRecord() {
294 ensure_thread_stopped();
295}
296