4 // Copyright (C) 2006 Matthias Braun <matze@braunis.de>
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 /** Used SDL_mixer and glest source as reference */
23 #include "sound_file.hpp"
32 #include <vorbis/codec.h>
33 #include <vorbis/vorbisfile.h>
35 #include "lisp/parser.hpp"
36 #include "lisp/lisp.hpp"
37 #include "file_system.hpp"
39 class SoundError : public std::exception
42 SoundError(const std::string& message) throw();
43 virtual ~SoundError() throw();
45 const char* what() const throw();
50 SoundError::SoundError(const std::string& message) throw()
52 this->message = message;
55 SoundError::~SoundError() throw()
59 SoundError::what() const throw()
61 return message.c_str();
64 class WavSoundFile : public SoundFile
67 WavSoundFile(PHYSFS_file* file);
70 size_t read(void* buffer, size_t buffer_size);
76 PHYSFS_sint64 datastart;
79 static inline uint32_t read32LE(PHYSFS_file* file)
82 if(PHYSFS_readULE32(file, &result) == 0)
83 throw SoundError("file too short");
88 static inline uint16_t read16LE(PHYSFS_file* file)
91 if(PHYSFS_readULE16(file, &result) == 0)
92 throw SoundError("file too short");
97 WavSoundFile::WavSoundFile(PHYSFS_file* file)
102 if(PHYSFS_read(file, magic, sizeof(magic), 1) != 1)
103 throw SoundError("Couldn't read file magic (not a wave file)");
104 if(strncmp(magic, "RIFF", 4) != 0) {
105 log_debug << "MAGIC: " << magic << std::endl;
106 throw SoundError("file is not a RIFF wav file");
109 uint32_t wavelen = read32LE(file);
112 if(PHYSFS_read(file, magic, sizeof(magic), 1) != 1)
113 throw SoundError("Couldn't read chunk header (not a wav file?)");
114 if(strncmp(magic, "WAVE", 4) != 0)
115 throw SoundError("file is not a valid RIFF/WAVE file");
120 // search audio data format chunk
122 if(PHYSFS_read(file, chunkmagic, sizeof(chunkmagic), 1) != 1)
123 throw SoundError("EOF while searching format chunk");
124 chunklen = read32LE(file);
126 if(strncmp(chunkmagic, "fmt ", 4) == 0)
129 if(strncmp(chunkmagic, "fact", 4) == 0
130 || strncmp(chunkmagic, "LIST", 4) == 0) {
132 if(PHYSFS_seek(file, PHYSFS_tell(file) + chunklen) == 0)
133 throw SoundError("EOF while searching fmt chunk");
135 throw SoundError("complex WAVE files not supported");
140 throw SoundError("Format chunk too short");
143 uint16_t encoding = read16LE(file);
145 throw SoundError("only PCM encoding supported");
146 channels = read16LE(file);
147 rate = read32LE(file);
148 uint32_t byterate = read32LE(file);
150 uint16_t blockalign = read16LE(file);
152 bits_per_sample = read16LE(file);
155 if(PHYSFS_seek(file, PHYSFS_tell(file) + (chunklen-16)) == 0)
156 throw SoundError("EOF while reading rest of format chunk");
159 // set file offset to DATA chunk data
161 if(PHYSFS_read(file, chunkmagic, sizeof(chunkmagic), 1) != 1)
162 throw SoundError("EOF while searching data chunk");
163 chunklen = read32LE(file);
165 if(strncmp(chunkmagic, "data", 4) == 0)
169 if(PHYSFS_seek(file, PHYSFS_tell(file) + chunklen) == 0)
170 throw SoundError("EOF while searching fmt chunk");
173 datastart = PHYSFS_tell(file);
174 size = static_cast<size_t> (chunklen);
177 WavSoundFile::~WavSoundFile()
183 WavSoundFile::reset()
185 if(PHYSFS_seek(file, datastart) == 0)
186 throw SoundError("Couldn't seek to data start");
190 WavSoundFile::read(void* buffer, size_t buffer_size)
192 PHYSFS_sint64 end = datastart + size;
193 PHYSFS_sint64 cur = PHYSFS_tell(file);
197 size_t readsize = std::min(static_cast<size_t> (end - cur), buffer_size);
198 if(PHYSFS_read(file, buffer, readsize, 1) != 1)
199 throw SoundError("read error while reading samples");
201 #ifdef WORDS_BIGENDIAN
202 if (bits_per_sample != 16)
204 char *tmp = (char*)buffer;
208 for (i = 0; i < readsize / 2; i++)
211 tmp[2*i] = tmp[2*i+1];
221 //---------------------------------------------------------------------------
223 class OggSoundFile : public SoundFile
226 OggSoundFile(PHYSFS_file* file, double loop_begin, double loop_at);
229 size_t read(void* buffer, size_t buffer_size);
233 static size_t cb_read(void* ptr, size_t size, size_t nmemb, void* source);
234 static int cb_seek(void* source, ogg_int64_t offset, int whence);
235 static int cb_close(void* source);
236 static long cb_tell(void* source);
239 OggVorbis_File vorbis_file;
240 ogg_int64_t loop_begin;
242 size_t normal_buffer_loop;
245 OggSoundFile::OggSoundFile(PHYSFS_file* file, double loop_begin, double loop_at)
249 ov_callbacks callbacks = { cb_read, cb_seek, cb_close, cb_tell };
250 ov_open_callbacks(file, &vorbis_file, 0, 0, callbacks);
252 vorbis_info* vi = ov_info(&vorbis_file, -1);
254 channels = vi->channels;
256 bits_per_sample = 16;
257 size = static_cast<size_t> (ov_pcm_total(&vorbis_file, -1) * 2);
259 double sample_len = 1.0f / rate;
260 double samples_begin = loop_begin / sample_len;
261 double sample_loop = loop_at / sample_len;
263 this->loop_begin = (ogg_int64_t) samples_begin;
265 this->loop_at = (ogg_int64_t) -1;
267 this->loop_at = (ogg_int64_t) sample_loop;
271 OggSoundFile::~OggSoundFile()
273 ov_clear(&vorbis_file);
277 OggSoundFile::read(void* _buffer, size_t buffer_size)
279 char* buffer = reinterpret_cast<char*> (_buffer);
281 size_t totalBytesRead = 0;
283 while(buffer_size>0) {
284 #ifdef WORDS_BIGENDIAN
290 size_t bytes_to_read = buffer_size;
292 size_t bytes_per_sample = 2;
293 ogg_int64_t time = ov_pcm_tell(&vorbis_file);
294 ogg_int64_t samples_left_till_loop = loop_at - time;
295 ogg_int64_t bytes_left_till_loop
296 = samples_left_till_loop * bytes_per_sample;
297 if(bytes_left_till_loop <= 4)
300 if(bytes_left_till_loop < (ogg_int64_t) bytes_to_read) {
301 bytes_to_read = (size_t) bytes_left_till_loop;
306 = ov_read(&vorbis_file, buffer, bytes_to_read, bigendian,
311 buffer_size -= bytesRead;
313 totalBytesRead += bytesRead;
316 return totalBytesRead;
320 OggSoundFile::reset()
322 ov_pcm_seek(&vorbis_file, loop_begin);
326 OggSoundFile::cb_read(void* ptr, size_t size, size_t nmemb, void* source)
328 PHYSFS_file* file = reinterpret_cast<PHYSFS_file*> (source);
331 = PHYSFS_read(file, ptr, static_cast<PHYSFS_uint32> (size),
332 static_cast<PHYSFS_uint32> (nmemb));
336 return static_cast<size_t> (res);
340 OggSoundFile::cb_seek(void* source, ogg_int64_t offset, int whence)
342 PHYSFS_file* file = reinterpret_cast<PHYSFS_file*> (source);
346 if(PHYSFS_seek(file, static_cast<PHYSFS_uint64> (offset)) == 0)
350 if(PHYSFS_seek(file, PHYSFS_tell(file) + offset) == 0)
354 if(PHYSFS_seek(file, PHYSFS_fileLength(file) + offset) == 0)
368 OggSoundFile::cb_close(void* source)
370 PHYSFS_file* file = reinterpret_cast<PHYSFS_file*> (source);
376 OggSoundFile::cb_tell(void* source)
378 PHYSFS_file* file = reinterpret_cast<PHYSFS_file*> (source);
379 return static_cast<long> (PHYSFS_tell(file));
382 //---------------------------------------------------------------------------
384 SoundFile* load_music_file(const std::string& filename)
386 lisp::Parser parser(false);
387 const lisp::Lisp* root = parser.parse(filename);
388 const lisp::Lisp* music = root->get_lisp("supertux-music");
390 throw SoundError("file is not a supertux-music file.");
392 std::string raw_music_file;
393 float loop_begin = 0;
396 music->get("file", raw_music_file);
397 music->get("loop-begin", loop_begin);
398 music->get("loop-at", loop_at);
401 throw SoundError("can't loop from negative value");
404 std::string basedir = FileSystem::dirname(filename);
405 raw_music_file = FileSystem::normalize(basedir + raw_music_file);
407 PHYSFS_file* file = PHYSFS_openRead(raw_music_file.c_str());
409 std::stringstream msg;
410 msg << "Couldn't open '" << raw_music_file << "': " << PHYSFS_getLastError();
411 throw SoundError(msg.str());
414 return new OggSoundFile(file, loop_begin, loop_at);
417 SoundFile* load_sound_file(const std::string& filename)
419 if(filename.length() > 6
420 && filename.compare(filename.length()-6, 6, ".music") == 0) {
421 return load_music_file(filename);
424 PHYSFS_file* file = PHYSFS_openRead(filename.c_str());
426 std::stringstream msg;
427 msg << "Couldn't open '" << filename << "': " << PHYSFS_getLastError() << ", using dummy sound file.";
428 throw SoundError(msg.str());
433 if(PHYSFS_read(file, magic, sizeof(magic), 1) != 1)
434 throw SoundError("Couldn't read magic, file too short");
435 PHYSFS_seek(file, 0);
436 if(strncmp(magic, "RIFF", 4) == 0)
437 return new WavSoundFile(file);
438 else if(strncmp(magic, "OggS", 4) == 0)
439 return new OggSoundFile(file, 0, -1);
441 throw SoundError("Unknown file format");
442 } catch(std::exception& e) {
443 std::stringstream msg;
444 msg << "Couldn't read '" << filename << "': " << e.what();
445 throw SoundError(msg.str());