Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

View File

@@ -0,0 +1,340 @@
/* 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 "twine/audio/music.h"
#include "audio/audiostream.h"
#include "audio/midiparser.h"
#include "audio/midiplayer.h"
#include "backends/audiocd/audiocd.h"
#include "common/debug.h"
#include "common/system.h"
#include "common/textconsole.h"
#include "twine/resources/hqr.h"
#include "twine/resources/resources.h"
#include "twine/twine.h"
namespace TwinE {
/**
* LBA1 default number of tracks
* <pre>
* TRACK 01 MODE1/2352
* INDEX 01 00:00:00
* TRACK 02 AUDIO
* INDEX 01 10:47:52
* TRACK 03 AUDIO
* INDEX 01 14:02:01
* TRACK 04 AUDIO
* INDEX 01 17:02:19
* TRACK 05 AUDIO
* INDEX 01 19:34:45
* TRACK 06 AUDIO
* INDEX 01 22:22:34
* TRACK 07 AUDIO
* INDEX 01 25:09:32
* TRACK 08 AUDIO
* INDEX 01 26:47:72
* TRACK 09 AUDIO
* INDEX 01 30:29:07
* TRACK 10 AUDIO
* INDEX 01 32:04:62
* </pre>
*/
TwinEMidiPlayer::TwinEMidiPlayer(TwinEEngine *engine) : _engine(engine) {
MidiPlayer::createDriver();
int ret = _driver->open();
if (ret == 0) {
if (_nativeMT32) {
_driver->sendMT32Reset();
} else {
_driver->sendGMReset();
}
_driver->setTimerCallback(this, &timerCallback);
}
}
void TwinEMidiPlayer::play(byte *buf, int size, bool loop) {
if (_parser == nullptr) {
if (_engine->_cfgfile.MidiType == MIDIFILE_DOS) {
_parser = MidiParser::createParser_XMIDI();
} else {
_parser = MidiParser::createParser_SMF();
}
}
if (!_parser->loadMusic(buf, size)) {
warning("Failed to load midi music");
return;
}
_parser->setTrack(0);
_parser->setMidiDriver(this);
_parser->setTimerRate(_driver->getBaseTempo());
_parser->property(MidiParser::mpCenterPitchWheelOnUnload, 1);
syncVolume();
debug("play midi with volume: %i", getVolume());
_isLooping = loop;
_isPlaying = true;
}
Music::Music(TwinEEngine *engine) : _engine(engine), _midiPlayer(engine) {
}
int32 Music::getLengthTrackCDR(int track) const {
// TODO:
return -1;
}
bool Music::playMidi(int32 midiIdx) {
const int32 loop = 1;
if (_engine->isDotEmuEnhanced() || _engine->isLba1Classic()) {
// the midi tracks are stored in the lba1-xx files and the adeline logo jingle
// is in lba1-32.xx - while the midiIdx is 31
const int32 trackOffset = 1;
Common::Path trackName(Common::String::format("lba1-%02i", midiIdx + trackOffset));
Audio::SeekableAudioStream *stream = Audio::SeekableAudioStream::openStreamFile(trackName);
if (stream != nullptr) {
const int volume = _engine->_system->getMixer()->getVolumeForSoundType(Audio::Mixer::kMusicSoundType);
_engine->_system->getMixer()->playStream(Audio::Mixer::kMusicSoundType, &_midiHandle,
Audio::makeLoopingAudioStream(stream, loop), volume);
debug("Play midi music track %i", midiIdx);
return true;
}
}
const char *filename;
if (_engine->_cfgfile.MidiType == MIDIFILE_DOS) {
filename = Resources::HQR_MIDI_MI_DOS_FILE;
} else if (_engine->_cfgfile.MidiType == MIDIFILE_WIN) {
filename = Resources::HQR_MIDI_MI_WIN_FILE;
} else {
debug("midi disabled - skip playing %i", midiIdx);
return false;
}
int32 midiSize = HQR::getAllocEntry(&midiPtr, filename, midiIdx);
if (midiSize == 0) {
debug("Could not find midi file for index %i", midiIdx);
return false;
}
debug("Play midi file for index %i", midiIdx);
_midiPlayer.play(midiPtr, midiSize, loop == 0 || loop > 1);
return true;
}
bool Music::playTrackCDR(int32 track) {
if (_engine->isLBA2()) {
static const char *musicTracksLba2[] = {
"",
"TADPCM1",
"TADPCM2",
"TADPCM3",
"TADPCM4",
"TADPCM5",
"JADPCM01",
"", // Track6.wav
"JADPCM02",
"JADPCM03",
"JADPCM04",
"JADPCM05",
"JADPCM06",
"JADPCM07",
"JADPCM08",
"JADPCM09",
"JADPCM10",
"JADPCM11",
"JADPCM12",
"JADPCM13",
"JADPCM14",
"JADPCM15",
"JADPCM16",
"JADPCM17",
"JADPCM18",
"LOGADPCM"};
const char *basename = musicTracksLba2[track];
Audio::SeekableAudioStream *stream = Audio::SeekableAudioStream::openStreamFile(basename);
if (stream != nullptr) {
const int volume = _engine->_system->getMixer()->getVolumeForSoundType(Audio::Mixer::kMusicSoundType);
_engine->_system->getMixer()->playStream(Audio::Mixer::kMusicSoundType, &_midiHandle,
Audio::makeLoopingAudioStream(stream, 1), volume);
debug(3, "Play audio track %s for track id %i", basename, track);
return true;
}
debug(3, "Failed to find a supported format for audio track: %s", basename);
// TODO: are there versions with real audio cd?
// us release starting at track 0
// other releases at track 6
return false;
}
AudioCDManager *cdrom = g_system->getAudioCDManager();
int offset = 1;
// usually the tracks are starting at track_02.xxx for gog and steam releases.
// But some users might have ripped the original cd audio tracks and named them
// from track_01.xxx onwards. So we need to check if the first track exists.
//
// see https://bugs.scummvm.org/ticket/15410 and https://bugs.scummvm.org/ticket/14669
if (cdrom->existExtractedCDAudioFiles(1)) {
offset = 0;
}
return cdrom->play(track + offset, 1, 0, 0);
}
bool Music::initCdrom() {
AudioCDManager *cdrom = g_system->getAudioCDManager();
return cdrom->open();
}
void Music::stopMusic() {
stopMusicCD();
stopMusicMidi();
}
void Music::stopMusicCD() {
AudioCDManager *cdrom = g_system->getAudioCDManager();
cdrom->stop();
}
void Music::fadeMusicMidi(uint32 time) {
// TODO implement fade out
stopMusicMidi();
}
void Music::stopMusicMidi() {
if (_engine->isDotEmuEnhanced() || _engine->isLba1Classic() || _engine->isLBA2()) {
_engine->_system->getMixer()->stopHandle(_midiHandle);
}
_midiPlayer.stop();
free(midiPtr);
midiPtr = nullptr;
numXmi = -1;
}
bool Music::playMusic(int32 track) {
if (track == -1) {
stopMusic();
return true;
}
if (!_engine->_cfgfile.Sound) {
return false;
}
if (_engine->isCDROM()) {
if (_flagVoiceCD || track < 1 || track > 9) {
if (playMidiFile(track)) {
return true;
}
} else {
if (playCdTrack(track)) {
return true;
}
}
} else {
if (playMidiFile(track)) {
return true;
}
}
warning("Failed to play track %i", track);
return false;
}
bool Music::playMidiFile(int32 midiIdx) {
if (!_engine->_cfgfile.Sound) {
debug("sound disabled - skip playing %i", midiIdx);
return false;
}
if (_engine->isCDROM()) {
stopMusicCD();
}
if (midiIdx != numXmi || !isMidiPlaying()) {
stopMusicMidi();
numXmi = midiIdx;
if (!playMidi(midiIdx)) {
return false;
}
// volumeMidi(100);
}
return true;
}
int32 Music::getMusicCD() {
AudioCDManager *cdrom = g_system->getAudioCDManager();
// if (_engine->_system->getMillis() > endMusicCD) {
if (!cdrom->isPlaying()) {
currentMusicCD = -1;
}
return currentMusicCD;
}
bool Music::playCdTrack(int32 track) {
fadeMusicMidi(1);
numXmi = -1;
if (track != getMusicCD()) {
stopMusicCD();
// TODO: endMusicCD = _engine->toSeconds(getLengthTrackCDR(track)) / 75 + _engine->toSeconds(1);
if (playTrackCDR(track)) {
// TODO: endMusicCD += _engine->_system->getMillis();
debug("Play cd music track %i", track);
currentMusicCD = track;
}
}
return true;
}
void Music::playAllMusic(int num) {
if (num != numXmi || !isMidiPlaying()) {
stopMusicMidi();
numXmi = num;
playMidi(num);
// volumeMidi(100);
}
if (num != getMusicCD()) {
stopMusicCD();
// TODO: endMusicCD = _engine->toSeconds(getLengthTrackCDR(num)) / 75 + _engine->toSeconds(1);
if (playTrackCDR(num)) {
// TODO: endMusicCD += _engine->_system->getMillis();
currentMusicCD = num;
}
}
}
bool Music::isMidiPlaying() const {
if (_engine->isDotEmuEnhanced() || _engine->isLba1Classic()) {
return _engine->_system->getMixer()->isSoundHandleActive(_midiHandle);
}
return _midiPlayer.isPlaying();
}
void Music::musicVolume(int32 volume) {
_engine->_system->getMixer()->setVolumeForSoundType(Audio::Mixer::SoundType::kMusicSoundType, volume);
_midiPlayer.setVolume(volume);
}
} // namespace TwinE

