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 "glk/sound.h"
24 #include "glk/glk.h"
25 #include "glk/events.h"
26 #include "common/file.h"
27 #include "audio/audiostream.h"
28 #include "audio/decoders/aiff.h"
29 #include "audio/decoders/mp3.h"
30 #include "audio/decoders/raw.h"
31 #include "audio/decoders/wave.h"
32
33 namespace Glk {
34
~Sounds()35 Sounds::~Sounds() {
36 for (int idx = (int)_sounds.size() - 1; idx >= 0; --idx)
37 delete _sounds[idx];
38 }
39
removeSound(schanid_t snd)40 void Sounds::removeSound(schanid_t snd) {
41 for (uint idx = 0; idx < _sounds.size(); ++idx) {
42 if (_sounds[idx] == snd) {
43 _sounds.remove_at(idx);
44 break;
45 }
46 }
47 }
48
create(uint rock,uint volume)49 schanid_t Sounds::create(uint rock, uint volume) {
50 schanid_t snd = new SoundChannel(this, volume);
51 _sounds.push_back(snd);
52 return snd;
53 }
54
iterate(schanid_t chan,uint * rockptr)55 schanid_t Sounds::iterate(schanid_t chan, uint *rockptr) {
56 for (int idx = 0; idx < (int)_sounds.size() - 1; ++idx) {
57 if (_sounds[idx] == chan) {
58 schanid_t next = _sounds[idx + 1];
59 if (*rockptr)
60 *rockptr = next->_rock;
61
62 return next;
63 }
64 }
65
66 return nullptr;
67 }
68
poll()69 void Sounds::poll() {
70 for (uint idx = 0; idx < _sounds.size(); ++idx)
71 _sounds[idx]->poll();
72 }
73
74 /*--------------------------------------------------------------------------*/
75
SoundChannel(Sounds * owner,uint volume)76 SoundChannel::SoundChannel(Sounds *owner, uint volume) : _owner(owner),
77 _soundNum(0), _rock(0), _notify(0) {
78 _defaultVolume = MIN(volume, (uint)GLK_MAXVOLUME);
79
80 if (g_vm->gli_register_obj)
81 _dispRock = (*g_vm->gli_register_obj)(this, gidisp_Class_Schannel);
82 }
83
~SoundChannel()84 SoundChannel::~SoundChannel() {
85 stop();
86 _owner->removeSound(this);
87
88 if (g_vm->gli_unregister_obj)
89 (*g_vm->gli_unregister_obj)(this, gidisp_Class_Schannel, _dispRock);
90 }
91
play(uint soundNum,uint repeats,uint notify)92 uint SoundChannel::play(uint soundNum, uint repeats, uint notify) {
93 stop();
94 if (repeats == 0)
95 return 1;
96
97 // Find a sound of the given name
98 Audio::AudioStream *stream;
99 Common::File f;
100 Common::String nameSnd = Common::String::format("sound%u.snd", soundNum);
101 Common::String nameWav = Common::String::format("sound%u.wav", soundNum);
102 Common::String nameAiff = Common::String::format("sound%u.aiff", soundNum);
103 #ifdef USE_MAD
104 Common::String nameMp3 = Common::String::format("sound%u.mp3", soundNum);
105 #endif
106
107 if (f.exists(nameSnd) && f.open(nameSnd)) {
108 if (f.readUint16BE() != (f.size() - 2))
109 error("Invalid sound filesize");
110 byte headerRepeats = f.readByte();
111 if (headerRepeats > 0)
112 repeats = headerRepeats;
113 f.skip(1);
114 uint freq = f.readUint16BE();
115 f.skip(2);
116 uint size = f.readUint16BE();
117
118 Common::SeekableReadStream *s = f.readStream(size);
119 stream = Audio::makeRawStream(s, freq, Audio::FLAG_UNSIGNED);
120
121 #ifdef USE_MAD
122 } else if (f.exists(nameMp3) && f.open(nameMp3)) {
123 Common::SeekableReadStream *s = f.readStream(f.size());
124 stream = Audio::makeMP3Stream(s, DisposeAfterUse::YES);
125 #endif
126 } else if (f.exists(nameWav) && f.open(nameWav)) {
127 Common::SeekableReadStream *s = f.readStream(f.size());
128 stream = Audio::makeWAVStream(s, DisposeAfterUse::YES);
129
130 } else if (f.exists(nameAiff) && f.open(nameAiff)) {
131 Common::SeekableReadStream *s = f.readStream(f.size());
132 stream = Audio::makeAIFFStream(s, DisposeAfterUse::YES);
133
134 } else {
135 warning("Could not find sound %u", soundNum);
136 return 1;
137 }
138
139 _soundNum = soundNum;
140 _notify = notify;
141
142 // Set up a repeat if multiple repeats are specified
143 if (repeats > 1) {
144 Audio::RewindableAudioStream *rwStream = dynamic_cast<Audio::RewindableAudioStream *>(stream);
145 assert(rwStream);
146 stream = new Audio::LoopingAudioStream(rwStream, repeats, DisposeAfterUse::YES);
147 }
148
149 // Start playing the audio
150 g_vm->_mixer->playStream(Audio::Mixer::kPlainSoundType, &_handle, stream, -1,
151 _defaultVolume * 255 / GLK_MAXVOLUME);
152 return 0;
153 }
154
stop()155 void SoundChannel::stop() {
156 g_vm->_mixer->stopHandle(_handle);
157 }
158
poll()159 void SoundChannel::poll() {
160 if (!g_vm->_mixer->isSoundHandleActive(_handle) && _notify != 0) {
161 uint notify = _notify;
162 _notify = 0;
163 g_vm->_events->store(evtype_SoundNotify, nullptr, _soundNum, notify);
164 }
165 }
166
setVolume(uint volume,uint duration,uint notify)167 void SoundChannel::setVolume(uint volume, uint duration, uint notify) {
168 uint newVol = volume * 255 / GLK_MAXVOLUME;
169 g_vm->_mixer->setChannelVolume(_handle, newVol);
170
171 if (notify) {
172 warning("TODO: Gradual volume change");
173 g_vm->_events->store(evtype_VolumeNotify, nullptr, 0, notify);
174 }
175 }
176
pause()177 void SoundChannel::pause() {
178 g_vm->_mixer->pauseHandle(_handle, true);
179 }
180
unpause()181 void SoundChannel::unpause() {
182 g_vm->_mixer->pauseHandle(_handle, false);
183 }
184
185 } // End of namespace Glk
186