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