00001 #include "input_wav.hpp"
00002 #include "endian.hpp"
00003 #include "utility.hpp"
00004
00005
00006 static inline adr_u16 Read16(adr_u8* m) {
00007 return (adr_u16)(
00008 (m[0] << 0) +
00009 (m[1] << 8)
00010 );
00011 }
00012
00013 static inline adr_u32 Read32(adr_u8* m) {
00014 return (adr_u32)(
00015 (m[0] << 0) +
00016 (m[1] << 8) +
00017 (m[2] << 16) +
00018 (m[3] << 24)
00019 );
00020 }
00021
00022 static inline bool IsValidSampleSize(adr_u32 size) {
00023 return (size == 8 || size == 16);
00024 }
00025
00027
00028 WAVInputStream::WAVInputStream()
00029 {
00030 m_file = 0;
00031
00032 m_channel_count = 0;
00033 m_bits_per_sample = 0;
00034 m_sample_rate = 0;
00035
00036 m_data_chunk_location = 0;
00037 m_data_chunk_length = 0;
00038
00039 m_samples_left_in_chunk = 0;
00040 }
00041
00043
00044 WAVInputStream::~WAVInputStream()
00045 {
00046 delete m_file;
00047 m_file = 0;
00048 }
00049
00051
00052 bool
00053 WAVInputStream::Initialize(IFile* file)
00054 {
00055 m_file = file;
00056
00057
00058 char riff_id[4];
00059 adr_u32 riff_length;
00060 char riff_datatype[4];
00061
00062 adr_u32 size = 0;
00063 size += file->Read(riff_id, 4);
00064 size += file->Read(&riff_length, 4);
00065 size += file->Read(riff_datatype, 4);
00066
00067 riff_length = LittleToHost32(riff_length);
00068
00069 if (size != 12 ||
00070 memcmp(riff_id, "RIFF", 4) != 0 ||
00071 riff_length == 0 ||
00072 memcmp(riff_datatype, "WAVE", 4) != 0) {
00073
00074
00075 m_file = 0;
00076 return false;
00077 }
00078
00079 if (FindFormatChunk() && FindDataChunk()) {
00080 return true;
00081 } else {
00082 m_file = 0;
00083 return false;
00084 }
00085 }
00086
00088
00089 void
00090 WAVInputStream::GetFormat(
00091 int& channel_count,
00092 int& sample_rate,
00093 int& bits_per_sample)
00094 {
00095 channel_count = m_channel_count;
00096 sample_rate = m_sample_rate;
00097 bits_per_sample = m_bits_per_sample;
00098 }
00099
00101
00102 int
00103 WAVInputStream::Read(int sample_count, void* samples)
00104 {
00105 if (m_samples_left_in_chunk == 0) {
00106 return 0;
00107 }
00108
00109 const int samples_to_read = adr_min(sample_count, m_samples_left_in_chunk);
00110 const int sample_size = m_channel_count * m_bits_per_sample / 8;
00111 const int bytes_to_read = samples_to_read * sample_size;
00112
00113 const int read = m_file->Read(samples, bytes_to_read);
00114 const int samples_read = read / sample_size;
00115
00116
00117 if (read != bytes_to_read) {
00118 m_samples_left_in_chunk = 0;
00119 return samples_read;
00120 }
00121
00122 m_samples_left_in_chunk -= samples_read;
00123 return samples_read;
00124 }
00125
00127
00128 bool
00129 WAVInputStream::Reset()
00130 {
00131
00132 m_samples_left_in_chunk = m_data_chunk_length;
00133 return m_file->Seek(m_data_chunk_location, ADR_BEGIN);
00134 }
00135
00137
00138 bool
00139 WAVInputStream::FindFormatChunk()
00140 {
00141
00142 m_file->Seek(12, ADR_BEGIN);
00143
00144
00145 while (true) {
00146 char chunk_id[4];
00147 adr_u32 chunk_length;
00148
00149 int size = m_file->Read(chunk_id, 4);
00150 size += m_file->Read(&chunk_length, 4);
00151 chunk_length = LittleToHost32(chunk_length);
00152
00153
00154 if (size != 8) {
00155 return false;
00156 }
00157
00158
00159 if (memcmp(chunk_id, "fmt ", 4) == 0 && chunk_length >= 16) {
00160
00161
00162 adr_u8 chunk[16];
00163 size = m_file->Read(chunk, 16);
00164
00165
00166 if (size < 16) {
00167 return false;
00168 }
00169
00170 chunk_length -= size;
00171
00172
00173 adr_u16 format_tag = Read16(chunk + 0);
00174 adr_u16 channel_count = Read16(chunk + 2);
00175 adr_u32 samples_per_second = Read32(chunk + 4);
00176 adr_u32 bytes_per_second = Read32(chunk + 8);
00177 adr_u16 block_align = Read16(chunk + 12);
00178 adr_u16 bits_per_sample = Read16(chunk + 14);
00179
00180
00181
00182 if (format_tag != 1 ||
00183 channel_count > 2 ||
00184 !IsValidSampleSize(bits_per_sample)) {
00185 return false;
00186 }
00187
00188
00189 if (!SkipBytes(chunk_length)) {
00190
00191 return false;
00192 }
00193
00194
00195 m_channel_count = channel_count;
00196 m_bits_per_sample = bits_per_sample;
00197 m_sample_rate = samples_per_second;
00198 return true;
00199
00200 } else {
00201
00202
00203 if (!SkipBytes(chunk_length)) {
00204
00205 return false;
00206 }
00207
00208 }
00209 }
00210 }
00211
00213
00214 bool
00215 WAVInputStream::FindDataChunk()
00216 {
00217
00218 m_file->Seek(12, ADR_BEGIN);
00219
00220
00221 while (true) {
00222 char chunk_id[4];
00223 adr_u32 chunk_length;
00224
00225 int size = m_file->Read(chunk_id, 4);
00226 size += m_file->Read(&chunk_length, 4);
00227 chunk_length = LittleToHost32(chunk_length);
00228
00229
00230 if (size != 8) {
00231 return false;
00232 }
00233
00234
00235 if (memcmp(chunk_id, "data", 4) == 0) {
00236
00237
00238 int sample_size = m_channel_count * m_bits_per_sample / 8;
00239
00240 m_data_chunk_location = m_file->Tell();
00241 m_data_chunk_length = chunk_length / sample_size;
00242 m_samples_left_in_chunk = m_data_chunk_length;
00243 return true;
00244
00245 } else {
00246
00247
00248 if (!SkipBytes(chunk_length)) {
00249
00250 return false;
00251 }
00252
00253 }
00254 }
00255 }
00256
00258
00259 bool
00260 WAVInputStream::SkipBytes(int size)
00261 {
00262 if (m_file->Seek(size, ADR_CURRENT)) {
00263 m_samples_left_in_chunk = m_data_chunk_length;
00264 return true;
00265 } else {
00266 return false;
00267 }
00268 }
00269