110
engines/twine/audio/music.h Normal file
View File

@@ -0,0 +1,110 @@
/* 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/>.
*
*/
#ifndef TWINE_MUSIC_H
#define TWINE_MUSIC_H
#include "audio/midiplayer.h"
#include "audio/mixer.h"
#include "common/scummsys.h"
namespace TwinE {
class TwinEEngine;
class TwinEMidiPlayer : public Audio::MidiPlayer {
private:
TwinEEngine *_engine;
public:
TwinEMidiPlayer(TwinEEngine *engine);
void play(byte *buf, int size, bool loop);
};
class Music {
private:
TwinEEngine *_engine;
TwinEMidiPlayer _midiPlayer;
void fadeMusicMidi(uint32 time = 1);
/** Auxiliar midi pointer to */
uint8 *midiPtr = nullptr;
Audio::SoundHandle _midiHandle;
/** Track number of the current playing music */
int32 numXmi = -1;
int32 currentMusicCD = -1;
// int32 endMusicCD = -1;
const bool _flagVoiceCD = false;
public:
// TODO: implement the handling
int32 _nextMusic = -1; // lba2: NextMusic
int32 _nextMusicTimer; // lba2: NextMusicTimer
bool _stopLastMusic = false; // lba2: StopLastMusic
private:
/** Stop CD music */
void stopMusicCD();
bool playMidi(int32 midiIdx);
int32 getLengthTrackCDR(int track) const;
bool playTrackCDR(int32 track);
public:
Music(TwinEEngine *engine);
/**
* Music volume
* @param current volume number
*/
void musicVolume(int32 volume);
/**
* Play CD music
* @param track track number to play
*/
bool playCdTrack(int32 track);
/**
* Generic play music, according with settings it plays CD or high quality sounds instead
* @param track track number to play
*/
bool playMusic(int32 track);
/**
* Play MIDI music
* @param midiIdx music index under mini_mi_win.hqr
* @note valid indices for lba1 are [1-32]
*/
bool playMidiFile(int32 midiIdx);
void playAllMusic(int track);
/** Stop MIDI music */
void stopMusicMidi();
/** Initialize CD-Rom */
bool initCdrom();
/** Stop MIDI and Track music */
void stopMusic();
bool isMidiPlaying() const;
int32 getMusicCD();
};
} // namespace TwinE
#endif

