Fix a type-punning warning.
[asdcplib-cth.git] / src / AtmosSyncChannel_Mixer.cpp
1 /*
2 Copyright (c) 2013-2014, 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    AtmosSyncChannel_Mixer.cpp
28     \version $Id: AtmosSyncChannel_Mixer.cpp,v 1.3 2014/09/22 16:17:05 jhurst Exp $
29     \brief   Read WAV files(s), multiplex multiple PCM frame buffers including Atmos Sync into one
30 */
31
32 #include <AtmosSyncChannel_Mixer.h>
33
34 #include <algorithm>
35
36 #include <AS_DCP.h>
37 #include <KM_log.h>
38 #include <PCMDataProviders.h>
39
40 using namespace ASDCP;
41 using namespace Kumu;
42
43 //
44 ASDCP::AtmosSyncChannelMixer::AtmosSyncChannelMixer(const byte_t * trackUUID)
45     : m_inputs(), m_outputs(), m_trackUUID(), m_ADesc(), m_ChannelCount(0), m_FramesRead(0)
46 {
47   ::memcpy(m_trackUUID, trackUUID, UUIDlen);
48 }
49
50 ASDCP::AtmosSyncChannelMixer::~AtmosSyncChannelMixer()
51 {
52   clear();
53 }
54
55 void
56 ASDCP::AtmosSyncChannelMixer::clear()
57 {
58   m_outputs.clear();
59   std::for_each(m_inputs.begin(), m_inputs.end(), delete_input());
60   m_inputs.clear();
61 }
62
63 //
64 Result_t
65 ASDCP::AtmosSyncChannelMixer::OpenRead(ui32_t argc, const char** argv, const Rational& PictureRate)
66 {
67   ASDCP_TEST_NULL(argv);
68   PathList_t TmpFileList;
69
70   for ( ui32_t i = 0; i < argc; ++i )
71     {
72       ASDCP_TEST_NULL_STR(argv[i]);
73       TmpFileList.push_back(argv[i]);
74     }
75
76   return OpenRead(TmpFileList, PictureRate);
77 }
78
79 //
80 Result_t
81 ASDCP::AtmosSyncChannelMixer::OpenRead(const Kumu::PathList_t& argv, const Rational& PictureRate)
82 {
83   Result_t result = RESULT_OK;
84   PathList_t::iterator fi;
85   Kumu::PathList_t file_list;
86   PCM::AudioDescriptor tmpDesc;
87
88   if ( argv.size() == 1 && PathIsDirectory(argv.front()) )
89   {
90     DirScanner Dir;
91     char name_buf[MaxFilePath];
92     result = Dir.Open(argv.front().c_str());
93
94     if ( KM_SUCCESS(result) )
95       result = Dir.GetNext(name_buf);
96
97     while ( KM_SUCCESS(result) )
98         {
99           if ( name_buf[0] != '.' ) // no hidden files
100       {
101         std::string tmp_path = argv.front() + "/" + name_buf;
102         file_list.push_back(tmp_path);
103       }
104
105           result = Dir.GetNext(name_buf);
106         }
107
108     if ( result == RESULT_ENDOFFILE )
109         {
110           result = RESULT_OK;
111           file_list.sort();
112         }
113   }
114   else
115   {
116     file_list = argv;
117   }
118
119   for ( fi = file_list.begin(); KM_SUCCESS(result) && fi != file_list.end(); ++fi )
120   {
121     result = OpenRead(*fi, PictureRate);
122   }
123
124   if ( ASDCP_SUCCESS(result) && (m_ChannelCount < ATMOS::SYNC_CHANNEL))
125   {
126     // atmos sync channel has not been added
127     result = MixInSilenceChannels();
128     if ( ASDCP_SUCCESS(result) )
129       result = MixInAtmosSyncChannel();
130   }
131
132   if ( ASDCP_SUCCESS(result) )
133   {
134     m_ADesc.ChannelCount = m_ChannelCount;
135     m_ADesc.AvgBps = (ui32_t)(ceil(m_ADesc.AudioSamplingRate.Quotient()) * m_ADesc.BlockAlign);
136   }
137   else
138   {
139     clear();
140   }
141
142   return result;
143 }
144
145 //
146 Result_t
147 ASDCP::AtmosSyncChannelMixer::OpenRead(const std::string& file, const Rational& PictureRate)
148 {
149   Result_t result = RESULT_OK;
150   PCM::AudioDescriptor tmpDesc;
151   ui32_t numChannels = 0;
152   mem_ptr<WAVDataProvider> I = new WAVDataProvider;
153   result = I->OpenRead(file.c_str(), PictureRate);
154
155   if ( ASDCP_SUCCESS(result))
156   {
157     result = I->FillAudioDescriptor(tmpDesc);
158   }
159
160   if ( ASDCP_SUCCESS(result) )
161   {
162
163     if ( m_ChannelCount == 0 )
164     {
165       m_ADesc = tmpDesc;
166     }
167     else
168     {
169
170       if ( tmpDesc.AudioSamplingRate != m_ADesc.AudioSamplingRate )
171       {
172         DefaultLogSink().Error("AudioSamplingRate mismatch in PCM parser list.");
173         return RESULT_FORMAT;
174       }
175
176       if ( tmpDesc.QuantizationBits  != m_ADesc.QuantizationBits )
177       {
178         DefaultLogSink().Error("QuantizationBits mismatch in PCM parser list.");
179         return RESULT_FORMAT;
180       }
181
182       if ( tmpDesc.ContainerDuration < m_ADesc.ContainerDuration )
183         m_ADesc.ContainerDuration = tmpDesc.ContainerDuration;
184
185       m_ADesc.BlockAlign += tmpDesc.BlockAlign;
186     }
187   }
188
189
190   if ( ASDCP_SUCCESS(result) )
191   {
192     numChannels = tmpDesc.ChannelCount; // default to all channels
193     if ((m_ChannelCount < ATMOS::SYNC_CHANNEL) && (m_ChannelCount + numChannels) > (ATMOS::SYNC_CHANNEL - 1))
194     {
195       // need to insert an atmos channel between the channels of this file.
196       numChannels = ATMOS::SYNC_CHANNEL - m_ChannelCount - 1;
197       m_outputs.push_back(std::make_pair(numChannels, I.get()));
198       m_ChannelCount += numChannels;
199       MixInAtmosSyncChannel();
200       numChannels = tmpDesc.ChannelCount - numChannels;
201     }
202     m_outputs.push_back(std::make_pair(numChannels, I.get()));
203     m_inputs.push_back(I);
204     I.release();
205     m_ChannelCount += numChannels;
206   }
207   return result;
208 }
209
210 Result_t
211 ASDCP::AtmosSyncChannelMixer::MixInSilenceChannels()
212 {
213   Result_t result = RESULT_OK;
214   PCM::AudioDescriptor tmpDesc;
215   ui32_t numSilenceChannels = ATMOS::SYNC_CHANNEL - m_ChannelCount - 1;
216   if (numSilenceChannels > 0)
217   {
218     mem_ptr<SilenceDataProvider> I = new SilenceDataProvider(numSilenceChannels,
219                                                              m_ADesc.QuantizationBits,
220                                                              m_ADesc.AudioSamplingRate.Numerator,
221                                                              m_ADesc.EditRate);
222     result = I->FillAudioDescriptor(tmpDesc);
223     if ( ASDCP_SUCCESS(result) )
224     {
225       m_ADesc.BlockAlign += tmpDesc.BlockAlign;
226       m_ChannelCount += tmpDesc.ChannelCount;
227       m_outputs.push_back(std::make_pair(numSilenceChannels, I.get()));
228       m_inputs.push_back(I);
229       I.release();
230       assert(m_ChannelCount == (ATMOS::SYNC_CHANNEL - 1));
231     }
232   }
233   return result;
234 }
235
236 //
237 Result_t
238 ASDCP::AtmosSyncChannelMixer::MixInAtmosSyncChannel()
239 {
240   Result_t result = RESULT_OK;
241   PCM::AudioDescriptor tmpDesc;
242   mem_ptr<AtmosSyncDataProvider> I = new AtmosSyncDataProvider(m_ADesc.QuantizationBits,
243                                                                m_ADesc.AudioSamplingRate.Numerator,
244                                                                m_ADesc.EditRate, m_trackUUID);
245   result = I->FillAudioDescriptor(tmpDesc);
246   if ( ASDCP_SUCCESS(result) )
247   {
248     m_ADesc.BlockAlign += tmpDesc.BlockAlign;
249     m_ChannelCount += tmpDesc.ChannelCount;
250     m_outputs.push_back(std::make_pair(tmpDesc.ChannelCount, I.get()));
251     m_inputs.push_back(I);
252     I.release();
253     assert(m_ChannelCount == ATMOS::SYNC_CHANNEL);
254   }
255   return result;
256 }
257
258 //
259 Result_t
260 ASDCP::AtmosSyncChannelMixer::AppendSilenceChannels(const ui32_t& channel_count)
261 {
262   if ( m_ADesc.QuantizationBits == 0 )
263     {
264       DefaultLogSink().Error("Mixer object contains no channels, call OpenRead() first.\n");
265       return RESULT_PARAM;
266     }
267
268   Result_t result = RESULT_OK;
269   PCM::AudioDescriptor tmpDesc;
270
271   if ( channel_count > 0 )
272     {
273       Kumu::mem_ptr<SilenceDataProvider> I =
274         new SilenceDataProvider(channel_count,
275                                 m_ADesc.QuantizationBits,
276                                 m_ADesc.AudioSamplingRate.Numerator,
277                                 m_ADesc.EditRate);
278
279       result = I->FillAudioDescriptor(tmpDesc);
280
281       if ( ASDCP_SUCCESS(result) )
282         {
283           m_ADesc.BlockAlign += tmpDesc.BlockAlign;
284           m_ChannelCount += tmpDesc.ChannelCount;
285           m_ADesc.ChannelCount = m_ChannelCount;
286           m_ADesc.AvgBps = (ui32_t)(ceil(m_ADesc.AudioSamplingRate.Quotient()) * m_ADesc.BlockAlign);
287
288           m_outputs.push_back(std::make_pair(channel_count, I.get()));
289           m_inputs.push_back(I);
290           I.release();
291         }
292     }
293
294   return result;
295 }
296
297 //
298 Result_t
299 ASDCP::AtmosSyncChannelMixer::FillAudioDescriptor(PCM::AudioDescriptor& ADesc) const
300 {
301   ADesc = m_ADesc;
302   return RESULT_OK;
303 }
304
305 //
306 Result_t
307 ASDCP::AtmosSyncChannelMixer::Reset()
308 {
309   Result_t result = RESULT_OK;
310   SourceList::iterator it;
311   SourceList::iterator lastInput = m_inputs.end();
312
313   for ( it = m_inputs.begin(); it != lastInput && ASDCP_SUCCESS(result) ; ++it )
314     result = (*it)->Reset();
315
316   return result;
317 }
318
319
320 //2
321 Result_t
322 ASDCP::AtmosSyncChannelMixer::ReadFrame(PCM::FrameBuffer& OutFB)
323 {
324
325
326   Result_t result = RESULT_OK;
327   SourceList::iterator iter;
328   SourceList::iterator lastInput = m_inputs.end();
329   ui32_t bufSize = PCM::CalcFrameBufferSize(m_ADesc);
330   assert( bufSize <= OutFB.Capacity());
331
332   for ( iter = m_inputs.begin(); iter != lastInput && ASDCP_SUCCESS(result) ; ++iter )
333     result = (*iter)->ReadFrame();
334
335   if ( ASDCP_SUCCESS(result) )
336   {
337     OutFB.Size(bufSize);
338     byte_t* Out_p = OutFB.Data();
339     byte_t* End_p = Out_p + OutFB.Size();
340     ui32_t bytesWritten = 0;
341     OutputList::iterator iter;
342     OutputList::iterator lastOutput = m_outputs.end();
343
344     while ( Out_p < End_p && ASDCP_SUCCESS(result) )
345         {
346         iter = m_outputs.begin();
347         while ( iter != lastOutput && ASDCP_SUCCESS(result) )
348         {
349             result = ((*iter).second)->PutSample((*iter).first, Out_p, &bytesWritten);
350             Out_p += bytesWritten;
351             ++iter;
352         }
353     }
354
355     if ( ASDCP_SUCCESS(result) )
356     {
357       assert(Out_p == End_p);
358       OutFB.FrameNumber(m_FramesRead++);
359     }
360   }
361
362   return result;
363 }
364
365
366 //
367 // end AtmosSyncChannel_Mixer.cpp
368 //