Imported Upstream version 1.2
[platform/upstream/alure.git] / src / codec_wav.cpp
1 /*
2  * ALURE  OpenAL utility library
3  * Copyright (c) 2009-2010 by Chris Robinson.
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy
6  * of this software and associated documentation files (the "Software"), to
7  * deal in the Software without restriction, including without limitation the
8  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9  * sell copies of the Software, and to permit persons to whom the Software is
10  * furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21  * IN THE SOFTWARE.
22  */
23
24 #include "config.h"
25
26 #include "main.h"
27
28 #include <string.h>
29 #include <assert.h>
30
31 #include <istream>
32
33
34 struct wavStream : public alureStream {
35 private:
36     ALenum format;
37     int samplerate;
38     int blockAlign;
39     int sampleSize;
40     int channels;
41     long dataStart;
42     long dataLen;
43     size_t remLen;
44
45 public:
46     static void Init() { }
47     static void Deinit() { }
48
49     virtual bool IsValid()
50     { return (dataStart > 0 && format != AL_NONE); }
51
52     virtual bool GetFormat(ALenum *fmt, ALuint *frequency, ALuint *blockalign)
53     {
54         *fmt = format;
55         *frequency = samplerate;
56         *blockalign = blockAlign;
57         return true;
58     }
59
60     virtual ALuint GetData(ALubyte *data, ALuint bytes)
61     {
62         std::streamsize rem = ((remLen >= bytes) ? bytes : remLen) / blockAlign;
63         fstream->read(reinterpret_cast<char*>(data), rem*blockAlign);
64
65         std::streamsize got = fstream->gcount();
66         got -= got%blockAlign;
67         remLen -= got;
68
69         if(BigEndian && sampleSize == 16)
70         {
71             for(std::streamsize i = 0;i < got;i+=2)
72                 swap(data[i], data[i+1]);
73         }
74         else if(BigEndian && sampleSize == 32)
75         {
76             for(std::streamsize i = 0;i < got;i+=4)
77             {
78                 swap(data[i+0], data[i+3]);
79                 swap(data[i+1], data[i+2]);
80             }
81         }
82         else if(BigEndian && sampleSize == 64)
83         {
84             for(std::streamsize i = 0;i < got;i+=8)
85             {
86                 swap(data[i+0], data[i+7]);
87                 swap(data[i+1], data[i+6]);
88                 swap(data[i+2], data[i+5]);
89                 swap(data[i+3], data[i+4]);
90             }
91         }
92
93         return got;
94     }
95
96     virtual bool Rewind()
97     {
98         fstream->clear();
99         if(fstream->seekg(dataStart))
100         {
101             remLen = dataLen;
102             return true;
103         }
104
105         SetError("Seek failed");
106         return false;
107     }
108
109     virtual alureInt64 GetLength()
110     {
111         alureInt64 ret = dataLen;
112         return ret / channels * 8 / sampleSize;
113     }
114
115     wavStream(std::istream *_fstream)
116       : alureStream(_fstream), format(0), dataStart(0)
117     {
118         ALubyte buffer[25];
119         ALuint length;
120
121         if(!fstream->read(reinterpret_cast<char*>(buffer), 12) ||
122            memcmp(buffer, "RIFF", 4) != 0 || memcmp(buffer+8, "WAVE", 4) != 0)
123             return;
124
125         while(!dataStart || format == AL_NONE)
126         {
127             char tag[4];
128             if(!fstream->read(tag, 4))
129                 break;
130
131             /* read chunk length */
132             length = read_le32(fstream);
133
134             if(memcmp(tag, "fmt ", 4) == 0 && length >= 16)
135             {
136                 /* Data type (should be 1 for PCM data, 3 for float PCM data,
137                  * 7 for muLaw, and 17 for IMA4 data) */
138                 int type = read_le16(fstream);
139                 if(type != 0x0001 && type != 0x0003 && type != 0x0007 &&
140                    type != 0x0011)
141                     break;
142
143                 /* mono or stereo data */
144                 channels = read_le16(fstream);
145
146                 /* sample frequency */
147                 samplerate = read_le32(fstream);
148
149                 /* skip average bytes per second */
150                 fstream->ignore(4);
151
152                 /* bytes per block */
153                 blockAlign = read_le16(fstream);
154                 if(blockAlign == 0)
155                     break;
156
157                 /* bits per sample */
158                 sampleSize = read_le16(fstream);
159
160                 length -= 16;
161
162                 /* Look for any extra data and try to find the format */
163                 ALuint extrabytes = 0;
164                 if(length >= 2)
165                 {
166                     extrabytes = read_le16(fstream);
167                     length -= 2;
168                 }
169                 extrabytes = std::min<ALuint>(extrabytes, length);
170
171                 if(type == 0x0001)
172                     format = GetSampleFormat(channels, sampleSize, false);
173                 else if(type == 0x0003)
174                     format = GetSampleFormat(channels, sampleSize, true);
175                 else if(type == 0x0007)
176                 {
177                     if(sampleSize == 8)
178                     {
179                         if(channels == 1)
180                             format = AL_FORMAT_MONO_MULAW;
181                         else if(channels == 2)
182                             format = AL_FORMAT_STEREO_MULAW;
183                         else if(channels == 4)
184                             format = AL_FORMAT_QUAD_MULAW;
185                         else if(channels == 6)
186                             format = AL_FORMAT_51CHN_MULAW;
187                         else if(channels == 7)
188                             format = AL_FORMAT_61CHN_MULAW;
189                         else if(channels == 8)
190                             format = AL_FORMAT_71CHN_MULAW;
191                     }
192                 }
193                 else if(type == 0x0011 && extrabytes >= 2)
194                 {
195                     int samples = read_le16(fstream);
196                     length -= 2;
197
198                     /* AL_EXT_IMA4 only supports 36 bytes-per-channel block
199                      * alignment, which has 65 uncompressed sample frames */
200                     if(blockAlign == 36*channels && samples == 65*channels &&
201                        alIsExtensionPresent("AL_EXT_IMA4"))
202                     {
203                          if(channels == 1)
204                              format = AL_FORMAT_MONO_IMA4;
205                          else if(channels == 2)
206                              format = AL_FORMAT_STEREO_IMA4;
207                     }
208                 }
209             }
210             else if(memcmp(tag, "data", 4) == 0)
211             {
212                 dataStart = fstream->tellg();
213                 dataLen = remLen = length;
214             }
215
216             fstream->seekg(length, std::ios_base::cur);
217         }
218
219         if(dataStart > 0 && format != AL_NONE)
220             fstream->seekg(dataStart);
221     }
222
223     virtual ~wavStream()
224     { }
225 };
226 // Priority = 10, prefer this decoder over external ones
227 static DecoderDecl<wavStream,10> wavStream_decoder;
228 Decoder &alure_init_wav(void)
229 { return wavStream_decoder; }