41 #include "compose.hpp"
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)
78 DCP_ASSERT (!_sync || _asset->channels() >= 14);
79 DCP_ASSERT (!_sync || _asset->standard() == Standard::SMPTE);
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;
94 _state->desc.ChannelFormat = ASDCP::PCM::CF_CFG_4;
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());
115 SoundAssetWriter::start ()
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));
122 if (_asset->standard() == Standard::SMPTE) {
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)
128 DCP_ASSERT (essence_descriptor);
129 essence_descriptor->ChannelAssignment = asdcp_smpte_dict->ul(ASDCP::MDD_DCAudioChannelCfg_4_WTF);
131 auto soundfield =
new ASDCP::MXF::SoundfieldGroupLabelSubDescriptor(asdcp_smpte_dict);
132 GenRandomValue (soundfield->MCALinkID);
133 if (
auto lang = _asset->language()) {
134 soundfield->RFC5646SpokenLanguage = *lang;
137 const MCASoundField field = _asset->
channels() > 10 ? MCASoundField::SEVEN_POINT_ONE : MCASoundField::FIVE_POINT_ONE;
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
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
153 _state->mxf_writer.OP1aHeader().AddChildObject(soundfield);
154 essence_descriptor->SubDescriptors.push_back(soundfield->InstanceUID);
159 int descriptors = max(_asset->
channels(), field == MCASoundField::FIVE_POINT_ONE ? 6 : 8);
161 auto const used = used_audio_channels();
163 for (
auto i = 0; i < descriptors; ++i) {
165 if (find(used.begin(), used.end(), dcp_channel) == used.end()) {
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;
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);
194 DCP_ASSERT (frames > 0);
196 static float const clip = 1.0f - (1.0f / pow (2, 23));
204 for (
int i = 0; i < frames; ++i) {
206 byte_t* out = _state->frame_buffer.Data() + _frame_buffer_offset;
209 for (
int j = 0; j < ch; ++j) {
211 if (j == 13 &&
_sync) {
215 float x = data[j][i];
218 }
else if (x < -clip) {
224 *out++ = (s & 0xff00) >> 8;
225 *out++ = (s & 0xff0000) >> 16;
227 _frame_buffer_offset += 3 * ch;
229 DCP_ASSERT (_frame_buffer_offset <=
int(_state->frame_buffer.Capacity()));
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());
241 SoundAssetWriter::write_current_frame ()
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))));
259 if (_frame_buffer_offset > 0) {
260 write_current_frame ();
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))));
283 int edit_rate_code = 0;
285 int remaining_bits = 0;
288 auto const edit_rate = _asset->edit_rate ();
293 }
else if (edit_rate ==
Fraction(25, 1)) {
297 }
else if (edit_rate ==
Fraction(30, 1)) {
301 }
else if (edit_rate ==
Fraction(48, 1)) {
305 }
else if (edit_rate ==
Fraction(50, 1)) {
309 }
else if (edit_rate ==
Fraction(60, 1)) {
313 }
else if (edit_rate ==
Fraction(96, 1)) {
317 }
else if (edit_rate ==
Fraction(100, 1)) {
321 }
else if (edit_rate ==
Fraction(120, 1)) {
330 DCP_ASSERT (
id.DecodeHex(_asset->id().c_str()));
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);
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]);
345 bs.write_from_byte (0, 4);
346 bs.write_from_word (0, remaining_bits);
Parent class for classes which can write MXF-based assets.
boost::filesystem::path _file
void set_file(boost::filesystem::path file) const
An exception related to a file.
A fraction (i.e. a thing with an integer numerator and an integer denominator).
A miscellaneous exception.
A helper class for writing to SoundAssets.
void write(float const *const *, int)
std::vector< bool > create_sync_packets()
Representation of a sound asset.
int sampling_rate() const
int64_t _intrinsic_duration
Exceptions thrown by libdcp.
Namespace for everything in libdcp.