input_wav.cpp

Go to the documentation of this file.
00001 #include <string.h>
00002 #include "debug.h"
00003 #include "input_wav.h"
00004 #include "utility.h"
00005 
00006 
00007 namespace audiere {
00008 
00009   static inline bool IsValidSampleSize(u32 size) {
00010     return (size == 8 || size == 16);
00011   }
00012 
00013 
00014   WAVInputStream::WAVInputStream() {
00015     m_file = 0;
00016 
00017     m_channel_count = 0;
00018     m_sample_rate   = 0;
00019     m_sample_format = SF_U8;  // reasonable default?
00020 
00021     m_data_chunk_location = 0;
00022     m_data_chunk_length   = 0;
00023 
00024     m_frames_left_in_chunk = 0;
00025   }
00026 
00027 
00029   bool
00030   WAVInputStream::initialize(FilePtr file) {
00031     m_file = file;
00032 
00033     // read the RIFF header
00034     char riff_id[4];
00035     u8   riff_length_buffer[4];
00036     char riff_datatype[4];
00037 
00038     u32 size = 0;
00039     size += file->read(riff_id, 4);
00040     size += file->read(riff_length_buffer, 4);
00041     size += file->read(riff_datatype, 4);
00042 
00043     int riff_length = read32_le(riff_length_buffer);
00044 
00045     if (size != 12 ||
00046         memcmp(riff_id, "RIFF", 4) != 0 ||
00047         riff_length == 0 ||
00048         memcmp(riff_datatype, "WAVE", 4) != 0) {
00049 
00050       ADR_LOG("Couldn't read RIFF header");
00051 
00052       // so we don't destroy the file
00053       m_file = 0;
00054       return false;
00055     }
00056 
00057     if (findFormatChunk() && findDataChunk()) {
00058       return true;
00059     } else {
00060       m_file = 0;
00061       return false;
00062     }
00063   }
00064 
00065 
00066   void
00067   WAVInputStream::getFormat(
00068     int& channel_count,
00069     int& sample_rate,
00070     SampleFormat& sample_format)
00071   {
00072     channel_count = m_channel_count;
00073     sample_rate   = m_sample_rate;
00074     sample_format = m_sample_format;
00075   }
00076 
00077 
00078   int
00079   WAVInputStream::doRead(int frame_count, void* buffer) {
00080     if (m_frames_left_in_chunk == 0) {
00081       return 0;
00082     }
00083 
00084     const int frames_to_read = std::min(frame_count, m_frames_left_in_chunk);
00085     const int frame_size = m_channel_count * GetSampleSize(m_sample_format);
00086     const int bytes_to_read = frames_to_read * frame_size;
00087   
00088     const int read = m_file->read(buffer, bytes_to_read);
00089     const int frames_read = read / frame_size;
00090 
00091 #if WORDS_BIGENDIAN
00092     if (m_sample_format == SF_S16) {
00093       // make little endian into host endian
00094       u8* out = (u8*)buffer;
00095       for (int i = 0; i < frames_read * m_channel_count; ++i) {
00096         std::swap(out[0], out[1]);
00097         out += 2;
00098       }
00099     }
00100 #endif
00101 
00102     // assume that if we didn't get a full read, we're done
00103     if (read != bytes_to_read) {
00104       m_frames_left_in_chunk = 0;
00105       return frames_read;
00106     }
00107 
00108     m_frames_left_in_chunk -= frames_read;
00109     return frames_read;
00110   }
00111 
00112 
00113   void
00114   WAVInputStream::reset() {
00115     // seek to the beginning of the data chunk
00116     m_frames_left_in_chunk = m_data_chunk_length;
00117     m_file->seek(m_data_chunk_location, File::BEGIN);
00118   }
00119 
00120 
00121   bool
00122   WAVInputStream::isSeekable() {
00123     return true;
00124   }
00125 
00126 
00127   int
00128   WAVInputStream::getLength() {
00129     return m_data_chunk_length;
00130   }
00131 
00132 
00133   void
00134   WAVInputStream::setPosition(int position) {
00135     int frame_size = m_channel_count * GetSampleSize(m_sample_format);
00136     m_frames_left_in_chunk = m_data_chunk_length - position;
00137     m_file->seek(m_data_chunk_location + position * frame_size, File::BEGIN);
00138   }
00139 
00140 
00141   int
00142   WAVInputStream::getPosition() {
00143     return m_data_chunk_length - m_frames_left_in_chunk;
00144   }
00145 
00146 
00147   bool
00148   WAVInputStream::findFormatChunk() {
00149     ADR_GUARD("WAVInputStream::findFormatChunk");
00150 
00151     // seek to just after the RIFF header
00152     m_file->seek(12, File::BEGIN);
00153 
00154     // search for a format chunk
00155     for (;;) {
00156       char chunk_id[4];
00157       u8   chunk_length_buffer[4];
00158 
00159       int size = m_file->read(chunk_id, 4);
00160       size    += m_file->read(chunk_length_buffer, 4);
00161       u32 chunk_length = read32_le(chunk_length_buffer);
00162 
00163       // if we couldn't read enough, we're done
00164       if (size != 8) {
00165         return false;
00166       }
00167 
00168       // if we found a format chunk, excellent!
00169       if (memcmp(chunk_id, "fmt ", 4) == 0 && chunk_length >= 16) {
00170 
00171         ADR_LOG("Found format chunk");
00172 
00173         // read format chunk
00174         u8 chunk[16];
00175         size = m_file->read(chunk, 16);
00176 
00177         // could we read the entire format chunk?
00178         if (size < 16) {
00179           return false;
00180         }
00181 
00182         chunk_length -= size;
00183 
00184         // parse the memory into useful information
00185         u16 format_tag         = read16_le(chunk + 0);
00186         u16 channel_count      = read16_le(chunk + 2);
00187         u32 samples_per_second = read32_le(chunk + 4);
00188         //u32 bytes_per_second   = read32_le(chunk + 8);
00189         //u16 block_align        = read16_le(chunk + 12);
00190         u16 bits_per_sample    = read16_le(chunk + 14);
00191 
00192         // format_tag must be 1 (WAVE_FORMAT_PCM)
00193         // we only support mono and stereo
00194         if (format_tag != 1 ||
00195             channel_count > 2 ||
00196             !IsValidSampleSize(bits_per_sample)) {
00197           ADR_LOG("Invalid WAV");
00198           return false;
00199         }
00200 
00201         // skip the rest of the chunk
00202         if (!skipBytes(chunk_length)) {
00203           // oops, end of stream
00204           return false;
00205         }
00206 
00207         // figure out the sample format
00208         if (bits_per_sample == 8) {
00209           m_sample_format = SF_U8;
00210         } else if (bits_per_sample == 16) {
00211           m_sample_format = SF_S16;
00212         } else {
00213           return false;
00214         }
00215 
00216         // store the other important .wav attributes
00217         m_channel_count = channel_count;
00218         m_sample_rate   = samples_per_second;
00219         return true;
00220 
00221       } else {
00222 
00223         // skip the rest of the chunk
00224         if (!skipBytes(chunk_length)) {
00225           // oops, end of stream
00226           return false;
00227         }
00228 
00229       }
00230     }
00231   }
00232 
00233 
00234   bool
00235   WAVInputStream::findDataChunk() {
00236     ADR_GUARD("WAVInputStream::findDataChunk");
00237 
00238     // seek to just after the RIFF header
00239     m_file->seek(12, File::BEGIN);
00240 
00241     // search for a data chunk
00242     while (true) {
00243       char chunk_id[4];
00244       u8   chunk_length_buffer[4];
00245 
00246       int size = m_file->read(chunk_id, 4);
00247       size    += m_file->read(chunk_length_buffer, 4);
00248       u32 chunk_length = read32_le(chunk_length_buffer);
00249 
00250       // if we couldn't read enough, we're done
00251       if (size != 8) {
00252         ADR_LOG("Couldn't read chunk header");
00253         return false;
00254       }
00255 
00256       // if we found a data chunk, excellent!
00257       if (memcmp(chunk_id, "data", 4) == 0) {
00258 
00259         ADR_LOG("Found data chunk");
00260 
00261         // calculate the frame size so we can truncate the data chunk
00262         int frame_size = m_channel_count * GetSampleSize(m_sample_format);
00263 
00264         m_data_chunk_location  = m_file->tell();
00265         m_data_chunk_length    = chunk_length / frame_size;
00266         m_frames_left_in_chunk = m_data_chunk_length;
00267         return true;
00268 
00269       } else {
00270 
00271         ADR_IF_DEBUG {
00272           char str[80];
00273           sprintf(str, "Skipping: %d bytes in chunk '%c%c%c%c'",
00274                   (int)chunk_length,
00275                   chunk_id[0], chunk_id[1], chunk_id[2], chunk_id[3]);
00276           ADR_LOG(str);
00277         }
00278 
00279         // skip the rest of the chunk
00280         if (!skipBytes(chunk_length)) {
00281           // oops, end of stream
00282           return false;
00283         }
00284 
00285       }
00286     }
00287   }
00288 
00289 
00290   bool
00291   WAVInputStream::skipBytes(int size) {
00292     return m_file->seek(size, File::CURRENT);
00293   }
00294 
00295 
00296 }

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