aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKari Salminen2007-08-15 22:00:31 +0000
committerKari Salminen2007-08-15 22:00:31 +0000
commitc987d6aaf0744e088c3119d7270e8a02ad47044a (patch)
tree28e6f1ac543bc8125748b214b6f9f0e17d505e96
parentb99153050a8bb909c6d20b01cb6b5e8ba3224483 (diff)
downloadscummvm-rg350-c987d6aaf0744e088c3119d7270e8a02ad47044a.tar.gz
scummvm-rg350-c987d6aaf0744e088c3119d7270e8a02ad47044a.tar.bz2
scummvm-rg350-c987d6aaf0744e088c3119d7270e8a02ad47044a.zip
Added rudimentary classes for different AGI sound resources (IIgsMidi, IIgsSample, PCjrSound). Made existing code to at least work with PCjrSound.
svn-id: r28630
-rw-r--r--engines/agi/agi.cpp2
-rw-r--r--engines/agi/agi.h2
-rw-r--r--engines/agi/agi_v2.cpp3
-rw-r--r--engines/agi/agi_v3.cpp3
-rw-r--r--engines/agi/sound.cpp161
-rw-r--r--engines/agi/sound.h60
6 files changed, 147 insertions, 84 deletions
diff --git a/engines/agi/agi.cpp b/engines/agi/agi.cpp
index ee3e2f36e0..ec8c819ab3 100644
--- a/engines/agi/agi.cpp
+++ b/engines/agi/agi.cpp
@@ -370,7 +370,7 @@ int AgiEngine::agiInit() {
memset(&_game.views[i], 0, sizeof(struct AgiView));
memset(&_game.pictures[i], 0, sizeof(struct AgiPicture));
memset(&_game.logics[i], 0, sizeof(struct AgiLogic));
- memset(&_game.sounds[i], 0, sizeof(class AgiSound));
+ memset(&_game.sounds[i], 0, sizeof(class AgiSound *)); // _game.sounds contains pointers now
memset(&_game.dirView[i], 0, sizeof(struct AgiDir));
memset(&_game.dirPic[i], 0, sizeof(struct AgiDir));
memset(&_game.dirLogic[i], 0, sizeof(struct AgiDir));
diff --git a/engines/agi/agi.h b/engines/agi/agi.h
index ae880da82c..046688f240 100644
--- a/engines/agi/agi.h
+++ b/engines/agi/agi.h
@@ -525,7 +525,7 @@ struct AgiGame {
AgiPicture pictures[MAX_DIRS]; /**< AGI picture resources */
AgiLogic logics[MAX_DIRS]; /**< AGI logic resources */
AgiView views[MAX_DIRS]; /**< AGI view resources */
- AgiSound sounds[MAX_DIRS]; /**< AGI sound resources */
+ AgiSound *sounds[MAX_DIRS]; /**< Pointers to AGI sound resources */
/* view table */
VtEntry viewTable[MAX_VIEWTABLE];
diff --git a/engines/agi/agi_v2.cpp b/engines/agi/agi_v2.cpp
index 1cd9126350..593ca40384 100644
--- a/engines/agi/agi_v2.cpp
+++ b/engines/agi/agi_v2.cpp
@@ -254,7 +254,8 @@ int AgiLoader_v2::loadResource(int t, int n) {
data = loadVolRes(&_vm->_game.dirSound[n]);
if (data != NULL) {
- _vm->_game.sounds[n].rdata = data;
+ // Freeing of the raw resource from memory is delegated to the createFromRawResource-function
+ _vm->_game.sounds[n] = AgiSound::createFromRawResource(data, _vm->_game.dirSound[n].len, n, *_vm->_sound);
_vm->_game.dirSound[n].flags |= RES_LOADED;
} else {
ec = errBadResource;
diff --git a/engines/agi/agi_v3.cpp b/engines/agi/agi_v3.cpp
index c6fb850c60..dc419a7afe 100644
--- a/engines/agi/agi_v3.cpp
+++ b/engines/agi/agi_v3.cpp
@@ -345,7 +345,8 @@ int AgiLoader_v3::loadResource(int t, int n) {
data = loadVolRes(&_vm->_game.dirSound[n]);
if (data != NULL) {
- _vm->_game.sounds[n].rdata = data;
+ // Freeing of the raw resource from memory is delegated to the createFromRawResource-function
+ _vm->_game.sounds[n] = AgiSound::createFromRawResource(data, _vm->_game.dirSound[n].len, n, *_vm->_sound);
_vm->_game.dirSound[n].flags |= RES_LOADED;
} else {
ec = errBadResource;
diff --git a/engines/agi/sound.cpp b/engines/agi/sound.cpp
index f95ad1c923..0fe294af45 100644
--- a/engines/agi/sound.cpp
+++ b/engines/agi/sound.cpp
@@ -42,6 +42,81 @@ static bool g_useChorus = true;
/* TODO: add support for variable sampling rate in the output device
*/
+AgiSound *AgiSound::createFromRawResource(uint8 *data, uint32 len, int resnum, SoundMgr &manager) {
+ if (data == NULL || len < 2) return NULL; // Check for too small resource or no resource at all
+ uint16 type = READ_LE_UINT16(data);
+
+ switch (type) { // Create a sound object based on the type
+ case AGI_SOUND_SAMPLE : return new IIgsSample(data, len, resnum, manager);
+ case AGI_SOUND_MIDI : return new IIgsMidi (data, len, resnum, manager);
+ case AGI_SOUND_4CHN : return new PCjrSound (data, len, resnum, manager);
+ }
+
+ warning("Sound resource (%d) has unknown type (0x%04x). Not using the sound", resnum, type);
+ return NULL;
+}
+
+IIgsMidi::IIgsMidi(uint8 *data, uint32 len, int resnum, SoundMgr &manager) : AgiSound(manager) {
+ _data = data; // Save the resource pointer
+ _len = len; // Save the resource's length
+ _type = READ_LE_UINT16(data); // Read sound resource's type
+ _isValid = (_type == AGI_SOUND_MIDI) && (_data != NULL) && (_len >= 2);
+
+ if (!_isValid) // Check for errors
+ warning("Error creating Apple IIGS midi sound from resource %d (Type %d, length %d)", resnum, _type, len);
+}
+
+PCjrSound::PCjrSound(uint8 *data, uint32 len, int resnum, SoundMgr &manager) : AgiSound(manager) {
+ _data = data; // Save the resource pointer
+ _len = len; // Save the resource's length
+ _type = READ_LE_UINT16(data); // Read sound resource's type
+ _isValid = (_type == AGI_SOUND_4CHN) && (_data != NULL) && (_len >= 2);
+
+ if (!_isValid) // Check for errors
+ warning("Error creating PCjr 4-channel sound from resource %d (Type %d, length %d)", resnum, _type, len);
+}
+
+const uint8 *PCjrSound::getVoicePointer(uint voiceNum) {
+ assert(voiceNum >= 0 && voiceNum < 4);
+ uint16 voiceStartOffset = READ_LE_UINT16(_data + voiceNum * 2);
+ return _data + voiceStartOffset;
+}
+
+IIgsSample::IIgsSample(uint8 *data, uint32 len, int resnum, SoundMgr &manager) : AgiSound(manager) {
+ Common::MemoryReadStream stream(data, len, true);
+
+ // Check that the header was read ok and that it's of the correct type
+ if (_header.read(stream) && _header.type == AGI_SOUND_SAMPLE) { // An Apple IIGS AGI sample resource
+ uint32 sampleStartPos = stream.pos();
+ uint32 tailLen = stream.size() - sampleStartPos;
+
+ if (tailLen < _header.sampleSize) { // Check if there's no room for the sample data in the stream
+ // Apple IIGS Manhunter I: Sound resource 16 has only 16074 bytes
+ // of sample data although header says it should have 16384 bytes.
+ warning("Apple IIGS sample (%d) too short (%d bytes. Should be %d bytes). Using the part that's left",
+ resnum, tailLen, _header.sampleSize);
+ _header.sampleSize = (uint16) tailLen; // Use the part that's left
+ }
+
+ if (_header.pitch > 0x7F) { // Check if the pitch is invalid
+ warning("Apple IIGS sample (%d) has too high pitch (0x%02x)", resnum, _header.pitch);
+ _header.pitch &= 0x7F; // Apple IIGS AGI probably did it this way too
+ }
+
+ // Finalize the header info using the 8-bit unsigned sample data
+ _header.finalize(stream);
+
+ // Convert sample data from 8-bit unsigned to 16-bit signed format
+ stream.seek(sampleStartPos);
+ _sample = new int16[_header.sampleSize];
+ if (_sample != NULL)
+ _isValid = _manager.convertWave(stream, _sample, _header.sampleSize);
+ }
+
+ if (!_isValid) // Check for errors
+ warning("Error creating Apple IIGS sample from resource %d (Type %d, length %d)", resnum, _header.type, len);
+}
+
/** Reads an Apple IIGS envelope from then given stream. */
bool IIgsEnvelope::read(Common::SeekableReadStream &stream) {
for (int segNum = 0; segNum < ENVELOPE_SEGMENT_COUNT; segNum++) {
@@ -187,69 +262,10 @@ bool SoundMgr::finalizeInstruments(Common::SeekableReadStream &uint8Wave) {
return true;
}
-/**
- * Load an Apple IIGS AGI sample resource from the given stream and
- * create an AudioStream out of it.
- *
- * @param stream The source stream.
- * @param resnum Sound resource number. Optional. Used for error messages.
- * @return A non-null AudioStream pointer if successful, NULL otherwise.
- * @note In case of failure (i.e. NULL is returned), stream is reset back
- * to its original position and its I/O failed -status is cleared.
- * TODO: Add better handling of invalid resource number when printing error messages.
- * TODO: Add support for looping sounds.
- * FIXME: Fix sample rate calculation, it's probably not accurate at the moment.
- */
-Audio::AudioStream *SoundMgr::makeIIgsSampleStream(Common::SeekableReadStream &stream, int resnum) {
- const uint32 startPos = stream.pos();
- IIgsSampleHeader header;
- Audio::AudioStream *result = NULL;
- bool readHeaderOk = header.read(stream);
-
- // Check that the header was read ok and that it's of the correct type
- // and that there's room for the sample data in the stream.
- if (readHeaderOk && header.type == AGI_SOUND_SAMPLE) { // An Apple IIGS AGI sample resource
- uint32 tailLen = stream.size() - stream.pos();
- if (tailLen < header.sampleSize) { // Check if there's no room for the sample data in the stream
- // Apple IIGS Manhunter I: Sound resource 16 has only 16074 bytes
- // of sample data although header says it should have 16384 bytes.
- warning("Apple IIGS sample (%d) too short (%d bytes. Should be %d bytes). Using the part that's left", resnum, tailLen, header.sampleSize);
- header.sampleSize = (uint16) tailLen; // Use the part that's left
- }
- if (header.pitch > 0x7F) { // Check if the pitch is invalid
- warning("Apple IIGS sample (%d) has too high pitch (0x%02x)", resnum, header.pitch);
- header.pitch &= 0x7F; // Apple IIGS AGI probably did it this way too
- }
- // Allocate memory for the sample data and read it in
- byte *sampleData = (byte *) malloc(header.sampleSize);
- uint32 readBytes = stream.read(sampleData, header.sampleSize);
- if (readBytes == header.sampleSize) { // Check that we got all the data we requested
- // Create a stream out of the read sample data (Needed by the finalize-function)
- Common::MemoryReadStream sampleStream(sampleData, readBytes);
- header.finalize(sampleStream);
- // Make an audio stream from the mono, 8 bit, unsigned input data
- byte flags = Audio::Mixer::FLAG_AUTOFREE | Audio::Mixer::FLAG_UNSIGNED;
- int rate = (int) (1076 * pow(SEMITONE, header.pitch));
- result = Audio::makeLinearInputStream(sampleData, header.sampleSize, rate, flags, 0, 0);
- } else // Couldn't read enough data, so let's delete the sample data buffer
- delete sampleData;
- }
-
- // If couldn't make a sample out of the input stream for any reason then
- // rewind back to stream's starting position and clear I/O failed -status.
- if (result == NULL) {
- stream.seek(startPos);
- stream.clearIOFailed();
- }
-
- return result;
-}
-
static int playing;
static ChannelInfo chn[NUM_CHANNELS];
static int endflag = -1;
static int playingSound = -1;
-static uint8 *song;
static uint8 env;
@@ -300,13 +316,13 @@ static int noteToPeriod(int note) {
void SoundMgr::unloadSound(int resnum) {
if (_vm->_game.dirSound[resnum].flags & RES_LOADED) {
- if (_vm->_game.sounds[resnum].isPlaying()) {
- _vm->_game.sounds[resnum].stop();
+ if (_vm->_game.sounds[resnum]->isPlaying()) {
+ _vm->_game.sounds[resnum]->stop();
}
- /* Release RAW data for sound */
- free(_vm->_game.sounds[resnum].rdata);
- _vm->_game.sounds[resnum].rdata = NULL;
+ // Release the sound resource's data
+ delete _vm->_game.sounds[resnum];
+ _vm->_game.sounds[resnum] = NULL;
_vm->_game.dirSound[resnum].flags &= ~RES_LOADED;
}
}
@@ -314,23 +330,21 @@ void SoundMgr::unloadSound(int resnum) {
void SoundMgr::startSound(int resnum, int flag) {
int i, type;
- if (_vm->_game.sounds[resnum].isPlaying())
+ if (_vm->_game.sounds[resnum] != NULL && _vm->_game.sounds[resnum]->isPlaying())
return;
stopSound();
- if (_vm->_game.sounds[resnum].rdata == NULL)
+ if (_vm->_game.sounds[resnum] == NULL) // Is this needed at all?
return;
- type = READ_LE_UINT16(_vm->_game.sounds[resnum].rdata);
+ type = _vm->_game.sounds[resnum]->type();
if (type != AGI_SOUND_SAMPLE && type != AGI_SOUND_MIDI && type != AGI_SOUND_4CHN)
return;
- _vm->_game.sounds[resnum].play();
- _vm->_game.sounds[resnum].type = type;
+ _vm->_game.sounds[resnum]->play();
playingSound = resnum;
- song = (uint8 *)_vm->_game.sounds[resnum].rdata;
switch (type) {
#if 0
@@ -365,6 +379,7 @@ void SoundMgr::startSound(int resnum, int flag) {
break;
#endif
case AGI_SOUND_4CHN:
+ PCjrSound *pcjrSound = (PCjrSound *) _vm->_game.sounds[resnum];
/* Initialize channel info */
for (i = 0; i < NUM_CHANNELS; i++) {
chn[i].type = type;
@@ -375,7 +390,7 @@ void SoundMgr::startSound(int resnum, int flag) {
}
chn[i].ins = waveform;
chn[i].size = WAVEFORM_SIZE;
- chn[i].ptr = song + READ_LE_UINT16(song + i * 2);
+ chn[i].ptr = pcjrSound->getVoicePointer(i % 4);
chn[i].timer = 0;
chn[i].vol = 0;
chn[i].end = 0;
@@ -399,7 +414,7 @@ void SoundMgr::stopSound() {
stopNote(i);
if (playingSound != -1) {
- _vm->_game.sounds[playingSound].stop();
+ _vm->_game.sounds[playingSound]->stop();
playingSound = -1;
}
}
@@ -611,7 +626,7 @@ void SoundMgr::playSound() {
_vm->setflag(endflag, true);
if (playingSound != -1)
- _vm->_game.sounds[playingSound].stop();
+ _vm->_game.sounds[playingSound]->stop();
playingSound = -1;
endflag = -1;
}
diff --git a/engines/agi/sound.h b/engines/agi/sound.h
index ccc63d2fdc..058f4ab67e 100644
--- a/engines/agi/sound.h
+++ b/engines/agi/sound.h
@@ -162,7 +162,7 @@ struct AgiNote {
uint8 attenuation; ///< Note volume attenuation (4-bit)
/** Reads an AgiNote through the given pointer. */
- void read(uint8 *ptr) {
+ void read(const uint8 *ptr) {
duration = READ_LE_UINT16(ptr);
uint16 freqByte0 = *(ptr + 2); // Bits 4-9 of the frequency divisor
uint16 freqByte1 = *(ptr + 3); // Bits 0-3 of the frequency divisor
@@ -180,7 +180,7 @@ struct ChannelInfo {
#define AGI_SOUND_MIDI 0x0002
#define AGI_SOUND_4CHN 0x0008
uint32 type;
- uint8 *ptr; // Pointer to the AgiNote data
+ const uint8 *ptr; // Pointer to the AgiNote data
int16 *ins;
int32 size;
uint32 phase;
@@ -199,20 +199,66 @@ struct ChannelInfo {
uint32 env;
};
+class SoundMgr;
+
/**
* AGI sound resource structure.
*/
class AgiSound {
public:
- uint32 flen; /**< size of raw data */
- uint8 *rdata; /**< raw sound data */
- uint16 type; /**< sound resource type */
-
+ AgiSound(SoundMgr &manager) : _manager(manager), _isPlaying(false), _isValid(false) {}
virtual void play() { _isPlaying = true; }
virtual void stop() { _isPlaying = false; }
virtual bool isPlaying() { return _isPlaying; }
+ virtual uint16 type() = 0;
+
+ /**
+ * A named constructor for creating different types of AgiSound objects
+ * from a raw sound resource.
+ *
+ * NOTE: This function should take responsibility for freeing the raw resource
+ * from memory using free() or delegate the responsibility onwards to some other
+ * function!
+ */
+ static AgiSound *createFromRawResource(uint8 *data, uint32 len, int resnum, SoundMgr &manager);
+
+protected:
+ SoundMgr &_manager; ///< AGI sound manager object
+ bool _isPlaying; ///< Is the sound playing?
+ bool _isValid; ///< Is this a valid sound object?
+};
+
+class PCjrSound : public AgiSound {
+public:
+ PCjrSound(uint8 *data, uint32 len, int resnum, SoundMgr &manager);
+ ~PCjrSound() { if (_data != NULL) free(_data); }
+ virtual uint16 type() { return _type; }
+ const uint8 *getVoicePointer(uint voiceNum);
+protected:
+ uint8 *_data; ///< Raw sound resource data
+ uint32 _len; ///< Length of the raw sound resource
+ uint16 _type; ///< Sound resource type
+};
+
+class IIgsMidi : public AgiSound {
+public:
+ IIgsMidi(uint8 *data, uint32 len, int resnum, SoundMgr &manager);
+ ~IIgsMidi() { if (_data != NULL) free(_data); }
+ virtual uint16 type() { return _type; }
+protected:
+ uint8 *_data; ///< Raw sound resource data
+ uint32 _len; ///< Length of the raw sound resource
+ uint16 _type; ///< Sound resource type
+};
+
+class IIgsSample : public AgiSound {
+public:
+ IIgsSample(uint8 *data, uint32 len, int resnum, SoundMgr &manager);
+ ~IIgsSample() { delete[] _sample; }
+ virtual uint16 type() { return _header.type; }
protected:
- bool _isPlaying; ///< Is the sound playing?
+ IIgsSampleHeader _header; ///< Apple IIGS AGI sample header
+ int16 *_sample; ///< Sample data (16-bit signed format)
};
/** Apple IIGS AGI instrument set information. */