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, vector<dcp::Channel> extra_active_channels, bool sync, bool include_mca_subdescriptors)
73 : AssetWriter (asset, file)
74 , _state (new SoundAssetWriter::ASDCPState)
76 , _extra_active_channels(extra_active_channels)
78 , _include_mca_subdescriptors(include_mca_subdescriptors)
80 DCP_ASSERT (!_sync || _asset->channels() >= 14);
81 DCP_ASSERT (!_sync || _asset->standard() == Standard::SMPTE);
83 /* None of these are allowed in extra_active_channels; some are implicit, and (it seems) should never have a descriptor
86 vector<Channel> disallowed_extra = {
95 Channel::SIGN_LANGUAGE,
96 Channel::CHANNEL_COUNT
98 for (auto disallowed: disallowed_extra) {
99 DCP_ASSERT(std::find(extra_active_channels.begin(), extra_active_channels.end(), disallowed) == extra_active_channels.end());
102 /* Derived from ASDCP::Wav::SimpleWaveHeader::FillADesc */
103 _state->desc.EditRate = ASDCP::Rational (_asset->edit_rate().numerator, _asset->edit_rate().denominator);
104 _state->desc.AudioSamplingRate = ASDCP::Rational (_asset->sampling_rate(), 1);
105 _state->desc.Locked = 0;
106 _state->desc.ChannelCount = _asset->channels();
107 _state->desc.QuantizationBits = 24;
108 _state->desc.BlockAlign = 3 * _asset->channels();
109 _state->desc.AvgBps = _asset->sampling_rate() * _state->desc.BlockAlign;
110 _state->desc.LinkedTrackID = 0;
111 if (asset->standard() == Standard::INTEROP) {
112 _state->desc.ChannelFormat = ASDCP::PCM::CF_NONE;
114 /* As required by Bv2.1 */
115 _state->desc.ChannelFormat = ASDCP::PCM::CF_CFG_4;
118 /* I'm fairly sure this is not necessary, as ContainerDuration is written
119 in ASDCP's WriteMXFFooter, but it stops a valgrind warning.
121 _state->desc.ContainerDuration = 0;
123 _state->frame_buffer.Capacity (ASDCP::PCM::CalcFrameBufferSize (_state->desc));
124 _state->frame_buffer.Size (ASDCP::PCM::CalcFrameBufferSize (_state->desc));
125 memset (_state->frame_buffer.Data(), 0, _state->frame_buffer.Capacity());
127 _asset->fill_writer_info (&_state->writer_info, _asset->id());
130 _fsk.set_data (create_sync_packets());
135 SoundAssetWriter::~SoundAssetWriter()
138 /* Last-resort finalization to close the file, at least */
140 _state->mxf_writer.Finalize();
147 SoundAssetWriter::start ()
149 auto r = _state->mxf_writer.OpenWrite (_file.string().c_str(), _state->writer_info, _state->desc);
150 if (ASDCP_FAILURE(r)) {
151 boost::throw_exception (FileError("could not open audio MXF for writing", _file.string(), r));
154 if (_asset->standard() == Standard::SMPTE && _include_mca_subdescriptors) {
156 ASDCP::MXF::WaveAudioDescriptor* essence_descriptor = nullptr;
157 _state->mxf_writer.OP1aHeader().GetMDObjectByType(
158 asdcp_smpte_dict->ul(ASDCP::MDD_WaveAudioDescriptor), reinterpret_cast<ASDCP::MXF::InterchangeObject**>(&essence_descriptor)
160 DCP_ASSERT (essence_descriptor);
161 essence_descriptor->ChannelAssignment = asdcp_smpte_dict->ul(ASDCP::MDD_DCAudioChannelCfg_4_WTF);
163 auto soundfield = new ASDCP::MXF::SoundfieldGroupLabelSubDescriptor(asdcp_smpte_dict);
164 GenRandomValue (soundfield->MCALinkID);
165 if (auto lang = _asset->language()) {
166 soundfield->RFC5646SpokenLanguage = *lang;
169 MCASoundField const field =
171 find(_extra_active_channels.begin(), _extra_active_channels.end(), dcp::Channel::BSL) != _extra_active_channels.end() ||
172 find(_extra_active_channels.begin(), _extra_active_channels.end(), dcp::Channel::BSR) != _extra_active_channels.end()
173 ) ? MCASoundField::SEVEN_POINT_ONE : MCASoundField::FIVE_POINT_ONE;
175 if (field == MCASoundField::SEVEN_POINT_ONE) {
176 soundfield->MCATagSymbol = "sg71";
177 soundfield->MCATagName = "7.1DS";
178 LIBDCP_DISABLE_WARNINGS
179 soundfield->MCALabelDictionaryID = asdcp_smpte_dict->ul(ASDCP::MDD_DCAudioSoundfield_71);
180 LIBDCP_ENABLE_WARNINGS
182 soundfield->MCATagSymbol = "sg51";
183 soundfield->MCATagName = "5.1";
184 LIBDCP_DISABLE_WARNINGS
185 soundfield->MCALabelDictionaryID = asdcp_smpte_dict->ul(ASDCP::MDD_DCAudioSoundfield_51);
186 LIBDCP_ENABLE_WARNINGS
189 _state->mxf_writer.OP1aHeader().AddChildObject(soundfield);
190 essence_descriptor->SubDescriptors.push_back(soundfield->InstanceUID);
192 std::vector<dcp::Channel> dcp_channels = {
201 std::copy(_extra_active_channels.begin(), _extra_active_channels.end(), back_inserter(dcp_channels));
202 std::sort(dcp_channels.begin(), dcp_channels.end());
203 auto last = std::unique(dcp_channels.begin(), dcp_channels.end());
204 dcp_channels.erase(last, dcp_channels.end());
206 for (auto dcp_channel: dcp_channels) {
207 auto channel = new ASDCP::MXF::AudioChannelLabelSubDescriptor(asdcp_smpte_dict);
208 GenRandomValue (channel->MCALinkID);
209 channel->SoundfieldGroupLinkID = soundfield->MCALinkID;
210 channel->MCAChannelID = static_cast<int>(dcp_channel) + 1;
211 channel->MCATagSymbol = "ch" + channel_to_mca_id(dcp_channel, field);
212 channel->MCATagName = channel_to_mca_name(dcp_channel, field);
213 if (auto lang = _asset->language()) {
214 channel->RFC5646SpokenLanguage = *lang;
216 LIBDCP_DISABLE_WARNINGS
217 channel->MCALabelDictionaryID = channel_to_mca_universal_label(dcp_channel, field, asdcp_smpte_dict);
218 LIBDCP_ENABLE_WARNINGS
219 _state->mxf_writer.OP1aHeader().AddChildObject(channel);
220 essence_descriptor->SubDescriptors.push_back(channel->InstanceUID);
224 _asset->set_file (_file);
230 SoundAssetWriter::write(float const * const * data, int data_channels, int frames)
232 do_write(data, data_channels, frames);
237 SoundAssetWriter::write(int32_t const * const * data, int data_channels, int frames)
239 do_write(data, data_channels, frames);
244 SoundAssetWriter::write_current_frame ()
246 auto const r = _state->mxf_writer.WriteFrame (_state->frame_buffer, _crypto_context->context(), _crypto_context->hmac());
247 if (ASDCP_FAILURE(r)) {
248 boost::throw_exception (MiscError(String::compose("could not write audio MXF frame (%1)", static_cast<int>(r))));
254 /* We need a new set of sync packets for this frame */
255 _fsk.set_data (create_sync_packets());
260 SoundAssetWriter::finalize ()
262 if (_frame_buffer_offset > 0) {
263 write_current_frame ();
267 auto const r = _state->mxf_writer.Finalize();
268 if (ASDCP_FAILURE(r)) {
269 boost::throw_exception (MiscError(String::compose ("could not finalise audio MXF (%1)", static_cast<int>(r))));
273 _asset->_intrinsic_duration = _frames_written;
274 return AssetWriter::finalize ();
278 /** Calculate and return the sync packets required for this edit unit (aka "frame") */
280 SoundAssetWriter::create_sync_packets ()
282 /* Parts of this code assumes 48kHz */
283 DCP_ASSERT (_asset->sampling_rate() == 48000);
285 /* Encoding of edit rate */
286 int edit_rate_code = 0;
287 /* How many 0 bits are used to pad the end of the packet */
288 int remaining_bits = 0;
289 /* How many packets in this edit unit (i.e. "frame") */
291 auto const edit_rate = _asset->edit_rate ();
292 if (edit_rate == Fraction(24, 1)) {
296 } else if (edit_rate == Fraction(25, 1)) {
300 } else if (edit_rate == Fraction(30, 1)) {
304 } else if (edit_rate == Fraction(48, 1)) {
308 } else if (edit_rate == Fraction(50, 1)) {
312 } else if (edit_rate == Fraction(60, 1)) {
316 } else if (edit_rate == Fraction(96, 1)) {
320 } else if (edit_rate == Fraction(100, 1)) {
324 } else if (edit_rate == Fraction(120, 1)) {
333 DCP_ASSERT (id.DecodeHex(_asset->id().c_str()));
335 for (int i = 0; i < packets; ++i) {
336 bs.write_from_byte (0x4d);
337 bs.write_from_byte (0x56);
338 bs.start_crc (0x1021);
339 bs.write_from_byte (edit_rate_code, 4);
340 bs.write_from_byte (0, 2);
341 bs.write_from_byte (_sync_packet, 2);
342 bs.write_from_byte (id.Value()[i * 4 + 0]);
343 bs.write_from_byte (id.Value()[i * 4 + 1]);
344 bs.write_from_byte (id.Value()[i * 4 + 2]);
345 bs.write_from_byte (id.Value()[i * 4 + 3]);
346 bs.write_from_word (_frames_written, 24);
348 bs.write_from_byte (0, 4);
349 bs.write_from_word (0, remaining_bits);
352 if (_sync_packet == 4) {
362 SoundAssetWriter::frame_buffer_data() const
364 return _state->frame_buffer.Data();
369 SoundAssetWriter::frame_buffer_capacity() const
371 return _state->frame_buffer.Capacity();