tweaks - AbgBps, stereo image UL
[asdcplib.git] / src / Wav.cpp
1 /*
2 Copyright (c) 2005-2006, 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 / 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.SampleRate = 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 }
69
70
71 //
72 ASDCP::Result_t
73 ASDCP::Wav::SimpleWaveHeader::WriteToFile(Kumu::FileWriter& OutFile) const
74 {
75   ui32_t write_count;
76   byte_t tmp_header[SimpleWavHeaderLength];
77   byte_t* p = tmp_header;
78
79   static ui32_t fmt_len =
80     sizeof(format)
81     + sizeof(nchannels)
82     + sizeof(samplespersec)
83     + sizeof(avgbps)
84     + sizeof(blockalign)
85     + sizeof(bitspersample)
86     + sizeof(cbsize);
87
88   ui32_t RIFF_len = data_len + SimpleWavHeaderLength - 8;
89
90   memcpy(p, &FCC_RIFF, sizeof(fourcc)); p += 4;
91   *((ui32_t*)p) = KM_i32_LE(RIFF_len); p += 4;
92   memcpy(p, &FCC_WAVE, sizeof(fourcc)); p += 4;
93   memcpy(p, &FCC_fmt_, sizeof(fourcc)); p += 4;
94   *((ui32_t*)p) = KM_i32_LE(fmt_len); p += 4;
95   *((ui16_t*)p) = KM_i16_LE(format); p += 2;
96   *((ui16_t*)p) = KM_i16_LE(nchannels); p += 2;
97   *((ui32_t*)p) = KM_i32_LE(samplespersec); p += 4;
98   *((ui32_t*)p) = KM_i32_LE(avgbps); p += 4;
99   *((ui16_t*)p) = KM_i16_LE(blockalign); p += 2;
100   *((ui16_t*)p) = KM_i16_LE(bitspersample); p += 2;
101   *((ui16_t*)p) = KM_i16_LE(cbsize); p += 2;
102   memcpy(p, &FCC_data, sizeof(fourcc)); p += 4;
103   *((ui32_t*)p) = KM_i32_LE(data_len); p += 4;
104
105   return OutFile.Write(tmp_header, SimpleWavHeaderLength, &write_count);
106 }
107
108 //
109 ASDCP::Result_t
110 ASDCP::Wav::SimpleWaveHeader::ReadFromFile(const Kumu::FileReader& InFile, ui32_t* data_start)
111 {
112   ui32_t read_count = 0;
113   ui32_t local_data_start = 0;
114   ASDCP::PCM::FrameBuffer TmpBuffer(MaxWavHeader);
115
116   if ( data_start == 0 )
117     data_start = &local_data_start;
118
119   Result_t result = InFile.Read(TmpBuffer.Data(), TmpBuffer.Capacity(), &read_count);
120
121   if ( ASDCP_SUCCESS(result) )
122     result = ReadFromBuffer(TmpBuffer.RoData(), read_count, data_start);
123
124     return result;
125 }
126
127 ASDCP::Result_t
128 ASDCP::Wav::SimpleWaveHeader::ReadFromBuffer(const byte_t* buf, ui32_t buf_len, ui32_t* data_start)
129 {
130   if ( buf_len < SimpleWavHeaderLength )
131     return RESULT_SMALLBUF;
132
133   *data_start = 0;
134   const byte_t* p = buf;
135   const byte_t* end_p = p + buf_len;
136
137   fourcc test_RIFF(p); p += 4;
138   if ( test_RIFF != FCC_RIFF )
139     {
140       DefaultLogSink().Debug("File does not begin with RIFF header\n");      
141       return RESULT_RAW_FORMAT;
142     }
143
144   ui32_t RIFF_len = KM_i32_LE(*(ui32_t*)p); p += 4;
145
146   fourcc test_WAVE(p); p += 4;
147   if ( test_WAVE != FCC_WAVE )
148     {
149       DefaultLogSink().Debug("File does not contain a WAVE header\n");
150       return RESULT_RAW_FORMAT;
151     }
152
153   fourcc test_fcc;
154
155   while ( p < end_p )
156     {
157       test_fcc = fourcc(p); p += 4;
158       ui32_t chunk_size = KM_i32_LE(*(ui32_t*)p); p += 4;
159
160       if ( test_fcc == FCC_data )
161         {
162           if ( chunk_size > RIFF_len )
163             {
164               DefaultLogSink().Error("Chunk size %u larger than file: %u\n", chunk_size, RIFF_len);
165               return RESULT_RAW_FORMAT;
166             }
167
168           data_len = chunk_size;
169           *data_start = p - buf;
170           break;
171         }
172
173       if ( test_fcc == FCC_fmt_ )
174         {
175           ui16_t format = KM_i16_LE(*(ui16_t*)p); p += 2;
176
177           if ( format != 1 )
178             {
179               DefaultLogSink().Error("Expecting uncompressed essence, got format type %hu\n", format);
180               return RESULT_RAW_FORMAT;
181             }
182
183           nchannels = KM_i16_LE(*(ui16_t*)p); p += 2;
184           samplespersec = KM_i32_LE(*(ui32_t*)p); p += 4;
185           avgbps = KM_i32_LE(*(ui32_t*)p); p += 4;
186           blockalign = KM_i16_LE(*(ui16_t*)p); p += 2;
187           bitspersample = KM_i16_LE(*(ui16_t*)p); p += 2;
188           p += chunk_size - 16;
189         }
190       else
191         {
192           p += chunk_size;
193         }
194     }
195
196   if ( *data_start == 0 ) // can't have no data!
197     {
198       DefaultLogSink().Error("No data chunk found, file contains no essence\n");
199       return RESULT_RAW_FORMAT;
200     }
201
202   return RESULT_OK;
203 }
204
205 //------------------------------------------------------------------------------------------
206 // conversion algorithms from http://www.borg.com/~jglatt/tech/aiff.htm
207
208 //
209 void
210 Rat_to_extended(ASDCP::Rational rate, byte_t* buf)
211 {
212   memset(buf, 0, 10);
213   ui32_t value = (ui32_t)ceil(rate.Quotient()); 
214   ui32_t exp = value;
215   exp >>= 1;
216   ui8_t i = 0;
217
218   for ( ; i < 32; i++ )
219     {
220       exp >>= 1;
221       if ( ! exp )
222         break;
223     }
224
225   *(buf+1) = i;
226
227    for ( i = 32; i != 0 ; i-- )
228      {
229        if ( value & 0x80000000 )
230          break;
231        value <<= 1;
232      }
233
234    *(ui32_t*)(buf+2) = KM_i32_BE(value);
235 }
236
237 //
238 ASDCP::Rational
239 extended_to_Rat(const byte_t* buf)
240 {
241   ui32_t last = 0;
242   ui32_t mantissa = KM_i32_BE(*(ui32_t*)(buf+2));
243
244   byte_t exp = 30 - *(buf+1);
245
246   while ( exp-- )
247     {
248       last = mantissa;
249       mantissa >>= 1;
250     }
251
252   if ( last & 0x00000001 )
253     mantissa++;
254
255   return ASDCP::Rational(mantissa, 1);
256 }
257
258 //
259 void
260 ASDCP::AIFF::SimpleAIFFHeader::FillADesc(ASDCP::PCM::AudioDescriptor& ADesc, ASDCP::Rational PictureRate) const
261 {
262   ADesc.SampleRate = PictureRate;
263
264   ADesc.ChannelCount = numChannels;
265   ADesc.AudioSamplingRate = extended_to_Rat(sampleRate);
266   ADesc.QuantizationBits = sampleSize;
267   ADesc.BlockAlign = sampleSize / 8;
268   ADesc.AvgBps = ADesc.BlockAlign * ADesc.AudioSamplingRate.Quotient();
269   ui32_t FrameBufferSize = ASDCP::PCM::CalcFrameBufferSize(ADesc);
270   ADesc.ContainerDuration = data_len / FrameBufferSize;
271 }
272
273 //
274 ASDCP::Result_t
275 ASDCP::AIFF::SimpleAIFFHeader::ReadFromFile(const Kumu::FileReader& InFile, ui32_t* data_start)
276 {
277   ui32_t read_count = 0;
278   ui32_t local_data_start = 0;
279   ASDCP::PCM::FrameBuffer TmpBuffer(Wav::MaxWavHeader);
280
281   if ( data_start == 0 )
282     data_start = &local_data_start;
283
284   Result_t result = InFile.Read(TmpBuffer.Data(), TmpBuffer.Capacity(), &read_count);
285
286   if ( ASDCP_SUCCESS(result) )
287     result = ReadFromBuffer(TmpBuffer.RoData(), read_count, data_start);
288
289     return result;
290 }
291
292 //
293 ASDCP::Result_t
294 ASDCP::AIFF::SimpleAIFFHeader::ReadFromBuffer(const byte_t* buf, ui32_t buf_len, ui32_t* data_start)
295 {
296   if ( buf_len < 32 )
297     return RESULT_SMALLBUF;
298
299   *data_start = 0;
300   const byte_t* p = buf;
301   const byte_t* end_p = p + buf_len;
302
303   fourcc test_FORM(p); p += 4;
304   if ( test_FORM != FCC_FORM )
305     {
306       DefaultLogSink().Debug("File does not begin with FORM header\n");
307       return RESULT_RAW_FORMAT;
308     }
309
310   ui32_t RIFF_len = KM_i32_BE(*(ui32_t*)p); p += 4;
311
312   fourcc test_AIFF(p); p += 4;
313   if ( test_AIFF != FCC_AIFF )
314     {
315       DefaultLogSink().Debug("File does not contain an AIFF header\n");
316       return RESULT_RAW_FORMAT;
317     }
318
319   fourcc test_fcc;
320
321   while ( p < end_p )
322     {
323       test_fcc = fourcc(p); p += 4;
324       ui32_t chunk_size = KM_i32_BE(*(ui32_t*)p); p += 4;
325
326       if ( test_fcc == FCC_COMM )
327         {
328           numChannels = KM_i16_BE(*(ui16_t*)p); p += 2;
329           numSampleFrames = KM_i32_BE(*(ui32_t*)p); p += 4;
330           sampleSize = KM_i16_BE(*(ui16_t*)p); p += 2;
331           memcpy(sampleRate, p, 10);
332           p += 10;
333         }
334       else if ( test_fcc == FCC_SSND )
335         {
336           if ( chunk_size > RIFF_len )
337             {
338               DefaultLogSink().Error("Chunk size %u larger than file: %u\n", chunk_size, RIFF_len);
339               return RESULT_RAW_FORMAT;
340             }
341
342           ui32_t offset = KM_i32_BE(*(ui32_t*)p); p += 4;
343           p += 4; // blockSize;
344
345           data_len = chunk_size - 8;
346           *data_start = (p - buf) + offset;
347           break;
348         }
349       else
350         {
351           p += chunk_size;
352         }
353     }
354
355   if ( *data_start == 0 ) // can't have no data!
356     {
357       DefaultLogSink().Error("No data chunk found, file contains no essence\n");
358       return RESULT_RAW_FORMAT;
359     }
360
361   return RESULT_OK;
362 }
363
364
365
366 //
367 // end Wav.cpp
368 //