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>
36 class WavSoundFile : public SoundFile
39 WavSoundFile(PHYSFS_file* file);
42 size_t read(void* buffer, size_t buffer_size);
48 PHYSFS_sint64 datastart;
51 static inline uint32_t read32LE(PHYSFS_file* file)
54 if(PHYSFS_readULE32(file, &result) == 0)
55 throw std::runtime_error("file too short");
60 static inline uint16_t read16LE(PHYSFS_file* file)
63 if(PHYSFS_readULE16(file, &result) == 0)
64 throw std::runtime_error("file too short");
69 WavSoundFile::WavSoundFile(PHYSFS_file* file)
74 if(PHYSFS_read(file, magic, sizeof(magic), 1) != 1)
75 throw std::runtime_error("Couldn't read file magic (not a wave file)");
76 if(strncmp(magic, "RIFF", 4) != 0) {
77 log_debug << "MAGIC: " << magic << std::endl;
78 throw std::runtime_error("file is not a RIFF wav file");
81 uint32_t wavelen = read32LE(file);
84 if(PHYSFS_read(file, magic, sizeof(magic), 1) != 1)
85 throw std::runtime_error("Couldn't read chunk header (not a wav file?)");
86 if(strncmp(magic, "WAVE", 4) != 0)
87 throw std::runtime_error("file is not a valid RIFF/WAVE file");
92 // search audio data format chunk
94 if(PHYSFS_read(file, chunkmagic, sizeof(chunkmagic), 1) != 1)
95 throw std::runtime_error("EOF while searching format chunk");
96 chunklen = read32LE(file);
98 if(strncmp(chunkmagic, "fmt ", 4) == 0)
101 if(strncmp(chunkmagic, "fact", 4) == 0
102 || strncmp(chunkmagic, "LIST", 4) == 0) {
104 if(PHYSFS_seek(file, PHYSFS_tell(file) + chunklen) == 0)
105 throw std::runtime_error("EOF while searching fmt chunk");
107 throw std::runtime_error("complex WAVE files not supported");
112 throw std::runtime_error("Format chunk too short");
115 uint16_t encoding = read16LE(file);
117 throw std::runtime_error("only PCM encoding supported");
118 channels = read16LE(file);
119 rate = read32LE(file);
120 uint32_t byterate = read32LE(file);
122 uint16_t blockalign = read16LE(file);
124 bits_per_sample = read16LE(file);
127 if(PHYSFS_seek(file, PHYSFS_tell(file) + (chunklen-16)) == 0)
128 throw std::runtime_error("EOF while reading reast of format chunk");
131 // set file offset to DATA chunk data
133 if(PHYSFS_read(file, chunkmagic, sizeof(chunkmagic), 1) != 1)
134 throw std::runtime_error("EOF while searching data chunk");
135 chunklen = read32LE(file);
137 if(strncmp(chunkmagic, "data", 4) == 0)
141 if(PHYSFS_seek(file, PHYSFS_tell(file) + chunklen) == 0)
142 throw std::runtime_error("EOF while searching fmt chunk");
145 datastart = PHYSFS_tell(file);
146 size = static_cast<size_t> (chunklen);
149 WavSoundFile::~WavSoundFile()
155 WavSoundFile::reset()
157 if(PHYSFS_seek(file, datastart) == 0)
158 throw std::runtime_error("Couldn't seek to data start");
162 WavSoundFile::read(void* buffer, size_t buffer_size)
164 PHYSFS_sint64 end = datastart + size;
165 PHYSFS_sint64 cur = PHYSFS_tell(file);
169 size_t readsize = std::min(static_cast<size_t> (end - cur), buffer_size);
170 if(PHYSFS_read(file, buffer, readsize, 1) != 1)
171 throw std::runtime_error("read error while reading samples");
173 #ifdef WORDS_BIGENDIAN
174 if (bits_per_sample != 16)
176 char *tmp = (char*)buffer;
180 for (i = 0; i < readsize / 2; i++)
183 tmp[2*i] = tmp[2*i+1];
193 //---------------------------------------------------------------------------
195 class OggSoundFile : public SoundFile
198 OggSoundFile(PHYSFS_file* file);
201 size_t read(void* buffer, size_t buffer_size);
205 static size_t cb_read(void* ptr, size_t size, size_t nmemb, void* source);
206 static int cb_seek(void* source, ogg_int64_t offset, int whence);
207 static int cb_close(void* source);
208 static long cb_tell(void* source);
211 OggVorbis_File vorbis_file;
214 OggSoundFile::OggSoundFile(PHYSFS_file* file)
218 ov_callbacks callbacks = { cb_read, cb_seek, cb_close, cb_tell };
219 ov_open_callbacks(file, &vorbis_file, 0, 0, callbacks);
221 vorbis_info* vi = ov_info(&vorbis_file, -1);
222 channels = vi->channels;
224 bits_per_sample = 16;
225 size = static_cast<size_t> (ov_pcm_total(&vorbis_file, -1) * 2);
228 OggSoundFile::~OggSoundFile()
230 ov_clear(&vorbis_file);
234 OggSoundFile::read(void* _buffer, size_t buffer_size)
236 char* buffer = reinterpret_cast<char*> (_buffer);
238 size_t totalBytesRead= 0;
240 while(buffer_size>0){
242 = ov_read(&vorbis_file, buffer, static_cast<int> (buffer_size),
243 #ifdef WORDS_BIGENDIAN
252 buffer_size -= bytesRead;
254 totalBytesRead += bytesRead;
257 return totalBytesRead;
261 OggSoundFile::reset()
263 ov_raw_seek(&vorbis_file, 0);
267 OggSoundFile::cb_read(void* ptr, size_t size, size_t nmemb, void* source)
269 PHYSFS_file* file = reinterpret_cast<PHYSFS_file*> (source);
272 = PHYSFS_read(file, ptr, static_cast<PHYSFS_uint32> (size),
273 static_cast<PHYSFS_uint32> (nmemb));
277 return static_cast<size_t> (res);
281 OggSoundFile::cb_seek(void* source, ogg_int64_t offset, int whence)
283 PHYSFS_file* file = reinterpret_cast<PHYSFS_file*> (source);
287 if(PHYSFS_seek(file, static_cast<PHYSFS_uint64> (offset)) == 0)
291 if(PHYSFS_seek(file, PHYSFS_tell(file) + offset) == 0)
295 if(PHYSFS_seek(file, PHYSFS_fileLength(file) + offset) == 0)
309 OggSoundFile::cb_close(void* source)
311 PHYSFS_file* file = reinterpret_cast<PHYSFS_file*> (source);
317 OggSoundFile::cb_tell(void* source)
319 PHYSFS_file* file = reinterpret_cast<PHYSFS_file*> (source);
320 return static_cast<long> (PHYSFS_tell(file));
323 //---------------------------------------------------------------------------
326 SoundFile* load_sound_file(const std::string& filename)
328 PHYSFS_file* file = PHYSFS_openRead(filename.c_str());
330 std::stringstream msg;
331 msg << "Couldn't open '" << filename << "': " << PHYSFS_getLastError();
332 throw std::runtime_error(msg.str());
337 if(PHYSFS_read(file, magic, sizeof(magic), 1) != 1)
338 throw std::runtime_error("Couldn't read magic, file too short");
339 PHYSFS_seek(file, 0);
340 if(strncmp(magic, "RIFF", 4) == 0)
341 return new WavSoundFile(file);
342 else if(strncmp(magic, "OggS", 4) == 0)
343 return new OggSoundFile(file);
345 throw std::runtime_error("Unknown file format");
346 } catch(std::exception& e) {
347 std::stringstream msg;
348 msg << "Couldn't read '" << filename << "': " << e.what();
349 throw std::runtime_error(msg.str());