Prefixing squirrel output with [SQUIRREL]
[supertux.git] / src / audio / wav_sound_file.cpp
1 //  SuperTux
2 //  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
3 //
4 //  This program is free software: you can redistribute it and/or modify
5 //  it under the terms of the GNU General Public License as published by
6 //  the Free Software Foundation, either version 3 of the License, or
7 //  (at your option) any later version.
8 //
9 //  This program is distributed in the hope that it will be useful,
10 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
11 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 //  GNU General Public License for more details.
13 //
14 //  You should have received a copy of the GNU General Public License
15 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
17 #include "audio/wav_sound_file.hpp"
18
19 #include <string.h>
20 #include <stdint.h>
21 #include <assert.h>
22
23 #include "audio/sound_error.hpp"
24 #include "util/log.hpp"
25
26 static inline uint32_t read32LE(PHYSFS_file* file)
27 {
28   uint32_t result;
29   if(PHYSFS_readULE32(file, &result) == 0)
30     throw SoundError("file too short");
31
32   return result;
33 }
34
35 static inline uint16_t read16LE(PHYSFS_file* file)
36 {
37   uint16_t result;
38   if(PHYSFS_readULE16(file, &result) == 0)
39     throw SoundError("file too short");
40
41   return result;
42 }
43
44 WavSoundFile::WavSoundFile(PHYSFS_file* file_) :
45   file(file_),
46   datastart()
47 {
48   assert(file);
49   char magic[4];
50   if(PHYSFS_read(file, magic, sizeof(magic), 1) != 1)
51     throw SoundError("Couldn't read file magic (not a wave file)");
52   if(strncmp(magic, "RIFF", 4) != 0) {
53     log_debug << "MAGIC: " << magic << std::endl;
54     throw SoundError("file is not a RIFF wav file");
55   }
56
57   uint32_t wavelen = read32LE(file);
58   (void) wavelen;
59
60   if(PHYSFS_read(file, magic, sizeof(magic), 1) != 1)
61     throw SoundError("Couldn't read chunk header (not a wav file?)");
62   if(strncmp(magic, "WAVE", 4) != 0)
63     throw SoundError("file is not a valid RIFF/WAVE file");
64
65   char chunkmagic[4];
66   uint32_t chunklen;
67
68   // search audio data format chunk
69   do {
70     if(PHYSFS_read(file, chunkmagic, sizeof(chunkmagic), 1) != 1)
71       throw SoundError("EOF while searching format chunk");
72     chunklen = read32LE(file);
73
74     if(strncmp(chunkmagic, "fmt ", 4) == 0)
75       break;
76
77     if(strncmp(chunkmagic, "fact", 4) == 0
78        || strncmp(chunkmagic, "LIST", 4) == 0) {
79       // skip chunk
80       if(PHYSFS_seek(file, PHYSFS_tell(file) + chunklen) == 0)
81         throw SoundError("EOF while searching fmt chunk");
82     } else {
83       throw SoundError("complex WAVE files not supported");
84     }
85   } while(true);
86
87   if(chunklen < 16)
88     throw SoundError("Format chunk too short");
89
90   // parse format
91   uint16_t encoding = read16LE(file);
92   if(encoding != 1)
93     throw SoundError("only PCM encoding supported");
94   channels = read16LE(file);
95   rate = read32LE(file);
96   uint32_t byterate = read32LE(file);
97   (void) byterate;
98   uint16_t blockalign = read16LE(file);
99   (void) blockalign;
100   bits_per_sample = read16LE(file);
101
102   if(chunklen > 16) {
103     if(PHYSFS_seek(file, PHYSFS_tell(file) + (chunklen-16)) == 0)
104       throw SoundError("EOF while reading rest of format chunk");
105   }
106
107   // set file offset to DATA chunk data
108   do {
109     if(PHYSFS_read(file, chunkmagic, sizeof(chunkmagic), 1) != 1)
110       throw SoundError("EOF while searching data chunk");
111     chunklen = read32LE(file);
112
113     if(strncmp(chunkmagic, "data", 4) == 0)
114       break;
115
116     // skip chunk
117     if(PHYSFS_seek(file, PHYSFS_tell(file) + chunklen) == 0)
118       throw SoundError("EOF while searching fmt chunk");
119   } while(true);
120
121   datastart = PHYSFS_tell(file);
122   size = static_cast<size_t> (chunklen);
123 }
124
125 WavSoundFile::~WavSoundFile()
126 {
127   PHYSFS_close(file);
128 }
129
130 void
131 WavSoundFile::reset()
132 {
133   if(PHYSFS_seek(file, datastart) == 0)
134     throw SoundError("Couldn't seek to data start");
135 }
136
137 size_t
138 WavSoundFile::read(void* buffer, size_t buffer_size)
139 {
140   PHYSFS_sint64 end = datastart + size;
141   PHYSFS_sint64 cur = PHYSFS_tell(file);
142   if(cur >= end)
143     return 0;
144
145   size_t readsize = std::min(static_cast<size_t> (end - cur), buffer_size);
146   if(PHYSFS_read(file, buffer, readsize, 1) != 1)
147     throw SoundError("read error while reading samples");
148
149 #ifdef WORDS_BIGENDIAN
150   if (bits_per_sample != 16)
151     return readsize;
152   char *tmp = (char*)buffer;
153
154   size_t i;
155   char c;
156   for (i = 0; i < readsize / 2; i++)
157   {
158     c          = tmp[2*i];
159     tmp[2*i]   = tmp[2*i+1];
160     tmp[2*i+1] = c;
161   }
162
163   buffer = tmp;
164 #endif
165
166   return readsize;
167 }
168
169 /* EOF */