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