37
37
#include " core/io/resource_saver.h"
38
38
#include " scene/resources/audio_stream_wav.h"
39
39
40
+ #define DRWAV_IMPLEMENTATION
41
+ #define DRWAV_NO_STDIO
42
+ #define DR_WAV_LIBSNDFILE_COMPAT
43
+ #define DRWAV_MALLOC (sz ) memalloc(sz)
44
+ #define DRWAV_REALLOC (p, sz ) memrealloc(p, sz)
45
+ #define DRWAV_FREE (p ) \
46
+ if (p) \
47
+ memfree (p)
48
+
49
+ #include " thirdparty/dr_libs/dr_wav.h"
50
+
40
51
const float TRIM_DB_LIMIT = -50 ;
41
52
const int TRIM_FADE_OUT_FRAMES = 500 ;
42
53
@@ -45,11 +56,15 @@ String ResourceImporterWAV::get_importer_name() const {
45
56
}
46
57
47
58
String ResourceImporterWAV::get_visible_name () const {
48
- return " Microsoft WAV" ;
59
+ return " Microsoft WAV/Apple AIFF " ;
49
60
}
50
61
51
62
void ResourceImporterWAV::get_recognized_extensions (List<String> *p_extensions) const {
52
63
p_extensions->push_back (" wav" );
64
+ p_extensions->push_back (" wave" );
65
+ p_extensions->push_back (" aif" );
66
+ p_extensions->push_back (" aiff" );
67
+ p_extensions->push_back (" aifc" );
53
68
}
54
69
55
70
String ResourceImporterWAV::get_save_extension () const {
@@ -97,219 +112,72 @@ void ResourceImporterWAV::get_import_options(const String &p_path, List<ImportOp
97
112
}
98
113
99
114
Error ResourceImporterWAV::import (ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
100
- /* STEP 1, READ WAVE FILE */
115
+ // STEP 1, READ FILE
101
116
102
117
Error err;
103
118
Ref<FileAccess> file = FileAccess::open (p_source_file, FileAccess::READ, &err);
104
119
105
- ERR_FAIL_COND_V_MSG (err != OK, ERR_CANT_OPEN , " Cannot open file '" + p_source_file + " '." );
120
+ ERR_FAIL_COND_V_MSG (err != OK, err , " Cannot open file '" + p_source_file + " '." );
106
121
107
- /* CHECK RIFF */
108
- char riff[5 ];
109
- riff[4 ] = 0 ;
110
- file->get_buffer ((uint8_t *)&riff, 4 ); // RIFF
122
+ // Lambdas allow dr_wav to use FileAccess
123
+ drwav_read_proc read_fa = [](void *p_user_data, void *p_buffer_out, size_t bytes_to_read) -> size_t {
124
+ Ref<FileAccess> lfile = *(Ref<FileAccess> *)p_user_data;
111
125
112
- if (riff[0 ] != ' R' || riff[1 ] != ' I' || riff[2 ] != ' F' || riff[3 ] != ' F' ) {
113
- ERR_FAIL_V_MSG (ERR_FILE_UNRECOGNIZED, vformat (" Not a WAV file. File should start with 'RIFF', but found '%s', in file of size %d bytes" , riff, file->get_length ()));
114
- }
126
+ bytes_to_read = MIN (bytes_to_read, lfile->get_length () - lfile->get_position ());
127
+ lfile->get_buffer ((uint8_t *)p_buffer_out, bytes_to_read);
128
+ return bytes_to_read;
129
+ };
115
130
116
- /* GET FILESIZE */
131
+ drwav_seek_proc seek_fa = [](void *p_user_data, int p_offset, drwav_seek_origin origin) -> drwav_bool32 {
132
+ Ref<FileAccess> lfile = *(Ref<FileAccess> *)p_user_data;
133
+ uint64_t new_offset = p_offset + (origin == drwav_seek_origin_current ? lfile->get_position () : 0 );
117
134
118
- // The file size in header is 8 bytes less than the actual size.
119
- // See https://docs.fileformat.com/audio/wav/
120
- const int FILE_SIZE_HEADER_OFFSET = 8 ;
121
- uint32_t file_size_header = file->get_32 () + FILE_SIZE_HEADER_OFFSET;
122
- uint64_t file_size = file->get_length ();
123
- if (file_size != file_size_header) {
124
- WARN_PRINT (vformat (" File size %d is %s than the expected size %d. (%s)" , file_size, file_size > file_size_header ? " larger" : " smaller" , file_size_header, p_source_file));
125
- }
135
+ if (new_offset > lfile->get_length () || (p_offset < 0 && (size_t )-p_offset > lfile->get_position ())) {
136
+ return DRWAV_FALSE;
137
+ }
126
138
127
- /* CHECK WAVE */
139
+ lfile->seek (new_offset);
140
+ return DRWAV_TRUE;
141
+ };
128
142
129
- char wave[5 ];
130
- wave[4 ] = 0 ;
131
- file->get_buffer ((uint8_t *)&wave, 4 ); // WAVE
143
+ drwav wav;
144
+ if (!drwav_init_with_metadata (&wav, read_fa, seek_fa, &file, DRWAV_WITH_METADATA, nullptr )) {
145
+ ERR_FAIL_V_MSG (ERR_FILE_UNRECOGNIZED, " Could not read file '" + p_source_file + " '. Invalid/corrupted data or unsupported format." );
146
+ }
132
147
133
- if (wave[ 0 ] != ' W ' || wave[ 1 ] != ' A ' || wave[ 2 ] != ' V ' || wave[ 3 ] != ' E ' ) {
134
- ERR_FAIL_V_MSG (ERR_FILE_UNRECOGNIZED, vformat ( " Not a WAV file. Header should contain 'WAVE', but found '%s', in file of size %d bytes " , wave, file-> get_length ()) );
148
+ if (wav. totalPCMFrameCount > INT32_MAX ) {
149
+ ERR_FAIL_V_MSG (ERR_FILE_UNRECOGNIZED, " Could not read file ' " + p_source_file + " '. Audio data exceeds maximum supported size of 2,147,483,647 frames. " );
135
150
}
136
151
137
- // Let users override potential loop points from the WAV.
138
- // We parse the WAV loop points only with "Detect From WAV" (0).
139
- int import_loop_mode = p_options[" edit/loop_mode" ];
152
+ int format_bits = wav.bitsPerSample ;
153
+ int format_channels = wav.channels ;
154
+ int format_freq = wav.sampleRate ;
155
+ int frames = wav.totalPCMFrameCount ;
140
156
141
- int format_bits = 0 ;
142
- int format_channels = 0 ;
157
+ int import_loop_mode = p_options[" edit/loop_mode" ];
143
158
144
- AudioStreamWAV::LoopMode loop_mode = AudioStreamWAV::LOOP_DISABLED;
145
- uint16_t compression_code = 1 ;
146
- bool format_found = false ;
147
- bool data_found = false ;
148
- int format_freq = 0 ;
149
159
int loop_begin = 0 ;
150
160
int loop_end = 0 ;
151
- int frames = 0 ;
152
-
153
- Vector<float > data;
154
-
155
- while (!file->eof_reached ()) {
156
- /* chunk */
157
- char chunkID[4 ];
158
- file->get_buffer ((uint8_t *)&chunkID, 4 ); // RIFF
159
-
160
- /* chunk size */
161
- uint32_t chunksize = file->get_32 ();
162
- uint32_t file_pos = file->get_position (); // save file pos, so we can skip to next chunk safely
163
-
164
- if (file->eof_reached ()) {
165
- // ERR_PRINT("EOF REACH");
166
- break ;
167
- }
168
-
169
- if (chunkID[0 ] == ' f' && chunkID[1 ] == ' m' && chunkID[2 ] == ' t' && chunkID[3 ] == ' ' && !format_found) {
170
- /* IS FORMAT CHUNK */
171
-
172
- // Issue: #7755 : Not a bug - usage of other formats (format codes) are unsupported in current importer version.
173
- // Consider revision for engine version 3.0
174
- compression_code = file->get_16 ();
175
- if (compression_code != 1 && compression_code != 3 ) {
176
- ERR_FAIL_V_MSG (ERR_INVALID_DATA, " Format not supported for WAVE file (not PCM). Save WAVE files as uncompressed PCM or IEEE float instead." );
177
- }
178
-
179
- format_channels = file->get_16 ();
180
- if (format_channels != 1 && format_channels != 2 ) {
181
- ERR_FAIL_V_MSG (ERR_INVALID_DATA, " Format not supported for WAVE file (not stereo or mono)." );
182
- }
183
-
184
- format_freq = file->get_32 (); // sampling rate
185
-
186
- file->get_32 (); // average bits/second (unused)
187
- file->get_16 (); // block align (unused)
188
- format_bits = file->get_16 (); // bits per sample
189
-
190
- if (format_bits % 8 || format_bits == 0 ) {
191
- ERR_FAIL_V_MSG (ERR_INVALID_DATA, " Invalid amount of bits in the sample (should be one of 8, 16, 24 or 32)." );
192
- }
193
-
194
- if (compression_code == 3 && format_bits % 32 ) {
195
- ERR_FAIL_V_MSG (ERR_INVALID_DATA, " Invalid amount of bits in the IEEE float sample (should be 32 or 64)." );
196
- }
197
-
198
- /* Don't need anything else, continue */
199
- format_found = true ;
200
- }
201
-
202
- if (chunkID[0 ] == ' d' && chunkID[1 ] == ' a' && chunkID[2 ] == ' t' && chunkID[3 ] == ' a' && !data_found) {
203
- /* IS DATA CHUNK */
204
- data_found = true ;
205
-
206
- if (!format_found) {
207
- ERR_PRINT (" 'data' chunk before 'format' chunk found." );
161
+ AudioStreamWAV::LoopMode loop_mode = AudioStreamWAV::LOOP_DISABLED;
162
+ if (import_loop_mode == 0 ) {
163
+ for (uint32_t meta = 0 ; meta < wav.metadataCount ; ++meta) {
164
+ drwav_metadata md = wav.pMetadata [meta];
165
+ if (md.type == drwav_metadata_type_smpl && md.data .smpl .sampleLoopCount > 0 ) {
166
+ drwav_smpl_loop loop = md.data .smpl .pLoops [0 ];
167
+ loop_mode = (AudioStreamWAV::LoopMode)(loop.type + 1 );
168
+ loop_begin = loop.firstSampleByteOffset ;
169
+ loop_end = loop.lastSampleByteOffset ;
208
170
break ;
209
171
}
210
-
211
- uint64_t remaining_bytes = file_size - file_pos;
212
- frames = chunksize;
213
- if (remaining_bytes < chunksize) {
214
- WARN_PRINT (vformat (" Data chunk size is smaller than expected. Proceeding with actual data size. (%s)" , p_source_file));
215
- frames = remaining_bytes;
216
- }
217
-
218
- ERR_FAIL_COND_V (format_channels == 0 , ERR_INVALID_DATA);
219
- frames /= format_channels;
220
- frames /= (format_bits >> 3 );
221
-
222
- /* print_line("chunksize: "+itos(chunksize));
223
- print_line("channels: "+itos(format_channels));
224
- print_line("bits: "+itos(format_bits));
225
- */
226
-
227
- data.resize (frames * format_channels);
228
-
229
- if (compression_code == 1 ) {
230
- if (format_bits == 8 ) {
231
- for (int i = 0 ; i < frames * format_channels; i++) {
232
- // 8 bit samples are UNSIGNED
233
-
234
- data.write [i] = int8_t (file->get_8 () - 128 ) / 128 .f ;
235
- }
236
- } else if (format_bits == 16 ) {
237
- for (int i = 0 ; i < frames * format_channels; i++) {
238
- // 16 bit SIGNED
239
-
240
- data.write [i] = int16_t (file->get_16 ()) / 32768 .f ;
241
- }
242
- } else {
243
- for (int i = 0 ; i < frames * format_channels; i++) {
244
- // 16+ bits samples are SIGNED
245
- // if sample is > 16 bits, just read extra bytes
246
-
247
- uint32_t s = 0 ;
248
- for (int b = 0 ; b < (format_bits >> 3 ); b++) {
249
- s |= ((uint32_t )file->get_8 ()) << (b * 8 );
250
- }
251
- s <<= (32 - format_bits);
252
-
253
- data.write [i] = (int32_t (s) >> 16 ) / 32768 .f ;
254
- }
255
- }
256
- } else if (compression_code == 3 ) {
257
- if (format_bits == 32 ) {
258
- for (int i = 0 ; i < frames * format_channels; i++) {
259
- // 32 bit IEEE Float
260
-
261
- data.write [i] = file->get_float ();
262
- }
263
- } else if (format_bits == 64 ) {
264
- for (int i = 0 ; i < frames * format_channels; i++) {
265
- // 64 bit IEEE Float
266
-
267
- data.write [i] = file->get_double ();
268
- }
269
- }
270
- }
271
-
272
- if (file->eof_reached ()) {
273
- ERR_FAIL_V_MSG (ERR_FILE_CORRUPT, " Premature end of file." );
274
- }
275
172
}
173
+ }
276
174
277
- if (import_loop_mode == 0 && chunkID[0 ] == ' s' && chunkID[1 ] == ' m' && chunkID[2 ] == ' p' && chunkID[3 ] == ' l' ) {
278
- // Loop point info!
279
-
280
- /* *
281
- * Consider exploring next document:
282
- * http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/Docs/RIFFNEW.pdf
283
- * Especially on page:
284
- * 16 - 17
285
- * Timestamp:
286
- * 22:38 06.07.2017 GMT
287
- **/
288
-
289
- for (int i = 0 ; i < 10 ; i++) {
290
- file->get_32 (); // i wish to know why should i do this... no doc!
291
- }
175
+ Vector<float > data;
176
+ data.resize (frames * format_channels);
177
+ drwav_read_pcm_frames_f32 (&wav, frames, data.ptrw ());
292
178
293
- // only read 0x00 (loop forward), 0x01 (loop ping-pong) and 0x02 (loop backward)
294
- // Skip anything else because it's not supported, reserved for future uses or sampler specific
295
- // from https://sites.google.com/site/musicgapi/technical-documents/wav-file-format#smpl (loop type values table)
296
- int loop_type = file->get_32 ();
297
- if (loop_type == 0x00 || loop_type == 0x01 || loop_type == 0x02 ) {
298
- if (loop_type == 0x00 ) {
299
- loop_mode = AudioStreamWAV::LOOP_FORWARD;
300
- } else if (loop_type == 0x01 ) {
301
- loop_mode = AudioStreamWAV::LOOP_PINGPONG;
302
- } else if (loop_type == 0x02 ) {
303
- loop_mode = AudioStreamWAV::LOOP_BACKWARD;
304
- }
305
- loop_begin = file->get_32 ();
306
- loop_end = file->get_32 ();
307
- }
308
- }
309
- // Move to the start of the next chunk. Note that RIFF requires a padding byte for odd
310
- // chunk sizes.
311
- file->seek (file_pos + chunksize + (chunksize & 1 ));
312
- }
179
+ drwav_uninit (&wav);
180
+ file->close ();
313
181
314
182
// STEP 2, APPLY CONVERSIONS
315
183
0 commit comments