ReadFileIntoString() modified to return OK when the file is empty
[asdcplib.git] / src / Wav.cpp
index 42e0d21f8791c9c2c5e8ae2f586723e96d244ab9..2ffadd404cecc360a8f5827db0e73d87b46ada14 100755 (executable)
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2005, John Hurst
+Copyright (c) 2005-2018, John Hurst
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
@@ -29,11 +29,13 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     \brief   Wave file common elements
 */
 
-#include <Wav.h>
+#include "Wav.h"
 #include <assert.h>
+#include <KM_log.h>
+using Kumu::DefaultLogSink;
 
 
-const ui32_t SimpleHeaderLength = 46;
+const ui32_t SimpleWavHeaderLength = 46;
 
 //
 ASDCP::Wav::SimpleWaveHeader::SimpleWaveHeader(ASDCP::PCM::AudioDescriptor& ADesc)
@@ -42,8 +44,8 @@ ASDCP::Wav::SimpleWaveHeader::SimpleWaveHeader(ASDCP::PCM::AudioDescriptor& ADes
   nchannels = ADesc.ChannelCount;
   bitspersample = ADesc.QuantizationBits;
   samplespersec = (ui32_t)ceil(ADesc.AudioSamplingRate.Quotient());
-  avgbps = samplespersec * nchannels * ((bitspersample + 7) / 8);
   blockalign = nchannels * ((bitspersample + 7) / 8);
+  avgbps = samplespersec * blockalign;
   cbsize = 0;    
   data_len = ASDCP::PCM::CalcFrameBufferSize(ADesc) * ADesc.ContainerDuration;
 }
