xref: /haiku/src/tests/kits/midi/synth_file_reader/SynthFileReader.cpp (revision 4c8e85b316c35a9161f5a1c50ad70bc91c83a76f)
1 /*
2 
3 SynthFileReader.cpp
4 
5 Copyright (c) 2002 Haiku.
6 
7 
8 Author:
9 	Michael Pfeiffer
10 
11 Permission is hereby granted, free of charge, to any person obtaining a copy of
12 this software and associated documentation files (the "Software"), to deal in
13 the Software without restriction, including without limitation the rights to
14 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
15 of the Software, and to permit persons to whom the Software is furnished to do
16 so, subject to the following conditions:
17 
18 The above copyright notice and this permission notice shall be included in all
19 copies or substantial portions of the Software.
20 
21 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27 THE SOFTWARE.
28 
29 */
30 
31 #include "SynthFileReader.h"
32 #include "SynthFile.h"
33 #include <string.h>
34 #include <ctype.h>
35 
36 #define DEBUG 1
37 #include <Debug.h>
38 
39 SSynthFileReader::SSynthFileReader(const char* synthFile) {
40 	fFile = fopen(synthFile, "r+b");
41 	tag tag;
42 	if (fFile) {
43 		if (Read(tag) && TagEquals(tag, "IREZ")) {
44 			return;
45 		}
46 		fclose(fFile); fFile = NULL;
47 	}
48 
49 }
50 
51 SSynthFileReader::~SSynthFileReader() {
52 	if (fFile) {
53 		fclose(fFile); fFile = NULL;
54 	}
55 }
56 
57 status_t SSynthFileReader::InitCheck() const {
58 	return fFile != NULL ? B_OK : B_ERROR;
59 }
60 
61 bool SSynthFileReader::TagEquals(const char* tag1, const char* tag2) const {
62 	return strncmp(tag1, tag2, 4) == 0;
63 }
64 
65 bool SSynthFileReader::Read(void* data, uint32 size) {
66 	return 1 == fread(data, size, 1, fFile);
67 }
68 
69 bool SSynthFileReader::Read(tag &tag) {
70 	return Read((void*)tag, sizeof(tag));
71 }
72 
73 bool SSynthFileReader::Read(uint64 &n, uint32 size) {
74 	uint8 number[8];
75 	ASSERT(size <= sizeof(number));
76 	if (Read((void*)number, size)) {
77 		n = 0;
78 		for (unsigned int i = 0; i < size; i ++) {
79 			n <<= 8;
80 			n += number[i];
81 		}
82 		return true;
83 	}
84 	return false;
85 }
86 
87 bool SSynthFileReader::Read(uint32 &n) {
88 	uint64 num;
89 	bool ok = Read(num, 4);
90 	n = num;
91 	return ok;
92 }
93 
94 
95 bool SSynthFileReader::Read(uint16 &n) {
96 	uint64 num;
97 	bool ok = Read(num, 2);
98 	n = num;
99 	return ok;
100 }
101 
102 
103 bool SSynthFileReader::Read(uint8 &n) {
104 	return Read((void*)&n, 1);
105 }
106 
107 bool SSynthFileReader::Read(BString& s, uint32 len) {
108 	char* str = s.LockBuffer(len+1);
109 	str[len] = 0;
110 	bool ok = Read((void*)str, len);
111 	s.UnlockBuffer(len+1);
112 	return ok;
113 }
114 
115 bool SSynthFileReader::Skip(uint32 bytes) {
116 	fseek(fFile, bytes, SEEK_CUR);
117 	return true;
118 }
119 
120 
121 bool SSynthFileReader::ReadHeader(uint32& version, uint32& chunks, uint32& nextChunk) {
122 	tag tag;
123 	fseek(fFile, 0, SEEK_SET);
124 	if (Read(tag) && TagEquals(tag, "IREZ") && Read(version) && Read(chunks)) {
125 		nextChunk = ftell(fFile);
126 		return true;
127 	}
128 	return false;
129 }
130 
131 
132 bool SSynthFileReader::NextChunk(tag& tag, uint32& nextChunk) {
133 	fseek(fFile, nextChunk, SEEK_SET);
134 	return Read(nextChunk) && Read(tag);
135 }
136 
137 
138 bool SSynthFileReader::ReadInstHeader(uint16& inst, uint16& snd, uint16& snds, BString& name, uint32& size) {
139 	uint8 len;
140 
141 	if (Skip(2) && Read(inst) && Read(len) && Read(name, len) && Read(size) && Read(snd) && Skip(10) && Read(snds)) {
142 		size -= 14;
143 		return true;
144 	}
145 	return false;
146 }
147 
148 
149 bool SSynthFileReader::ReadSoundInRange(uint8& start, uint8& end, uint16& snd, uint32& size) {
150 	size -= 4;
151 	return Read(start) && Read(end) && Read(snd) && Skip(4);
152 }
153 
154 
155 bool SSynthFileReader::ReadSoundHeader(uint16& inst, BString& name, uint32& size) {
156 	uint8 len;
157 	return Skip(2) && Read(inst) && Read(len) && Read(name, len) && Read(size);
158 }
159 
160 
161 status_t SSynthFileReader::Initialize(SSynthFile* synth) {
162 	uint32   version;
163 	uint32   chunks;
164 	uint32   nextChunk;
165 	tag      t;
166 
167 	if (ReadHeader(version, chunks, nextChunk)) {
168 		for (uint32 chunk = 0; chunk < chunks; chunk ++) {
169 			if (NextChunk(t, nextChunk)) {
170 				if (TagEquals(t, "INST")) {
171 					uint32  offset = Tell();
172 					uint16  inst;
173 					uint16  snd;
174 					uint16  snds;
175 					BString name;
176 					uint32  size;
177 					if (ReadInstHeader(inst, snd, snds, name, size)) {
178 						SInstrument* instr = synth->GetInstrument(inst);
179 						instr->SetOffset(offset);
180 						instr->SetName(name.String());
181 						instr->SetDefaultSound(synth->GetSound(snd));
182 						for (int s = 0; s < snds; s ++) {
183 							uint8 start, end;
184 							if (ReadSoundInRange(start, end, snd, size)) {
185 								instr->Sounds()->AddItem(new SSoundInRange(start, end, synth->GetSound(snd)));
186 							} else {
187 								return B_ERROR;
188 							}
189 						}
190 					} else {
191 						return B_ERROR;
192 					}
193 				} else if (TagEquals(t, "snd ")) {
194 					uint32  offset = Tell();
195 					uint16  inst;
196 					BString name;
197 					uint32  size;
198 					if (ReadSoundHeader(inst, name, size)) {
199 						SSound* s = synth->GetSound(inst);
200 						s->SetOffset(offset);
201 						s->SetName(name.String());
202 					} else {
203 						return B_ERROR;
204 					}
205 				} else {
206 					// skip
207 				}
208 			} else {
209 				return B_ERROR;
210 			}
211 		}
212 		return B_OK;
213 	}
214 	return B_ERROR;
215 }
216 
217 // debugging
218 void SSynthFileReader::Print(tag tag) {
219 	printf("%.*s", 4, tag);
220 }
221 
222 
223 void SSynthFileReader::Dump(uint32 bytes) {
224 	uint8 byte;
225 	int   col = 0;
226 	const int cols = 16;
227 	bool  first = true;
228 	long  start = ftell(fFile);
229 
230 	for (;bytes > 0 && Read(byte); bytes --, col = (col + 1) % cols) {
231 		if (col == 0) {
232 			if (first) first = false;
233 			else printf("\n");
234 			printf("%6.6lx(%3.3lx)  ", ftell(fFile)-1, ftell(fFile)-start-1);
235 		}
236 		printf("%2.2x ", (uint)byte);
237 		if (isprint(byte)) printf("'%c' ", (char)byte);
238 		else printf("    ");
239 	}
240 }
241 
242 void SSynthFileReader::Dump(bool play, uint32 instrOnly) {
243 	tag    tag;
244 	uint32 version;
245 	uint32 nEntries;
246 	uint32 next;
247 	uint32 cur;
248 
249 	printf("SynthFileReader::Dump\n");
250 	printf("=================\n\n");
251 
252 	// read header
253 	fseek(fFile, 0, SEEK_SET);
254 	Read(tag); ASSERT(TagEquals(tag, "IREZ"));
255 	if (Read(version) && Read(nEntries)) {
256 		printf("version= %ld entries= %ld\n", version, nEntries);
257 
258 		// dump rest of file
259 //		for (uint32 i = 0; i < nEntries; i++) {
260 		while (Read(next)) {
261 			printf("next=%lx cur=%lx ", next, cur);
262 			cur = ftell(fFile);
263 			if (Read(tag)) {
264 				Print(tag);
265 				if (!play && TagEquals(tag, "INST")) {
266 					uint32  inst;
267 					uint8   len;
268 					BString name;
269 					uint32  size;
270 
271 					if (Read(inst) && Read(len) && Read(name, len) && Read(size)) {
272 						uint32 rest = size;
273 						printf(" inst=%d '%s' size=%lx", (int)inst, name.String(), size);
274 
275 						printf("\n"); Dump(12); printf("\n");
276 						rest -= 10;
277 
278 						uint16 elems;
279 						Read(elems);
280 						rest -= 4;
281 
282 						printf("elems = %d\n", elems);
283 
284 						if (elems > 0 || rest >= 16) {
285 							Dump(elems * 8 + 21); printf("\n");
286 
287 							rest -= elems * 8 + 21;
288 						} else {
289 							printf("rest %ld\n", rest);
290 							Dump(rest);
291 							rest = 0;
292 						}
293 
294 						bool prev_was_sust = false;
295 						while (rest > 0) {
296 							Read(tag); rest -= 4;
297 							Print(tag); printf("\n");
298 							int s = 0;
299 							if (TagEquals(tag, "ADSR")) {
300 								s = 1+4+4;
301 							} else if (TagEquals(tag, "LINE")) {
302 								s = 8;
303 							} else if (TagEquals(tag, "SUST")) {
304 								s = 8;
305 							} else if (TagEquals(tag, "LAST")) {
306 								s = 0;
307 								if (rest > 0) {
308 									SSynthFileReader::tag tag2;
309 									long pos = ftell(fFile);
310 									Read(tag2);
311 									fseek(fFile, pos, SEEK_SET);
312 									if (!isalpha(tag2[0])) {
313 										s = 4;
314 									}
315 								}
316 							} else if (TagEquals(tag, "LPGF")) {
317 								s = 12;
318 							} else if (TagEquals(tag, "LPFR")) {
319 								s = 9;
320 							} else if (TagEquals(tag, "SINE")) {
321 								s = 8;
322 							} else if (TagEquals(tag, "LPRE")) {
323 								s = 9;
324 							} else if (TagEquals(tag, "PITC")) {
325 								s = 9;
326 							} else if (TagEquals(tag, "LPAM")) {
327 								s = 9;
328 							} else if (TagEquals(tag, "VOLU")) {
329 								s = 9;
330 							} else if (TagEquals(tag, "SPAN")) {
331 								s = 9;
332 							} else if (TagEquals(tag, "TRIA")) {
333 								s = 8;
334 							} else if (TagEquals(tag, "SQU2")) {
335 								s = 8;
336 							} else if (TagEquals(tag, "SQUA")) {
337 								s = 8;
338 							// Patches*.hsb
339 							} else if (TagEquals(tag, "CURV")) {
340 								s = 0;
341 							} else {
342 								// ASSERT(false);
343 								printf("unknown tag "); Print(tag); printf("\n");
344 								// ASSERT(false);
345 								break;
346 							}
347 							prev_was_sust = TagEquals(tag, "SUST");
348 							Dump(s); printf("\n");
349 							if (rest < s) {
350 								printf("wrong size: rest=%ld size= %d\n", rest, s);
351 								break;
352 							}
353 							rest -= s;
354 						}
355 						if (rest > 0) {
356 							printf("Rest:\n");
357 							Dump(rest); printf("\n");
358 						}
359 					}
360 				} else if (TagEquals(tag, "snd ")) {
361 					uint32  inst;
362 					uint8   len;
363 					BString name;
364 					uint32  size;
365 
366 					if (Read(inst) && Read(len) && Read(name, len) && Read(size)) {
367 						printf(" inst=%lx '%s' size=%lx\n", inst, name.String(), size);
368 						uint32 rest = size;
369 						Dump(28); printf("\n"); rest -= 28;
370 						uint16 rate;
371 						Read(rate); rest -= 2;
372 						printf("rate=%d\n", (int)rate);
373 						Dump(16*3+7); printf("\n"); rest -= 16*3+7;
374 						printf("size=%ld offsetToNext=%ld\n", size, next-cur);
375 						if (play && (instrOnly==0xffff || instrOnly == inst)) Play(rate, 0, rest);
376 					}
377 				}
378 				printf("\n");
379 			} else {
380 				exit(-1);
381 			}
382 			fseek(fFile, next, SEEK_SET);
383 		}
384 	} else {
385 		printf("Could not read header\n");
386 	}
387 }
388 
389 #include <GameSoundDefs.h>
390 #include <SimpleGameSound.h>
391 
392 void SSynthFileReader::Play(uint16 rate, uint32 offset, uint32 size) {
393 	uint8* samples = new uint8[size];
394 	fseek(fFile, offset, SEEK_CUR);
395 	Read((void*)samples, size);
396 
397 	BSimpleGameSound* s;
398 	gs_audio_format format = {
399 		rate,
400 		1,
401 		gs_audio_format::B_GS_S16,
402 		2,
403 		0
404 	};
405 	s = new BSimpleGameSound((void*)samples, size/2, &format);
406 	if (s->InitCheck() == B_OK) {
407 		s->StartPlaying();
408 		s->SetIsLooping(true);
409 		printf("hit enter "); while (getchar() != '\n');
410 		s->StopPlaying();
411 	} else {
412 		printf("Could not initialize BSimpleGameSound!\n");
413 	}
414 	delete s;
415 }
416