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