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 "audio/decoders/raw.h"
24 #include "audio/decoders/wave.h"
25 #include "audio/decoders/wave_types.h"
26 #include "common/memstream.h"
27 #include "titanic/sound/wave_file.h"
28 #include "titanic/sound/sound_manager.h"
29 #include "titanic/support/simple_file.h"
30 
31 namespace Titanic {
32 
33 /**
34  * This creates a ScummVM audio stream around a CAudioBuffer buffer,
35  * allowing for streaming audio output for the music room music
36  */
37 class AudioBufferStream : public Audio::SeekableAudioStream {
38 private:
39 	CAudioBuffer *_audioBuffer;
40 public:
AudioBufferStream(CAudioBuffer * audioBuffer)41 	AudioBufferStream(CAudioBuffer *audioBuffer) : _audioBuffer(audioBuffer) {}
42 
43 	int readBuffer(int16 *buffer, const int numSamples) override;
isStereo() const44 	bool isStereo() const override { return false; }
45 	bool endOfData() const override;
getRate() const46 	int getRate() const override { return 22050; }
getLength() const47 	Audio::Timestamp getLength() const override { return Audio::Timestamp(); }
seek(const Audio::Timestamp & where)48 	bool seek(const Audio::Timestamp &where) override { return false; }
49 };
50 
readBuffer(int16 * buffer,const int numSamples)51 int AudioBufferStream::readBuffer(int16 *buffer, const int numSamples) {
52 	return _audioBuffer->read(buffer, numSamples);
53 }
54 
endOfData() const55 bool AudioBufferStream::endOfData() const {
56 	return _audioBuffer->isFinished();
57 }
58 
59 /*------------------------------------------------------------------------*/
60 
CWaveFile(Audio::Mixer * mixer)61 CWaveFile::CWaveFile(Audio::Mixer *mixer) : _mixer(mixer), _pendingAudioStream(nullptr),
62 		_waveData(nullptr), _waveSize(0), _dataSize(0), _headerSize(0),
63 		_rate(0), _flags(0), _wavType(0), _soundType(Audio::Mixer::kPlainSoundType) {
64 	setup();
65 }
66 
setup()67 void CWaveFile::setup() {
68 	_loadMode = LOADMODE_SCUMMVM;
69 	_dataSize = 0;
70 	_audioBuffer = nullptr;
71 	_disposeAudioBuffer = DisposeAfterUse::NO;
72 	_channel = -1;
73 }
74 
~CWaveFile()75 CWaveFile::~CWaveFile() {
76 	// Delete any pending audio stream if it wasn't used
77 	delete _pendingAudioStream;
78 
79 	if (_disposeAudioBuffer == DisposeAfterUse::YES && _audioBuffer)
80 		delete _audioBuffer;
81 
82 	free(_waveData);
83 }
84 
getDurationTicks() const85 uint CWaveFile::getDurationTicks() const {
86 	if (!_rate)
87 		return 0;
88 
89 	// FIXME: The original uses acmStreamSize to calculate
90 	// a desired size. Since I have no idea how the system API
91 	// method works, for now I'm using a simple ratio of a
92 	// sample output to input value
93 	double newSize = (double)_dataSize * (1475712.0 / 199836.0);
94 	return (uint)(newSize * 1000.0 / _rate);
95 }
96 
loadSound(const CString & name)97 bool CWaveFile::loadSound(const CString &name) {
98 	StdCWadFile file;
99 	if (!file.open(name))
100 		return false;
101 
102 	Common::SeekableReadStream *stream = file.readStream();
103 	uint wavSize = stream->size();
104 	byte *data = (byte *)malloc(wavSize);
105 	stream->read(data, wavSize);
106 
107 	load(data, wavSize);
108 	_soundType = Audio::Mixer::kSFXSoundType;
109 	return true;
110 }
111 
loadSpeech(CDialogueFile * dialogueFile,int speechIndex)112 bool CWaveFile::loadSpeech(CDialogueFile *dialogueFile, int speechIndex) {
113 	DialogueResource *res = dialogueFile->openWaveEntry(speechIndex);
114 	if (!res)
115 		return false;
116 
117 	byte *data = (byte *)malloc(res->_size);
118 	dialogueFile->read(res, data, res->_size);
119 	load(data, res->_size);
120 
121 	_soundType = Audio::Mixer::kSpeechSoundType;
122 	return true;
123 }
124 
loadMusic(const CString & name)125 bool CWaveFile::loadMusic(const CString &name) {
126 	StdCWadFile file;
127 	if (!file.open(name))
128 		return false;
129 
130 	Common::SeekableReadStream *stream = file.readStream();
131 	uint wavSize = stream->size();
132 	byte *data = new byte[wavSize];
133 	stream->read(data, wavSize);
134 	delete stream;
135 
136 	load(data, wavSize);
137 	_soundType = Audio::Mixer::kMusicSoundType;
138 	return true;
139 }
140 
loadMusic(CAudioBuffer * buffer,DisposeAfterUse::Flag disposeAfterUse)141 bool CWaveFile::loadMusic(CAudioBuffer *buffer, DisposeAfterUse::Flag disposeAfterUse) {
142 	_audioBuffer = buffer;
143 	_disposeAudioBuffer = disposeAfterUse;
144 	_loadMode = LOADMODE_AUDIO_BUFFER;
145 
146 	_pendingAudioStream = new AudioBufferStream(_audioBuffer);
147 	return true;
148 }
149 
load(byte * data,uint dataSize)150 void CWaveFile::load(byte *data, uint dataSize) {
151 	_waveData = data;
152 	_waveSize = dataSize;
153 
154 	// Parse the wave header
155 	Common::MemoryReadStream wavStream(data, dataSize, DisposeAfterUse::NO);
156 	if (!Audio::loadWAVFromStream(wavStream, _dataSize, _rate, _flags, &_wavType))
157 		error("Invalid wave file");
158 	_headerSize = wavStream.pos();
159 }
160 
createAudioStream()161 Audio::SeekableAudioStream *CWaveFile::createAudioStream() {
162 	Audio::SeekableAudioStream *stream;
163 
164 	if (_pendingAudioStream) {
165 		stream = _pendingAudioStream;
166 		_pendingAudioStream = nullptr;
167 	} else {
168 		// Create a new ScummVM audio stream for the wave file data
169 		stream = Audio::makeWAVStream(
170 			new Common::MemoryReadStream(_waveData, _waveSize, DisposeAfterUse::NO),
171 			DisposeAfterUse::YES);
172 	}
173 
174 	_rate = stream->getRate();
175 	return stream;
176 }
177 
178 
lock()179 const int16 *CWaveFile::lock() {
180 	switch (_loadMode) {
181 	case LOADMODE_SCUMMVM:
182 		// Sanity checking that only raw 16-bit LE 22Khz waves can be locked
183 		assert(_waveData && _rate == AUDIO_SAMPLING_RATE);
184 		assert(_flags == (Audio::FLAG_LITTLE_ENDIAN | Audio::FLAG_16BITS));
185 		assert(_wavType == Audio::kWaveFormatPCM);
186 
187 		// Return a pointer to the data section of the wave file
188 		return (const int16 *)(_waveData + _headerSize);
189 
190 	default:
191 		return nullptr;
192 	}
193 }
194 
unlock(const int16 * ptr)195 void CWaveFile::unlock(const int16 *ptr) {
196 	// No implementation needed in ScummVM
197 }
198 
play(int numLoops,byte volume)199 Audio::SoundHandle CWaveFile::play(int numLoops, byte volume) {
200 	Audio::SeekableAudioStream *audioStream = createAudioStream();
201 	Audio::SoundHandle handle;
202 
203 	Audio::AudioStream *stream = audioStream;
204 	if (numLoops != 0)
205 		stream = new Audio::LoopingAudioStream(audioStream,
206 			(numLoops == -1) ? 0 : numLoops);
207 
208 	_mixer->playStream(_soundType, &handle, stream, -1,
209 		volume, 0, DisposeAfterUse::YES);
210 	return handle;
211 }
212 
213 } // End of namespace Titanic
214