1/*
2
3SynthFileReader.cpp
4
5Copyright (c) 2002 Haiku.
6
7
8Author:
9	Michael Pfeiffer
10
11Permission is hereby granted, free of charge, to any person obtaining a copy of
12this software and associated documentation files (the "Software"), to deal in
13the Software without restriction, including without limitation the rights to
14use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
15of the Software, and to permit persons to whom the Software is furnished to do
16so, subject to the following conditions:
17
18The above copyright notice and this permission notice shall be included in all
19copies or substantial portions of the Software.
20
21THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27THE 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
39SSynthFileReader::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
51SSynthFileReader::~SSynthFileReader() {
52	if (fFile) {
53		fclose(fFile); fFile = NULL;
54	}
55}
56
57status_t SSynthFileReader::InitCheck() const {
58	return fFile != NULL ? B_OK : B_ERROR;
59}
60
61bool SSynthFileReader::TagEquals(const char* tag1, const char* tag2) const {
62	return strncmp(tag1, tag2, 4) == 0;
63}
64
65bool SSynthFileReader::Read(void* data, uint32 size) {
66	return 1 == fread(data, size, 1, fFile);
67}
68
69bool SSynthFileReader::Read(tag &tag) {
70	return Read((void*)tag, sizeof(tag));
71}
72
73bool 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
87bool SSynthFileReader::Read(uint32 &n) {
88	uint64 num;
89	bool ok = Read(num, 4);
90	n = num;
91	return ok;
92}
93
94
95bool SSynthFileReader::Read(uint16 &n) {
96	uint64 num;
97	bool ok = Read(num, 2);
98	n = num;
99	return ok;
100}
101
102
103bool SSynthFileReader::Read(uint8 &n) {
104	return Read((void*)&n, 1);
105}
106
107bool 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
115bool SSynthFileReader::Skip(uint32 bytes) {
116	fseek(fFile, bytes, SEEK_CUR);
117	return true;
118}
119
120
121bool 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
132bool SSynthFileReader::NextChunk(tag& tag, uint32& nextChunk) {
133	fseek(fFile, nextChunk, SEEK_SET);
134	return Read(nextChunk) && Read(tag);
135}
136
137
138bool 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
149bool 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
155bool 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
161status_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
218void SSynthFileReader::Print(tag tag) {
219	printf("%.*s", 4, tag);
220}
221
222
223void 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
242void 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
392void 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