Make similar changes to the previous commit for _xml_id.
[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
35 /** @file  src/sound_asset_writer.cc
36  *  @brief SoundAssetWriter class
37  */
38
39
40 #include "bitstream.h"
41 #include "compose.hpp"
42 #include "crypto_context.h"
43 #include "dcp_assert.h"
44 #include "exceptions.h"
45 #include "sound_asset.h"
46 #include "sound_asset_writer.h"
47 #include "warnings.h"
48 LIBDCP_DISABLE_WARNINGS
49 #include <asdcp/AS_DCP.h>
50 #include <asdcp/Metadata.h>
51 LIBDCP_ENABLE_WARNINGS
52 #include <iostream>
53
54
55 using std::min;
56 using std::max;
57 using std::cout;
58 using std::string;
59 using std::vector;
60 using namespace dcp;
61
62
63 struct SoundAssetWriter::ASDCPState
64 {
65         ASDCP::PCM::MXFWriter mxf_writer;
66         ASDCP::PCM::FrameBuffer frame_buffer;
67         ASDCP::WriterInfo writer_info;
68         ASDCP::PCM::AudioDescriptor desc;
69 };
70
71
72 SoundAssetWriter::SoundAssetWriter (SoundAsset* asset, boost::filesystem::path file, bool sync)
73         : AssetWriter (asset, file)
74         , _state (new SoundAssetWriter::ASDCPState)
75         , _asset (asset)
76         , _sync (sync)
77 {
78         DCP_ASSERT (!_sync || _asset->channels() >= 14);
79         DCP_ASSERT (!_sync || _asset->standard() == Standard::SMPTE);
80
81         /* Derived from ASDCP::Wav::SimpleWaveHeader::FillADesc */
82         _state->desc.EditRate = ASDCP::Rational (_asset->edit_rate().numerator, _asset->edit_rate().denominator);
83         _state->desc.AudioSamplingRate = ASDCP::Rational (_asset->sampling_rate(), 1);
84         _state->desc.Locked = 0;
85         _state->desc.ChannelCount = _asset->channels();
86         _state->desc.QuantizationBits = 24;
87         _state->desc.BlockAlign = 3 * _asset->channels();
88         _state->desc.AvgBps = _asset->sampling_rate() * _state->desc.BlockAlign;
89         _state->desc.LinkedTrackID = 0;
90         if (asset->standard() == Standard::INTEROP) {
91                 _state->desc.ChannelFormat = ASDCP::PCM::CF_NONE;
92         } else {
93                 /* As required by Bv2.1 */
94                 _state->desc.ChannelFormat = ASDCP::PCM::CF_CFG_4;
95         }
96
97         /* I'm fairly sure this is not necessary, as ContainerDuration is written
98            in ASDCP's WriteMXFFooter, but it stops a valgrind warning.
99         */
100         _state->desc.ContainerDuration = 0;
101
102         _state->frame_buffer.Capacity (ASDCP::PCM::CalcFrameBufferSize (_state->desc));
103         _state->frame_buffer.Size (ASDCP::PCM::CalcFrameBufferSize (_state->desc));
104         memset (_state->frame_buffer.Data(), 0, _state->frame_buffer.Capacity());
105
106         _asset->fill_writer_info (&_state->writer_info, _asset->id());
107
108         if (_sync) {
109                 _fsk.set_data (create_sync_packets());
110         }
111 }
112
113
114 void
115 SoundAssetWriter::start ()
116 {
117         auto r = _state->mxf_writer.OpenWrite (_file.string().c_str(), _state->writer_info, _state->desc);
118         if (ASDCP_FAILURE(r)) {
119                 boost::throw_exception (FileError("could not open audio MXF for writing", _file.string(), r));
120         }
121
122         if (_asset->standard() == Standard::SMPTE) {
123
124                 ASDCP::MXF::WaveAudioDescriptor* essence_descriptor = nullptr;
125                 _state->mxf_writer.OP1aHeader().GetMDObjectByType(
126                         asdcp_smpte_dict->ul(ASDCP::MDD_WaveAudioDescriptor), reinterpret_cast<ASDCP::MXF::InterchangeObject**>(&essence_descriptor)
127                         );
128                 DCP_ASSERT (essence_descriptor);
129                 essence_descriptor->ChannelAssignment = asdcp_smpte_dict->ul(ASDCP::MDD_DCAudioChannelCfg_MCA);
130
131                 auto soundfield = new ASDCP::MXF::SoundfieldGroupLabelSubDescriptor(asdcp_smpte_dict);
132                 GenRandomValue (soundfield->MCALinkID);
133                 if (auto lang = _asset->language()) {
134                         soundfield->RFC5646SpokenLanguage = *lang;
135                 }
136
137                 const MCASoundField field = _asset->channels() > 10 ? MCASoundField::SEVEN_POINT_ONE : MCASoundField::FIVE_POINT_ONE;
138
139                 if (field == MCASoundField::SEVEN_POINT_ONE) {
140                         soundfield->MCATagSymbol = "sg71";
141                         soundfield->MCATagName = "7.1DS";
142 LIBDCP_DISABLE_WARNINGS
143                         soundfield->MCALabelDictionaryID = asdcp_smpte_dict->ul(ASDCP::MDD_DCAudioSoundfield_71);
144 LIBDCP_ENABLE_WARNINGS
145                 } else {
146                         soundfield->MCATagSymbol = "sg51";
147                         soundfield->MCATagName = "5.1";
148 LIBDCP_DISABLE_WARNINGS
149                         soundfield->MCALabelDictionaryID = asdcp_smpte_dict->ul(ASDCP::MDD_DCAudioSoundfield_51);
150 LIBDCP_ENABLE_WARNINGS
151                 }
152
153                 _state->mxf_writer.OP1aHeader().AddChildObject(soundfield);
154                 essence_descriptor->SubDescriptors.push_back(soundfield->InstanceUID);
155
156                 /* We must describe at least the number of channels in `field', even if they aren't
157                  * in the asset (I think)
158                  */
159                 int descriptors = max(_asset->channels(), field == MCASoundField::FIVE_POINT_ONE ? 6 : 8);
160
161                 auto const used = used_audio_channels();
162
163                 for (auto i = 0; i < descriptors; ++i) {
164                         auto dcp_channel = static_cast<dcp::Channel>(i);
165                         if (find(used.begin(), used.end(), dcp_channel) == used.end()) {
166                                 continue;
167                         }
168                         auto channel = new ASDCP::MXF::AudioChannelLabelSubDescriptor(asdcp_smpte_dict);
169                         GenRandomValue (channel->MCALinkID);
170                         channel->SoundfieldGroupLinkID = soundfield->MCALinkID;
171                         channel->MCAChannelID = i + 1;
172                         channel->MCATagSymbol = "ch" + channel_to_mca_id(dcp_channel, field);
173                         channel->MCATagName = channel_to_mca_name(dcp_channel, field);
174                         if (auto lang = _asset->language()) {
175                                 channel->RFC5646SpokenLanguage = *lang;
176                         }
177 LIBDCP_DISABLE_WARNINGS
178                         channel->MCALabelDictionaryID = channel_to_mca_universal_label(dcp_channel, field, asdcp_smpte_dict);
179 LIBDCP_ENABLE_WARNINGS
180                         _state->mxf_writer.OP1aHeader().AddChildObject(channel);
181                         essence_descriptor->SubDescriptors.push_back(channel->InstanceUID);
182                 }
183         }
184
185         _asset->set_file (_file);
186         _started = true;
187 }
188
189
190 void
191 SoundAssetWriter::write (float const * const * data, int frames)
192 {
193         DCP_ASSERT (!_finalized);
194         DCP_ASSERT (frames > 0);
195
196         static float const clip = 1.0f - (1.0f / pow (2, 23));
197
198         if (!_started) {
199                 start ();
200         }
201
202         int const ch = _asset->channels ();
203
204         for (int i = 0; i < frames; ++i) {
205
206                 byte_t* out = _state->frame_buffer.Data() + _frame_buffer_offset;
207
208                 /* Write one sample per channel */
209                 for (int j = 0; j < ch; ++j) {
210                         int32_t s = 0;
211                         if (j == 13 && _sync) {
212                                 s = _fsk.get();
213                         } else {
214                                 /* Convert sample to 24-bit int, clipping if necessary. */
215                                 float x = data[j][i];
216                                 if (x > clip) {
217                                         x = clip;
218                                 } else if (x < -clip) {
219                                         x = -clip;
220                                 }
221                                 s = x * (1 << 23);
222                         }
223                         *out++ = (s & 0xff);
224                         *out++ = (s & 0xff00) >> 8;
225                         *out++ = (s & 0xff0000) >> 16;
226                 }
227                 _frame_buffer_offset += 3 * ch;
228
229                 DCP_ASSERT (_frame_buffer_offset <= int(_state->frame_buffer.Capacity()));
230
231                 /* Finish the MXF frame if required */
232                 if (_frame_buffer_offset == int (_state->frame_buffer.Capacity())) {
233                         write_current_frame ();
234                         _frame_buffer_offset = 0;
235                         memset (_state->frame_buffer.Data(), 0, _state->frame_buffer.Capacity());
236                 }
237         }
238 }
239
240 void
241 SoundAssetWriter::write_current_frame ()
242 {
243         auto const r = _state->mxf_writer.WriteFrame (_state->frame_buffer, _crypto_context->context(), _crypto_context->hmac());
244         if (ASDCP_FAILURE(r)) {
245                 boost::throw_exception (MiscError(String::compose("could not write audio MXF frame (%1)", static_cast<int>(r))));
246         }
247
248         ++_frames_written;
249
250         if (_sync) {
251                 /* We need a new set of sync packets for this frame */
252                 _fsk.set_data (create_sync_packets());
253         }
254 }
255
256 bool
257 SoundAssetWriter::finalize ()
258 {
259         if (_frame_buffer_offset > 0) {
260                 write_current_frame ();
261         }
262
263         if (_started) {
264                 auto const r = _state->mxf_writer.Finalize();
265                 if (ASDCP_FAILURE(r)) {
266                         boost::throw_exception (MiscError(String::compose ("could not finalise audio MXF (%1)", static_cast<int>(r))));
267                 }
268         }
269
270         _asset->_intrinsic_duration = _frames_written;
271         return AssetWriter::finalize ();
272 }
273
274
275 /** Calculate and return the sync packets required for this edit unit (aka "frame") */
276 vector<bool>
277 SoundAssetWriter::create_sync_packets ()
278 {
279         /* Parts of this code assumes 48kHz */
280         DCP_ASSERT (_asset->sampling_rate() == 48000);
281
282         /* Encoding of edit rate */
283         int edit_rate_code = 0;
284         /* How many 0 bits are used to pad the end of the packet */
285         int remaining_bits = 0;
286         /* How many packets in this edit unit (i.e. "frame") */
287         int packets = 0;
288         auto const edit_rate = _asset->edit_rate ();
289         if (edit_rate == Fraction(24, 1)) {
290                 edit_rate_code = 0;
291                 remaining_bits = 25;
292                 packets = 4;
293         } else if (edit_rate == Fraction(25, 1)) {
294                 edit_rate_code = 1;
295                 remaining_bits = 20;
296                 packets = 4;
297         } else if (edit_rate == Fraction(30, 1)) {
298                 edit_rate_code = 2;
299                 remaining_bits = 0;
300                 packets = 4;
301         } else if (edit_rate == Fraction(48, 1)) {
302                 edit_rate_code = 3;
303                 remaining_bits = 25;
304                 packets = 2;
305         } else if (edit_rate == Fraction(50, 1)) {
306                 edit_rate_code = 4;
307                 remaining_bits = 20;
308                 packets = 2;
309         } else if (edit_rate == Fraction(60, 1)) {
310                 edit_rate_code = 5;
311                 remaining_bits = 0;
312                 packets = 2;
313         } else if (edit_rate == Fraction(96, 1)) {
314                 edit_rate_code = 6;
315                 remaining_bits = 25;
316                 packets = 1;
317         } else if (edit_rate == Fraction(100, 1)) {
318                 edit_rate_code = 7;
319                 remaining_bits = 20;
320                 packets = 1;
321         } else if (edit_rate == Fraction(120, 1)) {
322                 edit_rate_code = 8;
323                 remaining_bits = 0;
324                 packets = 1;
325         }
326
327         Bitstream bs;
328
329         Kumu::UUID id;
330         DCP_ASSERT (id.DecodeHex(_asset->id().c_str()));
331
332         for (int i = 0; i < packets; ++i) {
333                 bs.write_from_byte (0x4d);
334                 bs.write_from_byte (0x56);
335                 bs.start_crc (0x1021);
336                 bs.write_from_byte (edit_rate_code, 4);
337                 bs.write_from_byte (0, 2);
338                 bs.write_from_byte (_sync_packet, 2);
339                 bs.write_from_byte (id.Value()[i * 4 + 0]);
340                 bs.write_from_byte (id.Value()[i * 4 + 1]);
341                 bs.write_from_byte (id.Value()[i * 4 + 2]);
342                 bs.write_from_byte (id.Value()[i * 4 + 3]);
343                 bs.write_from_word (_frames_written, 24);
344                 bs.write_crc ();
345                 bs.write_from_byte (0, 4);
346                 bs.write_from_word (0, remaining_bits);
347
348                 ++_sync_packet;
349                 if (_sync_packet == 4) {
350                         _sync_packet = 0;
351                 }
352         }
353
354         return bs.get();
355 }
356