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