Allow fractional frames per second when computing Time from frames.
[libdcp.git] / asdcplib / src / PCM_Parser.cpp
1 /*
2 Copyright (c) 2004-2011, 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    PCM_Parser.cpp
28     \version $Id: PCM_Parser.cpp,v 1.7 2011/05/13 01:50:19 jhurst Exp $
29     \brief   AS-DCP library, PCM raw essence reader implementation
30 */
31
32 #include <Wav.h>
33 #include <assert.h>
34 #include <KM_log.h>
35 using Kumu::DefaultLogSink;
36
37 using namespace ASDCP;
38 using namespace ASDCP::PCM;
39 using namespace ASDCP::Wav;
40
41
42 //------------------------------------------------------------------------------------------
43
44 //
45 class ASDCP::PCM::WAVParser::h__WAVParser
46 {
47   Kumu::FileReader m_FileReader;
48   bool             m_EOF;
49   ui32_t           m_DataStart;
50   ui32_t           m_DataLength;
51   ui32_t           m_ReadCount;
52   ui32_t           m_FrameBufferSize;
53   ui32_t           m_FramesRead;
54   Rational         m_PictureRate;
55
56   ASDCP_NO_COPY_CONSTRUCT(h__WAVParser);
57
58 public:
59   AudioDescriptor  m_ADesc;
60
61
62   h__WAVParser() :
63     m_EOF(false), m_DataStart(0), m_DataLength(0), m_ReadCount(0),
64     m_FrameBufferSize(0), m_FramesRead(0) {}
65
66   ~h__WAVParser()
67   {
68     Close();
69    }
70
71   Result_t OpenRead(const char* filename, const Rational& PictureRate);
72   void     Close();
73   void     Reset();
74   Result_t ReadFrame(FrameBuffer&);
75 };
76
77
78 //
79 void
80 ASDCP::PCM::WAVParser::h__WAVParser::Close()
81 {
82   m_FileReader.Close();
83 }
84
85 //
86 void
87 ASDCP::PCM::WAVParser::h__WAVParser::Reset()
88 {
89   m_FileReader.Seek(m_DataStart);
90   m_FramesRead = 0;
91   m_ReadCount = 0;
92 }
93
94 //
95 ASDCP::Result_t
96 ASDCP::PCM::WAVParser::h__WAVParser::OpenRead(const char* filename, const Rational& PictureRate)
97 {
98   ASDCP_TEST_NULL_STR(filename);
99
100   Result_t result = m_FileReader.OpenRead(filename);
101
102   if ( ASDCP_SUCCESS(result) )
103     {
104       SimpleWaveHeader WavHeader;
105       result = WavHeader.ReadFromFile(m_FileReader, &m_DataStart);
106   
107       if ( ASDCP_SUCCESS(result) )
108         {
109           WavHeader.FillADesc(m_ADesc, PictureRate);
110           m_FrameBufferSize = ASDCP::PCM::CalcFrameBufferSize(m_ADesc);
111           m_DataLength = WavHeader.data_len;
112           m_ADesc.ContainerDuration = m_DataLength / m_FrameBufferSize;
113           m_ADesc.ChannelFormat = PCM::CF_NONE;
114           Reset();
115         }
116       else
117         {
118           ASDCP::AIFF::SimpleAIFFHeader AIFFHeader;
119           m_FileReader.Seek(0);
120
121           result = AIFFHeader.ReadFromFile(m_FileReader, &m_DataStart);
122
123           if ( ASDCP_SUCCESS(result) )
124             {
125               AIFFHeader.FillADesc(m_ADesc, PictureRate);
126               m_FrameBufferSize = ASDCP::PCM::CalcFrameBufferSize(m_ADesc);
127               m_DataLength = AIFFHeader.data_len;
128               m_ADesc.ContainerDuration = m_DataLength / m_FrameBufferSize;
129               m_ADesc.ChannelFormat = PCM::CF_NONE;
130               Reset();
131             }
132         }
133     }
134
135   return result;
136 }
137
138 //
139 ASDCP::Result_t
140 ASDCP::PCM::WAVParser::h__WAVParser::ReadFrame(FrameBuffer& FB)
141 {
142   FB.Size(0);
143
144   if ( m_EOF || m_ReadCount >= m_DataLength )
145     return RESULT_ENDOFFILE;
146
147   if ( FB.Capacity() < m_FrameBufferSize )
148     {
149       DefaultLogSink().Error("FrameBuf.Capacity: %u FrameLength: %u\n",
150                              FB.Capacity(), m_FrameBufferSize);
151       return RESULT_SMALLBUF;
152     }
153
154   ui32_t read_count = 0;
155   Result_t result = m_FileReader.Read(FB.Data(), m_FrameBufferSize, &read_count);
156
157   if ( result == RESULT_ENDOFFILE )
158     {
159       m_EOF = true;
160
161       if ( read_count > 0 )
162         result = RESULT_OK;
163     }
164
165   if ( ASDCP_SUCCESS(result) )
166     {
167       m_ReadCount += read_count;
168       FB.Size(read_count);
169       FB.FrameNumber(m_FramesRead++);
170     }
171
172   return result;
173 }
174
175
176 //------------------------------------------------------------------------------------------
177
178 ASDCP::PCM::WAVParser::WAVParser()
179 {
180 }
181
182 ASDCP::PCM::WAVParser::~WAVParser()
183 {
184 }
185
186 // Opens the stream for reading, parses enough data to provide a complete
187 // set of stream metadata for the MXFWriter below.
188 ASDCP::Result_t
189 ASDCP::PCM::WAVParser::OpenRead(const char* filename, const Rational& PictureRate) const
190 {
191   const_cast<ASDCP::PCM::WAVParser*>(this)->m_Parser = new h__WAVParser;
192
193   Result_t result = m_Parser->OpenRead(filename, PictureRate);
194
195   if ( ASDCP_FAILURE(result) )
196     const_cast<ASDCP::PCM::WAVParser*>(this)->m_Parser.release();
197
198   return result;
199 }
200
201 // Rewinds the stream to the beginning.
202 ASDCP::Result_t
203 ASDCP::PCM::WAVParser::Reset() const
204 {
205   if ( m_Parser.empty() )
206     return RESULT_INIT;
207
208   m_Parser->Reset();
209   return RESULT_OK;
210 }
211
212 // Places a frame of data in the frame buffer. Fails if the buffer is too small
213 // or the stream is empty.
214 ASDCP::Result_t
215 ASDCP::PCM::WAVParser::ReadFrame(FrameBuffer& FB) const
216 {
217   if ( m_Parser.empty() )
218     return RESULT_INIT;
219
220   return m_Parser->ReadFrame(FB);
221 }
222
223 ASDCP::Result_t
224 ASDCP::PCM::WAVParser::FillAudioDescriptor(AudioDescriptor& ADesc) const
225 {
226   if ( m_Parser.empty() )
227     return RESULT_INIT;
228
229   ADesc = m_Parser->m_ADesc;
230   return RESULT_OK;
231 }
232
233
234 //
235 // end PCM_Parser.cpp
236 //