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"
44 #include "sound_asset.h"
45 #include "sound_frame.h"
47 #include <boost/filesystem.hpp>
48 #include <boost/shared_array.hpp>
57 namespace sound_asset_writer {
60 int32_t convert(T) { return {}; }
63 inline int32_t convert(int32_t x)
65 int constexpr clip = (1 << 23);
66 return std::max(-clip, std::min(clip, x));
70 inline int32_t convert(float x)
72 float constexpr clip = 1.0f - (1.0f / (1 << 23));
73 float constexpr scale = (1 << 23);
74 auto const clipped = std::max(-clip, std::min(clip, x));
75 return std::lround(clipped * scale);
84 /** @class SoundAssetWriter
85 * @brief A helper class for writing to SoundAssets.
87 * Objects of this class can only be created with SoundAsset::start_write().
89 * Sound samples can be written to the SoundAsset by calling write() with
90 * a buffer of float values. finalize() must be called after the last samples
93 class SoundAssetWriter : public AssetWriter
98 /** @param data Pointer an array of float pointers, one for each channel.
99 * @param channels Number of channels in data; if this is less than the channels in the asset
100 * the remaining asset channels will be padded with silence.
101 * @param frames Number of frames i.e. number of floats that are given for each channel.
103 void write(float const * const * data, int channels, int frames);
105 /** @param data Pointer an array of int32_t pointers, one for each channel.
106 * The 24-bit audio sample should be in the lower 24 bits of the int32_t.
107 * @param channels Number of channels in data; if this is less than the channels in the asset
108 * the remaining asset channels will be padded with silence.
109 * @param frames Number of frames i.e. number of floats that are given for each channel.
111 void write(int32_t const * const * data, int channels, int frames);
113 bool finalize () override;
116 friend class SoundAsset;
117 friend struct ::sync_test1;
119 byte_t* frame_buffer_data() const;
120 int frame_buffer_capacity() const;
124 do_write(T const * const * data, int data_channels, int frames)
126 DCP_ASSERT(!_finalized);
127 DCP_ASSERT(frames > 0);
129 auto const asset_channels = _asset->channels();
130 DCP_ASSERT(data_channels <= asset_channels);
136 for (int i = 0; i < frames; ++i) {
138 auto out = frame_buffer_data() + _frame_buffer_offset;
140 /* Write one sample per asset channel */
141 for (int j = 0; j < asset_channels; ++j) {
143 if (j == 13 && _sync) {
145 } else if (j < data_channels) {
146 s = sound_asset_writer::convert(data[j][i]);
149 *out++ = (s & 0xff00) >> 8;
150 *out++ = (s & 0xff0000) >> 16;
152 _frame_buffer_offset += 3 * asset_channels;
154 DCP_ASSERT(_frame_buffer_offset <= frame_buffer_capacity());
156 /* Finish the MXF frame if required */
157 if (_frame_buffer_offset == frame_buffer_capacity()) {
158 write_current_frame();
159 _frame_buffer_offset = 0;
160 memset(frame_buffer_data(), 0, frame_buffer_capacity());
165 SoundAssetWriter(SoundAsset *, boost::filesystem::path, std::vector<dcp::Channel> extra_active_channels, bool sync, bool include_mca_subdescriptors);
168 void write_current_frame ();
169 std::vector<bool> create_sync_packets ();
171 /* do this with an opaque pointer so we don't have to include
176 std::shared_ptr<ASDCPState> _state;
178 SoundAsset* _asset = nullptr;
179 int _frame_buffer_offset = 0;
181 std::vector<dcp::Channel> _extra_active_channels;
182 /** true to ignore any signal passed to write() on channel 14 and instead write a sync track */
184 /** index of the sync packet (0-3) which starts the next edit unit */
185 int _sync_packet = 0;
187 bool _include_mca_subdescriptors = true;