[Scummvm-cvs-logs] SF.net SVN: scummvm: [28630] scummvm/trunk/engines/agi

buddha_ at users.sourceforge.net buddha_ at users.sourceforge.net
Thu Aug 16 00:00:32 CEST 2007


Revision: 28630
          http://scummvm.svn.sourceforge.net/scummvm/?rev=28630&view=rev
Author:   buddha_
Date:     2007-08-15 15:00:31 -0700 (Wed, 15 Aug 2007)

Log Message:
-----------
Added rudimentary classes for different AGI sound resources (IIgsMidi, IIgsSample, PCjrSound). Made existing code to at least work with PCjrSound.

Modified Paths:
--------------
    scummvm/trunk/engines/agi/agi.cpp
    scummvm/trunk/engines/agi/agi.h
    scummvm/trunk/engines/agi/agi_v2.cpp
    scummvm/trunk/engines/agi/agi_v3.cpp
    scummvm/trunk/engines/agi/sound.cpp
    scummvm/trunk/engines/agi/sound.h

Modified: scummvm/trunk/engines/agi/agi.cpp
===================================================================
--- scummvm/trunk/engines/agi/agi.cpp	2007-08-15 18:37:52 UTC (rev 28629)
+++ scummvm/trunk/engines/agi/agi.cpp	2007-08-15 22:00:31 UTC (rev 28630)
@@ -370,7 +370,7 @@
 		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));

Modified: scummvm/trunk/engines/agi/agi.h
===================================================================
--- scummvm/trunk/engines/agi/agi.h	2007-08-15 18:37:52 UTC (rev 28629)
+++ scummvm/trunk/engines/agi/agi.h	2007-08-15 22:00:31 UTC (rev 28630)
@@ -525,7 +525,7 @@
 	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];

Modified: scummvm/trunk/engines/agi/agi_v2.cpp
===================================================================
--- scummvm/trunk/engines/agi/agi_v2.cpp	2007-08-15 18:37:52 UTC (rev 28629)
+++ scummvm/trunk/engines/agi/agi_v2.cpp	2007-08-15 22:00:31 UTC (rev 28630)
@@ -254,7 +254,8 @@
 		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;

Modified: scummvm/trunk/engines/agi/agi_v3.cpp
===================================================================
--- scummvm/trunk/engines/agi/agi_v3.cpp	2007-08-15 18:37:52 UTC (rev 28629)
+++ scummvm/trunk/engines/agi/agi_v3.cpp	2007-08-15 22:00:31 UTC (rev 28630)
@@ -345,7 +345,8 @@
 
 		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;

Modified: scummvm/trunk/engines/agi/sound.cpp
===================================================================
--- scummvm/trunk/engines/agi/sound.cpp	2007-08-15 18:37:52 UTC (rev 28629)
+++ scummvm/trunk/engines/agi/sound.cpp	2007-08-15 22:00:31 UTC (rev 28630)
@@ -42,6 +42,81 @@
 /* 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 @@
 	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 @@
 
 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::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 @@
 		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 @@
 			}
 			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 @@
 		stopNote(i);
 
 	if (playingSound != -1) {
-		_vm->_game.sounds[playingSound].stop();
+		_vm->_game.sounds[playingSound]->stop();
 		playingSound = -1;
 	}
 }
@@ -611,7 +626,7 @@
 			_vm->setflag(endflag, true);
 
 		if (playingSound != -1)
-			_vm->_game.sounds[playingSound].stop();
+			_vm->_game.sounds[playingSound]->stop();
 		playingSound = -1;
 		endflag = -1;
 	}

Modified: scummvm/trunk/engines/agi/sound.h
===================================================================
--- scummvm/trunk/engines/agi/sound.h	2007-08-15 18:37:52 UTC (rev 28629)
+++ scummvm/trunk/engines/agi/sound.h	2007-08-15 22:00:31 UTC (rev 28630)
@@ -162,7 +162,7 @@
 	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 @@
 #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,22 +199,68 @@
 	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:
-	bool _isPlaying; ///< Is the sound playing?
+	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:
+	IIgsSampleHeader _header; ///< Apple IIGS AGI sample header
+	int16 *_sample;           ///< Sample data (16-bit signed format)
+};
+
 /** Apple IIGS AGI instrument set information. */
 struct instrumentSetInfo {
 	uint byteCount;          ///< Length of the whole instrument set in bytes


This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.




More information about the Scummvm-git-logs mailing list