Assorted c++11 cleanups.
[libdcp.git] / src / sound_asset_writer.cc
1 /*
2     Copyright (C) 2012-2021 Carl Hetherington <cth@carlh.net>
3
4     This file is part of libdcp.
5
6     libdcp is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     libdcp is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with libdcp.  If not, see <http://www.gnu.org/licenses/>.
18
19     In addition, as a special exception, the copyright holders give
20     permission to link the code of portions of this program with the
21     OpenSSL library under certain conditions as described in each
22     individual source file, and distribute linked combinations
23     including the two.
24
25     You must obey the GNU General Public License in all respects
26     for all of the code used other than OpenSSL.  If you modify
27     file(s) with this exception, you may extend this exception to your
28     version of the file(s), but you are not obligated to do so.  If you
29     do not wish to do so, delete this exception statement from your
30     version.  If you delete this exception statement from all source
31     files in the program, then also delete it here.
32 */
33
34 #include "bitstream.h"
35 #include "sound_asset_writer.h"
36 #include "sound_asset.h"
37 #include "exceptions.h"
38 #include "dcp_assert.h"
39 #include "compose.hpp"
40 #include "crypto_context.h"
41 #include <asdcp/AS_DCP.h>
42 #include <asdcp/Metadata.h>
43 #include <iostream>
44
45 using std::min;
46 using std::max;
47 using std::cout;
48 using std::string;
49 using std::vector;
50 using namespace dcp;
51
52
53 struct SoundAssetWriter::ASDCPState
54 {
55         ASDCP::PCM::MXFWriter mxf_writer;
56         ASDCP::PCM::FrameBuffer frame_buffer;
57         ASDCP::WriterInfo writer_info;
58         ASDCP::PCM::AudioDescriptor desc;
59 };
60
61 SoundAssetWriter::SoundAssetWriter (SoundAsset* asset, boost::filesystem::path file, vector<Channel> active_channels, bool sync)
62         : AssetWriter (asset, file)
63         , _state (new SoundAssetWriter::ASDCPState)
64         , _asset (asset)
65         , _frame_buffer_offset (0)
66         , _sync (sync)
67         , _sync_packet (0)
68         , _active_channels (active_channels)
69 {
70         DCP_ASSERT (!_sync || _asset->channels() >= 14);
71         DCP_ASSERT (!_sync || _asset->standard() == Standard::SMPTE);
72
73         /* Derived from ASDCP::Wav::SimpleWaveHeader::FillADesc */
74         _state->desc.EditRate = ASDCP::Rational (_asset->edit_rate().numerator, _asset->edit_rate().denominator);
75         _state->desc.AudioSamplingRate = ASDCP::Rational (_asset->sampling_rate(), 1);
76         _state->desc.Locked = 0;
77         _state->desc.ChannelCount = _asset->channels ();
78         _state->desc.QuantizationBits = 24;
79         _state->desc.BlockAlign = 3 * _asset->channels();
80         _state->desc.AvgBps = _asset->sampling_rate() * _state->desc.BlockAlign;
81         _state->desc.LinkedTrackID = 0;
82         if (asset->standard() == Standard::INTEROP) {
83                 _state->desc.ChannelFormat = ASDCP::PCM::CF_NONE;
84         } else {
85                 /* Just use WTF ("wild track format") for SMPTE for now; searches suggest that this
86                    uses the same assignment as Interop.
87                 */
88                 _state->desc.ChannelFormat = ASDCP::PCM::CF_CFG_4;
89         }
90
91         /* I'm fairly sure this is not necessary, as ContainerDuration is written
92            in ASDCP's WriteMXFFooter, but it stops a valgrind warning.
93         */
94         _state->desc.ContainerDuration = 0;
95
96         _state->frame_buffer.Capacity (ASDCP::PCM::CalcFrameBufferSize (_state->desc));
97         _state->frame_buffer.Size (ASDCP::PCM::CalcFrameBufferSize (_state->desc));
98         memset (_state->frame_buffer.Data(), 0, _state->frame_buffer.Capacity());
99
100         _asset->fill_writer_info (&_state->writer_info, _asset->id());
101
102         if (_sync) {
103                 _fsk.set_data (create_sync_packets());
104         }
105 }
106
107
108 void
109 SoundAssetWriter::start ()
110 {
111         Kumu::Result_t r = _state->mxf_writer.OpenWrite (_file.string().c_str(), _state->writer_info, _state->desc);
112         if (ASDCP_FAILURE (r)) {
113                 boost::throw_exception (FileError ("could not open audio MXF for writing", _file.string(), r));
114         }
115
116         if (_asset->standard() == Standard::SMPTE && !_active_channels.empty()) {
117
118                 ASDCP::MXF::WaveAudioDescriptor* essence_descriptor = 0;
119                 _state->mxf_writer.OP1aHeader().GetMDObjectByType(
120                         asdcp_smpte_dict->ul(ASDCP::MDD_WaveAudioDescriptor), reinterpret_cast<ASDCP::MXF::InterchangeObject**>(&essence_descriptor)
121                         );
122                 DCP_ASSERT (essence_descriptor);
123                 essence_descriptor->ChannelAssignment = asdcp_smpte_dict->ul(ASDCP::MDD_DCAudioChannelCfg_MCA);
124
125                 ASDCP::MXF::SoundfieldGroupLabelSubDescriptor* soundfield = new ASDCP::MXF::SoundfieldGroupLabelSubDescriptor(asdcp_smpte_dict);
126                 GenRandomValue (soundfield->MCALinkID);
127                 soundfield->RFC5646SpokenLanguage = _asset->language();
128
129                 const MCASoundField field = _asset->channels() > 10 ? MCASoundField::SEVEN_POINT_ONE : MCASoundField::FIVE_POINT_ONE;
130
131                 if (field == MCASoundField::SEVEN_POINT_ONE) {
132                         soundfield->MCATagSymbol = "sg71";
133                         soundfield->MCATagName = "7.1DS";
134                         soundfield->MCALabelDictionaryID = asdcp_smpte_dict->ul(ASDCP::MDD_DCAudioSoundfield_71);
135                 } else {
136                         soundfield->MCATagSymbol = "sg51";
137                         soundfield->MCATagName = "5.1";
138                         soundfield->MCALabelDictionaryID = asdcp_smpte_dict->ul(ASDCP::MDD_DCAudioSoundfield_51);
139                 }
140
141                 _state->mxf_writer.OP1aHeader().AddChildObject(soundfield);
142                 essence_descriptor->SubDescriptors.push_back(soundfield->InstanceUID);
143
144                 for (auto i: _active_channels) {
145                         ASDCP::MXF::AudioChannelLabelSubDescriptor* channel = new ASDCP::MXF::AudioChannelLabelSubDescriptor(asdcp_smpte_dict);
146                         GenRandomValue (channel->MCALinkID);
147                         channel->SoundfieldGroupLinkID = soundfield->MCALinkID;
148                         channel->MCAChannelID = static_cast<int>(i) + 1;
149                         channel->MCATagSymbol = "ch" + channel_to_mca_id(i, field);
150                         channel->MCATagName = channel_to_mca_name(i, field);
151                         channel->RFC5646SpokenLanguage = _asset->language();
152                         channel->MCALabelDictionaryID = channel_to_mca_universal_label(i, field, asdcp_smpte_dict);
153                         _state->mxf_writer.OP1aHeader().AddChildObject(channel);
154                         essence_descriptor->SubDescriptors.push_back(channel->InstanceUID);
155                 }
156         }
157
158         _asset->set_file (_file);
159         _started = true;
160 }
161
162
163 /** @param data Pointer an array of float pointers, one for each channel.
164  *  @param frames Number of frames i.e. number of floats that are given for each channel.
165  */
166 void
167 SoundAssetWriter::write (float const * const * data, int frames)
168 {
169         DCP_ASSERT (!_finalized);
170         DCP_ASSERT (frames > 0);
171
172         static float const clip = 1.0f - (1.0f / pow (2, 23));
173
174         if (!_started) {
175                 start ();
176         }
177
178         int const ch = _asset->channels ();
179
180         for (int i = 0; i < frames; ++i) {
181
182                 byte_t* out = _state->frame_buffer.Data() + _frame_buffer_offset;
183
184                 /* Write one sample per channel */
185                 for (int j = 0; j < ch; ++j) {
186                         int32_t s = 0;
187                         if (j == 13 && _sync) {
188                                 s = _fsk.get();
189                         } else {
190                                 /* Convert sample to 24-bit int, clipping if necessary. */
191                                 float x = data[j][i];
192                                 if (x > clip) {
193                                         x = clip;
194                                 } else if (x < -clip) {
195                                         x = -clip;
196                                 }
197                                 s = x * (1 << 23);
198                         }
199                         *out++ = (s & 0xff);
200                         *out++ = (s & 0xff00) >> 8;
201                         *out++ = (s & 0xff0000) >> 16;
202                 }
203                 _frame_buffer_offset += 3 * ch;
204
205                 DCP_ASSERT (_frame_buffer_offset <= int (_state->frame_buffer.Capacity()));
206
207                 /* Finish the MXF frame if required */
208                 if (_frame_buffer_offset == int (_state->frame_buffer.Capacity())) {
209                         write_current_frame ();
210                         _frame_buffer_offset = 0;
211                         memset (_state->frame_buffer.Data(), 0, _state->frame_buffer.Capacity());
212                 }
213         }
214 }
215
216 void
217 SoundAssetWriter::write_current_frame ()
218 {
219         ASDCP::Result_t const r = _state->mxf_writer.WriteFrame (_state->frame_buffer, _crypto_context->context(), _crypto_context->hmac());
220         if (ASDCP_FAILURE (r)) {
221                 boost::throw_exception (MiscError (String::compose ("could not write audio MXF frame (%1)", int (r))));
222         }
223
224         ++_frames_written;
225
226         if (_sync) {
227                 /* We need a new set of sync packets for this frame */
228                 _fsk.set_data (create_sync_packets());
229         }
230 }
231
232 bool
233 SoundAssetWriter::finalize ()
234 {
235         if (_frame_buffer_offset > 0) {
236                 write_current_frame ();
237         }
238
239         if (_started) {
240                 ASDCP::Result_t const r = _state->mxf_writer.Finalize();
241                 if (ASDCP_FAILURE(r)) {
242                         boost::throw_exception (MiscError (String::compose ("could not finalise audio MXF (%1)", int(r))));
243                 }
244         }
245
246         _asset->_intrinsic_duration = _frames_written;
247         return AssetWriter::finalize ();
248 }
249
250
251 /** Calculate and return the sync packets required for this edit unit (aka "frame") */
252 vector<bool>
253 SoundAssetWriter::create_sync_packets ()
254 {
255         /* Parts of this code assumes 48kHz */
256         DCP_ASSERT (_asset->sampling_rate() == 48000);
257
258         /* Encoding of edit rate */
259         int edit_rate_code = 0;
260         /* How many 0 bits are used to pad the end of the packet */
261         int remaining_bits = 0;
262         /* How many packets in this edit unit (i.e. "frame") */
263         int packets = 0;
264         Fraction const edit_rate = _asset->edit_rate ();
265         if (edit_rate == Fraction(24, 1)) {
266                 edit_rate_code = 0;
267                 remaining_bits = 25;
268                 packets = 4;
269         } else if (edit_rate == Fraction(25, 1)) {
270                 edit_rate_code = 1;
271                 remaining_bits = 20;
272                 packets = 4;
273         } else if (edit_rate == Fraction(30, 1)) {
274                 edit_rate_code = 2;
275                 remaining_bits = 0;
276                 packets = 4;
277         } else if (edit_rate == Fraction(48, 1)) {
278                 edit_rate_code = 3;
279                 remaining_bits = 25;
280                 packets = 2;
281         } else if (edit_rate == Fraction(50, 1)) {
282                 edit_rate_code = 4;
283                 remaining_bits = 20;
284                 packets = 2;
285         } else if (edit_rate == Fraction(60, 1)) {
286                 edit_rate_code = 5;
287                 remaining_bits = 0;
288                 packets = 2;
289         } else if (edit_rate == Fraction(96, 1)) {
290                 edit_rate_code = 6;
291                 remaining_bits = 25;
292                 packets = 1;
293         } else if (edit_rate == Fraction(100, 1)) {
294                 edit_rate_code = 7;
295                 remaining_bits = 20;
296                 packets = 1;
297         } else if (edit_rate == Fraction(120, 1)) {
298                 edit_rate_code = 8;
299                 remaining_bits = 0;
300                 packets = 1;
301         }
302
303         Bitstream bs;
304
305         Kumu::UUID id;
306         DCP_ASSERT (id.DecodeHex(_asset->id().c_str()));
307
308         for (int i = 0; i < packets; ++i) {
309                 bs.write_from_byte (0x4d);
310                 bs.write_from_byte (0x56);
311                 bs.start_crc (0x1021);
312                 bs.write_from_byte (edit_rate_code, 4);
313                 bs.write_from_byte (0, 2);
314                 bs.write_from_byte (_sync_packet, 2);
315                 bs.write_from_byte (id.Value()[i * 4 + 0]);
316                 bs.write_from_byte (id.Value()[i * 4 + 1]);
317                 bs.write_from_byte (id.Value()[i * 4 + 2]);
318                 bs.write_from_byte (id.Value()[i * 4 + 3]);
319                 bs.write_from_word (_frames_written, 24);
320                 bs.write_crc ();
321                 bs.write_from_byte (0, 4);
322                 bs.write_from_word (0, remaining_bits);
323
324                 ++_sync_packet;
325                 if (_sync_packet == 4) {
326                         _sync_packet = 0;
327                 }
328         }
329
330         return bs.get();
331 }
332