2 Copyright (C) 2012-2021 Carl Hetherington <cth@carlh.net>
4 This file is part of libdcp.
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.
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.
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/>.
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
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.
35 /** @file src/sound_asset_writer.cc
36 * @brief SoundAssetWriter class
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"
48 LIBDCP_DISABLE_WARNINGS
49 #include <asdcp/AS_DCP.h>
50 #include <asdcp/Metadata.h>
51 LIBDCP_ENABLE_WARNINGS
63 struct SoundAssetWriter::ASDCPState
65 ASDCP::PCM::MXFWriter mxf_writer;
66 ASDCP::PCM::FrameBuffer frame_buffer;
67 ASDCP::WriterInfo writer_info;
68 ASDCP::PCM::AudioDescriptor desc;
72 SoundAssetWriter::SoundAssetWriter (SoundAsset* asset, boost::filesystem::path file, bool sync)
73 : AssetWriter (asset, file)
74 , _state (new SoundAssetWriter::ASDCPState)
78 DCP_ASSERT (!_sync || _asset->channels() >= 14);
79 DCP_ASSERT (!_sync || _asset->standard() == Standard::SMPTE);
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;
93 /* As required by Bv2.1 */
94 _state->desc.ChannelFormat = ASDCP::PCM::CF_CFG_4;
97 /* I'm fairly sure this is not necessary, as ContainerDuration is written
98 in ASDCP's WriteMXFFooter, but it stops a valgrind warning.
100 _state->desc.ContainerDuration = 0;
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());
106 _asset->fill_writer_info (&_state->writer_info, _asset->id());
109 _fsk.set_data (create_sync_packets());
114 SoundAssetWriter::~SoundAssetWriter()
117 /* Last-resort finalization to close the file, at least */
119 _state->mxf_writer.Finalize();
126 SoundAssetWriter::start ()
128 auto r = _state->mxf_writer.OpenWrite (_file.string().c_str(), _state->writer_info, _state->desc);
129 if (ASDCP_FAILURE(r)) {
130 boost::throw_exception (FileError("could not open audio MXF for writing", _file.string(), r));
133 if (_asset->standard() == Standard::SMPTE) {
135 ASDCP::MXF::WaveAudioDescriptor* essence_descriptor = nullptr;
136 _state->mxf_writer.OP1aHeader().GetMDObjectByType(
137 asdcp_smpte_dict->ul(ASDCP::MDD_WaveAudioDescriptor), reinterpret_cast<ASDCP::MXF::InterchangeObject**>(&essence_descriptor)
139 DCP_ASSERT (essence_descriptor);
140 essence_descriptor->ChannelAssignment = asdcp_smpte_dict->ul(ASDCP::MDD_DCAudioChannelCfg_4_WTF);
142 auto soundfield = new ASDCP::MXF::SoundfieldGroupLabelSubDescriptor(asdcp_smpte_dict);
143 GenRandomValue (soundfield->MCALinkID);
144 if (auto lang = _asset->language()) {
145 soundfield->RFC5646SpokenLanguage = *lang;
148 const MCASoundField field = _asset->channels() > 10 ? MCASoundField::SEVEN_POINT_ONE : MCASoundField::FIVE_POINT_ONE;
150 if (field == MCASoundField::SEVEN_POINT_ONE) {
151 soundfield->MCATagSymbol = "sg71";
152 soundfield->MCATagName = "7.1DS";
153 LIBDCP_DISABLE_WARNINGS
154 soundfield->MCALabelDictionaryID = asdcp_smpte_dict->ul(ASDCP::MDD_DCAudioSoundfield_71);
155 LIBDCP_ENABLE_WARNINGS
157 soundfield->MCATagSymbol = "sg51";
158 soundfield->MCATagName = "5.1";
159 LIBDCP_DISABLE_WARNINGS
160 soundfield->MCALabelDictionaryID = asdcp_smpte_dict->ul(ASDCP::MDD_DCAudioSoundfield_51);
161 LIBDCP_ENABLE_WARNINGS
164 _state->mxf_writer.OP1aHeader().AddChildObject(soundfield);
165 essence_descriptor->SubDescriptors.push_back(soundfield->InstanceUID);
167 /* We must describe at least the number of channels in `field', even if they aren't
168 * in the asset (I think)
170 int descriptors = max(_asset->channels(), field == MCASoundField::FIVE_POINT_ONE ? 6 : 8);
172 auto const used = used_audio_channels();
174 for (auto i = 0; i < descriptors; ++i) {
175 auto dcp_channel = static_cast<dcp::Channel>(i);
176 if (find(used.begin(), used.end(), dcp_channel) == used.end()) {
179 auto channel = new ASDCP::MXF::AudioChannelLabelSubDescriptor(asdcp_smpte_dict);
180 GenRandomValue (channel->MCALinkID);
181 channel->SoundfieldGroupLinkID = soundfield->MCALinkID;
182 channel->MCAChannelID = i + 1;
183 channel->MCATagSymbol = "ch" + channel_to_mca_id(dcp_channel, field);
184 channel->MCATagName = channel_to_mca_name(dcp_channel, field);
185 if (auto lang = _asset->language()) {
186 channel->RFC5646SpokenLanguage = *lang;
188 LIBDCP_DISABLE_WARNINGS
189 channel->MCALabelDictionaryID = channel_to_mca_universal_label(dcp_channel, field, asdcp_smpte_dict);
190 LIBDCP_ENABLE_WARNINGS
191 _state->mxf_writer.OP1aHeader().AddChildObject(channel);
192 essence_descriptor->SubDescriptors.push_back(channel->InstanceUID);
196 _asset->set_file (_file);
202 SoundAssetWriter::write (float const * const * data, int frames)
204 DCP_ASSERT (!_finalized);
205 DCP_ASSERT (frames > 0);
207 static float const clip = 1.0f - (1.0f / pow (2, 23));
213 int const ch = _asset->channels ();
215 for (int i = 0; i < frames; ++i) {
217 byte_t* out = _state->frame_buffer.Data() + _frame_buffer_offset;
219 /* Write one sample per channel */
220 for (int j = 0; j < ch; ++j) {
222 if (j == 13 && _sync) {
225 /* Convert sample to 24-bit int, clipping if necessary. */
226 float x = data[j][i];
229 } else if (x < -clip) {
235 *out++ = (s & 0xff00) >> 8;
236 *out++ = (s & 0xff0000) >> 16;
238 _frame_buffer_offset += 3 * ch;
240 DCP_ASSERT (_frame_buffer_offset <= int(_state->frame_buffer.Capacity()));
242 /* Finish the MXF frame if required */
243 if (_frame_buffer_offset == int (_state->frame_buffer.Capacity())) {
244 write_current_frame ();
245 _frame_buffer_offset = 0;
246 memset (_state->frame_buffer.Data(), 0, _state->frame_buffer.Capacity());
252 SoundAssetWriter::write_current_frame ()
254 auto const r = _state->mxf_writer.WriteFrame (_state->frame_buffer, _crypto_context->context(), _crypto_context->hmac());
255 if (ASDCP_FAILURE(r)) {
256 boost::throw_exception (MiscError(String::compose("could not write audio MXF frame (%1)", static_cast<int>(r))));
262 /* We need a new set of sync packets for this frame */
263 _fsk.set_data (create_sync_packets());
268 SoundAssetWriter::finalize ()
270 if (_frame_buffer_offset > 0) {
271 write_current_frame ();
275 auto const r = _state->mxf_writer.Finalize();
276 if (ASDCP_FAILURE(r)) {
277 boost::throw_exception (MiscError(String::compose ("could not finalise audio MXF (%1)", static_cast<int>(r))));
281 _asset->_intrinsic_duration = _frames_written;
282 return AssetWriter::finalize ();
286 /** Calculate and return the sync packets required for this edit unit (aka "frame") */
288 SoundAssetWriter::create_sync_packets ()
290 /* Parts of this code assumes 48kHz */
291 DCP_ASSERT (_asset->sampling_rate() == 48000);
293 /* Encoding of edit rate */
294 int edit_rate_code = 0;
295 /* How many 0 bits are used to pad the end of the packet */
296 int remaining_bits = 0;
297 /* How many packets in this edit unit (i.e. "frame") */
299 auto const edit_rate = _asset->edit_rate ();
300 if (edit_rate == Fraction(24, 1)) {
304 } else if (edit_rate == Fraction(25, 1)) {
308 } else if (edit_rate == Fraction(30, 1)) {
312 } else if (edit_rate == Fraction(48, 1)) {
316 } else if (edit_rate == Fraction(50, 1)) {
320 } else if (edit_rate == Fraction(60, 1)) {
324 } else if (edit_rate == Fraction(96, 1)) {
328 } else if (edit_rate == Fraction(100, 1)) {
332 } else if (edit_rate == Fraction(120, 1)) {
341 DCP_ASSERT (id.DecodeHex(_asset->id().c_str()));
343 for (int i = 0; i < packets; ++i) {
344 bs.write_from_byte (0x4d);
345 bs.write_from_byte (0x56);
346 bs.start_crc (0x1021);
347 bs.write_from_byte (edit_rate_code, 4);
348 bs.write_from_byte (0, 2);
349 bs.write_from_byte (_sync_packet, 2);
350 bs.write_from_byte (id.Value()[i * 4 + 0]);
351 bs.write_from_byte (id.Value()[i * 4 + 1]);
352 bs.write_from_byte (id.Value()[i * 4 + 2]);
353 bs.write_from_byte (id.Value()[i * 4 + 3]);
354 bs.write_from_word (_frames_written, 24);
356 bs.write_from_byte (0, 4);
357 bs.write_from_word (0, remaining_bits);
360 if (_sync_packet == 4) {