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.h
36 * @brief SoundAssetWriter class
40 #include "asset_writer.h"
41 #include "dcp_assert.h"
43 #include "sound_asset.h"
44 #include "sound_frame.h"
46 #include <boost/filesystem.hpp>
47 #include <boost/shared_array.hpp>
56 namespace sound_asset_writer {
59 int32_t convert(T) { return {}; }
62 inline int32_t convert(int32_t x)
64 int constexpr clip = (1 << 23);
65 return std::max(-clip, std::min(clip, x));
69 inline int32_t convert(float x)
71 float constexpr clip = 1.0f - (1.0f / (1 << 23));
72 float constexpr scale = (1 << 23);
73 auto const clipped = std::max(-clip, std::min(clip, x));
74 return std::lround(clipped * scale);
83 /** @class SoundAssetWriter
84 * @brief A helper class for writing to SoundAssets.
86 * Objects of this class can only be created with SoundAsset::start_write().
88 * Sound samples can be written to the SoundAsset by calling write() with
89 * a buffer of float values. finalize() must be called after the last samples
92 class SoundAssetWriter : public AssetWriter
97 /** @param data Pointer an array of float pointers, one for each channel.
98 * @param channels Number of channels in data; if this is less than the channels in the asset
99 * the remaining asset channels will be padded with silence.
100 * @param frames Number of frames i.e. number of floats that are given for each channel.
102 void write(float const * const * data, int channels, int frames);
104 /** @param data Pointer an array of int32_t pointers, one for each channel.
105 * The 24-bit audio sample should be in the lower 24 bits of the int32_t.
106 * @param channels Number of channels in data; if this is less than the channels in the asset
107 * the remaining asset channels will be padded with silence.
108 * @param frames Number of frames i.e. number of floats that are given for each channel.
110 void write(int32_t const * const * data, int channels, int frames);
112 bool finalize () override;
115 friend class SoundAsset;
116 friend struct ::sync_test1;
118 byte_t* frame_buffer_data() const;
119 int frame_buffer_capacity() const;
123 do_write(T const * const * data, int data_channels, int frames)
125 DCP_ASSERT(!_finalized);
126 DCP_ASSERT(frames > 0);
128 auto const asset_channels = _asset->channels();
129 DCP_ASSERT(data_channels <= asset_channels);
135 for (int i = 0; i < frames; ++i) {
137 auto out = frame_buffer_data() + _frame_buffer_offset;
139 /* Write one sample per asset channel */
140 for (int j = 0; j < asset_channels; ++j) {
142 if (j == 13 && _sync) {
144 } else if (j < data_channels) {
145 s = sound_asset_writer::convert(data[j][i]);
148 *out++ = (s & 0xff00) >> 8;
149 *out++ = (s & 0xff0000) >> 16;
151 _frame_buffer_offset += 3 * asset_channels;
153 DCP_ASSERT(_frame_buffer_offset <= frame_buffer_capacity());
155 /* Finish the MXF frame if required */
156 if (_frame_buffer_offset == frame_buffer_capacity()) {
157 write_current_frame();
158 _frame_buffer_offset = 0;
159 memset(frame_buffer_data(), 0, frame_buffer_capacity());
164 SoundAssetWriter(SoundAsset *, boost::filesystem::path, std::vector<dcp::Channel> extra_active_channels, bool sync, bool include_mca_subdescriptors);
167 void write_current_frame ();
168 std::vector<bool> create_sync_packets ();
170 /* do this with an opaque pointer so we don't have to include
175 std::shared_ptr<ASDCPState> _state;
177 SoundAsset* _asset = nullptr;
178 int _frame_buffer_offset = 0;
180 std::vector<dcp::Channel> _extra_active_channels;
181 /** true to ignore any signal passed to write() on channel 14 and instead write a sync track */
183 /** index of the sync packet (0-3) which starts the next edit unit */
184 int _sync_packet = 0;
186 bool _include_mca_subdescriptors = true;