slouken@111
|
1 |
/*
|
slouken@111
|
2 |
SDL_mixer: An audio mixer library based on the SDL library
|
slouken@386
|
3 |
Copyright (C) 1997-2009 Sam Lantinga
|
slouken@111
|
4 |
|
slouken@111
|
5 |
This library is free software; you can redistribute it and/or
|
slouken@111
|
6 |
modify it under the terms of the GNU Library General Public
|
slouken@111
|
7 |
License as published by the Free Software Foundation; either
|
slouken@111
|
8 |
version 2 of the License, or (at your option) any later version.
|
slouken@111
|
9 |
|
slouken@111
|
10 |
This library is distributed in the hope that it will be useful,
|
slouken@111
|
11 |
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
slouken@111
|
12 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
slouken@111
|
13 |
Library General Public License for more details.
|
slouken@111
|
14 |
|
slouken@111
|
15 |
You should have received a copy of the GNU Library General Public
|
slouken@111
|
16 |
License along with this library; if not, write to the Free
|
slouken@111
|
17 |
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
slouken@111
|
18 |
|
slouken@111
|
19 |
This is the source needed to decode an AIFF file into a waveform.
|
slouken@111
|
20 |
It's pretty straightforward once you get going. The only
|
slouken@111
|
21 |
externally-callable function is Mix_LoadAIFF_RW(), which is meant to
|
slouken@111
|
22 |
act as identically to SDL_LoadWAV_RW() as possible.
|
slouken@111
|
23 |
|
slouken@111
|
24 |
This file by Torbjörn Andersson (torbjorn.andersson@eurotime.se)
|
slouken@216
|
25 |
8SVX file support added by Marc Le Douarain (mavati@club-internet.fr)
|
slouken@216
|
26 |
in december 2002.
|
slouken@111
|
27 |
*/
|
slouken@111
|
28 |
|
slouken@140
|
29 |
/* $Id$ */
|
slouken@138
|
30 |
|
slouken@142
|
31 |
#include <stdlib.h>
|
slouken@114
|
32 |
#include <string.h>
|
slouken@111
|
33 |
|
slouken@142
|
34 |
#include "SDL_endian.h"
|
slouken@111
|
35 |
#include "SDL_mixer.h"
|
slouken@111
|
36 |
#include "load_aiff.h"
|
slouken@111
|
37 |
|
slouken@111
|
38 |
/*********************************************/
|
slouken@111
|
39 |
/* Define values for AIFF (IFF audio) format */
|
slouken@111
|
40 |
/*********************************************/
|
slouken@111
|
41 |
#define FORM 0x4d524f46 /* "FORM" */
|
slouken@216
|
42 |
|
slouken@111
|
43 |
#define AIFF 0x46464941 /* "AIFF" */
|
slouken@111
|
44 |
#define SSND 0x444e5353 /* "SSND" */
|
slouken@111
|
45 |
#define COMM 0x4d4d4f43 /* "COMM" */
|
slouken@111
|
46 |
|
slouken@216
|
47 |
#define _8SVX 0x58565338 /* "8SVX" */
|
slouken@216
|
48 |
#define VHDR 0x52444856 /* "VHDR" */
|
slouken@216
|
49 |
#define BODY 0x59444F42 /* "BODY" */
|
slouken@216
|
50 |
|
slouken@111
|
51 |
/* This function was taken from libsndfile. I don't pretend to fully
|
slouken@111
|
52 |
* understand it.
|
slouken@111
|
53 |
*/
|
slouken@111
|
54 |
|
slouken@111
|
55 |
static Uint32 SANE_to_Uint32 (Uint8 *sanebuf)
|
slouken@111
|
56 |
{
|
slouken@111
|
57 |
/* Is the frequency outside of what we can represent with Uint32? */
|
slouken@111
|
58 |
if ( (sanebuf[0] & 0x80) || (sanebuf[0] <= 0x3F) || (sanebuf[0] > 0x40)
|
slouken@111
|
59 |
|| (sanebuf[0] == 0x40 && sanebuf[1] > 0x1C) )
|
slouken@111
|
60 |
return 0;
|
slouken@111
|
61 |
|
slouken@111
|
62 |
return ((sanebuf[2] << 23) | (sanebuf[3] << 15) | (sanebuf[4] << 7)
|
slouken@111
|
63 |
| (sanebuf[5] >> 1)) >> (29 - sanebuf[1]);
|
slouken@111
|
64 |
}
|
slouken@111
|
65 |
|
slouken@111
|
66 |
/* This function is based on SDL_LoadWAV_RW(). */
|
slouken@111
|
67 |
|
slouken@111
|
68 |
SDL_AudioSpec *Mix_LoadAIFF_RW (SDL_RWops *src, int freesrc,
|
slouken@111
|
69 |
SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len)
|
slouken@111
|
70 |
{
|
slouken@111
|
71 |
int was_error;
|
slouken@111
|
72 |
int found_SSND;
|
slouken@111
|
73 |
int found_COMM;
|
slouken@216
|
74 |
int found_VHDR;
|
slouken@216
|
75 |
int found_BODY;
|
slouken@142
|
76 |
long start = 0;
|
slouken@111
|
77 |
|
slouken@111
|
78 |
Uint32 chunk_type;
|
slouken@111
|
79 |
Uint32 chunk_length;
|
slouken@111
|
80 |
long next_chunk;
|
slouken@111
|
81 |
|
slouken@111
|
82 |
/* AIFF magic header */
|
slouken@111
|
83 |
Uint32 FORMchunk;
|
slouken@111
|
84 |
Uint32 AIFFmagic;
|
slouken@111
|
85 |
|
slouken@111
|
86 |
/* SSND chunk */
|
slouken@111
|
87 |
Uint32 offset;
|
slouken@111
|
88 |
Uint32 blocksize;
|
slouken@111
|
89 |
|
slouken@111
|
90 |
/* COMM format chunk */
|
slouken@142
|
91 |
Uint16 channels = 0;
|
slouken@142
|
92 |
Uint32 numsamples = 0;
|
slouken@142
|
93 |
Uint16 samplesize = 0;
|
slouken@111
|
94 |
Uint8 sane_freq[10];
|
slouken@142
|
95 |
Uint32 frequency = 0;
|
slouken@111
|
96 |
|
slouken@111
|
97 |
/* Make sure we are passed a valid data source */
|
slouken@111
|
98 |
was_error = 0;
|
slouken@111
|
99 |
if ( src == NULL ) {
|
slouken@111
|
100 |
was_error = 1;
|
slouken@111
|
101 |
goto done;
|
slouken@111
|
102 |
}
|
slouken@111
|
103 |
|
slouken@111
|
104 |
FORMchunk = SDL_ReadLE32(src);
|
slouken@111
|
105 |
chunk_length = SDL_ReadBE32(src);
|
slouken@111
|
106 |
if ( chunk_length == AIFF ) { /* The FORMchunk has already been read */
|
slouken@111
|
107 |
AIFFmagic = chunk_length;
|
slouken@111
|
108 |
chunk_length = FORMchunk;
|
slouken@111
|
109 |
FORMchunk = FORM;
|
slouken@111
|
110 |
} else {
|
slouken@111
|
111 |
AIFFmagic = SDL_ReadLE32(src);
|
slouken@111
|
112 |
}
|
slouken@216
|
113 |
if ( (FORMchunk != FORM) || ( (AIFFmagic != AIFF) && (AIFFmagic != _8SVX) ) ) {
|
slouken@216
|
114 |
SDL_SetError("Unrecognized file type (not AIFF nor 8SVX)");
|
slouken@111
|
115 |
was_error = 1;
|
slouken@111
|
116 |
goto done;
|
slouken@111
|
117 |
}
|
slouken@111
|
118 |
|
slouken@111
|
119 |
/* TODO: Better santity-checking. */
|
slouken@111
|
120 |
|
slouken@111
|
121 |
found_SSND = 0;
|
slouken@111
|
122 |
found_COMM = 0;
|
slouken@216
|
123 |
found_VHDR = 0;
|
slouken@216
|
124 |
found_BODY = 0;
|
slouken@111
|
125 |
|
slouken@111
|
126 |
do {
|
slouken@111
|
127 |
chunk_type = SDL_ReadLE32(src);
|
slouken@111
|
128 |
chunk_length = SDL_ReadBE32(src);
|
slouken@111
|
129 |
next_chunk = SDL_RWtell(src) + chunk_length;
|
slouken@111
|
130 |
/* Paranoia to avoid infinite loops */
|
slouken@111
|
131 |
if (chunk_length == 0)
|
slouken@111
|
132 |
break;
|
slouken@111
|
133 |
|
slouken@111
|
134 |
switch (chunk_type) {
|
slouken@111
|
135 |
case SSND:
|
slouken@111
|
136 |
found_SSND = 1;
|
slouken@111
|
137 |
offset = SDL_ReadBE32(src);
|
slouken@111
|
138 |
blocksize = SDL_ReadBE32(src);
|
slouken@111
|
139 |
start = SDL_RWtell(src) + offset;
|
slouken@111
|
140 |
break;
|
slouken@111
|
141 |
|
slouken@111
|
142 |
case COMM:
|
slouken@111
|
143 |
found_COMM = 1;
|
slouken@111
|
144 |
channels = SDL_ReadBE16(src);
|
slouken@111
|
145 |
numsamples = SDL_ReadBE32(src);
|
slouken@111
|
146 |
samplesize = SDL_ReadBE16(src);
|
slouken@111
|
147 |
SDL_RWread(src, sane_freq, sizeof(sane_freq), 1);
|
slouken@111
|
148 |
frequency = SANE_to_Uint32(sane_freq);
|
slouken@111
|
149 |
if (frequency == 0) {
|
slouken@111
|
150 |
SDL_SetError("Bad AIFF sample frequency");
|
slouken@111
|
151 |
was_error = 1;
|
slouken@111
|
152 |
goto done;
|
slouken@111
|
153 |
}
|
slouken@111
|
154 |
break;
|
slouken@111
|
155 |
|
slouken@216
|
156 |
case VHDR:
|
slouken@216
|
157 |
found_VHDR = 1;
|
slouken@216
|
158 |
SDL_ReadBE32(src);
|
slouken@216
|
159 |
SDL_ReadBE32(src);
|
slouken@216
|
160 |
SDL_ReadBE32(src);
|
slouken@216
|
161 |
frequency = SDL_ReadBE16(src);
|
slouken@216
|
162 |
channels = 1;
|
slouken@216
|
163 |
samplesize = 8;
|
slouken@216
|
164 |
break;
|
slouken@216
|
165 |
|
slouken@216
|
166 |
case BODY:
|
slouken@216
|
167 |
found_BODY = 1;
|
slouken@216
|
168 |
numsamples = chunk_length;
|
slouken@216
|
169 |
start = SDL_RWtell(src);
|
slouken@216
|
170 |
break;
|
slouken@216
|
171 |
|
slouken@111
|
172 |
default:
|
slouken@111
|
173 |
break;
|
slouken@111
|
174 |
}
|
slouken@216
|
175 |
/* a 0 pad byte can be stored for any odd-length chunk */
|
slouken@216
|
176 |
if (chunk_length&1)
|
slouken@216
|
177 |
next_chunk++;
|
slouken@216
|
178 |
} while ( ( ( (AIFFmagic == AIFF) && ( !found_SSND || !found_COMM ) )
|
slouken@216
|
179 |
|| ( (AIFFmagic == _8SVX ) && ( !found_VHDR || !found_BODY ) ) )
|
slouken@111
|
180 |
&& SDL_RWseek(src, next_chunk, SEEK_SET) != 1 );
|
slouken@111
|
181 |
|
slouken@216
|
182 |
if ( (AIFFmagic == AIFF) && !found_SSND ) {
|
slouken@111
|
183 |
SDL_SetError("Bad AIFF (no SSND chunk)");
|
slouken@111
|
184 |
was_error = 1;
|
slouken@111
|
185 |
goto done;
|
slouken@111
|
186 |
}
|
slouken@111
|
187 |
|
slouken@216
|
188 |
if ( (AIFFmagic == AIFF) && !found_COMM ) {
|
slouken@111
|
189 |
SDL_SetError("Bad AIFF (no COMM chunk)");
|
slouken@111
|
190 |
was_error = 1;
|
slouken@111
|
191 |
goto done;
|
slouken@111
|
192 |
}
|
slouken@111
|
193 |
|
slouken@216
|
194 |
if ( (AIFFmagic == _8SVX) && !found_VHDR ) {
|
slouken@216
|
195 |
SDL_SetError("Bad 8SVX (no VHDR chunk)");
|
slouken@216
|
196 |
was_error = 1;
|
slouken@216
|
197 |
goto done;
|
slouken@216
|
198 |
}
|
slouken@216
|
199 |
|
slouken@216
|
200 |
if ( (AIFFmagic == _8SVX) && !found_BODY ) {
|
slouken@216
|
201 |
SDL_SetError("Bad 8SVX (no BODY chunk)");
|
slouken@216
|
202 |
was_error = 1;
|
slouken@216
|
203 |
goto done;
|
slouken@216
|
204 |
}
|
slouken@216
|
205 |
|
slouken@111
|
206 |
/* Decode the audio data format */
|
slouken@111
|
207 |
memset(spec, 0, sizeof(*spec));
|
slouken@111
|
208 |
spec->freq = frequency;
|
slouken@111
|
209 |
switch (samplesize) {
|
slouken@111
|
210 |
case 8:
|
slouken@111
|
211 |
spec->format = AUDIO_S8;
|
slouken@111
|
212 |
break;
|
slouken@111
|
213 |
case 16:
|
slouken@111
|
214 |
spec->format = AUDIO_S16MSB;
|
slouken@111
|
215 |
break;
|
slouken@111
|
216 |
default:
|
slouken@111
|
217 |
SDL_SetError("Unsupported AIFF samplesize");
|
slouken@111
|
218 |
was_error = 1;
|
slouken@111
|
219 |
goto done;
|
slouken@111
|
220 |
}
|
slouken@111
|
221 |
spec->channels = (Uint8) channels;
|
slouken@111
|
222 |
spec->samples = 4096; /* Good default buffer size */
|
slouken@111
|
223 |
|
slouken@111
|
224 |
*audio_len = channels * numsamples * (samplesize / 8);
|
slouken@111
|
225 |
*audio_buf = (Uint8 *)malloc(*audio_len);
|
slouken@111
|
226 |
if ( *audio_buf == NULL ) {
|
slouken@111
|
227 |
SDL_SetError("Out of memory");
|
slouken@111
|
228 |
return(NULL);
|
slouken@111
|
229 |
}
|
slouken@111
|
230 |
SDL_RWseek(src, start, SEEK_SET);
|
slouken@111
|
231 |
if ( SDL_RWread(src, *audio_buf, *audio_len, 1) != 1 ) {
|
slouken@111
|
232 |
SDL_SetError("Unable to read audio data");
|
slouken@111
|
233 |
return(NULL);
|
slouken@111
|
234 |
}
|
slouken@111
|
235 |
|
slouken@111
|
236 |
/* Don't return a buffer that isn't a multiple of samplesize */
|
slouken@111
|
237 |
*audio_len &= ~((samplesize / 8) - 1);
|
slouken@111
|
238 |
|
slouken@111
|
239 |
done:
|
slouken@111
|
240 |
if ( freesrc && src ) {
|
slouken@111
|
241 |
SDL_RWclose(src);
|
slouken@111
|
242 |
}
|
slouken@111
|
243 |
if ( was_error ) {
|
slouken@111
|
244 |
spec = NULL;
|
slouken@111
|
245 |
}
|
slouken@111
|
246 |
return(spec);
|
slouken@111
|
247 |
}
|
slouken@216
|
248 |
|