1 /*************************************************************************/
2 /*  resource_format_wav.cpp                                              */
3 /*************************************************************************/
4 /*                       This file is part of:                           */
5 /*                           GODOT ENGINE                                */
6 /*                      https://godotengine.org                          */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur.                 */
9 /* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md)    */
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 #include "resource_format_wav.h"
31 #include "os/file_access.h"
32 #include "scene/resources/sample.h"
33 
load(const String & p_path,const String & p_original_path,Error * r_error)34 RES ResourceFormatLoaderWAV::load(const String &p_path, const String &p_original_path, Error *r_error) {
35 	if (r_error)
36 		*r_error = ERR_FILE_CANT_OPEN;
37 
38 	Error err;
39 	FileAccess *file = FileAccess::open(p_path, FileAccess::READ, &err);
40 
41 	ERR_FAIL_COND_V(err != OK, RES());
42 
43 	if (r_error)
44 		*r_error = ERR_FILE_CORRUPT;
45 
46 	/* CHECK RIFF */
47 	char riff[5];
48 	riff[4] = 0;
49 	file->get_buffer((uint8_t *)&riff, 4); //RIFF
50 
51 	if (riff[0] != 'R' || riff[1] != 'I' || riff[2] != 'F' || riff[3] != 'F') {
52 
53 		file->close();
54 		memdelete(file);
55 		ERR_FAIL_V(RES());
56 	}
57 
58 	/* GET FILESIZE */
59 	uint32_t filesize = file->get_32();
60 
61 	/* CHECK WAVE */
62 
63 	char wave[4];
64 
65 	file->get_buffer((uint8_t *)&wave, 4); //RIFF
66 
67 	if (wave[0] != 'W' || wave[1] != 'A' || wave[2] != 'V' || wave[3] != 'E') {
68 
69 		file->close();
70 		memdelete(file);
71 		ERR_EXPLAIN("Not a WAV file (no WAVE RIFF Header)")
72 		ERR_FAIL_V(RES());
73 	}
74 
75 	uint16_t compression_code = 1;
76 	bool format_found = false;
77 	bool data_found = false;
78 	int format_bits = 0;
79 	int format_channels = 0;
80 	int format_freq = 0;
81 	Sample::LoopFormat loop = Sample::LOOP_NONE;
82 	int loop_begin = 0;
83 	int loop_end = 0;
84 
85 	Ref<Sample> sample(memnew(Sample));
86 
87 	while (!file->eof_reached()) {
88 
89 		/* chunk */
90 		char chunkID[4];
91 		file->get_buffer((uint8_t *)&chunkID, 4); //RIFF
92 
93 		/* chunk size */
94 		uint32_t chunksize = file->get_32();
95 		uint32_t file_pos = file->get_pos(); //save file pos, so we can skip to next chunk safely
96 
97 		if (file->eof_reached()) {
98 
99 			//ERR_PRINT("EOF REACH");
100 			break;
101 		}
102 
103 		if (chunkID[0] == 'f' && chunkID[1] == 'm' && chunkID[2] == 't' && chunkID[3] == ' ' && !format_found) {
104 			/* IS FORMAT CHUNK */
105 
106 			compression_code = file->get_16();
107 			if (compression_code != 1 && compression_code != 3) {
108 				ERR_PRINT("Format not supported for WAVE file (not PCM). Save WAVE files as uncompressed PCM instead.");
109 				break;
110 			}
111 
112 			format_channels = file->get_16();
113 			if (format_channels != 1 && format_channels != 2) {
114 
115 				ERR_PRINT("Format not supported for WAVE file (not stereo or mono)");
116 				break;
117 			}
118 
119 			format_freq = file->get_32(); //sampling rate
120 
121 			file->get_32(); // average bits/second (unused)
122 			file->get_16(); // block align (unused)
123 			format_bits = file->get_16(); // bits per sample
124 
125 			if (format_bits % 8) {
126 
127 				ERR_PRINT("Strange number of bits in sample (not 8,16,24,32)");
128 				break;
129 			}
130 
131 			/* Dont need anything else, continue */
132 			format_found = true;
133 		}
134 
135 		if (chunkID[0] == 'd' && chunkID[1] == 'a' && chunkID[2] == 't' && chunkID[3] == 'a' && !data_found) {
136 			/* IS FORMAT CHUNK */
137 			data_found = true;
138 
139 			if (!format_found) {
140 				ERR_PRINT("'data' chunk before 'format' chunk found.");
141 				break;
142 			}
143 
144 			int frames = chunksize;
145 
146 			frames /= format_channels;
147 			frames /= (format_bits >> 3);
148 
149 			/*print_line("chunksize: "+itos(chunksize));
150 			print_line("channels: "+itos(format_channels));
151 			print_line("bits: "+itos(format_bits));
152 */
153 			sample->create(
154 					(format_bits == 8) ? Sample::FORMAT_PCM8 : Sample::FORMAT_PCM16,
155 					(format_channels == 2) ? true : false,
156 					frames);
157 			sample->set_mix_rate(format_freq);
158 
159 			int len = frames;
160 			if (format_channels == 2)
161 				len *= 2;
162 			if (format_bits > 8)
163 				len *= 2;
164 
165 			DVector<uint8_t> data;
166 			data.resize(len);
167 			DVector<uint8_t>::Write dataw = data.write();
168 			void *data_ptr = dataw.ptr();
169 
170 			if (format_bits == 8) {
171 				int8_t *data_ptr8 = (int8_t *)data_ptr;
172 				for (int i = 0; i < frames * format_channels; i++) {
173 					// 8 bit samples are UNSIGNED
174 
175 					uint8_t s = file->get_8();
176 					data_ptr8[i] = (int8_t)(s - 128);
177 				}
178 			} else if (format_bits == 32 && compression_code == 3) {
179 				int16_t *data_ptr16 = (int16_t *)data_ptr;
180 				for (int i = 0; i < frames * format_channels; i++) {
181 					//32 bit IEEE Float
182 
183 					float s = file->get_float();
184 					data_ptr16[i] = (int32_t)(s * 32768.f);
185 				}
186 			} else if (format_bits == 16) {
187 				int16_t *data_ptr16 = (int16_t *)data_ptr;
188 				for (int i = 0; i < frames * format_channels; i++) {
189 					//16 bit SIGNED
190 
191 					data_ptr16[i] = file->get_16();
192 				}
193 			} else {
194 				int16_t *data_ptr16 = (int16_t *)data_ptr;
195 				for (int i = 0; i < frames * format_channels; i++) {
196 					//16+ bits samples are SIGNED
197 					// if sample is > 16 bits, just read extra bytes
198 
199 					uint32_t s = 0;
200 					for (int b = 0; b < (format_bits >> 3); b++) {
201 
202 						s |= ((uint32_t)file->get_8()) << (b * 8);
203 					}
204 					s <<= (32 - format_bits);
205 
206 					data_ptr16[i] = s >> 16;
207 				}
208 			}
209 
210 			dataw = DVector<uint8_t>::Write();
211 
212 			sample->set_data(data);
213 
214 			if (file->eof_reached()) {
215 				file->close();
216 				memdelete(file);
217 				ERR_EXPLAIN("Premature end of file.");
218 				ERR_FAIL_V(RES());
219 			}
220 		}
221 
222 		if (chunkID[0] == 's' && chunkID[1] == 'm' && chunkID[2] == 'p' && chunkID[3] == 'l') {
223 			//loop point info!
224 
225 			for (int i = 0; i < 10; i++)
226 				file->get_32(); // i wish to know why should i do this... no doc!
227 
228 			loop = file->get_32() ? Sample::LOOP_PING_PONG : Sample::LOOP_FORWARD;
229 			loop_begin = file->get_32();
230 			loop_end = file->get_32();
231 		}
232 		file->seek(file_pos + chunksize);
233 	}
234 
235 	sample->set_loop_format(loop);
236 	sample->set_loop_begin(loop_begin);
237 	sample->set_loop_end(loop_end);
238 
239 	file->close();
240 	memdelete(file);
241 
242 	if (r_error)
243 		*r_error = OK;
244 
245 	return sample;
246 }
get_recognized_extensions(List<String> * p_extensions) const247 void ResourceFormatLoaderWAV::get_recognized_extensions(List<String> *p_extensions) const {
248 
249 	p_extensions->push_back("wav");
250 }
handles_type(const String & p_type) const251 bool ResourceFormatLoaderWAV::handles_type(const String &p_type) const {
252 
253 	return (p_type == "Sample");
254 }
255 
get_resource_type(const String & p_path) const256 String ResourceFormatLoaderWAV::get_resource_type(const String &p_path) const {
257 
258 	if (p_path.extension().to_lower() == "wav")
259 		return "Sample";
260 	return "";
261 }
262