@@ -52,7 +54,7 @@ ASDCP::Wav::SimpleWaveHeader::SimpleWaveHeader(ASDCP::PCM::AudioDescriptor& ADes
 void
 ASDCP::Wav::SimpleWaveHeader::FillADesc(ASDCP::PCM::AudioDescriptor& ADesc, ASDCP::Rational PictureRate) const
 {
-  ADesc.SampleRate = PictureRate;
+  ADesc.EditRate = PictureRate;
 
   ADesc.LinkedTrackID = 0;
   ADesc.Locked = 0;
@@ -63,15 +65,16 @@ ASDCP::Wav::SimpleWaveHeader::FillADesc(ASDCP::PCM::AudioDescriptor& ADesc, ASDC
   ADesc.QuantizationBits = bitspersample;
   ui32_t FrameBufferSize = ASDCP::PCM::CalcFrameBufferSize(ADesc);
   ADesc.ContainerDuration = data_len / FrameBufferSize;
+  ADesc.ChannelFormat = PCM::CF_NONE;
 }
 
 
 //
 ASDCP::Result_t
-ASDCP::Wav::SimpleWaveHeader::WriteToFile(ASDCP::FileWriter& OutFile) const
+ASDCP::Wav::SimpleWaveHeader::WriteToFile(Kumu::FileWriter& OutFile) const
 {
   ui32_t write_count;
-  byte_t tmp_header[SimpleHeaderLength];
+  byte_t tmp_header[SimpleWavHeaderLength];
   byte_t* p = tmp_header;
 
   static ui32_t fmt_len =
@@ -83,29 +86,29 @@ ASDCP::Wav::SimpleWaveHeader::WriteToFile(ASDCP::FileWriter& OutFile) const
     + sizeof(bitspersample)
     + sizeof(cbsize);
 
-  ui32_t RIFF_len = data_len + SimpleHeaderLength - 8;
+  ui32_t RIFF_len = data_len + SimpleWavHeaderLength - 8;
 
   memcpy(p, &FCC_RIFF, sizeof(fourcc)); p += 4;
-  *((ui32_t*)p) = ASDCP_i32_LE(RIFF_len); p += 4;
+  *((ui32_t*)p) = KM_i32_LE(RIFF_len); p += 4;
   memcpy(p, &FCC_WAVE, sizeof(fourcc)); p += 4;
   memcpy(p, &FCC_fmt_, sizeof(fourcc)); p += 4;
-  *((ui32_t*)p) = ASDCP_i32_LE(fmt_len); p += 4;
-  *((ui16_t*)p) = ASDCP_i16_LE(format); p += 2;
-  *((ui16_t*)p) = ASDCP_i16_LE(nchannels); p += 2;
-  *((ui32_t*)p) = ASDCP_i32_LE(samplespersec); p += 4;
-  *((ui32_t*)p) = ASDCP_i32_LE(avgbps); p += 4;
-  *((ui16_t*)p) = ASDCP_i16_LE(blockalign); p += 2;
-  *((ui16_t*)p) = ASDCP_i16_LE(bitspersample); p += 2;
-  *((ui16_t*)p) = ASDCP_i16_LE(cbsize); p += 2;
+  *((ui32_t*)p) = KM_i32_LE(fmt_len); p += 4;
+  *((ui16_t*)p) = KM_i16_LE(format); p += 2;
+  *((ui16_t*)p) = KM_i16_LE(nchannels); p += 2;
+  *((ui32_t*)p) = KM_i32_LE(samplespersec); p += 4;
+  *((ui32_t*)p) = KM_i32_LE(avgbps); p += 4;
+  *((ui16_t*)p) = KM_i16_LE(blockalign); p += 2;
+  *((ui16_t*)p) = KM_i16_LE(bitspersample); p += 2;
+  *((ui16_t*)p) = KM_i16_LE(cbsize); p += 2;
   memcpy(p, &FCC_data, sizeof(fourcc)); p += 4;
-  *((ui32_t*)p) = ASDCP_i32_LE(data_len); p += 4;
+  *((ui32_t*)p) = KM_i32_LE(data_len); p += 4;
 
-  return OutFile.Write(tmp_header, SimpleHeaderLength, &write_count);
+  return OutFile.Write(tmp_header, SimpleWavHeaderLength, &write_count);
 }
 
 //
 ASDCP::Result_t
-ASDCP::Wav::SimpleWaveHeader::ReadFromFile(const ASDCP::FileReader& InFile, ui32_t* data_start)
+ASDCP::Wav::SimpleWaveHeader::ReadFromFile(const Kumu::FileReader& InFile, ui32_t* data_start)
 {
   ui32_t read_count = 0;
   ui32_t local_data_start = 0;
@@ -125,7 +128,7 @@ ASDCP::Wav::SimpleWaveHeader::ReadFromFile(const ASDCP::FileReader& InFile, ui32
 ASDCP::Result_t
 ASDCP::Wav::SimpleWaveHeader::ReadFromBuffer(const byte_t* buf, ui32_t buf_len, ui32_t* data_start)
 {
-  if ( buf_len < SimpleHeaderLength )
+  if ( buf_len < SimpleWavHeaderLength )
     return RESULT_SMALLBUF;
 
   *data_start = 0;
@@ -135,16 +138,16 @@ ASDCP::Wav::SimpleWaveHeader::ReadFromBuffer(const byte_t* buf, ui32_t buf_len,
   fourcc test_RIFF(p); p += 4;
   if ( test_RIFF != FCC_RIFF )
     {
-      DefaultLogSink().Error("Files does not begin with RIFF header\n");      
+      //      DefaultLogSink().Debug("File does not begin with RIFF header\n");      
       return RESULT_RAW_FORMAT;
     }
 
-  ui32_t RIFF_len = ASDCP_i32_LE(*(ui32_t*)p); p += 4;
+  ui32_t RIFF_len = KM_i32_LE(*(ui32_t*)p); p += 4;
 
   fourcc test_WAVE(p); p += 4;
   if ( test_WAVE != FCC_WAVE )
     {
-      DefaultLogSink().Error("Files does not begin with WAVE header\n");
+      DefaultLogSink().Debug("File does not contain a WAVE header\n");
       return RESULT_RAW_FORMAT;
     }
 
@@ -153,13 +156,13 @@ ASDCP::Wav::SimpleWaveHeader::ReadFromBuffer(const byte_t* buf, ui32_t buf_len,
   while ( p < end_p )
     {
       test_fcc = fourcc(p); p += 4;
-      ui32_t chunk_size = ASDCP_i32_LE(*(ui32_t*)p); p += 4;
+      ui32_t chunk_size = KM_i32_LE(*(ui32_t*)p); p += 4;
 
       if ( test_fcc == FCC_data )
        {
          if ( chunk_size > RIFF_len )
            {
-             DefaultLogSink().Error("Chunk size %lu larger than file: %lu\n", chunk_size, RIFF_len);
+             DefaultLogSink().Error("Chunk size %u larger than file: %u\n", chunk_size, RIFF_len);
              return RESULT_RAW_FORMAT;
            }
 
@@ -170,20 +173,20 @@ ASDCP::Wav::SimpleWaveHeader::ReadFromBuffer(const byte_t* buf, ui32_t buf_len,
 
       if ( test_fcc == FCC_fmt_ )
        {
-         ui16_t format = ASDCP_i16_LE(*(ui16_t*)p); p += 2;
+         ui16_t format = KM_i16_LE(*(ui16_t*)p); p += 2;
 
-         if ( format != 1 )
+         if ( format != ASDCP_WAVE_FORMAT_PCM && format != ASDCP_WAVE_FORMAT_EXTENSIBLE )
            {
-             DefaultLogSink().Error("Expecting uncompressed essence, got format type %hu\n", format);
+             DefaultLogSink().Error("Expecting uncompressed PCM data, got format type %hd\n", format);
              return RESULT_RAW_FORMAT;
            }
 
-         nchannels = ASDCP_i16_LE(*(ui16_t*)p); p += 2;
-         samplespersec = ASDCP_i32_LE(*(ui32_t*)p); p += 4;
-         avgbps = ASDCP_i32_LE(*(ui32_t*)p); p += 4;
-         blockalign = ASDCP_i16_LE(*(ui16_t*)p); p += 2;
-         bitspersample = ASDCP_i16_LE(*(ui16_t*)p); p += 2;
-         p += chunk_size - 16;
+         nchannels = KM_i16_LE(*(ui16_t*)p); p += 2;
+         samplespersec = KM_i32_LE(*(ui32_t*)p); p += 4;
+         avgbps = KM_i32_LE(*(ui32_t*)p); p += 4;
+         blockalign = KM_i16_LE(*(ui16_t*)p); p += 2;
+         bitspersample = KM_i16_LE(*(ui16_t*)p); p += 2;
+         p += chunk_size - 16; // 16 is the number of bytes read in this block
        }
       else
        {
@@ -200,7 +203,397 @@ ASDCP::Wav::SimpleWaveHeader::ReadFromBuffer(const byte_t* buf, ui32_t buf_len,
   return RESULT_OK;
 }
 
+//------------------------------------------------------------------------------------------
+// conversion algorithms from http://www.borg.com/~jglatt/tech/aiff.htm
 
+//
+void
+Rat_to_extended(ASDCP::Rational rate, byte_t* buf)
+{
+  memset(buf, 0, 10);
+  ui32_t value = (ui32_t)ceil(rate.Quotient()); 
+  ui32_t exp = value;
+  exp >>= 1;
+  ui8_t i = 0;
+
+  for ( ; i < 32; i++ )
+    {
+      exp >>= 1;
+      if ( ! exp )
+       break;
+    }
+
+  *(buf+1) = i;
+
+   for ( i = 32; i != 0 ; i-- )
+     {
+       if ( value & 0x80000000 )
+        break;
+       value <<= 1;
+     }
+
+   *(ui32_t*)(buf+2) = KM_i32_BE(value);
+}
+
+//
+ASDCP::Rational
+extended_to_Rat(const byte_t* buf)
+{
+  ui32_t last = 0;
+  ui32_t mantissa = KM_i32_BE(*(ui32_t*)(buf+2));
+
+  byte_t exp = 30 - *(buf+1);
+
+  while ( exp-- )
+    {
+      last = mantissa;
+      mantissa >>= 1;
+    }
+
+  if ( last & 0x00000001 )
+    mantissa++;
+
+  return ASDCP::Rational(mantissa, 1);
+}
+
+//
+void
+ASDCP::AIFF::SimpleAIFFHeader::FillADesc(ASDCP::PCM::AudioDescriptor& ADesc, ASDCP::Rational PictureRate) const
+{
+  ADesc.EditRate = PictureRate;
+
+  ADesc.ChannelCount = numChannels;
+  ADesc.AudioSamplingRate = extended_to_Rat(sampleRate);
+  ADesc.QuantizationBits = sampleSize;
+  ADesc.BlockAlign = sampleSize / 8;
+  ADesc.AvgBps = (ui32_t) (ADesc.BlockAlign * ADesc.AudioSamplingRate.Quotient());
+  ui32_t FrameBufferSize = ASDCP::PCM::CalcFrameBufferSize(ADesc);
+  ADesc.ContainerDuration = data_len / FrameBufferSize;
+  ADesc.ChannelFormat = PCM::CF_NONE;
+}
+
+//
+ASDCP::Result_t
+ASDCP::AIFF::SimpleAIFFHeader::ReadFromFile(const Kumu::FileReader& InFile, ui32_t* data_start)
+{
+  ui32_t read_count = 0;
+  ui32_t local_data_start = 0;
+  ASDCP::PCM::FrameBuffer TmpBuffer(Wav::MaxWavHeader);
+
+  if ( data_start == 0 )
+    data_start = &local_data_start;
+
+  Result_t result = InFile.Read(TmpBuffer.Data(), TmpBuffer.Capacity(), &read_count);
+
+  if ( ASDCP_SUCCESS(result) )
+    result = ReadFromBuffer(TmpBuffer.RoData(), read_count, data_start);
+
+    return result;
+}
+
+//
+ASDCP::Result_t
+ASDCP::AIFF::SimpleAIFFHeader::ReadFromBuffer(const byte_t* buf, ui32_t buf_len, ui32_t* data_start)
+{
+  if ( buf_len < 32 )
+    return RESULT_SMALLBUF;
+
+  *data_start = 0;
+  const byte_t* p = buf;
+  const byte_t* end_p = p + buf_len;
+
+  fourcc test_FORM(p); p += 4;
+  if ( test_FORM != FCC_FORM )
+    {
+      //      DefaultLogSink().Debug("File does not begin with FORM header\n");
+      return RESULT_RAW_FORMAT;
+    }
+
+  ui32_t RIFF_len = KM_i32_BE(*(ui32_t*)p); p += 4;
+
+  fourcc test_AIFF(p); p += 4;
+  if ( test_AIFF != FCC_AIFF )
+    {
+      DefaultLogSink().Debug("File does not contain an AIFF header\n");
+      return RESULT_RAW_FORMAT;
+    }
+
+  fourcc test_fcc;
+
+  while ( p < end_p )
+    {
+      test_fcc = fourcc(p); p += 4;
+      ui32_t chunk_size = KM_i32_BE(*(ui32_t*)p); p += 4;
+
+      if ( test_fcc == FCC_COMM )
+       {
+         numChannels = KM_i16_BE(*(ui16_t*)p); p += 2;
+         numSampleFrames = KM_i32_BE(*(ui32_t*)p); p += 4;
+         sampleSize = KM_i16_BE(*(ui16_t*)p); p += 2;
+         memcpy(sampleRate, p, 10);
+         p += 10;
+       }
+      else if ( test_fcc == FCC_SSND )
+       {
+         if ( chunk_size > RIFF_len )
+            {
+              DefaultLogSink().Error("Chunk size %u larger than file: %u\n", chunk_size, RIFF_len);
+              return RESULT_RAW_FORMAT;
+            }
+
+         ui32_t offset = KM_i32_BE(*(ui32_t*)p); p += 4;
+         p += 4; // blockSize;
+
+         data_len = chunk_size - 8;
+         *data_start = (p - buf) + offset;
+         break;
+       }
+      else
+       {
+         p += chunk_size;
+       }
+    }
+
+  if ( *data_start == 0 ) // can't have no data!
+    {
+      DefaultLogSink().Error("No data chunk found, file contains no essence\n");
+      return RESULT_RAW_FORMAT;
+    }
+
+  return RESULT_OK;
+}
+
+ASDCP::RF64::SimpleRF64Header::SimpleRF64Header(ASDCP::PCM::AudioDescriptor& ADesc)
+{
+  format = 1;
+  nchannels = ADesc.ChannelCount;
+  bitspersample = ADesc.QuantizationBits;
+  samplespersec = (ui32_t)ceil(ADesc.AudioSamplingRate.Quotient());
+  blockalign = nchannels * ((bitspersample + 7) / 8);
+  avgbps = samplespersec * blockalign;
+  cbsize = 0;
+  data_len = static_cast<ui64_t>(ASDCP::PCM::CalcFrameBufferSize(ADesc)) * ADesc.ContainerDuration;
+}
+
+//
+void
+ASDCP::RF64::SimpleRF64Header::FillADesc(ASDCP::PCM::AudioDescriptor& ADesc, ASDCP::Rational PictureRate) const
+{
+  ADesc.EditRate = PictureRate;
+
+  ADesc.LinkedTrackID = 0;
+  ADesc.Locked = 0;
+  ADesc.ChannelCount = nchannels;
+  ADesc.AudioSamplingRate = Rational(samplespersec, 1);
+  ADesc.AvgBps = avgbps;
+  ADesc.BlockAlign = blockalign;
+  ADesc.QuantizationBits = bitspersample;
+  ui32_t FrameBufferSize = ASDCP::PCM::CalcFrameBufferSize(ADesc);
+  ADesc.ContainerDuration = data_len / FrameBufferSize;
+  ADesc.ChannelFormat = PCM::CF_NONE;
+}
+
+//
+ASDCP::Result_t
+ASDCP::RF64::SimpleRF64Header::WriteToFile(Kumu::FileWriter& OutFile) const
+{
+  static ui32_t fmt_len =
+    sizeof(format)
+    + sizeof(nchannels)
+    + sizeof(samplespersec)
+    + sizeof(avgbps)
+    + sizeof(blockalign)
+    + sizeof(bitspersample)
+    + sizeof(cbsize);
+
+  ui32_t write_count = 0;
+  ui64_t RIFF_len = data_len + SimpleWavHeaderLength - 8;
+  //  DefaultLogSink().Debug("RIFF_len is %llu.\n", RIFF_len);
+  byte_t* tmp_header = NULL;
+  ui32_t header_len = 0;
+
+  if (RIFF_len > MAX_RIFF_LEN)
+  {
+    DefaultLogSink().Debug("Will write out an RF64 wave file.\n");
+    ui32_t data32_len = ((data_len < MAX_RIFF_LEN) ? data_len : MAX_RIFF_LEN);
+    ui64_t data64_len = ((data_len < MAX_RIFF_LEN) ? 0 : data_len);
+    static ui32_t ds64_len =
+            sizeof(RIFF_len)
+            + sizeof(data64_len)
+            + sizeof(SAMPLE_COUNT)
+            + sizeof(TABLE_LEN);
+
+    header_len = SIMPLE_RF64_HEADER_LEN;
+    tmp_header = new byte_t[header_len];
+    byte_t* p = tmp_header;
+    memcpy(p, &FCC_RF64, sizeof(fourcc)); p += 4;
+    *((ui32_t*)p) = KM_i32_LE(MAX_RIFF_LEN); p += 4;
+    memcpy(p, &Wav::FCC_WAVE, sizeof(fourcc)); p += 4;
+    memcpy(p, &FCC_ds64, sizeof(fourcc)); p += 4;
+    *((ui32_t*)p) = KM_i32_LE(ds64_len); p += 4;
+    *((ui64_t*)p) = KM_i64_LE(RIFF_len); p += 8;
+    *((ui64_t*)p) = KM_i64_LE(data64_len); p += 8;
+    *((ui64_t*)p) = KM_i64_LE(SAMPLE_COUNT); p += 8;
+    *((ui32_t*)p) = KM_i32_LE(TABLE_LEN); p += 4;
+    memcpy(p, &Wav::FCC_fmt_, sizeof(fourcc)); p += 4;
+    *((ui32_t*)p) = KM_i32_LE(fmt_len); p += 4;
+    *((ui16_t*)p) = KM_i16_LE(format); p += 2;
+    *((ui16_t*)p) = KM_i16_LE(nchannels); p += 2;
+    *((ui32_t*)p) = KM_i32_LE(samplespersec); p += 4;
+    *((ui32_t*)p) = KM_i32_LE(avgbps); p += 4;
+    *((ui16_t*)p) = KM_i16_LE(blockalign); p += 2;
+    *((ui16_t*)p) = KM_i16_LE(bitspersample); p += 2;
+    *((ui16_t*)p) = KM_i16_LE(cbsize); p += 2;
+    memcpy(p, &Wav::FCC_data, sizeof(fourcc)); p += 4;
+    *((ui32_t*)p) = KM_i32_LE(data32_len); p += 4;
+    write_count = (p - tmp_header);
+  }
+  else
+  {
+    DefaultLogSink().Debug("Will write out a regular wave file.\n");
+    header_len = SimpleWavHeaderLength;
+    tmp_header = new byte_t[header_len];
+    byte_t* p = tmp_header;
+    memcpy(p, &Wav::FCC_RIFF, sizeof(fourcc)); p += 4;
+    *((ui32_t*)p) = KM_i32_LE(RIFF_len); p += 4;
+    memcpy(p, &Wav::FCC_WAVE, sizeof(fourcc)); p += 4;
+    memcpy(p, &Wav::FCC_fmt_, sizeof(fourcc)); p += 4;
+    *((ui32_t*)p) = KM_i32_LE(fmt_len); p += 4;
+    *((ui16_t*)p) = KM_i16_LE(format); p += 2;
+    *((ui16_t*)p) = KM_i16_LE(nchannels); p += 2;
+    *((ui32_t*)p) = KM_i32_LE(samplespersec); p += 4;
+    *((ui32_t*)p) = KM_i32_LE(avgbps); p += 4;
+    *((ui16_t*)p) = KM_i16_LE(blockalign); p += 2;
+    *((ui16_t*)p) = KM_i16_LE(bitspersample); p += 2;
+    *((ui16_t*)p) = KM_i16_LE(cbsize); p += 2;
+    memcpy(p, &Wav::FCC_data, sizeof(fourcc)); p += 4;
+    *((ui32_t*)p) = KM_i32_LE(data_len); p += 4;
+    write_count = (p - tmp_header);
+  }
+  if (header_len != write_count)
+  {
+      DefaultLogSink().Warn("Expected to write %u bytes but wrote %u bytes for header.\n",
+                            header_len, write_count);
+  }
+  write_count = 0;
+  ASDCP::Result_t r = OutFile.Write(tmp_header, header_len, &write_count);
+  delete [] tmp_header;
+  return r;
+}
+
+//
+ASDCP::Result_t
+ASDCP::RF64::SimpleRF64Header::ReadFromFile(const Kumu::FileReader& InFile, ui32_t* data_start)
+{
+  ui32_t read_count = 0;
+  ui32_t local_data_start = 0;
+  ASDCP::PCM::FrameBuffer TmpBuffer(Wav::MaxWavHeader);
+
+  if ( data_start == 0 )
+    data_start = &local_data_start;
+
+  Result_t result = InFile.Read(TmpBuffer.Data(), TmpBuffer.Capacity(), &read_count);
+
+  if ( ASDCP_SUCCESS(result) )
+    result = ReadFromBuffer(TmpBuffer.RoData(), read_count, data_start);
+  else
+    DefaultLogSink().Error("Failed to read %d bytes from file\n", Wav::MaxWavHeader);
+
+    return result;
+}
+
+ASDCP::Result_t
+ASDCP::RF64::SimpleRF64Header::ReadFromBuffer(const byte_t* buf, ui32_t buf_len, ui32_t* data_start)
+{
+    if ( buf_len < SIMPLE_RF64_HEADER_LEN )
+        return RESULT_SMALLBUF;
+
+    *data_start = 0;
+    const byte_t* p = buf;
+    const byte_t* end_p = p + buf_len;
+
+    fourcc test_RF64(p); p += 4;
+    if ( test_RF64 != FCC_RF64 )
+    {
+        DefaultLogSink().Debug("File does not begin with RF64 header\n");
+        return RESULT_RAW_FORMAT;
+    }
+
+    ui32_t tmp_len = KM_i32_LE(*(ui32_t*)p); p += 4;
+
+    fourcc test_WAVE(p); p += 4;
+    if ( test_WAVE != Wav::FCC_WAVE )
+    {
+        DefaultLogSink().Debug("File does not contain a WAVE header\n");
+        return RESULT_RAW_FORMAT;
+    }
+
+    fourcc test_ds64(p); p += 4;
+    if ( test_ds64 != FCC_ds64 )
+    {
+        DefaultLogSink().Debug("File does not contain a ds64 chunk\n");
+        return RESULT_RAW_FORMAT;
+    }
+    ui32_t ds64_len = KM_i32_LE(*(ui32_t*)p); p += 4;
+    ui64_t RIFF_len = ((tmp_len == MAX_RIFF_LEN) ? KM_i64_LE(*(ui64_t*)p) : tmp_len); p += 8;
+    data_len = KM_i64_LE(*(ui64_t*)p); p += 8;
+    p += (ds64_len - 16); // skip rest of ds64 chunk
+
+    fourcc test_fcc;
+
+    while ( p < end_p )
+    {
+        test_fcc = fourcc(p); p += 4;
+        ui32_t chunk_size = KM_i32_LE(*(ui32_t*)p); p += 4;
+
+        if ( test_fcc == Wav::FCC_data )
+        {
+            if ( chunk_size != MAX_RIFF_LEN )
+             {
+               if ( chunk_size > RIFF_len )
+                 {
+                   DefaultLogSink().Error("Chunk size %u larger than file: %u\n", chunk_size, RIFF_len);
+                   return RESULT_RAW_FORMAT;
+                 }
+
+                data_len = chunk_size;
+             }
+            *data_start = p - buf;
+            break;
+        }
+
+        if ( test_fcc == Wav::FCC_fmt_ )
+        {
+            ui16_t format = KM_i16_LE(*(ui16_t*)p); p += 2;
+
+            if ( format != Wav::ASDCP_WAVE_FORMAT_PCM && format != Wav::ASDCP_WAVE_FORMAT_EXTENSIBLE )
+            {
+                DefaultLogSink().Error("Expecting uncompressed PCM data, got format type %hd\n", format);
+                return RESULT_RAW_FORMAT;
+            }
+
+            nchannels = KM_i16_LE(*(ui16_t*)p); p += 2;
+            samplespersec = KM_i32_LE(*(ui32_t*)p); p += 4;
+            avgbps = KM_i32_LE(*(ui32_t*)p); p += 4;
+            blockalign = KM_i16_LE(*(ui16_t*)p); p += 2;
+            bitspersample = KM_i16_LE(*(ui16_t*)p); p += 2;
+            p += chunk_size - 16; // 16 is the number of bytes read in this block
+        }
+        else
+        {
+            p += chunk_size;
+        }
+    }
+
+    if ( *data_start == 0 ) // can't have no data!
+    {
+        DefaultLogSink().Error("No data chunk found, file contains no essence\n");
+        return RESULT_RAW_FORMAT;
+    }
+
+  return RESULT_OK;
+}
 
 //
 // end Wav.cpp