1/**************************************************************************/
2/* audio_stream_wav.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_stream_wav.h"
32
33#include "core/io/file_access.h"
34#include "core/io/marshalls.h"
35
36void AudioStreamPlaybackWAV::start(double p_from_pos) {
37 if (base->format == AudioStreamWAV::FORMAT_IMA_ADPCM) {
38 //no seeking in IMA_ADPCM
39 for (int i = 0; i < 2; i++) {
40 ima_adpcm[i].step_index = 0;
41 ima_adpcm[i].predictor = 0;
42 ima_adpcm[i].loop_step_index = 0;
43 ima_adpcm[i].loop_predictor = 0;
44 ima_adpcm[i].last_nibble = -1;
45 ima_adpcm[i].loop_pos = 0x7FFFFFFF;
46 ima_adpcm[i].window_ofs = 0;
47 }
48
49 offset = 0;
50 } else {
51 seek(p_from_pos);
52 }
53
54 sign = 1;
55 active = true;
56}
57
58void AudioStreamPlaybackWAV::stop() {
59 active = false;
60}
61
62bool AudioStreamPlaybackWAV::is_playing() const {
63 return active;
64}
65
66int AudioStreamPlaybackWAV::get_loop_count() const {
67 return 0;
68}
69
70double AudioStreamPlaybackWAV::get_playback_position() const {
71 return float(offset >> MIX_FRAC_BITS) / base->mix_rate;
72}
73
74void AudioStreamPlaybackWAV::seek(double p_time) {
75 if (base->format == AudioStreamWAV::FORMAT_IMA_ADPCM) {
76 return; //no seeking in ima-adpcm
77 }
78
79 double max = base->get_length();
80 if (p_time < 0) {
81 p_time = 0;
82 } else if (p_time >= max) {
83 p_time = max - 0.001;
84 }
85
86 offset = uint64_t(p_time * base->mix_rate) << MIX_FRAC_BITS;
87}
88
89template <class Depth, bool is_stereo, bool is_ima_adpcm>
90void AudioStreamPlaybackWAV::do_resample(const Depth *p_src, AudioFrame *p_dst, int64_t &p_offset, int32_t &p_increment, uint32_t p_amount, IMA_ADPCM_State *p_ima_adpcm) {
91 // this function will be compiled branchless by any decent compiler
92
93 int32_t final, final_r, next, next_r;
94 while (p_amount) {
95 p_amount--;
96 int64_t pos = p_offset >> MIX_FRAC_BITS;
97 if (is_stereo && !is_ima_adpcm) {
98 pos <<= 1;
99 }
100
101 if (is_ima_adpcm) {
102 int64_t sample_pos = pos + p_ima_adpcm[0].window_ofs;
103
104 while (sample_pos > p_ima_adpcm[0].last_nibble) {
105 static const int16_t _ima_adpcm_step_table[89] = {
106 7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
107 19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
108 50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
109 130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
110 337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
111 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
112 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
113 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
114 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
115 };
116
117 static const int8_t _ima_adpcm_index_table[16] = {
118 -1, -1, -1, -1, 2, 4, 6, 8,
119 -1, -1, -1, -1, 2, 4, 6, 8
120 };
121
122 for (int i = 0; i < (is_stereo ? 2 : 1); i++) {
123 int16_t nibble, diff, step;
124
125 p_ima_adpcm[i].last_nibble++;
126 const uint8_t *src_ptr = (const uint8_t *)base->data;
127 src_ptr += AudioStreamWAV::DATA_PAD;
128
129 uint8_t nbb = src_ptr[(p_ima_adpcm[i].last_nibble >> 1) * (is_stereo ? 2 : 1) + i];
130 nibble = (p_ima_adpcm[i].last_nibble & 1) ? (nbb >> 4) : (nbb & 0xF);
131 step = _ima_adpcm_step_table[p_ima_adpcm[i].step_index];
132
133 p_ima_adpcm[i].step_index += _ima_adpcm_index_table[nibble];
134 if (p_ima_adpcm[i].step_index < 0) {
135 p_ima_adpcm[i].step_index = 0;
136 }
137 if (p_ima_adpcm[i].step_index > 88) {
138 p_ima_adpcm[i].step_index = 88;
139 }
140
141 diff = step >> 3;
142 if (nibble & 1) {
143 diff += step >> 2;
144 }
145 if (nibble & 2) {
146 diff += step >> 1;
147 }
148 if (nibble & 4) {
149 diff += step;
150 }
151 if (nibble & 8) {
152 diff = -diff;
153 }
154
155 p_ima_adpcm[i].predictor += diff;
156 if (p_ima_adpcm[i].predictor < -0x8000) {
157 p_ima_adpcm[i].predictor = -0x8000;
158 } else if (p_ima_adpcm[i].predictor > 0x7FFF) {
159 p_ima_adpcm[i].predictor = 0x7FFF;
160 }
161
162 /* store loop if there */
163 if (p_ima_adpcm[i].last_nibble == p_ima_adpcm[i].loop_pos) {
164 p_ima_adpcm[i].loop_step_index = p_ima_adpcm[i].step_index;
165 p_ima_adpcm[i].loop_predictor = p_ima_adpcm[i].predictor;
166 }
167
168 //printf("%i - %i - pred %i\n",int(p_ima_adpcm[i].last_nibble),int(nibble),int(p_ima_adpcm[i].predictor));
169 }
170 }
171
172 final = p_ima_adpcm[0].predictor;
173 if (is_stereo) {
174 final_r = p_ima_adpcm[1].predictor;
175 }
176
177 } else {
178 final = p_src[pos];
179 if (is_stereo) {
180 final_r = p_src[pos + 1];
181 }
182
183 if constexpr (sizeof(Depth) == 1) { /* conditions will not exist anymore when compiled! */
184 final <<= 8;
185 if (is_stereo) {
186 final_r <<= 8;
187 }
188 }
189
190 if (is_stereo) {
191 next = p_src[pos + 2];
192 next_r = p_src[pos + 3];
193 } else {
194 next = p_src[pos + 1];
195 }
196
197 if constexpr (sizeof(Depth) == 1) {
198 next <<= 8;
199 if (is_stereo) {
200 next_r <<= 8;
201 }
202 }
203
204 int32_t frac = int64_t(p_offset & MIX_FRAC_MASK);
205
206 final = final + ((next - final) * frac >> MIX_FRAC_BITS);
207 if (is_stereo) {
208 final_r = final_r + ((next_r - final_r) * frac >> MIX_FRAC_BITS);
209 }
210 }
211
212 if (!is_stereo) {
213 final_r = final; //copy to right channel if stereo
214 }
215
216 p_dst->l = final / 32767.0;
217 p_dst->r = final_r / 32767.0;
218 p_dst++;
219
220 p_offset += p_increment;
221 }
222}
223
224int AudioStreamPlaybackWAV::mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) {
225 if (!base->data || !active) {
226 for (int i = 0; i < p_frames; i++) {
227 p_buffer[i] = AudioFrame(0, 0);
228 }
229 return 0;
230 }
231
232 int len = base->data_bytes;
233 switch (base->format) {
234 case AudioStreamWAV::FORMAT_8_BITS:
235 len /= 1;
236 break;
237 case AudioStreamWAV::FORMAT_16_BITS:
238 len /= 2;
239 break;
240 case AudioStreamWAV::FORMAT_IMA_ADPCM:
241 len *= 2;
242 break;
243 }
244
245 if (base->stereo) {
246 len /= 2;
247 }
248
249 /* some 64-bit fixed point precaches */
250
251 int64_t loop_begin_fp = ((int64_t)base->loop_begin << MIX_FRAC_BITS);
252 int64_t loop_end_fp = ((int64_t)base->loop_end << MIX_FRAC_BITS);
253 int64_t length_fp = ((int64_t)len << MIX_FRAC_BITS);
254 int64_t begin_limit = (base->loop_mode != AudioStreamWAV::LOOP_DISABLED) ? loop_begin_fp : 0;
255 int64_t end_limit = (base->loop_mode != AudioStreamWAV::LOOP_DISABLED) ? loop_end_fp : length_fp;
256 bool is_stereo = base->stereo;
257
258 int32_t todo = p_frames;
259
260 if (base->loop_mode == AudioStreamWAV::LOOP_BACKWARD) {
261 sign = -1;
262 }
263
264 float base_rate = AudioServer::get_singleton()->get_mix_rate();
265 float srate = base->mix_rate;
266 srate *= p_rate_scale;
267 float playback_speed_scale = AudioServer::get_singleton()->get_playback_speed_scale();
268 float fincrement = (srate * playback_speed_scale) / base_rate;
269 int32_t increment = int32_t(MAX(fincrement * MIX_FRAC_LEN, 1));
270 increment *= sign;
271
272 //looping
273
274 AudioStreamWAV::LoopMode loop_format = base->loop_mode;
275 AudioStreamWAV::Format format = base->format;
276
277 /* audio data */
278
279 uint8_t *dataptr = (uint8_t *)base->data;
280 const void *data = dataptr + AudioStreamWAV::DATA_PAD;
281 AudioFrame *dst_buff = p_buffer;
282
283 if (format == AudioStreamWAV::FORMAT_IMA_ADPCM) {
284 if (loop_format != AudioStreamWAV::LOOP_DISABLED) {
285 ima_adpcm[0].loop_pos = loop_begin_fp >> MIX_FRAC_BITS;
286 ima_adpcm[1].loop_pos = loop_begin_fp >> MIX_FRAC_BITS;
287 loop_format = AudioStreamWAV::LOOP_FORWARD;
288 }
289 }
290
291 while (todo > 0) {
292 int64_t limit = 0;
293 int32_t target = 0, aux = 0;
294
295 /** LOOP CHECKING **/
296
297 if (increment < 0) {
298 /* going backwards */
299
300 if (loop_format != AudioStreamWAV::LOOP_DISABLED && offset < loop_begin_fp) {
301 /* loopstart reached */
302 if (loop_format == AudioStreamWAV::LOOP_PINGPONG) {
303 /* bounce ping pong */
304 offset = loop_begin_fp + (loop_begin_fp - offset);
305 increment = -increment;
306 sign *= -1;
307 } else {
308 /* go to loop-end */
309 offset = loop_end_fp - (loop_begin_fp - offset);
310 }
311 } else {
312 /* check for sample not reaching beginning */
313 if (offset < 0) {
314 active = false;
315 break;
316 }
317 }
318 } else {
319 /* going forward */
320 if (loop_format != AudioStreamWAV::LOOP_DISABLED && offset >= loop_end_fp) {
321 /* loopend reached */
322
323 if (loop_format == AudioStreamWAV::LOOP_PINGPONG) {
324 /* bounce ping pong */
325 offset = loop_end_fp - (offset - loop_end_fp);
326 increment = -increment;
327 sign *= -1;
328 } else {
329 /* go to loop-begin */
330
331 if (format == AudioStreamWAV::FORMAT_IMA_ADPCM) {
332 for (int i = 0; i < 2; i++) {
333 ima_adpcm[i].step_index = ima_adpcm[i].loop_step_index;
334 ima_adpcm[i].predictor = ima_adpcm[i].loop_predictor;
335 ima_adpcm[i].last_nibble = loop_begin_fp >> MIX_FRAC_BITS;
336 }
337 offset = loop_begin_fp;
338 } else {
339 offset = loop_begin_fp + (offset - loop_end_fp);
340 }
341 }
342 } else {
343 /* no loop, check for end of sample */
344 if (offset >= length_fp) {
345 active = false;
346 break;
347 }
348 }
349 }
350
351 /** MIXCOUNT COMPUTING **/
352
353 /* next possible limit (looppoints or sample begin/end */
354 limit = (increment < 0) ? begin_limit : end_limit;
355
356 /* compute what is shorter, the todo or the limit? */
357 aux = (limit - offset) / increment + 1;
358 target = (aux < todo) ? aux : todo; /* mix target is the shorter buffer */
359
360 /* check just in case */
361 if (target <= 0) {
362 active = false;
363 break;
364 }
365
366 todo -= target;
367
368 switch (base->format) {
369 case AudioStreamWAV::FORMAT_8_BITS: {
370 if (is_stereo) {
371 do_resample<int8_t, true, false>((int8_t *)data, dst_buff, offset, increment, target, ima_adpcm);
372 } else {
373 do_resample<int8_t, false, false>((int8_t *)data, dst_buff, offset, increment, target, ima_adpcm);
374 }
375 } break;
376 case AudioStreamWAV::FORMAT_16_BITS: {
377 if (is_stereo) {
378 do_resample<int16_t, true, false>((int16_t *)data, dst_buff, offset, increment, target, ima_adpcm);
379 } else {
380 do_resample<int16_t, false, false>((int16_t *)data, dst_buff, offset, increment, target, ima_adpcm);
381 }
382
383 } break;
384 case AudioStreamWAV::FORMAT_IMA_ADPCM: {
385 if (is_stereo) {
386 do_resample<int8_t, true, true>((int8_t *)data, dst_buff, offset, increment, target, ima_adpcm);
387 } else {
388 do_resample<int8_t, false, true>((int8_t *)data, dst_buff, offset, increment, target, ima_adpcm);
389 }
390
391 } break;
392 }
393
394 dst_buff += target;
395 }
396
397 if (todo) {
398 int mixed_frames = p_frames - todo;
399 //bit was missing from mix
400 int todo_ofs = p_frames - todo;
401 for (int i = todo_ofs; i < p_frames; i++) {
402 p_buffer[i] = AudioFrame(0, 0);
403 }
404 return mixed_frames;
405 }
406 return p_frames;
407}
408
409void AudioStreamPlaybackWAV::tag_used_streams() {
410 base->tag_used(get_playback_position());
411}
412
413AudioStreamPlaybackWAV::AudioStreamPlaybackWAV() {}
414
415/////////////////////
416
417void AudioStreamWAV::set_format(Format p_format) {
418 format = p_format;
419}
420
421AudioStreamWAV::Format AudioStreamWAV::get_format() const {
422 return format;
423}
424
425void AudioStreamWAV::set_loop_mode(LoopMode p_loop_mode) {
426 loop_mode = p_loop_mode;
427}
428
429AudioStreamWAV::LoopMode AudioStreamWAV::get_loop_mode() const {
430 return loop_mode;
431}
432
433void AudioStreamWAV::set_loop_begin(int p_frame) {
434 loop_begin = p_frame;
435}
436
437int AudioStreamWAV::get_loop_begin() const {
438 return loop_begin;
439}
440
441void AudioStreamWAV::set_loop_end(int p_frame) {
442 loop_end = p_frame;
443}
444
445int AudioStreamWAV::get_loop_end() const {
446 return loop_end;
447}
448
449void AudioStreamWAV::set_mix_rate(int p_hz) {
450 ERR_FAIL_COND(p_hz == 0);
451 mix_rate = p_hz;
452}
453
454int AudioStreamWAV::get_mix_rate() const {
455 return mix_rate;
456}
457
458void AudioStreamWAV::set_stereo(bool p_enable) {
459 stereo = p_enable;
460}
461
462bool AudioStreamWAV::is_stereo() const {
463 return stereo;
464}
465
466double AudioStreamWAV::get_length() const {
467 int len = data_bytes;
468 switch (format) {
469 case AudioStreamWAV::FORMAT_8_BITS:
470 len /= 1;
471 break;
472 case AudioStreamWAV::FORMAT_16_BITS:
473 len /= 2;
474 break;
475 case AudioStreamWAV::FORMAT_IMA_ADPCM:
476 len *= 2;
477 break;
478 }
479
480 if (stereo) {
481 len /= 2;
482 }
483
484 return double(len) / mix_rate;
485}
486
487bool AudioStreamWAV::is_monophonic() const {
488 return false;
489}
490
491void AudioStreamWAV::set_data(const Vector<uint8_t> &p_data) {
492 AudioServer::get_singleton()->lock();
493 if (data) {
494 memfree(data);
495 data = nullptr;
496 data_bytes = 0;
497 }
498
499 int datalen = p_data.size();
500 if (datalen) {
501 const uint8_t *r = p_data.ptr();
502 int alloc_len = datalen + DATA_PAD * 2;
503 data = memalloc(alloc_len); //alloc with some padding for interpolation
504 memset(data, 0, alloc_len);
505 uint8_t *dataptr = (uint8_t *)data;
506 memcpy(dataptr + DATA_PAD, r, datalen);
507 data_bytes = datalen;
508 }
509
510 AudioServer::get_singleton()->unlock();
511}
512
513Vector<uint8_t> AudioStreamWAV::get_data() const {
514 Vector<uint8_t> pv;
515
516 if (data) {
517 pv.resize(data_bytes);
518 {
519 uint8_t *w = pv.ptrw();
520 uint8_t *dataptr = (uint8_t *)data;
521 memcpy(w, dataptr + DATA_PAD, data_bytes);
522 }
523 }
524
525 return pv;
526}
527
528Error AudioStreamWAV::save_to_wav(const String &p_path) {
529 if (format == AudioStreamWAV::FORMAT_IMA_ADPCM) {
530 WARN_PRINT("Saving IMA_ADPC samples are not supported yet");
531 return ERR_UNAVAILABLE;
532 }
533
534 int sub_chunk_2_size = data_bytes; //Subchunk2Size = Size of data in bytes
535
536 // Format code
537 // 1:PCM format (for 8 or 16 bit)
538 // 3:IEEE float format
539 int format_code = (format == FORMAT_IMA_ADPCM) ? 3 : 1;
540
541 int n_channels = stereo ? 2 : 1;
542
543 long sample_rate = mix_rate;
544
545 int byte_pr_sample = 0;
546 switch (format) {
547 case AudioStreamWAV::FORMAT_8_BITS:
548 byte_pr_sample = 1;
549 break;
550 case AudioStreamWAV::FORMAT_16_BITS:
551 byte_pr_sample = 2;
552 break;
553 case AudioStreamWAV::FORMAT_IMA_ADPCM:
554 byte_pr_sample = 4;
555 break;
556 }
557
558 String file_path = p_path;
559 if (!(file_path.substr(file_path.length() - 4, 4) == ".wav")) {
560 file_path += ".wav";
561 }
562
563 Ref<FileAccess> file = FileAccess::open(file_path, FileAccess::WRITE); //Overrides existing file if present
564
565 ERR_FAIL_COND_V(file.is_null(), ERR_FILE_CANT_WRITE);
566
567 // Create WAV Header
568 file->store_string("RIFF"); //ChunkID
569 file->store_32(sub_chunk_2_size + 36); //ChunkSize = 36 + SubChunk2Size (size of entire file minus the 8 bits for this and previous header)
570 file->store_string("WAVE"); //Format
571 file->store_string("fmt "); //Subchunk1ID
572 file->store_32(16); //Subchunk1Size = 16
573 file->store_16(format_code); //AudioFormat
574 file->store_16(n_channels); //Number of Channels
575 file->store_32(sample_rate); //SampleRate
576 file->store_32(sample_rate * n_channels * byte_pr_sample); //ByteRate
577 file->store_16(n_channels * byte_pr_sample); //BlockAlign = NumChannels * BytePrSample
578 file->store_16(byte_pr_sample * 8); //BitsPerSample
579 file->store_string("data"); //Subchunk2ID
580 file->store_32(sub_chunk_2_size); //Subchunk2Size
581
582 // Add data
583 Vector<uint8_t> stream_data = get_data();
584 const uint8_t *read_data = stream_data.ptr();
585 switch (format) {
586 case AudioStreamWAV::FORMAT_8_BITS:
587 for (unsigned int i = 0; i < data_bytes; i++) {
588 uint8_t data_point = (read_data[i] + 128);
589 file->store_8(data_point);
590 }
591 break;
592 case AudioStreamWAV::FORMAT_16_BITS:
593 for (unsigned int i = 0; i < data_bytes / 2; i++) {
594 uint16_t data_point = decode_uint16(&read_data[i * 2]);
595 file->store_16(data_point);
596 }
597 break;
598 case AudioStreamWAV::FORMAT_IMA_ADPCM:
599 //Unimplemented
600 break;
601 }
602
603 return OK;
604}
605
606Ref<AudioStreamPlayback> AudioStreamWAV::instantiate_playback() {
607 Ref<AudioStreamPlaybackWAV> sample;
608 sample.instantiate();
609 sample->base = Ref<AudioStreamWAV>(this);
610 return sample;
611}
612
613String AudioStreamWAV::get_stream_name() const {
614 return "";
615}
616
617void AudioStreamWAV::_bind_methods() {
618 ClassDB::bind_method(D_METHOD("set_data", "data"), &AudioStreamWAV::set_data);
619 ClassDB::bind_method(D_METHOD("get_data"), &AudioStreamWAV::get_data);
620
621 ClassDB::bind_method(D_METHOD("set_format", "format"), &AudioStreamWAV::set_format);
622 ClassDB::bind_method(D_METHOD("get_format"), &AudioStreamWAV::get_format);
623
624 ClassDB::bind_method(D_METHOD("set_loop_mode", "loop_mode"), &AudioStreamWAV::set_loop_mode);
625 ClassDB::bind_method(D_METHOD("get_loop_mode"), &AudioStreamWAV::get_loop_mode);
626
627 ClassDB::bind_method(D_METHOD("set_loop_begin", "loop_begin"), &AudioStreamWAV::set_loop_begin);
628 ClassDB::bind_method(D_METHOD("get_loop_begin"), &AudioStreamWAV::get_loop_begin);
629
630 ClassDB::bind_method(D_METHOD("set_loop_end", "loop_end"), &AudioStreamWAV::set_loop_end);
631 ClassDB::bind_method(D_METHOD("get_loop_end"), &AudioStreamWAV::get_loop_end);
632
633 ClassDB::bind_method(D_METHOD("set_mix_rate", "mix_rate"), &AudioStreamWAV::set_mix_rate);
634 ClassDB::bind_method(D_METHOD("get_mix_rate"), &AudioStreamWAV::get_mix_rate);
635
636 ClassDB::bind_method(D_METHOD("set_stereo", "stereo"), &AudioStreamWAV::set_stereo);
637 ClassDB::bind_method(D_METHOD("is_stereo"), &AudioStreamWAV::is_stereo);
638
639 ClassDB::bind_method(D_METHOD("save_to_wav", "path"), &AudioStreamWAV::save_to_wav);
640
641 ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_data", "get_data");
642 ADD_PROPERTY(PropertyInfo(Variant::INT, "format", PROPERTY_HINT_ENUM, "8-Bit,16-Bit,IMA-ADPCM"), "set_format", "get_format");
643 ADD_PROPERTY(PropertyInfo(Variant::INT, "loop_mode", PROPERTY_HINT_ENUM, "Disabled,Forward,Ping-Pong,Backward"), "set_loop_mode", "get_loop_mode");
644 ADD_PROPERTY(PropertyInfo(Variant::INT, "loop_begin"), "set_loop_begin", "get_loop_begin");
645 ADD_PROPERTY(PropertyInfo(Variant::INT, "loop_end"), "set_loop_end", "get_loop_end");
646 ADD_PROPERTY(PropertyInfo(Variant::INT, "mix_rate"), "set_mix_rate", "get_mix_rate");
647 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stereo"), "set_stereo", "is_stereo");
648
649 BIND_ENUM_CONSTANT(FORMAT_8_BITS);
650 BIND_ENUM_CONSTANT(FORMAT_16_BITS);
651 BIND_ENUM_CONSTANT(FORMAT_IMA_ADPCM);
652
653 BIND_ENUM_CONSTANT(LOOP_DISABLED);
654 BIND_ENUM_CONSTANT(LOOP_FORWARD);
655 BIND_ENUM_CONSTANT(LOOP_PINGPONG);
656 BIND_ENUM_CONSTANT(LOOP_BACKWARD);
657}
658
659AudioStreamWAV::AudioStreamWAV() {}
660
661AudioStreamWAV::~AudioStreamWAV() {
662 if (data) {
663 memfree(data);
664 data = nullptr;
665 data_bytes = 0;
666 }
667}
668