View File

@@ -0,0 +1,319 @@
/* 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 "twine/audio/sound.h"
#include "audio/audiostream.h"
#include "audio/decoders/raw.h"
#include "audio/decoders/voc.h"
#include "audio/mixer.h"
#include "common/config-manager.h"
#include "common/memstream.h"
#include "common/system.h"
#include "common/text-to-speech.h"
#include "common/types.h"
#include "common/util.h"
#include "twine/parser/text.h"
#include "twine/scene/collision.h"
#include "twine/movies.h"
#include "twine/scene/grid.h"
#include "twine/resources/hqr.h"
#include "twine/scene/movements.h"
#include "twine/resources/resources.h"
#include "twine/shared.h"
#include "twine/text.h"
#include "twine/twine.h"
namespace TwinE {
Sound::Sound(TwinEEngine *engine) : _engine(engine) {
}
Sound::~Sound() {
_engine->_system->getMixer()->stopAll();
}
void Sound::startRainSample() {
if (!_engine->_cfgfile.Sound) {
return;
}
#if 0
const int sample = SAMPLE_RAIN;
if (CubeMode == CUBE_EXTERIEUR && !TEMPETE_FINIE && !isSamplePlaying(sample)) {
const int rate = 0x1000;
const int offset = 300;
const int repeat = 0;
const int panning = 64;
const int volumeRain = 70;
// TODO: mixSample(sample, rate, offset, repeat, panning, volumeRain);
}
RestartRainSample = false;
#endif
}
void Sound::setChannelRate(int32 channelIdx, uint32 rate) {
if (channelIdx < 0 || channelIdx >= NUM_CHANNELS) {
return;
}
_engine->_system->getMixer()->setChannelRate(_samplesPlaying[channelIdx], rate);
}
void Sound::setChannelBalance(int32 channelIdx, uint8 volumeLeft, uint8 volumeRight) {
if (channelIdx < 0 || channelIdx >= NUM_CHANNELS) {
return;
}
Audio::Mixer *mixer = _engine->_system->getMixer();
const Audio::SoundHandle &handle = _samplesPlaying[channelIdx];
// balance value ranges from -127 to 127
int8 balance = (int8)(((int16)volumeRight - (int16)volumeLeft) * 127 / 255);
mixer->setChannelBalance(handle, balance);
}
void Sound::setChannelPosition(int32 channelIdx, int32 x, int32 y, int32 z) {
if (channelIdx < 0 || channelIdx >= NUM_CHANNELS) {
return;
}
const int32 camX = _engine->_grid->_startCube.x * SIZE_BRICK_XZ;
const int32 camY = _engine->_grid->_startCube.y * SIZE_BRICK_Y;
const int32 camZ = _engine->_grid->_startCube.z * SIZE_BRICK_XZ;
int32 distance = getDistance3D(camX, camY, camZ, x, y, z);
distance = boundRuleThree(0, distance, 10000, 255);
byte targetVolume = 0;
if (distance < Audio::Mixer::kMaxChannelVolume) {
targetVolume = Audio::Mixer::kMaxChannelVolume - distance;
}
_engine->_system->getMixer()->setChannelVolume(_samplesPlaying[channelIdx], targetVolume);
}
void Sound::playFlaSample(int32 index, int16 rate, int32 repeat, uint8 volumeLeft, uint8 volumeRight) {
if (!_engine->_cfgfile.Sound) {
return;
}
int channelIdx = getFreeSampleChannelIndex();
if (channelIdx == -1) {
warning("Failed to play fla sample for index: %i - no free channel", index);
return;
}
uint8 *sampPtr = nullptr;
const int32 sampSize = HQR::getAllocEntry(&sampPtr, Resources::HQR_FLASAMP_FILE, index);
if (sampSize == 0) {
warning("Failed to load %s", Resources::HQR_FLASAMP_FILE);
return;
}
// Fix incorrect sample files first byte
if (*sampPtr != 'C') {
_engine->_text->_hasHiddenVox = *sampPtr != '\0';
_engine->_text->_voxHiddenIndex++;
*sampPtr = 'C';
}
Common::MemoryReadStream *stream = new Common::MemoryReadStream(sampPtr, sampSize, DisposeAfterUse::YES);
Audio::SeekableAudioStream *audioStream = Audio::makeVOCStream(stream, Audio::FLAG_UNSIGNED, DisposeAfterUse::YES);
if (playSample(channelIdx, index, audioStream, repeat, Resources::HQR_FLASAMP_FILE)) {
setChannelRate(channelIdx, rate);
setChannelBalance(channelIdx, volumeLeft, volumeRight);
}
}
void Sound::mixSample(int32 index, uint16 pitchbend, int32 repeat, uint8 volumeLeft, uint8 volumeRight) {
mixSample3D(index, pitchbend, repeat, {}, -1);
int channelIdx = getSampleChannel(index);
setChannelBalance(channelIdx, volumeLeft, volumeRight);
}
void Sound::mixSample3D(int32 index, uint16 pitchbend, int32 repeat, const IVec3 &pos, int32 actorIdx) {
if (!_engine->_cfgfile.Sound) {
return;
}
int channelIdx = getFreeSampleChannelIndex();
if (channelIdx == -1) {
warning("Failed to play sample for index: %i - no free channel", index);
return;
}
if (actorIdx != -1) {
// TODO: implement balance
setChannelPosition(channelIdx, pos.x, pos.y, pos.z);
// save the actor index for the channel so we can check the position
_samplesPlayingActors[channelIdx] = actorIdx;
} else {
_samplesPlayingActors[channelIdx] = -1;
}
uint8 *sampPtr = _engine->_resources->_samplesTable[index];
uint32 sampSize = _engine->_resources->_samplesSizeTable[index];
Common::MemoryReadStream *stream = new Common::MemoryReadStream(sampPtr, sampSize, DisposeAfterUse::NO);
Audio::SeekableAudioStream *audioStream = Audio::makeVOCStream(stream, Audio::FLAG_UNSIGNED, DisposeAfterUse::YES);
playSample(channelIdx, index, audioStream, repeat, Resources::HQR_SAMPLES_FILE, Audio::Mixer::kSFXSoundType);
uint32 rate = 11025 + (pitchbend - 0x1000);
setChannelRate(channelIdx, rate);
}
bool Sound::playVoxSample(const TextEntry *text) {
if (!_engine->_cfgfile.Sound || text == nullptr) {
return false;
}
int channelIdx = getFreeSampleChannelIndex();
if (channelIdx == -1) {
warning("Failed to play vox sample for index: %i - no free channel", text->index);
return false;
}
if (_engine->isAndroid()) {
Common::Path basename(Common::String::format("%s%03i", _engine->_text->_currentOggBaseFile.c_str(), text->index));
Audio::SeekableAudioStream *audioStream = Audio::SeekableAudioStream::openStreamFile(basename);
if (audioStream != nullptr) {
return playSample(channelIdx, text->index, audioStream, 1, _engine->_text->_currentOggBaseFile.c_str(), Audio::Mixer::kSpeechSoundType);
}
}
uint8 *sampPtr = nullptr;
int32 sampSize = HQR::getAllocVoxEntry(&sampPtr, _engine->_text->_currentVoxBankFile.c_str(), text->index, _engine->_text->_voxHiddenIndex);
if (sampSize == 0) {
if (ConfMan.hasKey("tts_narrator") && ConfMan.getBool("tts_narrator")) {
Common::TextToSpeechManager *ttsMan = g_system->getTextToSpeechManager();
if (ttsMan != nullptr) {
ttsMan->stop();
return ttsMan->say(text->string);
}
} else {
debug(4, "TTS disabled");
}
warning("Failed to get vox sample for index: %i", text->index);
return false;
}
// Fix incorrect sample files first byte
if (*sampPtr != 'C') {
_engine->_text->_hasHiddenVox = *sampPtr != '\0';
_engine->_text->_voxHiddenIndex++;
*sampPtr = 'C';
}
Common::MemoryReadStream *stream = new Common::MemoryReadStream(sampPtr, sampSize, DisposeAfterUse::YES);
Audio::SeekableAudioStream *audioStream = Audio::makeVOCStream(stream, Audio::FLAG_UNSIGNED, DisposeAfterUse::YES);
return playSample(channelIdx, text->index, audioStream, 1, _engine->_text->_currentVoxBankFile.c_str(), Audio::Mixer::kSpeechSoundType);
}
bool Sound::playSample(int32 channelIdx, int32 index, Audio::SeekableAudioStream *audioStream, int32 loop, const char *name, Audio::Mixer::SoundType soundType) {
if (audioStream == nullptr) {
warning("Failed to create audio stream for %s: %i", name, index);
return false;
}
// infinite loop
if (loop == -1) {
loop = 0;
}
Audio::AudioStream *loopStream = Audio::makeLoopingAudioStream(audioStream, loop);
Audio::SoundHandle *handle = &_samplesPlaying[channelIdx];
const byte volume = Audio::Mixer::kMaxChannelVolume;
_engine->_system->getMixer()->playStream(soundType, handle, loopStream, index, volume);
return true;
}
void Sound::resumeSamples() {
if (!_engine->_cfgfile.Sound) {
return;
}
_engine->_system->getMixer()->pauseAll(false);
}
void Sound::pauseSamples() {
if (!_engine->_cfgfile.Sound) {
return;
}
_engine->_system->getMixer()->pauseAll(true);
}
void Sound::stopSamples() { // HQ_StopSample
if (!_engine->_cfgfile.Sound) {
return;
}
for (int channelIdx = 0; channelIdx < NUM_CHANNELS; channelIdx++) {
_engine->_system->getMixer()->stopHandle(_samplesPlaying[channelIdx]);
}
memset(_samplesPlayingActors, -1, sizeof(_samplesPlayingActors));
}
int32 Sound::getActorChannel(int32 actorIdx) {
for (int32 channelIdx = 0; channelIdx < NUM_CHANNELS; channelIdx++) {
if (_samplesPlayingActors[channelIdx] == actorIdx) {
return channelIdx;
}
}
return -1;
}
int32 Sound::getSampleChannel(int32 index) {
for (int32 channelIdx = 0; channelIdx < NUM_CHANNELS; channelIdx++) {
if (_engine->_system->getMixer()->getSoundID(_samplesPlaying[channelIdx]) == index) {
return channelIdx;
}
}
return -1;
}
void Sound::removeChannelWatch(int32 channelIdx) {
_samplesPlayingActors[channelIdx] = -1;
}
void Sound::stopSample(int32 index) {
if (!_engine->_cfgfile.Sound) {
return;
}
const int32 channelIdx = getSampleChannel(index);
if (channelIdx != -1) {
_engine->_system->getMixer()->stopID(index);
removeChannelWatch(channelIdx);
}
}
bool Sound::isChannelPlaying(int32 channelIdx) {
if (channelIdx >= 0 && channelIdx < ARRAYSIZE(_samplesPlaying)) {
if (_engine->_system->getMixer()->isSoundHandleActive(_samplesPlaying[channelIdx])) {
return true;
}
removeChannelWatch(channelIdx);
}
return false;
}
int32 Sound::isSamplePlaying(int32 index) {
const int32 channelIdx = getSampleChannel(index);
return isChannelPlaying(channelIdx);
}
int32 Sound::getFreeSampleChannelIndex() {
for (int channelIdx = 0; channelIdx < NUM_CHANNELS; channelIdx++) {
if (!_engine->_system->getMixer()->isSoundHandleActive(_samplesPlaying[channelIdx])) {
return channelIdx;
}
}
return -1;
}
} // namespace TwinE

138
engines/twine/audio/sound.h Normal file
View File

@@ -0,0 +1,138 @@
/* 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/>.
*
*/
#ifndef TWINE_SOUND_H
#define TWINE_SOUND_H
#include "audio/audiostream.h"
#include "audio/mixer.h"
#include "common/scummsys.h"
#include "common/types.h"
#include "twine/shared.h"
namespace TwinE {
class TextEntry;
#define NUM_CHANNELS 32
namespace Samples {
enum _Samples {
TwinsenHit = 0,
SoldierHit = 4,
ItemPopup = 11,
Explode = 37,
BigItemFound = 41,
TaskCompleted = 41,
Hit = 86,
ItemFound = 97,
WalkFloorBegin = 126, // BASE_STEP_SOUND
WalkFloorRightBegin = 141
};
}
class TwinEEngine;
class Sound {
private:
TwinEEngine *_engine;
/** Samples playing at the same time */
Audio::SoundHandle _samplesPlaying[NUM_CHANNELS];
/** Samples playing at a actors position */
int32 _samplesPlayingActors[NUM_CHANNELS]{0};
bool playSample(int32 channelIdx, int32 index, Audio::SeekableAudioStream *audioStream, int32 loop, const char *name, Audio::Mixer::SoundType soundType = Audio::Mixer::kPlainSoundType);
bool isChannelPlaying(int32 channelIdx);
/** Find a free channel slot to use */
int32 getFreeSampleChannelIndex();
/** Remove a sample from the channel usage list */
void removeChannelWatch(int32 channelIdx);
public:
int32 _parmSampleVolume = 127;
int32 _parmSampleDecalage = 0;
int32 _parmSampleFrequence = 0;
Sound(TwinEEngine *engine);
~Sound();
/** Get the channel where the sample is playing */
int32 getSampleChannel(int32 index);
/**
* Play FLA movie samples
* @param index sample index under flasamp.hqr file
* @param repeat number of times to repeat the sample
*/
void playFlaSample(int32 index, int16 rate, int32 repeat, uint8 volumeLeft, uint8 volumeRight);
void setChannelBalance(int32 channelIdx, uint8 volumeLeft, uint8 volumeRight);
void setChannelRate(int32 channelIdx, uint32 rate);
/** Update sample position in channel */
void setChannelPosition(int32 channelIdx, int32 x, int32 y, int32 z);
inline void setChannelPosition(int32 channelIdx, const IVec3 &pos) {
setChannelPosition(channelIdx, pos.x, pos.y, pos.z);
}
/**
* Play samples
* @param index sample index under flasamp.hqr file
* @param repeat number of times to repeat the sample
* @param pos sound generating entity position
* @param actorIdx
*/
void mixSample3D(int32 index, uint16 pitchbend, int32 repeat, const IVec3 &pos, int32 actorIdx); // HQ_3D_MixSample
void mixSample(int32 index, uint16 pitchbend, int32 repeat, uint8 volumeLeft, uint8 volumeRight); // HQ_MixSample
/** Pause samples */
void pauseSamples();
/** Resume samples */
void resumeSamples();
void startRainSample();
/** Stop samples */
void stopSamples();
/** Get the channel where the actor sample is playing */
int32 getActorChannel(int32 actorIdx);
/** Stops a specific sample */
void stopSample(int32 index); // HQ_StopOneSample
/** Check if a sample is playing */
int32 isSamplePlaying(int32 index);
/** Play VOX sample */
bool playVoxSample(const TextEntry *text);
};
} // namespace TwinE
#endif