1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "ultima/ultima4/sound/sound.h"
24 #include "ultima/ultima4/core/config.h"
25 #include "ultima/ultima4/sound/music.h"
26 #include "ultima/ultima4/core/settings.h"
27 #include "ultima/ultima4/core/utils.h"
28 #include "audio/audiostream.h"
29 #include "audio/decoders/flac.h"
30 #include "audio/decoders/mp3.h"
31 #include "audio/decoders/voc.h"
32 #include "audio/decoders/vorbis.h"
33 #include "audio/decoders/wave.h"
34 #include "common/file.h"
35
36 namespace Ultima {
37 namespace Ultima4 {
38
39 SoundManager *g_sound;
40
soundPlay(Sound sound,bool onlyOnce,int specificDurationInTicks)41 void soundPlay(Sound sound, bool onlyOnce, int specificDurationInTicks) {
42 g_sound->play(sound, onlyOnce, specificDurationInTicks);
43 }
44
soundStop(int channel)45 void soundStop(int channel) {
46 g_sound->stop(channel);
47 }
48
49 /*-------------------------------------------------------------------*/
50
SoundManager(Audio::Mixer * mixer)51 SoundManager::SoundManager(Audio::Mixer *mixer) : _mixer(mixer) {
52 g_sound = this;
53
54 // Load sound track filenames from xml config file
55 const Config *config = Config::getInstance();
56 _soundFilenames.reserve(SOUND_MAX);
57 _sounds.resize(SOUND_MAX);
58
59 Std::vector<ConfigElement> soundConfs = config->getElement("sound").getChildren();
60 Std::vector<ConfigElement>::const_iterator i = soundConfs.begin();
61 Std::vector<ConfigElement>::const_iterator theEnd = soundConfs.end();
62 for (; i != theEnd; ++i) {
63 if (i->getName() != "track")
64 continue;
65
66 _soundFilenames.push_back(i->getString("file"));
67 }
68 }
69
~SoundManager()70 SoundManager::~SoundManager() {
71 g_sound = nullptr;
72 _mixer->stopHandle(_soundHandle);
73
74 for (uint idx = 0; idx < _sounds.size(); ++idx)
75 delete _sounds[idx];
76 }
77
load(Sound sound)78 bool SoundManager::load(Sound sound) {
79 assertMsg(sound < SOUND_MAX, "Attempted to load an invalid sound");
80
81 if (_sounds[sound] == nullptr) {
82 Common::String pathname("data/sound/" + _soundFilenames[sound]);
83 Common::String basename = pathname.substr(pathname.findLastOf("/") + 1);
84 if (!basename.empty())
85 return load_sys(sound, pathname);
86 }
87
88 return true;
89 }
90
play(Sound sound,bool onlyOnce,int specificDurationInTicks)91 void SoundManager::play(Sound sound, bool onlyOnce, int specificDurationInTicks) {
92 assertMsg(sound < SOUND_MAX, "Attempted to play an invalid sound");
93
94 if (_sounds[sound] == nullptr) {
95 if (!load(sound)) {
96 return;
97 }
98 }
99
100 play_sys(sound, onlyOnce, specificDurationInTicks);
101 }
102
stop(int channel)103 void SoundManager::stop(int channel) {
104 stop_sys(channel);
105 }
106
load_sys(Sound sound,const Common::String & filename)107 bool SoundManager::load_sys(Sound sound, const Common::String &filename) {
108 Common::File f;
109 if (!f.open(filename))
110 return false;
111
112 Audio::SeekableAudioStream *audioStream = nullptr;
113
114 #ifdef USE_FLAC
115 if (filename.hasSuffixIgnoreCase(".fla"))
116 audioStream = Audio::makeFLACStream(f.readStream(f.size()), DisposeAfterUse::YES);
117 #endif
118 #ifdef USE_VORBIS
119 if (filename.hasSuffixIgnoreCase(".ogg"))
120 audioStream = Audio::makeVorbisStream(f.readStream(f.size()), DisposeAfterUse::YES);
121 #endif
122 #ifdef USE_MAD
123 if (filename.hasSuffixIgnoreCase(".mp3"))
124 audioStream = Audio::makeMP3Stream(f.readStream(f.size()), DisposeAfterUse::YES);
125 #endif
126 if (filename.hasSuffixIgnoreCase(".wav"))
127 audioStream = Audio::makeWAVStream(f.readStream(f.size()), DisposeAfterUse::YES);
128 if (filename.hasSuffixIgnoreCase(".voc"))
129 audioStream = Audio::makeVOCStream(f.readStream(f.size()), DisposeAfterUse::YES);
130
131 _sounds[sound] = audioStream;
132 return audioStream != nullptr;
133 }
134
play_sys(Sound sound,bool onlyOnce,int specificDurationMilli)135 void SoundManager::play_sys(Sound sound, bool onlyOnce, int specificDurationMilli) {
136 // Don't allow once only sounds if another sound is already playing
137 if (onlyOnce && _mixer->isSoundHandleActive(_soundHandle))
138 return;
139
140 // Ensure sound is stopped, and rewinded
141 _mixer->stopHandle(_soundHandle);
142 _sounds[sound]->rewind();
143
144 if (specificDurationMilli == -1) {
145 // Play a single sound effect
146 _mixer->playStream(Audio::Mixer::kSFXSoundType,
147 &_soundHandle, _sounds[sound], -1, Audio::Mixer::kMaxChannelVolume,
148 0, DisposeAfterUse::NO);
149 } else {
150 // Play a sound effect, looping if necessary, for a given duration
151 // TODO: Better handle cases where a number of loops won't fit
152 // exactly to give a desired duration
153 int duration = _sounds[sound]->getLength().msecs();
154 int loops = (specificDurationMilli + duration - 1) / duration;
155 assert(loops >= 0);
156
157 Audio::AudioStream *audioStream = new Audio::LoopingAudioStream(
158 _sounds[sound], loops, DisposeAfterUse::NO);
159
160 _mixer->playStream(Audio::Mixer::kSFXSoundType,
161 &_soundHandle, audioStream, -1, Audio::Mixer::kMaxChannelVolume,
162 0, DisposeAfterUse::NO);
163 }
164 }
165
stop_sys(int channel)166 void SoundManager::stop_sys(int channel) {
167 // Channel 1 is the dummy channel number used for sound effects
168 if (channel == 1)
169 _mixer->stopHandle(_soundHandle);
170 }
171
172 } // End of namespace Ultima4
173 } // End of namespace Ultima
174