0f148500d7759bbf37f8f884b0c1ba462a574406
[asdcplib.git] / src / Wav.cpp
1 /*
2 Copyright (c) 2005-2009, John Hurst
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
7 are met:
8 1. Redistributions of source code must retain the above copyright
9    notice, this list of conditions and the following disclaimer.
10 2. Redistributions in binary form must reproduce the above copyright
11    notice, this list of conditions and the following disclaimer in the
12    documentation and/or other materials provided with the distribution.
13 3. The name of the author may not be used to endorse or promote products
14    derived from this software without specific prior written permission.
15
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27 /*! \file    Wav.cpp
28     \version $Id$
29     \brief   Wave file common elements
30 */
31
32 #include "Wav.h"
33 #include <assert.h>
34 #include <KM_log.h>
35 using Kumu::DefaultLogSink;
36
37
38 const ui32_t SimpleWavHeaderLength = 46;
39
40 //
41 ASDCP::Wav::SimpleWaveHeader::SimpleWaveHeader(ASDCP::PCM::AudioDescriptor& ADesc)
42 {
43   format = 1;
44   nchannels = ADesc.ChannelCount;
45   bitspersample = ADesc.QuantizationBits;
46   samplespersec = (ui32_t)ceil(ADesc.AudioSamplingRate.Quotient());
47   blockalign = nchannels * ((bitspersample + 7) / 8);
48   avgbps = samplespersec * blockalign;
49   cbsize = 0;     
50   data_len = ASDCP::PCM::CalcFrameBufferSize(ADesc) * ADesc.ContainerDuration;
51 }
52
53 //
54 void
55 ASDCP::Wav::SimpleWaveHeader::FillADesc(ASDCP::PCM::AudioDescriptor& ADesc, ASDCP::Rational PictureRate) const
56 {
57   ADesc.EditRate = PictureRate;
58
59   ADesc.LinkedTrackID = 0;
60   ADesc.Locked = 0;
61   ADesc.ChannelCount = nchannels;
62   ADesc.AudioSamplingRate = Rational(samplespersec, 1);
63   ADesc.AvgBps = avgbps;
64   ADesc.BlockAlign = blockalign;
65   ADesc.QuantizationBits = bitspersample;
66   ui32_t FrameBufferSize = ASDCP::PCM::CalcFrameBufferSize(ADesc);
67   ADesc.ContainerDuration = data_len / FrameBufferSize;
68   ADesc.ChannelFormat = PCM::CF_NONE;
69 }
70
71
72 //
73 ASDCP::Result_t
74 ASDCP::Wav::SimpleWaveHeader::WriteToFile(Kumu::FileWriter& OutFile) const
75 {
76   ui32_t write_count;
77   byte_t tmp_header[SimpleWavHeaderLength];
78   byte_t* p = tmp_header;
79
80   static ui32_t fmt_len =
81     sizeof(format)
82     + sizeof(nchannels)
83     + sizeof(samplespersec)
84     + sizeof(avgbps)
85     + sizeof(blockalign)
86     + sizeof(bitspersample)
87     + sizeof(cbsize);
88
89   ui32_t RIFF_len = data_len + SimpleWavHeaderLength - 8;
90
91   memcpy(p, &FCC_RIFF, sizeof(fourcc)); p += 4;
92   *((ui32_t*)p) = KM_i32_LE(RIFF_len); p += 4;
93   memcpy(p, &FCC_WAVE, sizeof(fourcc)); p += 4;
94   memcpy(p, &FCC_fmt_, sizeof(fourcc)); p += 4;
95   *((ui32_t*)p) = KM_i32_LE(fmt_len); p += 4;
96   *((ui16_t*)p) = KM_i16_LE(format); p += 2;
97   *((ui16_t*)p) = KM_i16_LE(nchannels); p += 2;
98   *((ui32_t*)p) = KM_i32_LE(samplespersec); p += 4;
99   *((ui32_t*)p) = KM_i32_LE(avgbps); p += 4;
100   *((ui16_t*)p) = KM_i16_LE(blockalign); p += 2;
101   *((ui16_t*)p) = KM_i16_LE(bitspersample); p += 2;
102   *((ui16_t*)p) = KM_i16_LE(cbsize); p += 2;
103   memcpy(p, &FCC_data, sizeof(fourcc)); p += 4;
104   *((ui32_t*)p) = KM_i32_LE(data_len); p += 4;
105
106   return OutFile.Write(tmp_header, SimpleWavHeaderLength, &write_count);
107 }
108
109 //
110 ASDCP::Result_t
111 ASDCP::Wav::SimpleWaveHeader::ReadFromFile(const Kumu::FileReader& InFile, ui32_t* data_start)
112 {
113   ui32_t read_count = 0;
114   ui32_t local_data_start = 0;
115   ASDCP::PCM::FrameBuffer TmpBuffer(MaxWavHeader);
116
117   if ( data_start == 0 )
118     data_start = &local_data_start;
119
120   Result_t result = InFile.Read(TmpBuffer.Data(), TmpBuffer.Capacity(), &read_count);
121
122   if ( ASDCP_SUCCESS(result) )
123     result = ReadFromBuffer(TmpBuffer.RoData(), read_count, data_start);
124
125     return result;
126 }
127
128 ASDCP::Result_t
129 ASDCP::Wav::SimpleWaveHeader::ReadFromBuffer(const byte_t* buf, ui32_t buf_len, ui32_t* data_start)
130 {
131   if ( buf_len < SimpleWavHeaderLength )
132     return RESULT_SMALLBUF;
133
134   *data_start = 0;
135   const byte_t* p = buf;
136   const byte_t* end_p = p + buf_len;
137
138   fourcc test_RIFF(p); p += 4;
139   if ( test_RIFF != FCC_RIFF )
140     {
141       //      DefaultLogSink().Debug("File does not begin with RIFF header\n");      
142       return RESULT_RAW_FORMAT;
143     }
144
145   ui32_t RIFF_len = KM_i32_LE(*(ui32_t*)p); p += 4;
146
147   fourcc test_WAVE(p); p += 4;
148   if ( test_WAVE != FCC_WAVE )
149     {
150       DefaultLogSink().Debug("File does not contain a WAVE header\n");
151       return RESULT_RAW_FORMAT;
152     }
153
154   fourcc test_fcc;
155
156   while ( p < end_p )
157     {
158       test_fcc = fourcc(p); p += 4;
159       ui32_t chunk_size = KM_i32_LE(*(ui32_t*)p); p += 4;
160
161       if ( test_fcc == FCC_data )
162         {
163           if ( chunk_size > RIFF_len )
164             {
165               DefaultLogSink().Error("Chunk size %u larger than file: %u\n", chunk_size, RIFF_len);
166               return RESULT_RAW_FORMAT;
167             }
168
169           data_len = chunk_size;
170           *data_start = p - buf;
171           break;
172         }
173
174       if ( test_fcc == FCC_fmt_ )
175         {
176           ui16_t format = KM_i16_LE(*(ui16_t*)p); p += 2;
177
178           if ( format != WAVE_FORMAT_PCM && format != WAVE_FORMAT_EXTENSIBLE )
179             {
180               DefaultLogSink().Error("Expecting uncompressed PCM data, got format type %hd\n", format);
181               return RESULT_RAW_FORMAT;
182             }
183
184           nchannels = KM_i16_LE(*(ui16_t*)p); p += 2;
185           samplespersec = KM_i32_LE(*(ui32_t*)p); p += 4;
186           avgbps = KM_i32_LE(*(ui32_t*)p); p += 4;
187           blockalign = KM_i16_LE(*(ui16_t*)p); p += 2;
188           bitspersample = KM_i16_LE(*(ui16_t*)p); p += 2;
189           p += chunk_size - 16; // 16 is the number of bytes read in this block
190         }
191       else
192         {
193           p += chunk_size;
194         }
195     }
196
197   if ( *data_start == 0 ) // can't have no data!
198     {
199       DefaultLogSink().Error("No data chunk found, file contains no essence\n");
200       return RESULT_RAW_FORMAT;
201     }
202
203   return RESULT_OK;
204 }
205
206 //------------------------------------------------------------------------------------------
207 // conversion algorithms from http://www.borg.com/~jglatt/tech/aiff.htm
208
209 //
210 void
211 Rat_to_extended(ASDCP::Rational rate, byte_t* buf)
212 {
213   memset(buf, 0, 10);
214   ui32_t value = (ui32_t)ceil(rate.Quotient()); 
215   ui32_t exp = value;
216   exp >>= 1;
217   ui8_t i = 0;
218
219   for ( ; i < 32; i++ )
220     {
221       exp >>= 1;
222       if ( ! exp )
223         break;
224     }
225
226   *(buf+1) = i;
227
228    for ( i = 32; i != 0 ; i-- )
229      {
230        if ( value & 0x80000000 )
231          break;
232        value <<= 1;
233      }
234
235    *(ui32_t*)(buf+2) = KM_i32_BE(value);
236 }
237
238 //
239 ASDCP::Rational
240 extended_to_Rat(const byte_t* buf)
241 {
242   ui32_t last = 0;
243   ui32_t mantissa = KM_i32_BE(*(ui32_t*)(buf+2));
244
245   byte_t exp = 30 - *(buf+1);
246
247   while ( exp-- )
248     {
249       last = mantissa;
250       mantissa >>= 1;
251     }
252
253   if ( last & 0x00000001 )
254     mantissa++;
255
256   return ASDCP::Rational(mantissa, 1);
257 }
258
259 //
260 void
261 ASDCP::AIFF::SimpleAIFFHeader::FillADesc(ASDCP::PCM::AudioDescriptor& ADesc, ASDCP::Rational PictureRate) const
262 {
263   ADesc.EditRate = PictureRate;
264
265   ADesc.ChannelCount = numChannels;
266   ADesc.AudioSamplingRate = extended_to_Rat(sampleRate);
267   ADesc.QuantizationBits = sampleSize;
268   ADesc.BlockAlign = sampleSize / 8;
269   ADesc.AvgBps = (ui32_t) (ADesc.BlockAlign * ADesc.AudioSamplingRate.Quotient());
270   ui32_t FrameBufferSize = ASDCP::PCM::CalcFrameBufferSize(ADesc);
271   ADesc.ContainerDuration = data_len / FrameBufferSize;
272   ADesc.ChannelFormat = PCM::CF_NONE;
273 }
274
275 //
276 ASDCP::Result_t
277 ASDCP::AIFF::SimpleAIFFHeader::ReadFromFile(const Kumu::FileReader& InFile, ui32_t* data_start)
278 {
279   ui32_t read_count = 0;
280   ui32_t local_data_start = 0;
281   ASDCP::PCM::FrameBuffer TmpBuffer(Wav::MaxWavHeader);
282
283   if ( data_start == 0 )
284     data_start = &local_data_start;
285
286   Result_t result = InFile.Read(TmpBuffer.Data(), TmpBuffer.Capacity(), &read_count);
287
288   if ( ASDCP_SUCCESS(result) )
289     result = ReadFromBuffer(TmpBuffer.RoData(), read_count, data_start);
290
291     return result;
292 }
293
294 //
295 ASDCP::Result_t
296 ASDCP::AIFF::SimpleAIFFHeader::ReadFromBuffer(const byte_t* buf, ui32_t buf_len, ui32_t* data_start)
297 {
298   if ( buf_len < 32 )
299     return RESULT_SMALLBUF;
300
301   *data_start = 0;
302   const byte_t* p = buf;
303   const byte_t* end_p = p + buf_len;
304
305   fourcc test_FORM(p); p += 4;
306   if ( test_FORM != FCC_FORM )
307     {
308       //      DefaultLogSink().Debug("File does not begin with FORM header\n");
309       return RESULT_RAW_FORMAT;
310     }
311
312   ui32_t RIFF_len = KM_i32_BE(*(ui32_t*)p); p += 4;
313
314   fourcc test_AIFF(p); p += 4;
315   if ( test_AIFF != FCC_AIFF )
316     {
317       DefaultLogSink().Debug("File does not contain an AIFF header\n");
318       return RESULT_RAW_FORMAT;
319     }
320
321   fourcc test_fcc;
322
323   while ( p < end_p )
324     {
325       test_fcc = fourcc(p); p += 4;
326       ui32_t chunk_size = KM_i32_BE(*(ui32_t*)p); p += 4;
327
328       if ( test_fcc == FCC_COMM )
329         {
330           numChannels = KM_i16_BE(*(ui16_t*)p); p += 2;
331           numSampleFrames = KM_i32_BE(*(ui32_t*)p); p += 4;
332           sampleSize = KM_i16_BE(*(ui16_t*)p); p += 2;
333           memcpy(sampleRate, p, 10);
334           p += 10;
335         }
336       else if ( test_fcc == FCC_SSND )
337         {
338           if ( chunk_size > RIFF_len )
339             {
340               DefaultLogSink().Error("Chunk size %u larger than file: %u\n", chunk_size, RIFF_len);
341               return RESULT_RAW_FORMAT;
342             }
343
344           ui32_t offset = KM_i32_BE(*(ui32_t*)p); p += 4;
345           p += 4; // blockSize;
346
347           data_len = chunk_size - 8;
348           *data_start = (p - buf) + offset;
349           break;
350         }
351       else
352         {
353           p += chunk_size;
354         }
355     }
356
357   if ( *data_start == 0 ) // can't have no data!
358     {
359       DefaultLogSink().Error("No data chunk found, file contains no essence\n");
360       return RESULT_RAW_FORMAT;
361     }
362
363   return RESULT_OK;
364 }
365
366 ASDCP::RF64::SimpleRF64Header::SimpleRF64Header(ASDCP::PCM::AudioDescriptor& ADesc)
367 {
368   format = 1;
369   nchannels = ADesc.ChannelCount;
370   bitspersample = ADesc.QuantizationBits;
371   samplespersec = (ui32_t)ceil(ADesc.AudioSamplingRate.Quotient());
372   blockalign = nchannels * ((bitspersample + 7) / 8);
373   avgbps = samplespersec * blockalign;
374   data_len = ASDCP::PCM::CalcFrameBufferSize(ADesc) * ADesc.ContainerDuration;
375 }
376
377 //
378 void
379 ASDCP::RF64::SimpleRF64Header::FillADesc(ASDCP::PCM::AudioDescriptor& ADesc, ASDCP::Rational PictureRate) const
380 {
381   ADesc.EditRate = PictureRate;
382
383   ADesc.LinkedTrackID = 0;
384   ADesc.Locked = 0;
385   ADesc.ChannelCount = nchannels;
386   ADesc.AudioSamplingRate = Rational(samplespersec, 1);
387   ADesc.AvgBps = avgbps;
388   ADesc.BlockAlign = blockalign;
389   ADesc.QuantizationBits = bitspersample;
390   ui32_t FrameBufferSize = ASDCP::PCM::CalcFrameBufferSize(ADesc);
391   ADesc.ContainerDuration = data_len / FrameBufferSize;
392   ADesc.ChannelFormat = PCM::CF_NONE;
393 }
394
395 //
396 ASDCP::Result_t
397 ASDCP::RF64::SimpleRF64Header::WriteToFile(Kumu::FileWriter& OutFile) const
398 {
399   static ui32_t fmt_len =
400     sizeof(format)
401     + sizeof(nchannels)
402     + sizeof(samplespersec)
403     + sizeof(avgbps)
404     + sizeof(blockalign)
405     + sizeof(bitspersample);
406
407   ui32_t write_count = 0;
408   ui64_t RIFF_len = data_len + SimpleWavHeaderLength - 8;
409   DefaultLogSink().Debug("RIFF_len is %llu.\n", RIFF_len);
410   byte_t* tmp_header = NULL;
411   ui32_t header_len = 0;
412
413   if (RIFF_len > MAX_RIFF_LEN)
414   {
415     DefaultLogSink().Debug("Will write out an RF64 wave file.\n");
416     ui32_t data32_len = ((data_len < MAX_RIFF_LEN) ? data_len : MAX_RIFF_LEN);
417     ui64_t data64_len = ((data_len < MAX_RIFF_LEN) ? 0 : data_len);
418     static ui32_t ds64_len =
419             sizeof(RIFF_len)
420             + sizeof(data64_len)
421             + sizeof(SAMPLE_COUNT)
422             + sizeof(TABLE_LEN);
423
424     header_len = SIMPLE_RF64_HEADER_LEN;
425     tmp_header = new byte_t[header_len];
426     byte_t* p = tmp_header;
427     memcpy(p, &FCC_RF64, sizeof(fourcc)); p += 4;
428     *((ui32_t*)p) = KM_i32_LE(MAX_RIFF_LEN); p += 4;
429     memcpy(p, &Wav::FCC_WAVE, sizeof(fourcc)); p += 4;
430     memcpy(p, &FCC_ds64, sizeof(fourcc)); p += 4;
431     *((ui32_t*)p) = KM_i32_LE(ds64_len); p += 4;
432     *((ui64_t*)p) = KM_i64_LE(RIFF_len); p += 8;
433     *((ui64_t*)p) = KM_i64_LE(data64_len); p += 8;
434     *((ui64_t*)p) = KM_i64_LE(SAMPLE_COUNT); p += 8;
435     *((ui32_t*)p) = KM_i32_LE(TABLE_LEN); p += 4;
436     memcpy(p, &Wav::FCC_fmt_, sizeof(fourcc)); p += 4;
437     *((ui32_t*)p) = KM_i32_LE(fmt_len); p += 4;
438     *((ui16_t*)p) = KM_i16_LE(format); p += 2;
439     *((ui16_t*)p) = KM_i16_LE(nchannels); p += 2;
440     *((ui32_t*)p) = KM_i32_LE(samplespersec); p += 4;
441     *((ui32_t*)p) = KM_i32_LE(avgbps); p += 4;
442     *((ui16_t*)p) = KM_i16_LE(blockalign); p += 2;
443     *((ui16_t*)p) = KM_i16_LE(bitspersample); p += 2;
444     memcpy(p, &Wav::FCC_data, sizeof(fourcc)); p += 4;
445     *((ui32_t*)p) = KM_i32_LE(data32_len); p += 4;
446     write_count = (p - tmp_header);
447   }
448   else
449   {
450     DefaultLogSink().Debug("Will write out a regular wave file.\n");
451     header_len = SimpleWavHeaderLength;
452     tmp_header = new byte_t[header_len];
453     byte_t* p = tmp_header;
454     memcpy(p, &Wav::FCC_RIFF, sizeof(fourcc)); p += 4;
455     *((ui32_t*)p) = KM_i32_LE(RIFF_len); p += 4;
456     memcpy(p, &Wav::FCC_WAVE, sizeof(fourcc)); p += 4;
457     memcpy(p, &Wav::FCC_fmt_, sizeof(fourcc)); p += 4;
458     *((ui32_t*)p) = KM_i32_LE(fmt_len); p += 4;
459     *((ui16_t*)p) = KM_i16_LE(format); p += 2;
460     *((ui16_t*)p) = KM_i16_LE(nchannels); p += 2;
461     *((ui32_t*)p) = KM_i32_LE(samplespersec); p += 4;
462     *((ui32_t*)p) = KM_i32_LE(avgbps); p += 4;
463     *((ui16_t*)p) = KM_i16_LE(blockalign); p += 2;
464     *((ui16_t*)p) = KM_i16_LE(bitspersample); p += 2;
465     memcpy(p, &Wav::FCC_data, sizeof(fourcc)); p += 4;
466     *((ui32_t*)p) = KM_i32_LE(data_len); p += 4;
467     write_count = (p - tmp_header);
468   }
469   if (header_len != write_count)
470   {
471       DefaultLogSink().Warn("Expected to write %u bytes but wrote %u bytes for header.\n",
472                             header_len, write_count);
473   }
474   write_count = 0;
475   ASDCP::Result_t r = OutFile.Write(tmp_header, header_len, &write_count);
476   delete [] tmp_header;
477   return r;
478 }
479
480 //
481 ASDCP::Result_t
482 ASDCP::RF64::SimpleRF64Header::ReadFromFile(const Kumu::FileReader& InFile, ui32_t* data_start)
483 {
484   ui32_t read_count = 0;
485   ui32_t local_data_start = 0;
486   ASDCP::PCM::FrameBuffer TmpBuffer(Wav::MaxWavHeader);
487
488   if ( data_start == 0 )
489     data_start = &local_data_start;
490
491   Result_t result = InFile.Read(TmpBuffer.Data(), TmpBuffer.Capacity(), &read_count);
492
493   if ( ASDCP_SUCCESS(result) )
494     result = ReadFromBuffer(TmpBuffer.RoData(), read_count, data_start);
495   else
496     DefaultLogSink().Error("Failed to read %d bytes from file\n", Wav::MaxWavHeader);
497
498     return result;
499 }
500
501 ASDCP::Result_t
502 ASDCP::RF64::SimpleRF64Header::ReadFromBuffer(const byte_t* buf, ui32_t buf_len, ui32_t* data_start)
503 {
504     if ( buf_len < SIMPLE_RF64_HEADER_LEN )
505         return RESULT_SMALLBUF;
506
507     *data_start = 0;
508     const byte_t* p = buf;
509     const byte_t* end_p = p + buf_len;
510
511     fourcc test_RF64(p); p += 4;
512     if ( test_RF64 != FCC_RF64 )
513     {
514         DefaultLogSink().Debug("File does not begin with RF64 header\n");
515         return RESULT_RAW_FORMAT;
516     }
517
518     ui32_t tmp_len = KM_i32_LE(*(ui32_t*)p); p += 4;
519
520     fourcc test_WAVE(p); p += 4;
521     if ( test_WAVE != Wav::FCC_WAVE )
522     {
523         DefaultLogSink().Debug("File does not contain a WAVE header\n");
524         return RESULT_RAW_FORMAT;
525     }
526
527     fourcc test_ds64(p); p += 4;
528     if ( test_ds64 != FCC_ds64 )
529     {
530         DefaultLogSink().Debug("File does not contain a ds64 chunk\n");
531         return RESULT_RAW_FORMAT;
532     }
533     ui32_t ds64_len = KM_i32_LE(*(ui32_t*)p); p += 4;
534     ui64_t RIFF_len = ((tmp_len == MAX_RIFF_LEN) ? KM_i64_LE(*(ui64_t*)p) : tmp_len); p += 8;
535     data_len = KM_i64_LE(*(ui64_t*)p); p += 8;
536     p += (ds64_len - 16); // skip rest of ds64 chunk
537
538     fourcc test_fcc;
539
540     while ( p < end_p )
541     {
542         test_fcc = fourcc(p); p += 4;
543         ui32_t chunk_size = KM_i32_LE(*(ui32_t*)p); p += 4;
544
545         if ( test_fcc == Wav::FCC_data )
546         {
547             if ( chunk_size > RIFF_len )
548             {
549                 DefaultLogSink().Error("Chunk size %u larger than file: %u\n", chunk_size, RIFF_len);
550                 return RESULT_RAW_FORMAT;
551             }
552
553             if (chunk_size != MAX_RIFF_LEN)
554                 data_len = chunk_size;
555             *data_start = p - buf;
556             break;
557         }
558
559         if ( test_fcc == Wav::FCC_fmt_ )
560         {
561             ui16_t format = KM_i16_LE(*(ui16_t*)p); p += 2;
562
563             if ( format != Wav::WAVE_FORMAT_PCM && format != Wav::WAVE_FORMAT_EXTENSIBLE )
564             {
565                 DefaultLogSink().Error("Expecting uncompressed PCM data, got format type %hd\n", format);
566                 return RESULT_RAW_FORMAT;
567             }
568
569             nchannels = KM_i16_LE(*(ui16_t*)p); p += 2;
570             samplespersec = KM_i32_LE(*(ui32_t*)p); p += 4;
571             avgbps = KM_i32_LE(*(ui32_t*)p); p += 4;
572             blockalign = KM_i16_LE(*(ui16_t*)p); p += 2;
573             bitspersample = KM_i16_LE(*(ui16_t*)p); p += 2;
574             p += chunk_size - 16; // 16 is the number of bytes read in this block
575         }
576         else
577         {
578             p += chunk_size;
579         }
580     }
581
582     if ( *data_start == 0 ) // can't have no data!
583     {
584         DefaultLogSink().Error("No data chunk found, file contains no essence\n");
585         return RESULT_RAW_FORMAT;
586     }
587
588   return RESULT_OK;
589 }
590
591 //
592 // end Wav.cpp
593 //