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