98a615b148aa3941c9c69f703765d2cac696fde1
[supertux.git] / src / audio / sound_file.cpp
1 //  $Id$
2 //
3 //  SuperTux
4 //  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
5 //
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.
10 //
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.
15 //
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.
19
20 /** Used SDL_mixer and glest source as reference */
21 #include <config.h>
22
23 #include "sound_file.hpp"
24
25 #include <stdio.h>
26 #include <stdint.h>
27 #include <algorithm>
28 #include <stdexcept>
29 #include <sstream>
30 #include <assert.h>
31 #include <physfs.h>
32 #include <vorbis/codec.h>
33 #include <vorbis/vorbisfile.h>
34 #include "log.hpp"
35 #include "lisp/parser.hpp"
36 #include "lisp/lisp.hpp"
37 #include "file_system.hpp"
38
39 class WavSoundFile : public SoundFile
40 {
41 public:
42   WavSoundFile(PHYSFS_file* file);
43   ~WavSoundFile();
44
45   size_t read(void* buffer, size_t buffer_size);
46   void reset();
47
48 private:
49   PHYSFS_file* file;
50
51   PHYSFS_sint64 datastart;
52 };
53
54 static inline uint32_t read32LE(PHYSFS_file* file)
55 {
56   uint32_t result;
57   if(PHYSFS_readULE32(file, &result) == 0)
58     throw std::runtime_error("file too short");
59
60   return result;
61 }
62
63 static inline uint16_t read16LE(PHYSFS_file* file)
64 {
65   uint16_t result;
66   if(PHYSFS_readULE16(file, &result) == 0)
67     throw std::runtime_error("file too short");
68
69   return result;
70 }
71
72 WavSoundFile::WavSoundFile(PHYSFS_file* file)
73 {
74   this->file = file;
75
76   char magic[4];
77   if(PHYSFS_read(file, magic, sizeof(magic), 1) != 1)
78     throw std::runtime_error("Couldn't read file magic (not a wave file)");
79   if(strncmp(magic, "RIFF", 4) != 0) {
80     log_debug << "MAGIC: " << magic << std::endl;
81     throw std::runtime_error("file is not a RIFF wav file");
82   }
83
84   uint32_t wavelen = read32LE(file);
85   (void) wavelen;
86
87   if(PHYSFS_read(file, magic, sizeof(magic), 1) != 1)
88     throw std::runtime_error("Couldn't read chunk header (not a wav file?)");
89   if(strncmp(magic, "WAVE", 4) != 0)
90     throw std::runtime_error("file is not a valid RIFF/WAVE file");
91
92   char chunkmagic[4];
93   uint32_t chunklen;
94
95   // search audio data format chunk
96   do {
97     if(PHYSFS_read(file, chunkmagic, sizeof(chunkmagic), 1) != 1)
98       throw std::runtime_error("EOF while searching format chunk");
99     chunklen = read32LE(file);
100
101     if(strncmp(chunkmagic, "fmt ", 4) == 0)
102       break;
103
104     if(strncmp(chunkmagic, "fact", 4) == 0
105         || strncmp(chunkmagic, "LIST", 4) == 0) {
106       // skip chunk
107       if(PHYSFS_seek(file, PHYSFS_tell(file) + chunklen) == 0)
108         throw std::runtime_error("EOF while searching fmt chunk");
109     } else {
110       throw std::runtime_error("complex WAVE files not supported");
111     }
112   } while(true);
113
114   if(chunklen < 16)
115     throw std::runtime_error("Format chunk too short");
116
117   // parse format
118   uint16_t encoding = read16LE(file);
119   if(encoding != 1)
120     throw std::runtime_error("only PCM encoding supported");
121   channels = read16LE(file);
122   rate = read32LE(file);
123   uint32_t byterate = read32LE(file);
124   (void) byterate;
125   uint16_t blockalign = read16LE(file);
126   (void) blockalign;
127   bits_per_sample = read16LE(file);
128
129   if(chunklen > 16) {
130     if(PHYSFS_seek(file, PHYSFS_tell(file) + (chunklen-16)) == 0)
131       throw std::runtime_error("EOF while reading reast of format chunk");
132   }
133
134   // set file offset to DATA chunk data
135   do {
136     if(PHYSFS_read(file, chunkmagic, sizeof(chunkmagic), 1) != 1)
137       throw std::runtime_error("EOF while searching data chunk");
138     chunklen = read32LE(file);
139
140     if(strncmp(chunkmagic, "data", 4) == 0)
141       break;
142
143     // skip chunk
144     if(PHYSFS_seek(file, PHYSFS_tell(file) + chunklen) == 0)
145       throw std::runtime_error("EOF while searching fmt chunk");
146   } while(true);
147
148   datastart = PHYSFS_tell(file);
149   size = static_cast<size_t> (chunklen);
150 }
151
152 WavSoundFile::~WavSoundFile()
153 {
154   PHYSFS_close(file);
155 }
156
157 void
158 WavSoundFile::reset()
159 {
160   if(PHYSFS_seek(file, datastart) == 0)
161     throw std::runtime_error("Couldn't seek to data start");
162 }
163
164 size_t
165 WavSoundFile::read(void* buffer, size_t buffer_size)
166 {
167   PHYSFS_sint64 end = datastart + size;
168   PHYSFS_sint64 cur = PHYSFS_tell(file);
169   if(cur >= end)
170     return 0;
171
172   size_t readsize = std::min(static_cast<size_t> (end - cur), buffer_size);
173   if(PHYSFS_read(file, buffer, readsize, 1) != 1)
174     throw std::runtime_error("read error while reading samples");
175
176 #ifdef WORDS_BIGENDIAN
177   if (bits_per_sample != 16)
178     return readsize;
179   char *tmp = (char*)buffer;
180
181   size_t i;
182   char c;
183   for (i = 0; i < readsize / 2; i++)
184   {
185     c          = tmp[2*i];
186     tmp[2*i]   = tmp[2*i+1];
187     tmp[2*i+1] = c;
188   }
189
190   buffer = tmp;
191 #endif
192
193   return readsize;
194 }
195
196 //---------------------------------------------------------------------------
197
198 class OggSoundFile : public SoundFile
199 {
200 public:
201   OggSoundFile(PHYSFS_file* file, double loop_begin, double loop_at);
202   ~OggSoundFile();
203
204   size_t read(void* buffer, size_t buffer_size);
205   void reset();
206
207 private:
208   static size_t cb_read(void* ptr, size_t size, size_t nmemb, void* source);
209   static int cb_seek(void* source, ogg_int64_t offset, int whence);
210   static int cb_close(void* source);
211   static long cb_tell(void* source);
212
213   PHYSFS_file*   file;
214   OggVorbis_File vorbis_file;
215   ogg_int64_t    loop_begin;
216   ogg_int64_t    loop_at;
217   size_t         normal_buffer_loop;
218 };
219
220 OggSoundFile::OggSoundFile(PHYSFS_file* file, double loop_begin, double loop_at)
221 {
222   this->file = file;
223
224   ov_callbacks callbacks = { cb_read, cb_seek, cb_close, cb_tell };
225   ov_open_callbacks(file, &vorbis_file, 0, 0, callbacks);
226
227   vorbis_info* vi = ov_info(&vorbis_file, -1);
228
229   channels        = vi->channels;
230   rate            = vi->rate;
231   bits_per_sample = 16;
232   size            = static_cast<size_t> (ov_pcm_total(&vorbis_file, -1) * 2);
233
234   double sample_len    = 1.0f / rate;
235   double samples_begin = loop_begin / sample_len;
236   double sample_loop   = loop_at / sample_len;
237
238   this->loop_begin     = (ogg_int64_t) samples_begin;
239   if(loop_begin < 0) {
240     this->loop_at = (ogg_int64_t) -1;
241   } else {
242     this->loop_at = (ogg_int64_t) sample_loop;
243   }
244 }
245
246 OggSoundFile::~OggSoundFile()
247 {
248   ov_clear(&vorbis_file);
249 }
250
251 size_t
252 OggSoundFile::read(void* _buffer, size_t buffer_size)
253 {
254   char*  buffer         = reinterpret_cast<char*> (_buffer);
255   int    section        = 0;
256   size_t totalBytesRead = 0;
257
258   while(buffer_size>0) {
259 #ifdef WORDS_BIGENDIAN
260     int bigendian = 1;
261 #else
262     int bigendian = 0;
263 #endif
264
265     size_t bytes_to_read    = buffer_size;
266     if(loop_at > 0) {
267       size_t      bytes_per_sample       = 2;
268       ogg_int64_t time                   = ov_pcm_tell(&vorbis_file);
269       ogg_int64_t samples_left_till_loop = loop_at - time;
270       ogg_int64_t bytes_left_till_loop
271         = samples_left_till_loop * bytes_per_sample;
272       if(bytes_left_till_loop <= 4)
273         break;
274
275       if(bytes_left_till_loop < (ogg_int64_t) bytes_to_read) {
276         bytes_to_read    = (size_t) bytes_left_till_loop;
277       }
278     }
279
280     long bytesRead
281       = ov_read(&vorbis_file, buffer, bytes_to_read, bigendian,
282           2, 1, &section);
283     if(bytesRead == 0) {
284       break;
285     }
286     buffer_size    -= bytesRead;
287     buffer         += bytesRead;
288     totalBytesRead += bytesRead;
289   }
290
291   return totalBytesRead;
292 }
293
294 void
295 OggSoundFile::reset()
296 {
297   ov_pcm_seek(&vorbis_file, loop_begin);
298 }
299
300 size_t
301 OggSoundFile::cb_read(void* ptr, size_t size, size_t nmemb, void* source)
302 {
303   PHYSFS_file* file = reinterpret_cast<PHYSFS_file*> (source);
304
305   PHYSFS_sint64 res
306     = PHYSFS_read(file, ptr, static_cast<PHYSFS_uint32> (size),
307         static_cast<PHYSFS_uint32> (nmemb));
308   if(res <= 0)
309     return 0;
310
311   return static_cast<size_t> (res);
312 }
313
314 int
315 OggSoundFile::cb_seek(void* source, ogg_int64_t offset, int whence)
316 {
317   PHYSFS_file* file = reinterpret_cast<PHYSFS_file*> (source);
318
319   switch(whence) {
320     case SEEK_SET:
321       if(PHYSFS_seek(file, static_cast<PHYSFS_uint64> (offset)) == 0)
322         return -1;
323       break;
324     case SEEK_CUR:
325       if(PHYSFS_seek(file, PHYSFS_tell(file) + offset) == 0)
326         return -1;
327       break;
328     case SEEK_END:
329       if(PHYSFS_seek(file, PHYSFS_fileLength(file) + offset) == 0)
330         return -1;
331       break;
332     default:
333 #ifdef DEBUG
334       assert(false);
335 #else
336       return -1;
337 #endif
338   }
339   return 0;
340 }
341
342 int
343 OggSoundFile::cb_close(void* source)
344 {
345   PHYSFS_file* file = reinterpret_cast<PHYSFS_file*> (source);
346   PHYSFS_close(file);
347   return 0;
348 }
349
350 long
351 OggSoundFile::cb_tell(void* source)
352 {
353   PHYSFS_file* file = reinterpret_cast<PHYSFS_file*> (source);
354   return static_cast<long> (PHYSFS_tell(file));
355 }
356
357 //---------------------------------------------------------------------------
358
359 SoundFile* load_music_file(const std::string& filename)
360 {
361   lisp::Parser parser(false);
362   const lisp::Lisp* root = parser.parse(filename);
363   const lisp::Lisp* music = root->get_lisp("supertux-music");
364   if(music == NULL)
365     throw std::runtime_error("file is not a supertux-music file.");
366
367   std::string raw_music_file;
368   float loop_begin = 0;
369   float loop_at    = -1;
370
371   music->get("file", raw_music_file);
372   music->get("loop-begin", loop_begin);
373   music->get("loop-at", loop_at);
374   
375   if(loop_begin < 0) {
376     throw std::runtime_error("can't loop from negative value");
377   }
378
379   std::string basedir = FileSystem::dirname(filename);
380   raw_music_file = FileSystem::normalize(basedir + raw_music_file);
381
382   PHYSFS_file* file = PHYSFS_openRead(raw_music_file.c_str());
383   if(!file) {
384     std::stringstream msg;
385     msg << "Couldn't open '" << raw_music_file << "': " << PHYSFS_getLastError();
386     throw std::runtime_error(msg.str());
387   }
388
389   return new OggSoundFile(file, loop_begin, loop_at);
390 }
391
392 SoundFile* load_sound_file(const std::string& filename)
393 {
394   if(filename.length() > 6
395       && filename.compare(filename.length()-6, 6, ".music") == 0) {
396     return load_music_file(filename);
397   }
398
399   PHYSFS_file* file = PHYSFS_openRead(filename.c_str());
400   if(!file) {
401     log_warning << "Couldn't open '" << filename << "': " << PHYSFS_getLastError() << ", using dummy sound file." << std::endl;
402     file = PHYSFS_openRead("sounds/empty.wav");
403         if (!file) {
404     std::stringstream msg;
405         msg << "Couldn't open dummy sound file '" << filename << "': " << PHYSFS_getLastError();
406                 throw std::runtime_error(msg.str());
407         }
408   }
409
410   try {
411     char magic[4];
412     if(PHYSFS_read(file, magic, sizeof(magic), 1) != 1)
413       throw std::runtime_error("Couldn't read magic, file too short");
414     PHYSFS_seek(file, 0);
415     if(strncmp(magic, "RIFF", 4) == 0)
416       return new WavSoundFile(file);
417     else if(strncmp(magic, "OggS", 4) == 0)
418       return new OggSoundFile(file, 0, -1);
419     else
420       throw std::runtime_error("Unknown file format");
421   } catch(std::exception& e) {
422     std::stringstream msg;
423     msg << "Couldn't read '" << filename << "': " << e.what();
424     throw std::runtime_error(msg.str());
425   }
426 }