1 /**
2  * This file is roughly based on the WAVInputStream implementation,
3  * since AIFF and WAV files are so alike.
4  */
5 
6 #include <string.h>
7 #include "debug.h"
8 #include "input_aiff.h"
9 #include "utility.h"
10 
11 
12 namespace audiere {
13 
isValidSampleSize(u32 size)14   static inline bool isValidSampleSize(u32 size) {
15     return (size == 8 || size == 16);
16   }
17 
18 
AIFFInputStream()19   AIFFInputStream::AIFFInputStream() {
20     m_file = 0;
21 
22     m_channel_count = 0;
23     m_sample_rate   = 0;
24     m_sample_format = SF_U8;  // reasonable default?
25 
26     m_data_chunk_location = 0;
27     m_data_chunk_length   = 0;
28 
29     m_frames_left_in_chunk = 0;
30   }
31 
32 
33   /// @todo  this really should be replaced with a factory function
34   bool
initialize(FilePtr file)35   AIFFInputStream::initialize(FilePtr file) {
36     ADR_GUARD("AIFFInputStream::initialize");
37 
38     m_file = file;
39 
40     u8 header[12];
41     if (file->read(header, 12) != 12) {
42       ADR_LOG("Failed to read AIFF header");
43       m_file = 0;
44       return false;
45     }
46 
47     if (memcmp(header, "FORM", 4) != 0 ||
48         read32_be(header + 4) == 0 ||
49         memcmp(header + 8, "AIFF", 4) != 0)
50     {
51       ADR_LOG("Invalid AIFF header");
52       m_file = 0;
53       return false;
54     }
55 
56     if (findCommonChunk() && findSoundChunk()) {
57       return true;
58     } else {
59       m_file = 0;
60       return false;
61     }
62   }
63 
64 
65   void
getFormat(int & channel_count,int & sample_rate,SampleFormat & sample_format)66   AIFFInputStream::getFormat(
67     int& channel_count,
68     int& sample_rate,
69     SampleFormat& sample_format)
70   {
71     channel_count = m_channel_count;
72     sample_rate   = m_sample_rate;
73     sample_format = m_sample_format;
74   }
75 
76 
77   int
doRead(int frame_count,void * buffer)78   AIFFInputStream::doRead(int frame_count, void* buffer) {
79     if (m_frames_left_in_chunk == 0) {
80       return 0;
81     }
82 
83     const int frames_to_read = std::min(frame_count, m_frames_left_in_chunk);
84     const int frame_size = m_channel_count * GetSampleSize(m_sample_format);
85     const int bytes_to_read = frames_to_read * frame_size;
86 
87     const int read = m_file->read(buffer, bytes_to_read);
88     const int frames_read = read / frame_size;
89 
90 #ifndef WORDS_BIGENDIAN
91     if (m_sample_format == SF_S16) {
92       // make little endian into host endian
93       u8* out = (u8*)buffer;
94       for (int i = 0; i < frames_read * m_channel_count; ++i) {
95         std::swap(out[0], out[1]);
96         out += 2;
97       }
98     }
99 #endif
100 
101     // assume that if we didn't get a full read, we're done
102     if (read != bytes_to_read) {
103       m_frames_left_in_chunk = 0;
104       return frames_read;
105     }
106 
107     m_frames_left_in_chunk -= frames_read;
108     return frames_read;
109   }
110 
111 
112   void
reset()113   AIFFInputStream::reset() {
114     // seek to the beginning of the data chunk
115     m_frames_left_in_chunk = m_data_chunk_length;
116     if (!m_file->seek(m_data_chunk_location, File::BEGIN)) {
117       ADR_LOG("Seek in AIFFInputStream::reset");
118     }
119   }
120 
121 
122   bool
isSeekable()123   AIFFInputStream::isSeekable() {
124     return true;
125   }
126 
127 
128   int
getLength()129   AIFFInputStream::getLength() {
130     return m_data_chunk_length;
131   }
132 
133 
134   void
setPosition(int position)135   AIFFInputStream::setPosition(int position) {
136     int frame_size = m_channel_count * GetSampleSize(m_sample_format);
137     m_frames_left_in_chunk = m_data_chunk_length - position;
138     m_file->seek(m_data_chunk_location + position * frame_size, File::BEGIN);
139   }
140 
141 
142   int
getPosition()143   AIFFInputStream::getPosition() {
144     return m_data_chunk_length - m_frames_left_in_chunk;
145   }
146 
147 
148   bool
findCommonChunk()149   AIFFInputStream::findCommonChunk() {
150     ADR_GUARD("AIFFInputStream::findCommonChunk");
151 
152     // seek to just after the IFF header
153     m_file->seek(12, File::BEGIN);
154 
155     // search for a common chunk
156     for (;;) {
157       u8 chunk_header[8];
158       if (m_file->read(chunk_header, 8) != 8) {
159         return false;
160       }
161       u32 chunk_length = read32_be(chunk_header + 4);
162 
163       // if we found a format chunk, excellent!
164       if (memcmp(chunk_header, "COMM", 4) == 0 && chunk_length >= 18) {
165         ADR_LOG("Found common chunk");
166 
167         // read common chunk
168         u8 chunk[18];
169         if (m_file->read(chunk, 18) != 18) {
170           return false;
171         }
172 
173         chunk_length -= 18;
174 
175         // parse the memory into useful information
176         u16 channel_count   = read16_be(chunk + 0);
177         //u32 frame_count     = read32_be(chunk + 2);
178         u16 bits_per_sample = read16_be(chunk + 6);
179         u32 sample_rate     = readLD_be(chunk + 8);
180 
181         // we only support mono and stereo, 8-bit or 16-bit
182         if (channel_count > 2 ||
183             !isValidSampleSize(bits_per_sample)) {
184           ADR_LOG("Invalid AIFF");
185           return false;
186         }
187 
188         // skip the rest of the chunk
189         if (!skipBytes(chunk_length)) {
190           ADR_LOG("failed skipping rest of common chunk");
191           return false;
192         }
193 
194         // figure out the sample format
195         if (bits_per_sample == 8) {
196           m_sample_format = SF_U8;
197         } else if (bits_per_sample == 16) {
198           m_sample_format = SF_S16;
199         } else {
200           return false;
201         }
202 
203         // store the other important attributes
204         m_channel_count = channel_count;
205         m_sample_rate   = sample_rate;
206         return true;
207 
208       } else {
209 
210         // skip the rest of the chunk
211         if (!skipBytes(chunk_length)) {
212           // oops, end of stream
213           return false;
214         }
215 
216       }
217     }
218   }
219 
220 
221   bool
findSoundChunk()222   AIFFInputStream::findSoundChunk() {
223     ADR_GUARD("AIFFInputStream::findSoundChunk");
224 
225     // seek to just after the IFF header
226     m_file->seek(12, File::BEGIN);
227 
228     // search for a sound chunk
229     while (true) {
230       u8 chunk_header[8];
231       if (m_file->read(chunk_header, 8) != 8) {
232         ADR_LOG("Couldn't read SSND chunk header");
233         return false;
234       }
235       u32 chunk_length = read32_be(chunk_header + 4);
236 
237       // if we found a data chunk, excellent!
238       if (memcmp(chunk_header, "SSND", 4) == 0) {
239         ADR_LOG("Found sound chunk");
240 
241         u8 chunk_contents[8];
242         if (m_file->read(chunk_contents, 8) != 8) {
243           ADR_LOG("Couldn't read SSND chunk contents");
244           return false;
245         }
246         if (read32_be(chunk_contents + 0) != 0 ||
247             read32_be(chunk_contents + 4) != 0)
248         {
249           ADR_LOG("Block-aligned AIFF files not supported!");
250           return false;
251         }
252 
253         // calculate the frame size so we can truncate the data chunk
254         int frame_size = m_channel_count * GetSampleSize(m_sample_format);
255 
256         m_data_chunk_location  = m_file->tell();
257         m_data_chunk_length    = (chunk_length - 8) / frame_size;
258         m_frames_left_in_chunk = m_data_chunk_length;
259         return true;
260 
261       } else {
262 
263         ADR_IF_DEBUG {
264           const u8* ci = chunk_header;
265           char str[80];
266           sprintf(str, "Skipping: %d bytes in chunk '%c%c%c%c'",
267                   (int)chunk_length, ci[0], ci[1], ci[2], ci[3]);
268           ADR_LOG(str);
269         }
270 
271         // skip the rest of the chunk
272         if (!skipBytes(chunk_length)) {
273           // oops, end of stream
274           return false;
275         }
276 
277       }
278     }
279   }
280 
281 
282   bool
skipBytes(int size)283   AIFFInputStream::skipBytes(int size) {
284     return m_file->seek(size, File::CURRENT);
285   }
286 
287 }
288