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