1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
|
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#ifndef AGI_SOUND_H
#define AGI_SOUND_H
#include "sound/audiostream.h"
#include "sound/mixer.h"
namespace Agi {
#define BUFFER_SIZE 410
#define SOUND_EMU_NONE 0
#define SOUND_EMU_PC 1
#define SOUND_EMU_TANDY 2
#define SOUND_EMU_MAC 3
#define SOUND_EMU_AMIGA 4
#define WAVEFORM_SIZE 64
#define ENV_ATTACK 10000 /**< envelope attack rate */
#define ENV_DECAY 1000 /**< envelope decay rate */
#define ENV_SUSTAIN 100 /**< envelope sustain level */
#define ENV_RELEASE 7500 /**< envelope release rate */
#define NUM_CHANNELS 7 /**< number of sound channels */
/**
* AGI sound resource structure.
*/
struct AgiSound {
uint32 flen; /**< size of raw data */
uint8 *rdata; /**< raw sound data */
uint16 type; /**< sound resource type */
void play() {
_isPlaying = true;
}
void stop() {
_isPlaying = false;
}
bool isPlaying() {
return _isPlaying;
}
private:
bool _isPlaying; ///< Is the sound playing?
};
struct IIgsEnvelopeSegment {
uint8 bp;
uint16 inc; ///< 8b.8b fixed point, big endian?
};
#define ENVELOPE_SEGMENT_COUNT 8
struct IIgsEnvelope {
IIgsEnvelopeSegment seg[ENVELOPE_SEGMENT_COUNT];
/** Reads an Apple IIGS envelope from then given stream. */
bool read(Common::SeekableReadStream &stream) {
for (int segNum = 0; segNum < ENVELOPE_SEGMENT_COUNT; segNum++) {
seg[segNum].bp = stream.readByte();
seg[segNum].inc = stream.readUint16BE();
}
return !stream.ioFailed();
}
};
// 2**(1/12) i.e. the 12th root of 2
#define SEMITONE 1.059463094359295
// Size of the SIERRASTANDARD file (i.e. the wave file i.e. the sample data used by the instruments).
#define SIERRASTANDARD_SIZE 65536
// Maximum number of instruments in an Apple IIGS instrument set.
// Chosen empirically based on Apple IIGS AGI game data, increase if needed.
#define MAX_INSTRUMENTS 28
struct IIgsWaveInfo {
uint8 top;
uint addr;
uint size;
// Oscillator channel
#define OSC_CHANNEL_RIGHT 0
#define OSC_CHANNEL_LEFT 1
uint channel;
// Oscillator mode
#define OSC_MODE_LOOP 0
#define OSC_MODE_ONESHOT 1
#define OSC_MODE_SYNC_AM 2
#define OSC_MODE_SWAP 3
uint mode;
bool halt;
uint16 relPitch; ///< 8b.8b fixed point, big endian?
/** Reads an Apple IIGS wave information structure from the given stream. */
bool read(Common::SeekableReadStream &stream, bool ignoreAddr = false) {
top = stream.readByte();
addr = stream.readByte() * 256;
size = (1 << (stream.readByte() & 7)) * 256;
// Read packed mode byte and parse it into parts
byte packedModeByte = stream.readByte();
channel = (packedModeByte >> 4) & 1; // Bit 4
mode = (packedModeByte >> 1) & 3; // Bits 1-2
halt = (packedModeByte & 1) != 0; // Bit 0 (Converted to boolean)
relPitch = stream.readUint16BE();
// Zero the wave address if we want to ignore the wave address info
if (ignoreAddr)
addr = 0;
return !stream.ioFailed();
}
bool finalize(Common::SeekableReadStream &uint8Wave) {
uint32 startPos = uint8Wave.pos(); // Save stream's starting position
uint8Wave.seek(addr, SEEK_CUR); // Seek to wave's address
// Calculate the true sample size (A zero ends the sample prematurely)
uint trueSize = size; // Set a default value for the result
for (uint i = 0; i < size; i++) {
if (uint8Wave.readByte() == 0) {
trueSize = i;
break;
}
}
size = trueSize; // Set the true sample size
uint8Wave.seek(startPos); // Seek back to the stream's starting position
return true;
}
};
// Number of waves per Apple IIGS sound oscillator
#define WAVES_PER_OSCILLATOR 2
/** An Apple IIGS sound oscillator. Consists always of two waves. */
struct IIgsOscillator {
IIgsWaveInfo waves[WAVES_PER_OSCILLATOR];
bool finalize(Common::SeekableReadStream &uint8Wave) {
for (uint i = 0; i < WAVES_PER_OSCILLATOR; i++)
if (!waves[i].finalize(uint8Wave))
return false;
return true;
}
};
// Maximum number of oscillators in an Apple IIGS instrument.
// Chosen empirically based on Apple IIGS AGI game data, increase if needed.
#define MAX_OSCILLATORS 4
/** An Apple IIGS sound oscillator list. */
struct IIgsOscillatorList {
uint count; ///< Oscillator count
IIgsOscillator osc[MAX_OSCILLATORS]; ///< The oscillators
bool read(Common::SeekableReadStream &stream, uint oscillatorCount, bool ignoreAddr = false) {
// First read the A waves and then the B waves for the oscillators
for (uint waveNum = 0; waveNum < WAVES_PER_OSCILLATOR; waveNum++)
for (uint oscNum = 0; oscNum < oscillatorCount; oscNum++)
if (!osc[oscNum].waves[waveNum].read(stream, ignoreAddr))
return false;
count = oscillatorCount; // Set the oscillator count
return true;
}
bool finalize(Common::SeekableReadStream &uint8Wave) {
for (uint i = 0; i < count; i++)
if (!osc[i].finalize(uint8Wave))
return false;
return true;
}
};
struct IIgsInstrumentHeader {
IIgsEnvelope env;
uint8 relseg;
uint8 bendrange;
uint8 vibdepth;
uint8 vibspeed;
IIgsOscillatorList oscList;
/**
* Read an Apple IIGS instrument header from the given stream.
* @param stream The source stream from which to read the data.
* @param ignoreAddr Should we ignore wave infos' wave address variable's value?
* @return True if successful, false otherwise.
*/
bool read(Common::SeekableReadStream &stream, bool ignoreAddr = false) {
env.read(stream);
relseg = stream.readByte();
byte priority = stream.readByte(); // Not needed? 32 in all tested data.
bendrange = stream.readByte();
vibdepth = stream.readByte();
vibspeed = stream.readByte();
byte spare = stream.readByte(); // Not needed? 0 in all tested data.
byte wac = stream.readByte(); // Read A wave count
byte wbc = stream.readByte(); // Read B wave count
oscList.read(stream, wac, ignoreAddr); // Read the oscillators
return (wac == wbc) && !stream.ioFailed(); // A and B wave counts must match
}
bool finalize(Common::SeekableReadStream &uint8Wave) {
return oscList.finalize(uint8Wave);
}
};
struct IIgsSampleHeader {
uint16 type;
uint8 pitch; ///< Logarithmic, base is 2**(1/12), unknown multiplier (Possibly in range 1040-1080)
uint8 unknownByte_Ofs3; // 0x7F in Gold Rush's sound resource 60, 0 in all others.
uint8 volume; ///< Current guess: Logarithmic in 6 dB steps
uint8 unknownByte_Ofs5; ///< 0 in all tested samples.
uint16 instrumentSize; ///< Little endian. 44 in all tested samples. A guess.
uint16 sampleSize; ///< Little endian. Accurate in all tested samples excluding Manhunter I's sound resource 16.
IIgsInstrumentHeader instrument;
/**
* Read an Apple IIGS AGI sample header from the given stream.
* @param stream The source stream from which to read the data.
* @return True if successful, false otherwise.
*/
bool read(Common::SeekableReadStream &stream) {
type = stream.readUint16LE();
pitch = stream.readByte();
unknownByte_Ofs3 = stream.readByte();
volume = stream.readByte();
unknownByte_Ofs5 = stream.readByte();
instrumentSize = stream.readUint16LE();
sampleSize = stream.readUint16LE();
// Read the instrument header *ignoring* its wave address info
return instrument.read(stream, true);
}
bool finalize(Common::SeekableReadStream &uint8Wave) {
return instrument.finalize(uint8Wave);
}
};
#include "common/pack-start.h"
/**
* AGI sound note structure.
*/
struct AgiNote {
uint8 durLo; /**< LSB of note duration */
uint8 durHi; /**< MSB of note duration */
uint8 frq0; /**< LSB of note frequency */
uint8 frq1; /**< MSB of note frequency */
uint8 vol; /**< note volume */
};
#include "common/pack-end.h"
/**
* AGI engine sound channel structure.
*/
struct ChannelInfo {
#define AGI_SOUND_SAMPLE 0x0001
#define AGI_SOUND_MIDI 0x0002
#define AGI_SOUND_4CHN 0x0008
uint32 type;
struct AgiNote *ptr;
int16 *ins;
int32 size;
uint32 phase;
#define AGI_SOUND_LOOP 0x0001
#define AGI_SOUND_ENVELOPE 0x0002
uint32 flags;
#define AGI_SOUND_ENV_ATTACK 3
#define AGI_SOUND_ENV_DECAY 2
#define AGI_SOUND_ENV_SUSTAIN 1
#define AGI_SOUND_ENV_RELEASE 0
uint32 adsr;
int32 timer;
uint32 end;
uint32 freq;
uint32 vol;
uint32 env;
};
/** Apple IIGS AGI instrument set information. */
struct instrumentSetInfo {
uint byteCount; ///< Length of the whole instrument set in bytes
uint instCount; ///< Amount of instrument in the set
const char *md5; ///< MD5 hex digest of the whole instrument set
const char *waveFileMd5; ///< MD5 hex digest of the wave file (i.e. the sample data used by the instruments)
};
/** Apple IIGS AGI executable file information. */
struct IIgsExeInfo {
enum AgiGameID gameid; ///< Game ID
const char *exePrefix; ///< Prefix of the Apple IIGS AGI executable (e.g. "SQ", "PQ", "KQ4" etc)
uint agiVer; ///< Apple IIGS AGI version number, not strictly needed
uint exeSize; ///< Size of the Apple IIGS AGI executable file in bytes
uint instSetStart; ///< Starting offset of the instrument set inside the executable file
const instrumentSetInfo &instSet; ///< Information about the used instrument set
};
class AgiEngine;
class SoundMgr : public Audio::AudioStream {
AgiEngine *_vm;
public:
SoundMgr(AgiEngine *agi, Audio::Mixer *pMixer);
~SoundMgr();
virtual void setVolume(uint8 volume);
// AudioStream API
int readBuffer(int16 *buffer, const int numSamples) {
premixerCall(buffer, numSamples / 2);
return numSamples;
}
bool isStereo() const {
return false;
}
bool endOfData() const {
return false;
}
int getRate() const {
// FIXME: Ideally, we should use _sampleRate.
return 22050;
}
private:
Audio::Mixer *_mixer;
Audio::SoundHandle _soundHandle;
uint32 _sampleRate;
void premixerCall(int16 *buf, uint len);
public:
void decodeSound(int);
void unloadSound(int);
void playSound();
int initSound();
void deinitSound();
void startSound(int, int);
void stopSound();
void stopNote(int i);
void playNote(int i, int freq, int vol);
void playAgiSound();
uint32 mixSound();
bool loadInstruments();
#ifdef USE_IIGS_SOUND
void playMidiSound();
void playSampleSound();
bool finalizeInstruments(Common::SeekableReadStream &uint8Wave);
Audio::AudioStream *makeIIgsSampleStream(Common::SeekableReadStream &stream, int resnum = -1);
const IIgsExeInfo *getIIgsExeInfo(enum AgiGameID gameid) const;
bool loadInstrumentHeaders(const Common::String &exePath, const IIgsExeInfo &exeInfo);
bool convertWave(Common::SeekableReadStream &source, int16 *dest, uint length);
Common::MemoryReadStream *loadWaveFile(const Common::String &wavePath, const IIgsExeInfo &exeInfo);
#endif
};
} // End of namespace Agi
#endif /* AGI_SOUND_H */
|