Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/scene/resources/audio_stream_wav.h
9896 views
1
/**************************************************************************/
2
/* audio_stream_wav.h */
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
#pragma once
32
33
#include "servers/audio/audio_stream.h"
34
35
#include "thirdparty/misc/qoa.h"
36
37
class AudioStreamWAV;
38
39
class AudioStreamPlaybackWAV : public AudioStreamPlaybackResampled {
40
GDCLASS(AudioStreamPlaybackWAV, AudioStreamPlaybackResampled);
41
42
struct IMA_ADPCM_State {
43
int16_t step_index = 0;
44
int32_t predictor = 0;
45
/* values at loop point */
46
int16_t loop_step_index = 0;
47
int32_t loop_predictor = 0;
48
int32_t last_nibble = 0;
49
int32_t loop_pos = 0;
50
int32_t window_ofs = 0;
51
} ima_adpcm[2];
52
53
struct QOA_State {
54
qoa_desc desc = {};
55
uint32_t data_ofs = 0;
56
uint32_t frame_len = 0;
57
TightLocalVector<int16_t> dec;
58
uint32_t dec_len = 0;
59
} qoa;
60
61
int64_t offset = 0;
62
int8_t sign = 1;
63
bool active = false;
64
friend class AudioStreamWAV;
65
Ref<AudioStreamWAV> base;
66
67
template <typename Depth, bool is_stereo, bool is_ima_adpcm, bool is_qoa>
68
void decode_samples(const Depth *p_src, AudioFrame *p_dst, int64_t &p_offset, int8_t &p_increment, uint32_t p_amount, IMA_ADPCM_State *p_ima_adpcm, QOA_State *p_qoa);
69
70
bool _is_sample = false;
71
Ref<AudioSamplePlayback> sample_playback;
72
73
protected:
74
virtual int _mix_internal(AudioFrame *p_buffer, int p_frames) override;
75
virtual float get_stream_sampling_rate() override;
76
77
public:
78
virtual void start(double p_from_pos = 0.0) override;
79
virtual void stop() override;
80
virtual bool is_playing() const override;
81
82
virtual int get_loop_count() const override; //times it looped
83
84
virtual double get_playback_position() const override;
85
virtual void seek(double p_time) override;
86
87
virtual void tag_used_streams() override;
88
89
virtual void set_is_sample(bool p_is_sample) override;
90
virtual bool get_is_sample() const override;
91
virtual Ref<AudioSamplePlayback> get_sample_playback() const override;
92
virtual void set_sample_playback(const Ref<AudioSamplePlayback> &p_playback) override;
93
};
94
95
class AudioStreamWAV : public AudioStream {
96
GDCLASS(AudioStreamWAV, AudioStream);
97
RES_BASE_EXTENSION("sample")
98
99
public:
100
enum Format {
101
FORMAT_8_BITS,
102
FORMAT_16_BITS,
103
FORMAT_IMA_ADPCM,
104
FORMAT_QOA,
105
};
106
107
// Keep the ResourceImporterWAV `edit/loop_mode` enum hint in sync with these options.
108
enum LoopMode {
109
LOOP_DISABLED,
110
LOOP_FORWARD,
111
LOOP_PINGPONG,
112
LOOP_BACKWARD
113
};
114
115
private:
116
friend class AudioStreamPlaybackWAV;
117
118
Format format = FORMAT_8_BITS;
119
LoopMode loop_mode = LOOP_DISABLED;
120
bool stereo = false;
121
int loop_begin = 0;
122
int loop_end = 0;
123
int mix_rate = 44100;
124
TightLocalVector<uint8_t> data;
125
uint32_t data_bytes = 0;
126
127
Dictionary tags;
128
129
protected:
130
static void _bind_methods();
131
132
public:
133
static Ref<AudioStreamWAV> load_from_buffer(const Vector<uint8_t> &p_stream_data, const Dictionary &p_options);
134
static Ref<AudioStreamWAV> load_from_file(const String &p_path, const Dictionary &p_options);
135
136
void set_format(Format p_format);
137
Format get_format() const;
138
139
void set_loop_mode(LoopMode p_loop_mode);
140
LoopMode get_loop_mode() const;
141
142
void set_loop_begin(int p_frame);
143
int get_loop_begin() const;
144
145
void set_loop_end(int p_frame);
146
int get_loop_end() const;
147
148
void set_mix_rate(int p_hz);
149
int get_mix_rate() const;
150
151
void set_stereo(bool p_enable);
152
bool is_stereo() const;
153
154
void set_tags(const Dictionary &p_tags);
155
virtual Dictionary get_tags() const override;
156
157
virtual double get_length() const override; //if supported, otherwise return 0
158
159
virtual bool is_monophonic() const override;
160
161
void set_data(const Vector<uint8_t> &p_data);
162
Vector<uint8_t> get_data() const;
163
164
Error save_to_wav(const String &p_path);
165
166
virtual Ref<AudioStreamPlayback> instantiate_playback() override;
167
virtual String get_stream_name() const override;
168
169
virtual bool can_be_sampled() const override {
170
return true;
171
}
172
virtual Ref<AudioSample> generate_sample() const override;
173
174
static void _compress_ima_adpcm(const Vector<float> &p_data, Vector<uint8_t> &r_dst_data) {
175
static const int16_t _ima_adpcm_step_table[89] = {
176
7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
177
19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
178
50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
179
130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
180
337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
181
876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
182
2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
183
5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
184
15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
185
};
186
187
static const int8_t _ima_adpcm_index_table[16] = {
188
-1, -1, -1, -1, 2, 4, 6, 8,
189
-1, -1, -1, -1, 2, 4, 6, 8
190
};
191
192
int datalen = p_data.size();
193
int datamax = datalen;
194
if (datalen & 1) {
195
datalen++;
196
}
197
198
r_dst_data.resize(datalen / 2 + 4);
199
uint8_t *w = r_dst_data.ptrw();
200
201
int i, step_idx = 0, prev = 0;
202
uint8_t *out = w;
203
const float *in = p_data.ptr();
204
205
// Initial value is zero.
206
*(out++) = 0;
207
*(out++) = 0;
208
// Table index initial value.
209
*(out++) = 0;
210
// Unused.
211
*(out++) = 0;
212
213
for (i = 0; i < datalen; i++) {
214
int step, diff, vpdiff, mask;
215
uint8_t nibble;
216
int16_t xm_sample;
217
218
if (i >= datamax) {
219
xm_sample = 0;
220
} else {
221
xm_sample = CLAMP(in[i] * 32767.0, -32768, 32767);
222
}
223
224
diff = (int)xm_sample - prev;
225
226
nibble = 0;
227
step = _ima_adpcm_step_table[step_idx];
228
vpdiff = step >> 3;
229
if (diff < 0) {
230
nibble = 8;
231
diff = -diff;
232
}
233
mask = 4;
234
while (mask) {
235
if (diff >= step) {
236
nibble |= mask;
237
diff -= step;
238
vpdiff += step;
239
}
240
241
step >>= 1;
242
mask >>= 1;
243
}
244
245
if (nibble & 8) {
246
prev -= vpdiff;
247
} else {
248
prev += vpdiff;
249
}
250
251
prev = CLAMP(prev, -32768, 32767);
252
253
step_idx += _ima_adpcm_index_table[nibble];
254
step_idx = CLAMP(step_idx, 0, 88);
255
256
if (i & 1) {
257
*out |= nibble << 4;
258
out++;
259
} else {
260
*out = nibble;
261
}
262
}
263
}
264
265
static void _compress_qoa(const Vector<float> &p_data, Vector<uint8_t> &dst_data, qoa_desc *p_desc) {
266
uint32_t frames_len = (p_desc->samples + QOA_FRAME_LEN - 1) / QOA_FRAME_LEN * (QOA_LMS_LEN * 4 * p_desc->channels + 8);
267
uint32_t slices_len = (p_desc->samples + QOA_SLICE_LEN - 1) / QOA_SLICE_LEN * 8 * p_desc->channels;
268
dst_data.resize(8 + frames_len + slices_len);
269
270
for (uint32_t c = 0; c < p_desc->channels; c++) {
271
memset(p_desc->lms[c].history, 0, sizeof(p_desc->lms[c].history));
272
memset(p_desc->lms[c].weights, 0, sizeof(p_desc->lms[c].weights));
273
p_desc->lms[c].weights[2] = -(1 << 13);
274
p_desc->lms[c].weights[3] = (1 << 14);
275
}
276
277
TightLocalVector<int16_t> data16;
278
data16.resize(QOA_FRAME_LEN * p_desc->channels);
279
280
uint8_t *dst_ptr = dst_data.ptrw();
281
dst_ptr += qoa_encode_header(p_desc, dst_data.ptrw());
282
283
uint32_t frame_len = QOA_FRAME_LEN;
284
for (uint32_t s = 0; s < p_desc->samples; s += frame_len) {
285
frame_len = MIN(frame_len, p_desc->samples - s);
286
for (uint32_t i = 0; i < frame_len * p_desc->channels; i++) {
287
data16[i] = CLAMP(p_data[s * p_desc->channels + i] * 32767.0, -32768, 32767);
288
}
289
dst_ptr += qoa_encode_frame(data16.ptr(), p_desc, frame_len, dst_ptr);
290
}
291
}
292
};
293
294
VARIANT_ENUM_CAST(AudioStreamWAV::Format)
295
VARIANT_ENUM_CAST(AudioStreamWAV::LoopMode)
296
297