scummvm/engines/agi/sound.cpp

218 lines
5.8 KiB
C++
Raw Normal View History

/* ScummVM - Graphic Adventure Engine
2006-05-23 23:43:52 +00:00
*
* 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.
2006-05-23 23:43:52 +00:00
*
* 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.
*
2006-05-23 23:43:52 +00:00
* 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.
*
2006-05-23 23:43:52 +00:00
* 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.
*
*/
#include "agi/agi.h"
#include "agi/sound_2gs.h"
#include "agi/sound_coco3.h"
#include "agi/sound_midi.h"
#include "agi/sound_sarien.h"
#include "agi/sound_pcjr.h"
#include "common/textconsole.h"
2006-05-23 23:43:52 +00:00
namespace Agi {
//
// TODO: add support for variable sampling rate in the output device
//
2006-05-23 23:43:52 +00:00
AgiSound *AgiSound::createFromRawResource(uint8 *data, uint32 len, int resnum, int soundemu) {
2009-01-03 14:05:57 +00:00
if (data == NULL || len < 2) // Check for too small resource or no resource at all
return NULL;
uint16 type = READ_LE_UINT16(data);
// For V1 sound resources
if (type != AGI_SOUND_SAMPLE && (type & 0xFF) == 0x01)
return new PCjrSound(data, len, resnum);
switch (type) { // Create a sound object based on the type
case AGI_SOUND_SAMPLE:
return new IIgsSample(data, len, resnum);
case AGI_SOUND_MIDI:
return new IIgsMidi(data, len, resnum);
case AGI_SOUND_4CHN:
if (soundemu == SOUND_EMU_MIDI) {
return new MIDISound(data, len, resnum);
} else {
return new PCjrSound(data, len, resnum);
}
}
warning("Sound resource (%d) has unknown type (0x%04x). Not using the sound", resnum, type);
return NULL;
}
PCjrSound::PCjrSound(uint8 *data, uint32 len, int resnum) : AgiSound() {
_data = data; // Save the resource pointer
_len = len; // Save the resource's length
_type = READ_LE_UINT16(data); // Read sound resource's type
// Detect V1 sound resources
if ((_type & 0xFF) == 0x01)
_type = AGI_SOUND_4CHN;
_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) {
2007-08-16 18:42:28 +00:00
assert(voiceNum < 4);
uint16 voiceStartOffset = READ_LE_UINT16(_data + voiceNum * 2);
return _data + voiceStartOffset;
}
#if 0
static const uint16 period[] = {
2006-05-23 23:43:52 +00:00
1024, 1085, 1149, 1218, 1290, 1367,
1448, 1534, 1625, 1722, 1825, 1933
};
static int noteToPeriod(int note) {
2006-05-23 23:43:52 +00:00
return 10 * (period[note % 12] >> (note / 12 - 3));
}
#endif
2006-05-23 23:43:52 +00:00
void SoundMgr::unloadSound(int resnum) {
if (_vm->_game.dirSound[resnum].flags & RES_LOADED) {
if (_vm->_game.sounds[resnum]->isPlaying()) {
_vm->_game.sounds[resnum]->stop();
}
2006-05-23 23:43:52 +00:00
// Release the sound resource's data
delete _vm->_game.sounds[resnum];
_vm->_game.sounds[resnum] = NULL;
_vm->_game.dirSound[resnum].flags &= ~RES_LOADED;
2006-05-23 23:43:52 +00:00
}
}
/**
* Start playing a sound resource. The logic here is that when the sound is
* finished we set the given flag to be true. This way the condition can be
* detected by the game. On the other hand, if the game wishes to start
* playing a new sound before the current one is finished, we also let it
* do that.
* @param resnum the sound resource number
* @param flag the flag that is wished to be set true when finished
*/
void SoundMgr::startSound(int resnum, int flag) {
debugC(3, kDebugLevelSound, "startSound(resnum = %d, flag = %d)", resnum, flag);
2006-05-23 23:43:52 +00:00
if (_vm->_game.sounds[resnum] == NULL) // Is this needed at all?
2006-05-23 23:43:52 +00:00
return;
stopSound();
2006-05-23 23:43:52 +00:00
AgiSoundEmuType type = (AgiSoundEmuType)_vm->_game.sounds[resnum]->type();
2006-05-23 23:43:52 +00:00
if (type != AGI_SOUND_SAMPLE && type != AGI_SOUND_MIDI && type != AGI_SOUND_4CHN)
return;
debugC(3, kDebugLevelSound, " type = %d", type);
2006-05-23 23:43:52 +00:00
_vm->_game.sounds[resnum]->play();
_playingSound = resnum;
_soundGen->play(resnum);
2006-05-23 23:43:52 +00:00
// Reset the flag
_endflag = flag;
2011-08-14 20:37:32 +00:00
if (_vm->getVersion() < 0x2000) {
_vm->_game.vars[_endflag] = 0;
} else {
_vm->setflag(_endflag, false);
}
2006-05-23 23:43:52 +00:00
}
void SoundMgr::stopSound() {
debugC(3, kDebugLevelSound, "stopSound() --> %d", _playingSound);
if (_playingSound != -1) {
if (_vm->_game.sounds[_playingSound]) // sanity checking
_vm->_game.sounds[_playingSound]->stop();
_soundGen->stop();
_playingSound = -1;
2006-05-23 23:43:52 +00:00
}
// This is probably not needed most of the time, but there also should
// not be any harm doing it, so do it anyway.
2011-08-14 20:37:32 +00:00
if (_endflag != -1) {
if (_vm->getVersion() < 0x2000) {
_vm->_game.vars[_endflag] = 1;
} else {
_vm->setflag(_endflag, true);
}
}
_endflag = -1;
2006-05-23 23:43:52 +00:00
}
void SoundMgr::soundIsFinished() {
if (_endflag != -1)
_vm->setflag(_endflag, true);
if (_playingSound != -1)
_vm->_game.sounds[_playingSound]->stop();
_playingSound = -1;
_endflag = -1;
}
SoundMgr::SoundMgr(AgiBase *agi, Audio::Mixer *pMixer) {
_vm = agi;
_endflag = -1;
_playingSound = -1;
2006-05-23 23:43:52 +00:00
switch (_vm->_soundemu) {
case SOUND_EMU_NONE:
2006-05-23 23:43:52 +00:00
case SOUND_EMU_AMIGA:
case SOUND_EMU_MAC:
case SOUND_EMU_PC:
_soundGen = new SoundGenSarien(_vm, pMixer);
break;
case SOUND_EMU_PCJR:
_soundGen = new SoundGenPCJr(_vm, pMixer);
2006-05-23 23:43:52 +00:00
break;
case SOUND_EMU_APPLE2GS:
_soundGen = new SoundGen2GS(_vm, pMixer);
break;
case SOUND_EMU_COCO3:
_soundGen = new SoundGenCoCo3(_vm, pMixer);
break;
case SOUND_EMU_MIDI:
_soundGen = new SoundGenMIDI(_vm, pMixer);
break;
2006-05-23 23:43:52 +00:00
}
}
void SoundMgr::setVolume(uint8 volume) {
2006-05-23 23:43:52 +00:00
// TODO
}
SoundMgr::~SoundMgr() {
stopSound();
delete _soundGen;
2006-05-23 23:43:52 +00:00
}
} // End of namespace Agi