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  * Additional copyright for this file:
8  * Copyright (C) 1995-1997 Presto Studios, Inc.
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23  *
24  */
25 
26 #include "audio/audiostream.h"
27 #include "audio/decoders/aiff.h"
28 #include "audio/decoders/quicktime.h"
29 #include "common/file.h"
30 #include "common/system.h"
31 
32 #include "pegasus/fader.h"
33 #include "pegasus/sound.h"
34 
35 namespace Pegasus {
36 
Sound()37 Sound::Sound() {
38 	_stream = 0;
39 	_volume = 0xFF;
40 	_fader = 0;
41 }
42 
~Sound()43 Sound::~Sound() {
44 	disposeSound();
45 }
46 
disposeSound()47 void Sound::disposeSound() {
48 	stopSound();
49 	delete _stream; _stream = 0;
50 }
51 
initFromAIFFFile(const Common::String & fileName)52 void Sound::initFromAIFFFile(const Common::String &fileName) {
53 	disposeSound();
54 
55 	Common::File *file = new Common::File();
56 	if (!file->open(fileName)) {
57 		warning("Failed to open AIFF file '%s'", fileName.c_str());
58 		delete file;
59 		return;
60 	}
61 
62 	Audio::RewindableAudioStream *stream = Audio::makeAIFFStream(file, DisposeAfterUse::YES);
63 
64 	_stream = dynamic_cast<Audio::SeekableAudioStream *>(stream);
65 
66 	if (!_stream) {
67 		delete stream;
68 		warning("AIFF stream '%s' is not seekable", fileName.c_str());
69 		return;
70 	}
71 }
72 
initFromQuickTime(const Common::String & fileName)73 void Sound::initFromQuickTime(const Common::String &fileName) {
74 	disposeSound();
75 
76 	_stream = Audio::makeQuickTimeStream(fileName);
77 
78 	if (!_stream)
79 		warning("Failed to open QuickTime file '%s'", fileName.c_str());
80 }
81 
attachFader(SoundFader * fader)82 void Sound::attachFader(SoundFader *fader) {
83 	if (_fader)
84 		_fader->attachSound(0);
85 
86 	_fader = fader;
87 
88 	if (_fader)
89 		_fader->attachSound(this);
90 }
91 
playSound()92 void Sound::playSound() {
93 	if (!isSoundLoaded())
94 		return;
95 
96 	stopSound();
97 
98 	// Make sure the sound is back at the beginning before we play it
99 	_stream->rewind();
100 
101 	if (_fader)
102 		setVolume(_fader->getFaderValue());
103 
104 	g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &_handle, _stream, -1, _volume, 0, DisposeAfterUse::NO);
105 }
106 
loopSound()107 void Sound::loopSound() {
108 	if (!isSoundLoaded())
109 		return;
110 
111 	stopSound();
112 
113 	// Create a looping stream
114 	Audio::AudioStream *loopStream = new Audio::LoopingAudioStream(_stream, 0, DisposeAfterUse::NO);
115 
116 	// Assume that if there is a fader, we're going to fade the sound in.
117 	if (_fader)
118 		setVolume(0);
119 
120 	g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &_handle, loopStream, -1, _volume, 0, DisposeAfterUse::YES);
121 }
122 
playSoundSegment(uint32 start,uint32 end)123 void Sound::playSoundSegment(uint32 start, uint32 end) {
124 	if (!isSoundLoaded())
125 		return;
126 
127 	stopSound();
128 
129 	Audio::AudioStream *subStream = new Audio::SubSeekableAudioStream(_stream, Audio::Timestamp(0, start, 600), Audio::Timestamp(0, end, 600), DisposeAfterUse::NO);
130 
131 	g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &_handle, subStream, -1, _volume, 0, DisposeAfterUse::YES);
132 }
133 
stopSound()134 void Sound::stopSound() {
135 	g_system->getMixer()->stopHandle(_handle);
136 }
137 
setVolume(const uint16 volume)138 void Sound::setVolume(const uint16 volume) {
139 	// Clipping the volume to [0x00, 0xFF] instead of Apple's [0, 0x100]
140 	// We store the volume in case SetVolume is called before the sound starts
141 
142 	_volume = (volume == 0x100) ? 0xFF : volume;
143 	g_system->getMixer()->setChannelVolume(_handle, _volume);
144 }
145 
isPlaying()146 bool Sound::isPlaying() {
147 	return isSoundLoaded() && g_system->getMixer()->isSoundHandleActive(_handle);
148 }
149 
isSoundLoaded() const150 bool Sound::isSoundLoaded() const {
151 	return _stream != 0;
152 }
153 
SoundTimeBase()154 SoundTimeBase::SoundTimeBase() {
155 	setScale(600);
156 	_startScale = 600;
157 	_stopScale = 600;
158 	_setToStart = false;
159 }
160 
playSoundSegment(uint32 startTime,uint32 endTime)161 void SoundTimeBase::playSoundSegment(uint32 startTime, uint32 endTime) {
162 	_startTime = startTime;
163 	_stopTime = endTime;
164 	_setToStart = true;
165 	_time = Common::Rational(startTime, getScale());
166 	setRate(1);
167 	Sound::playSoundSegment(startTime, endTime);
168 }
169 
updateTime()170 void SoundTimeBase::updateTime() {
171 	if (_setToStart) {
172 		if (isPlaying()) {
173 			// Not at the end, let's get the time
174 			uint numFrames = g_system->getMixer()->getSoundElapsedTime(_handle) * 600 / 1000;
175 
176 			// WORKAROUND: Our mixer is woefully inaccurate and quite often returns
177 			// times that exceed the actual length of the clip. We'll just fake times
178 			// that are under the final time to ensure any trigger for the end time is
179 			// only sent when the sound has actually stopped.
180 			if (numFrames >= (_stopTime - _startTime))
181 				numFrames = _stopTime - _startTime - 1;
182 
183 			_time = Common::Rational(_startTime + numFrames, getScale());
184 		} else {
185 			// Assume we reached the end
186 			_setToStart = false;
187 			_time = Common::Rational(_stopTime, getScale());
188 		}
189 	}
190 }
191 
192 } // End of namespace Pegasus
193