input_aiff.cpp

Go to the documentation of this file.
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;  // reasonable default?
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       // make little endian into host endian
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     // assume that if we didn't get a full read, we're done
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     // seek to the beginning of the data chunk
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     // seek to just after the IFF header
00153     m_file->seek(12, File::BEGIN);
00154 
00155     // search for a common chunk
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       // if we found a format chunk, excellent!
00164       if (memcmp(chunk_header, "COMM", 4) == 0 && chunk_length >= 18) {
00165         ADR_LOG("Found common chunk");
00166 
00167         // read common chunk
00168         u8 chunk[18];
00169         if (m_file->read(chunk, 18) != 18) {
00170           return false;
00171         }
00172 
00173         chunk_length -= 18;
00174 
00175         // parse the memory into useful information
00176         u16 channel_count   = read16_be(chunk + 0);
00177         //u32 frame_count     = read32_be(chunk + 2);
00178         u16 bits_per_sample = read16_be(chunk + 6);
00179         u32 sample_rate     = readLD_be(chunk + 8);
00180 
00181         // we only support mono and stereo, 8-bit or 16-bit
00182         if (channel_count > 2 ||
00183             !isValidSampleSize(bits_per_sample)) {
00184           ADR_LOG("Invalid AIFF");
00185           return false;
00186         }
00187 
00188         // skip the rest of the chunk
00189         if (!skipBytes(chunk_length)) {
00190           ADR_LOG("failed skipping rest of common chunk");
00191           return false;
00192         }
00193 
00194         // figure out the sample format
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         // store the other important attributes
00204         m_channel_count = channel_count;
00205         m_sample_rate   = sample_rate;
00206         return true;
00207 
00208       } else {
00209 
00210         // skip the rest of the chunk
00211         if (!skipBytes(chunk_length)) {
00212           // oops, end of stream
00213           return false;
00214         }
00215 
00216       }
00217     }
00218   }
00219 
00220 
00221   bool
00222   AIFFInputStream::findSoundChunk() {
00223     ADR_GUARD("AIFFInputStream::findSoundChunk");
00224 
00225     // seek to just after the IFF header
00226     m_file->seek(12, File::BEGIN);
00227 
00228     // search for a sound chunk
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       // if we found a data chunk, excellent!
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         // calculate the frame size so we can truncate the data chunk
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         // skip the rest of the chunk
00272         if (!skipBytes(chunk_length)) {
00273           // oops, end of stream
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 }

Generated on Mon Feb 13 23:07:46 2006 for audiere by  doxygen 1.4.6