scummvm/engines/glk/sound.cpp
2023-12-24 13:19:25 +01:00

185 lines
5.1 KiB
C++

/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "glk/sound.h"
#include "glk/glk.h"
#include "glk/events.h"
#include "common/file.h"
#include "audio/audiostream.h"
#include "audio/decoders/aiff.h"
#include "audio/decoders/mp3.h"
#include "audio/decoders/raw.h"
#include "audio/decoders/wave.h"
namespace Glk {
Sounds::~Sounds() {
for (int idx = (int)_sounds.size() - 1; idx >= 0; --idx)
delete _sounds[idx];
}
void Sounds::removeSound(schanid_t snd) {
for (uint idx = 0; idx < _sounds.size(); ++idx) {
if (_sounds[idx] == snd) {
_sounds.remove_at(idx);
break;
}
}
}
schanid_t Sounds::create(uint rock, uint volume) {
schanid_t snd = new SoundChannel(this, volume);
_sounds.push_back(snd);
return snd;
}
schanid_t Sounds::iterate(schanid_t chan, uint *rockptr) {
for (int idx = 0; idx < (int)_sounds.size() - 1; ++idx) {
if (_sounds[idx] == chan) {
schanid_t next = _sounds[idx + 1];
if (*rockptr)
*rockptr = next->_rock;
return next;
}
}
return nullptr;
}
void Sounds::poll() {
for (uint idx = 0; idx < _sounds.size(); ++idx)
_sounds[idx]->poll();
}
/*--------------------------------------------------------------------------*/
SoundChannel::SoundChannel(Sounds *owner, uint volume) : _owner(owner),
_soundNum(0), _rock(0), _notify(0) {
_defaultVolume = MIN(volume, (uint)GLK_MAXVOLUME);
if (g_vm->gli_register_obj)
_dispRock = (*g_vm->gli_register_obj)(this, gidisp_Class_Schannel);
}
SoundChannel::~SoundChannel() {
stop();
_owner->removeSound(this);
if (g_vm->gli_unregister_obj)
(*g_vm->gli_unregister_obj)(this, gidisp_Class_Schannel, _dispRock);
}
uint SoundChannel::play(uint soundNum, uint repeats, uint notify) {
stop();
if (repeats == 0)
return 1;
// Find a sound of the given name
Audio::AudioStream *stream;
Common::File f;
Common::Path nameSnd(Common::String::format("sound%u.snd", soundNum));
Common::Path nameWav(Common::String::format("sound%u.wav", soundNum));
Common::Path nameAiff(Common::String::format("sound%u.aiff", soundNum));
#ifdef USE_MAD
Common::Path nameMp3(Common::String::format("sound%u.mp3", soundNum));
#endif
if (f.exists(nameSnd) && f.open(nameSnd)) {
if (f.readUint16BE() != (f.size() - 2))
error("Invalid sound filesize");
byte headerRepeats = f.readByte();
if (headerRepeats > 0)
repeats = headerRepeats;
f.skip(1);
uint freq = f.readUint16BE();
f.skip(2);
uint size = f.readUint16BE();
Common::SeekableReadStream *s = f.readStream(size);
stream = Audio::makeRawStream(s, freq, Audio::FLAG_UNSIGNED);
#ifdef USE_MAD
} else if (f.exists(nameMp3) && f.open(nameMp3)) {
Common::SeekableReadStream *s = f.readStream(f.size());
stream = Audio::makeMP3Stream(s, DisposeAfterUse::YES);
#endif
} else if (f.exists(nameWav) && f.open(nameWav)) {
Common::SeekableReadStream *s = f.readStream(f.size());
stream = Audio::makeWAVStream(s, DisposeAfterUse::YES);
} else if (f.exists(nameAiff) && f.open(nameAiff)) {
Common::SeekableReadStream *s = f.readStream(f.size());
stream = Audio::makeAIFFStream(s, DisposeAfterUse::YES);
} else {
warning("Could not find sound %u", soundNum);
return 1;
}
_soundNum = soundNum;
_notify = notify;
// Set up a repeat if multiple repeats are specified
if (repeats > 1) {
Audio::RewindableAudioStream *rwStream = dynamic_cast<Audio::RewindableAudioStream *>(stream);
assert(rwStream);
stream = new Audio::LoopingAudioStream(rwStream, repeats, DisposeAfterUse::YES);
}
// Start playing the audio
g_vm->_mixer->playStream(Audio::Mixer::kPlainSoundType, &_handle, stream, -1,
_defaultVolume * 255 / GLK_MAXVOLUME);
return 0;
}
void SoundChannel::stop() {
g_vm->_mixer->stopHandle(_handle);
}
void SoundChannel::poll() {
if (!g_vm->_mixer->isSoundHandleActive(_handle) && _notify != 0) {
uint notify = _notify;
_notify = 0;
g_vm->_events->store(evtype_SoundNotify, nullptr, _soundNum, notify);
}
}
void SoundChannel::setVolume(uint volume, uint duration, uint notify) {
uint newVol = volume * 255 / GLK_MAXVOLUME;
g_vm->_mixer->setChannelVolume(_handle, newVol);
if (notify) {
warning("TODO: Gradual volume change");
g_vm->_events->store(evtype_VolumeNotify, nullptr, 0, notify);
}
}
void SoundChannel::pause() {
g_vm->_mixer->pauseHandle(_handle, true);
}
void SoundChannel::unpause() {
g_vm->_mixer->pauseHandle(_handle, false);
}
} // End of namespace Glk