ff1d02c23057fd5458aa5826ff1675a0884e8ed7
[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 <iostream>
43
44 using std::min;
45 using std::max;
46 using std::cout;
47 using std::string;
48 using std::vector;
49 using namespace dcp;
50
51 struct SoundAssetWriter::ASDCPState
52 {
53         ASDCP::PCM::MXFWriter mxf_writer;
54         ASDCP::PCM::FrameBuffer frame_buffer;
55         ASDCP::WriterInfo writer_info;
56         ASDCP::PCM::AudioDescriptor desc;
57 };
58
59 SoundAssetWriter::SoundAssetWriter (SoundAsset* asset, boost::filesystem::path file, bool sync)
60         : AssetWriter (asset, file)
61         , _state (new SoundAssetWriter::ASDCPState)
62         , _asset (asset)
63         , _frame_buffer_offset (0)
64         , _sync (sync)
65         , _sync_packet (0)
66 {
67         DCP_ASSERT (!_sync || _asset->channels() >= 14);
68         DCP_ASSERT (!_sync || _asset->standard() == SMPTE);
69
70         /* Derived from ASDCP::Wav::SimpleWaveHeader::FillADesc */
71         _state->desc.EditRate = ASDCP::Rational (_asset->edit_rate().numerator, _asset->edit_rate().denominator);
72         _state->desc.AudioSamplingRate = ASDCP::Rational (_asset->sampling_rate(), 1);
73         _state->desc.Locked = 0;
74         _state->desc.ChannelCount = _asset->channels ();
75         _state->desc.QuantizationBits = 24;
76         _state->desc.BlockAlign = 3 * _asset->channels();
77         _state->desc.AvgBps = _asset->sampling_rate() * _state->desc.BlockAlign;
78         _state->desc.LinkedTrackID = 0;
79         if (asset->standard() == INTEROP) {
80                 _state->desc.ChannelFormat = ASDCP::PCM::CF_NONE;
81         } else {
82                 /* Just use WTF ("wild track format") for SMPTE for now; searches suggest that this
83                    uses the same assignment as Interop.
84                 */
85                 _state->desc.ChannelFormat = ASDCP::PCM::CF_CFG_4;
86         }
87
88         /* I'm fairly sure this is not necessary, as ContainerDuration is written
89            in ASDCP's WriteMXFFooter, but it stops a valgrind warning.
90         */
91         _state->desc.ContainerDuration = 0;
92
93         _state->frame_buffer.Capacity (ASDCP::PCM::CalcFrameBufferSize (_state->desc));
94         _state->frame_buffer.Size (ASDCP::PCM::CalcFrameBufferSize (_state->desc));
95         memset (_state->frame_buffer.Data(), 0, _state->frame_buffer.Capacity());
96
97         _asset->fill_writer_info (&_state->writer_info, _asset->id());
98
99         if (_sync) {
100                 _fsk.set_data (create_sync_packets());
101         }
102 }
103
104 void
105 SoundAssetWriter::write (float const * const * data, int frames)
106 {
107         DCP_ASSERT (!_finalized);
108         DCP_ASSERT (frames > 0);
109
110         static float const clip = 1.0f - (1.0f / pow (2, 23));
111
112         if (!_started) {
113                 Kumu::Result_t r = _state->mxf_writer.OpenWrite (_file.string().c_str(), _state->writer_info, _state->desc);
114                 if (ASDCP_FAILURE (r)) {
115                         boost::throw_exception (FileError ("could not open audio MXF for writing", _file.string(), r));
116                 }
117
118                 _asset->set_file (_file);
119                 _started = true;
120         }
121
122         int const ch = _asset->channels ();
123
124         for (int i = 0; i < frames; ++i) {
125
126                 byte_t* out = _state->frame_buffer.Data() + _frame_buffer_offset;
127
128                 /* Write one sample per channel */
129                 for (int j = 0; j < ch; ++j) {
130                         int32_t s = 0;
131                         if (j == 13 && _sync) {
132                                 s = _fsk.get();
133                         } else {
134                                 /* Convert sample to 24-bit int, clipping if necessary. */
135                                 float x = data[j][i];
136                                 if (x > clip) {
137                                         x = clip;
138                                 } else if (x < -clip) {
139                                         x = -clip;
140                                 }
141                                 s = x * (1 << 23);
142                         }
143                         *out++ = (s & 0xff);
144                         *out++ = (s & 0xff00) >> 8;
145                         *out++ = (s & 0xff0000) >> 16;
146                 }
147                 _frame_buffer_offset += 3 * ch;
148
149                 DCP_ASSERT (_frame_buffer_offset <= int (_state->frame_buffer.Capacity()));
150
151                 /* Finish the MXF frame if required */
152                 if (_frame_buffer_offset == int (_state->frame_buffer.Capacity())) {
153                         write_current_frame ();
154                         _frame_buffer_offset = 0;
155                         memset (_state->frame_buffer.Data(), 0, _state->frame_buffer.Capacity());
156                 }
157         }
158 }
159
160 void
161 SoundAssetWriter::write_current_frame ()
162 {
163         ASDCP::Result_t const r = _state->mxf_writer.WriteFrame (_state->frame_buffer, _crypto_context->context(), _crypto_context->hmac());
164         if (ASDCP_FAILURE (r)) {
165                 boost::throw_exception (MiscError (String::compose ("could not write audio MXF frame (%1)", int (r))));
166         }
167
168         ++_frames_written;
169
170         if (_sync) {
171                 /* We need a new set of sync packets for this frame */
172                 _fsk.set_data (create_sync_packets());
173         }
174 }
175
176 bool
177 SoundAssetWriter::finalize ()
178 {
179         if (_frame_buffer_offset > 0) {
180                 write_current_frame ();
181         }
182
183         if (_started) {
184                 ASDCP::Result_t const r = _state->mxf_writer.Finalize();
185                 if (ASDCP_FAILURE(r)) {
186                         boost::throw_exception (MiscError (String::compose ("could not finalise audio MXF (%1)", int(r))));
187                 }
188         }
189
190         _asset->_intrinsic_duration = _frames_written;
191         return AssetWriter::finalize ();
192 }
193
194
195 /** Calculate and return the sync packets required for this edit unit (aka "frame") */
196 vector<bool>
197 SoundAssetWriter::create_sync_packets ()
198 {
199         /* Parts of this code assumes 48kHz */
200         DCP_ASSERT (_asset->sampling_rate() == 48000);
201
202         /* Encoding of edit rate */
203         int edit_rate_code = 0;
204         /* How many 0 bits are used to pad the end of the packet */
205         int remaining_bits = 0;
206         /* How many packets in this edit unit (i.e. "frame") */
207         int packets = 0;
208         Fraction const edit_rate = _asset->edit_rate ();
209         if (edit_rate == Fraction(24, 1)) {
210                 edit_rate_code = 0;
211                 remaining_bits = 25;
212                 packets = 4;
213         } else if (edit_rate == Fraction(25, 1)) {
214                 edit_rate_code = 1;
215                 remaining_bits = 20;
216                 packets = 4;
217         } else if (edit_rate == Fraction(30, 1)) {
218                 edit_rate_code = 2;
219                 remaining_bits = 0;
220                 packets = 4;
221         } else if (edit_rate == Fraction(48, 1)) {
222                 edit_rate_code = 3;
223                 remaining_bits = 25;
224                 packets = 2;
225         } else if (edit_rate == Fraction(50, 1)) {
226                 edit_rate_code = 4;
227                 remaining_bits = 20;
228                 packets = 2;
229         } else if (edit_rate == Fraction(60, 1)) {
230                 edit_rate_code = 5;
231                 remaining_bits = 0;
232                 packets = 2;
233         } else if (edit_rate == Fraction(96, 1)) {
234                 edit_rate_code = 6;
235                 remaining_bits = 25;
236                 packets = 1;
237         } else if (edit_rate == Fraction(100, 1)) {
238                 edit_rate_code = 7;
239                 remaining_bits = 20;
240                 packets = 1;
241         } else if (edit_rate == Fraction(120, 1)) {
242                 edit_rate_code = 8;
243                 remaining_bits = 0;
244                 packets = 1;
245         }
246
247         Bitstream bs;
248
249         Kumu::UUID id;
250         DCP_ASSERT (id.DecodeHex(_asset->id().c_str()));
251
252         for (int i = 0; i < packets; ++i) {
253                 bs.write_from_byte (0x4d);
254                 bs.write_from_byte (0x56);
255                 bs.start_crc (0x1021);
256                 bs.write_from_byte (edit_rate_code, 4);
257                 bs.write_from_byte (0, 2);
258                 bs.write_from_byte (_sync_packet, 2);
259                 bs.write_from_byte (id.Value()[i * 4 + 0]);
260                 bs.write_from_byte (id.Value()[i * 4 + 1]);
261                 bs.write_from_byte (id.Value()[i * 4 + 2]);
262                 bs.write_from_byte (id.Value()[i * 4 + 3]);
263                 bs.write_from_word (_frames_written, 24);
264                 bs.write_crc ();
265                 bs.write_from_byte (0, 4);
266                 bs.write_from_word (0, remaining_bits);
267
268                 ++_sync_packet;
269                 if (_sync_packet == 4) {
270                         _sync_packet = 0;
271                 }
272         }
273
274         return bs.get();
275 }
276