Tidying.
[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                 /* Just use WTF ("wild track format") for SMPTE for now; searches suggest that this
92                    uses the same assignment as Interop.
93                 */
94                 _state->desc.ChannelFormat = ASDCP::PCM::CF_CFG_4;
95         }
96
97         /* I'm fairly sure this is not necessary, as ContainerDuration is written
98            in ASDCP's WriteMXFFooter, but it stops a valgrind warning.
99         */
100         _state->desc.ContainerDuration = 0;
101
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());
105
106         _asset->fill_writer_info (&_state->writer_info, _asset->id());
107
108         if (_sync) {
109                 _fsk.set_data (create_sync_packets());
110         }
111 }
112
113
114 void
115 SoundAssetWriter::start ()
116 {
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));
120         }
121
122         if (_asset->standard() == Standard::SMPTE && !_active_channels.empty()) {
123
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)
127                         );
128                 DCP_ASSERT (essence_descriptor);
129                 essence_descriptor->ChannelAssignment = asdcp_smpte_dict->ul(ASDCP::MDD_DCAudioChannelCfg_MCA);
130
131                 auto soundfield = new ASDCP::MXF::SoundfieldGroupLabelSubDescriptor(asdcp_smpte_dict);
132                 GenRandomValue (soundfield->MCALinkID);
133                 soundfield->RFC5646SpokenLanguage = _asset->language();
134
135                 const MCASoundField field = _asset->channels() > 10 ? MCASoundField::SEVEN_POINT_ONE : MCASoundField::FIVE_POINT_ONE;
136
137                 if (field == MCASoundField::SEVEN_POINT_ONE) {
138                         soundfield->MCATagSymbol = "sg71";
139                         soundfield->MCATagName = "7.1DS";
140                         soundfield->MCALabelDictionaryID = asdcp_smpte_dict->ul(ASDCP::MDD_DCAudioSoundfield_71);
141                 } else {
142                         soundfield->MCATagSymbol = "sg51";
143                         soundfield->MCATagName = "5.1";
144                         soundfield->MCALabelDictionaryID = asdcp_smpte_dict->ul(ASDCP::MDD_DCAudioSoundfield_51);
145                 }
146
147                 _state->mxf_writer.OP1aHeader().AddChildObject(soundfield);
148                 essence_descriptor->SubDescriptors.push_back(soundfield->InstanceUID);
149
150                 for (auto i: _active_channels) {
151                         auto channel = new ASDCP::MXF::AudioChannelLabelSubDescriptor(asdcp_smpte_dict);
152                         GenRandomValue (channel->MCALinkID);
153                         channel->SoundfieldGroupLinkID = soundfield->MCALinkID;
154                         channel->MCAChannelID = static_cast<int>(i) + 1;
155                         channel->MCATagSymbol = "ch" + channel_to_mca_id(i, field);
156                         channel->MCATagName = channel_to_mca_name(i, field);
157                         channel->RFC5646SpokenLanguage = _asset->language();
158                         channel->MCALabelDictionaryID = channel_to_mca_universal_label(i, field, asdcp_smpte_dict);
159                         _state->mxf_writer.OP1aHeader().AddChildObject(channel);
160                         essence_descriptor->SubDescriptors.push_back(channel->InstanceUID);
161                 }
162         }
163
164         _asset->set_file (_file);
165         _started = true;
166 }
167
168
169 void
170 SoundAssetWriter::write (float const * const * data, int frames)
171 {
172         DCP_ASSERT (!_finalized);
173         DCP_ASSERT (frames > 0);
174
175         static float const clip = 1.0f - (1.0f / pow (2, 23));
176
177         if (!_started) {
178                 start ();
179         }
180
181         int const ch = _asset->channels ();
182
183         for (int i = 0; i < frames; ++i) {
184
185                 byte_t* out = _state->frame_buffer.Data() + _frame_buffer_offset;
186
187                 /* Write one sample per channel */
188                 for (int j = 0; j < ch; ++j) {
189                         int32_t s = 0;
190                         if (j == 13 && _sync) {
191                                 s = _fsk.get();
192                         } else {
193                                 /* Convert sample to 24-bit int, clipping if necessary. */
194                                 float x = data[j][i];
195                                 if (x > clip) {
196                                         x = clip;
197                                 } else if (x < -clip) {
198                                         x = -clip;
199                                 }
200                                 s = x * (1 << 23);
201                         }
202                         *out++ = (s & 0xff);
203                         *out++ = (s & 0xff00) >> 8;
204                         *out++ = (s & 0xff0000) >> 16;
205                 }
206                 _frame_buffer_offset += 3 * ch;
207
208                 DCP_ASSERT (_frame_buffer_offset <= int(_state->frame_buffer.Capacity()));
209
210                 /* Finish the MXF frame if required */
211                 if (_frame_buffer_offset == int (_state->frame_buffer.Capacity())) {
212                         write_current_frame ();
213                         _frame_buffer_offset = 0;
214                         memset (_state->frame_buffer.Data(), 0, _state->frame_buffer.Capacity());
215                 }
216         }
217 }
218
219 void
220 SoundAssetWriter::write_current_frame ()
221 {
222         auto const r = _state->mxf_writer.WriteFrame (_state->frame_buffer, _crypto_context->context(), _crypto_context->hmac());
223         if (ASDCP_FAILURE(r)) {
224                 boost::throw_exception (MiscError(String::compose("could not write audio MXF frame (%1)", static_cast<int>(r))));
225         }
226
227         ++_frames_written;
228
229         if (_sync) {
230                 /* We need a new set of sync packets for this frame */
231                 _fsk.set_data (create_sync_packets());
232         }
233 }
234
235 bool
236 SoundAssetWriter::finalize ()
237 {
238         if (_frame_buffer_offset > 0) {
239                 write_current_frame ();
240         }
241
242         if (_started) {
243                 auto const r = _state->mxf_writer.Finalize();
244                 if (ASDCP_FAILURE(r)) {
245                         boost::throw_exception (MiscError(String::compose ("could not finalise audio MXF (%1)", static_cast<int>(r))));
246                 }
247         }
248
249         _asset->_intrinsic_duration = _frames_written;
250         return AssetWriter::finalize ();
251 }
252
253
254 /** Calculate and return the sync packets required for this edit unit (aka "frame") */
255 vector<bool>
256 SoundAssetWriter::create_sync_packets ()
257 {
258         /* Parts of this code assumes 48kHz */
259         DCP_ASSERT (_asset->sampling_rate() == 48000);
260
261         /* Encoding of edit rate */
262         int edit_rate_code = 0;
263         /* How many 0 bits are used to pad the end of the packet */
264         int remaining_bits = 0;
265         /* How many packets in this edit unit (i.e. "frame") */
266         int packets = 0;
267         auto const edit_rate = _asset->edit_rate ();
268         if (edit_rate == Fraction(24, 1)) {
269                 edit_rate_code = 0;
270                 remaining_bits = 25;
271                 packets = 4;
272         } else if (edit_rate == Fraction(25, 1)) {
273                 edit_rate_code = 1;
274                 remaining_bits = 20;
275                 packets = 4;
276         } else if (edit_rate == Fraction(30, 1)) {
277                 edit_rate_code = 2;
278                 remaining_bits = 0;
279                 packets = 4;
280         } else if (edit_rate == Fraction(48, 1)) {
281                 edit_rate_code = 3;
282                 remaining_bits = 25;
283                 packets = 2;
284         } else if (edit_rate == Fraction(50, 1)) {
285                 edit_rate_code = 4;
286                 remaining_bits = 20;
287                 packets = 2;
288         } else if (edit_rate == Fraction(60, 1)) {
289                 edit_rate_code = 5;
290                 remaining_bits = 0;
291                 packets = 2;
292         } else if (edit_rate == Fraction(96, 1)) {
293                 edit_rate_code = 6;
294                 remaining_bits = 25;
295                 packets = 1;
296         } else if (edit_rate == Fraction(100, 1)) {
297                 edit_rate_code = 7;
298                 remaining_bits = 20;
299                 packets = 1;
300         } else if (edit_rate == Fraction(120, 1)) {
301                 edit_rate_code = 8;
302                 remaining_bits = 0;
303                 packets = 1;
304         }
305
306         Bitstream bs;
307
308         Kumu::UUID id;
309         DCP_ASSERT (id.DecodeHex(_asset->id().c_str()));
310
311         for (int i = 0; i < packets; ++i) {
312                 bs.write_from_byte (0x4d);
313                 bs.write_from_byte (0x56);
314                 bs.start_crc (0x1021);
315                 bs.write_from_byte (edit_rate_code, 4);
316                 bs.write_from_byte (0, 2);
317                 bs.write_from_byte (_sync_packet, 2);
318                 bs.write_from_byte (id.Value()[i * 4 + 0]);
319                 bs.write_from_byte (id.Value()[i * 4 + 1]);
320                 bs.write_from_byte (id.Value()[i * 4 + 2]);
321                 bs.write_from_byte (id.Value()[i * 4 + 3]);
322                 bs.write_from_word (_frames_written, 24);
323                 bs.write_crc ();
324                 bs.write_from_byte (0, 4);
325                 bs.write_from_word (0, remaining_bits);
326
327                 ++_sync_packet;
328                 if (_sync_packet == 4) {
329                         _sync_packet = 0;
330                 }
331         }
332
333         return bs.get();
334 }
335