00001
00006 #include <string.h>
00007 #include "debug.h"
00008 #include "input_aiff.h"
00009 #include "utility.h"
00010
00011
00012 namespace audiere {
00013
00014 static inline bool isValidSampleSize(u32 size) {
00015 return (size == 8 || size == 16);
00016 }
00017
00018
00019 AIFFInputStream::AIFFInputStream() {
00020 m_file = 0;
00021
00022 m_channel_count = 0;
00023 m_sample_rate = 0;
00024 m_sample_format = SF_U8;
00025
00026 m_data_chunk_location = 0;
00027 m_data_chunk_length = 0;
00028
00029 m_frames_left_in_chunk = 0;
00030 }
00031
00032
00034 bool
00035 AIFFInputStream::initialize(FilePtr file) {
00036 ADR_GUARD("AIFFInputStream::initialize");
00037
00038 m_file = file;
00039
00040 u8 header[12];
00041 if (file->read(header, 12) != 12) {
00042 ADR_LOG("Failed to read AIFF header");
00043 m_file = 0;
00044 return false;
00045 }
00046
00047 if (memcmp(header, "FORM", 4) != 0 ||
00048 read32_be(header + 4) == 0 ||
00049 memcmp(header + 8, "AIFF", 4) != 0)
00050 {
00051 ADR_LOG("Invalid AIFF header");
00052 m_file = 0;
00053 return false;
00054 }
00055
00056 if (findCommonChunk() && findSoundChunk()) {
00057 return true;
00058 } else {
00059 m_file = 0;
00060 return false;
00061 }
00062 }
00063
00064
00065 void
00066 AIFFInputStream::getFormat(
00067 int& channel_count,
00068 int& sample_rate,
00069 SampleFormat& sample_format)
00070 {
00071 channel_count = m_channel_count;
00072 sample_rate = m_sample_rate;
00073 sample_format = m_sample_format;
00074 }
00075
00076
00077 int
00078 AIFFInputStream::doRead(int frame_count, void* buffer) {
00079 if (m_frames_left_in_chunk == 0) {
00080 return 0;
00081 }
00082
00083 const int frames_to_read = std::min(frame_count, m_frames_left_in_chunk);
00084 const int frame_size = m_channel_count * GetSampleSize(m_sample_format);
00085 const int bytes_to_read = frames_to_read * frame_size;
00086
00087 const int read = m_file->read(buffer, bytes_to_read);
00088 const int frames_read = read / frame_size;
00089
00090 #ifndef WORDS_BIGENDIAN
00091 if (m_sample_format == SF_S16) {
00092
00093 u8* out = (u8*)buffer;
00094 for (int i = 0; i < frames_read * m_channel_count; ++i) {
00095 std::swap(out[0], out[1]);
00096 out += 2;
00097 }
00098 }
00099 #endif
00100
00101
00102 if (read != bytes_to_read) {
00103 m_frames_left_in_chunk = 0;
00104 return frames_read;
00105 }
00106
00107 m_frames_left_in_chunk -= frames_read;
00108 return frames_read;
00109 }
00110
00111
00112 void
00113 AIFFInputStream::reset() {
00114
00115 m_frames_left_in_chunk = m_data_chunk_length;
00116 if (!m_file->seek(m_data_chunk_location, File::BEGIN)) {
00117 ADR_LOG("Seek in AIFFInputStream::reset");
00118 }
00119 }
00120
00121
00122 bool
00123 AIFFInputStream::isSeekable() {
00124 return true;
00125 }
00126
00127
00128 int
00129 AIFFInputStream::getLength() {
00130 return m_data_chunk_length;
00131 }
00132
00133
00134 void
00135 AIFFInputStream::setPosition(int position) {
00136 int frame_size = m_channel_count * GetSampleSize(m_sample_format);
00137 m_frames_left_in_chunk = m_data_chunk_length - position;
00138 m_file->seek(m_data_chunk_location + position * frame_size, File::BEGIN);
00139 }
00140
00141
00142 int
00143 AIFFInputStream::getPosition() {
00144 return m_data_chunk_length - m_frames_left_in_chunk;
00145 }
00146
00147
00148 bool
00149 AIFFInputStream::findCommonChunk() {
00150 ADR_GUARD("AIFFInputStream::findCommonChunk");
00151
00152
00153 m_file->seek(12, File::BEGIN);
00154
00155
00156 for (;;) {
00157 u8 chunk_header[8];
00158 if (m_file->read(chunk_header, 8) != 8) {
00159 return false;
00160 }
00161 u32 chunk_length = read32_be(chunk_header + 4);
00162
00163
00164 if (memcmp(chunk_header, "COMM", 4) == 0 && chunk_length >= 18) {
00165 ADR_LOG("Found common chunk");
00166
00167
00168 u8 chunk[18];
00169 if (m_file->read(chunk, 18) != 18) {
00170 return false;
00171 }
00172
00173 chunk_length -= 18;
00174
00175
00176 u16 channel_count = read16_be(chunk + 0);
00177
00178 u16 bits_per_sample = read16_be(chunk + 6);
00179 u32 sample_rate = readLD_be(chunk + 8);
00180
00181
00182 if (channel_count > 2 ||
00183 !isValidSampleSize(bits_per_sample)) {
00184 ADR_LOG("Invalid AIFF");
00185 return false;
00186 }
00187
00188
00189 if (!skipBytes(chunk_length)) {
00190 ADR_LOG("failed skipping rest of common chunk");
00191 return false;
00192 }
00193
00194
00195 if (bits_per_sample == 8) {
00196 m_sample_format = SF_U8;
00197 } else if (bits_per_sample == 16) {
00198 m_sample_format = SF_S16;
00199 } else {
00200 return false;
00201 }
00202
00203
00204 m_channel_count = channel_count;
00205 m_sample_rate = sample_rate;
00206 return true;
00207
00208 } else {
00209
00210
00211 if (!skipBytes(chunk_length)) {
00212
00213 return false;
00214 }
00215
00216 }
00217 }
00218 }
00219
00220
00221 bool
00222 AIFFInputStream::findSoundChunk() {
00223 ADR_GUARD("AIFFInputStream::findSoundChunk");
00224
00225
00226 m_file->seek(12, File::BEGIN);
00227
00228
00229 while (true) {
00230 u8 chunk_header[8];
00231 if (m_file->read(chunk_header, 8) != 8) {
00232 ADR_LOG("Couldn't read SSND chunk header");
00233 return false;
00234 }
00235 u32 chunk_length = read32_be(chunk_header + 4);
00236
00237
00238 if (memcmp(chunk_header, "SSND", 4) == 0) {
00239 ADR_LOG("Found sound chunk");
00240
00241 u8 chunk_contents[8];
00242 if (m_file->read(chunk_contents, 8) != 8) {
00243 ADR_LOG("Couldn't read SSND chunk contents");
00244 return false;
00245 }
00246 if (read32_be(chunk_contents + 0) != 0 ||
00247 read32_be(chunk_contents + 4) != 0)
00248 {
00249 ADR_LOG("Block-aligned AIFF files not supported!");
00250 return false;
00251 }
00252
00253
00254 int frame_size = m_channel_count * GetSampleSize(m_sample_format);
00255
00256 m_data_chunk_location = m_file->tell();
00257 m_data_chunk_length = (chunk_length - 8) / frame_size;
00258 m_frames_left_in_chunk = m_data_chunk_length;
00259 return true;
00260
00261 } else {
00262
00263 ADR_IF_DEBUG {
00264 const u8* ci = chunk_header;
00265 char str[80];
00266 sprintf(str, "Skipping: %d bytes in chunk '%c%c%c%c'",
00267 (int)chunk_length, ci[0], ci[1], ci[2], ci[3]);
00268 ADR_LOG(str);
00269 }
00270
00271
00272 if (!skipBytes(chunk_length)) {
00273
00274 return false;
00275 }
00276
00277 }
00278 }
00279 }
00280
00281
00282 bool
00283 AIFFInputStream::skipBytes(int size) {
00284 return m_file->seek(size, File::CURRENT);
00285 }
00286
00287 }