2 Copyright (C) 2012-2016 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.
34 /** @file src/sound_mxf.cc
35 * @brief SoundAsset class.
38 #include "sound_asset.h"
40 #include "exceptions.h"
41 #include "sound_frame.h"
42 #include "sound_asset_writer.h"
43 #include "sound_asset_reader.h"
44 #include "compose.hpp"
45 #include "dcp_assert.h"
46 #include <asdcp/KM_fileio.h>
47 #include <asdcp/AS_DCP.h>
48 #include <libxml++/nodes/element.h>
49 #include <boost/filesystem.hpp>
55 using boost::shared_ptr;
56 using boost::dynamic_pointer_cast;
59 SoundAsset::SoundAsset (boost::filesystem::path file)
62 ASDCP::PCM::MXFReader reader;
63 Kumu::Result_t r = reader.OpenRead (file.string().c_str());
64 if (ASDCP_FAILURE (r)) {
65 boost::throw_exception (MXFFileError ("could not open MXF file for reading", file.string(), r));
68 ASDCP::PCM::AudioDescriptor desc;
69 if (ASDCP_FAILURE (reader.FillAudioDescriptor (desc))) {
70 boost::throw_exception (DCPReadError ("could not read audio MXF information"));
73 _sampling_rate = desc.AudioSamplingRate.Numerator / desc.AudioSamplingRate.Denominator;
74 _channels = desc.ChannelCount;
75 _edit_rate = Fraction (desc.EditRate.Numerator, desc.EditRate.Denominator);
77 _intrinsic_duration = desc.ContainerDuration;
79 ASDCP::WriterInfo info;
80 if (ASDCP_FAILURE (reader.FillWriterInfo (info))) {
81 boost::throw_exception (DCPReadError ("could not read audio MXF information"));
84 _id = read_writer_info (info);
87 SoundAsset::SoundAsset (Fraction edit_rate, int sampling_rate, int channels)
88 : _edit_rate (edit_rate)
89 , _intrinsic_duration (0)
90 , _channels (channels)
91 , _sampling_rate (sampling_rate)
97 SoundAsset::equals (shared_ptr<const Asset> other, EqualityOptions opt, NoteHandler note) const
99 ASDCP::PCM::MXFReader reader_A;
100 DCP_ASSERT (file ());
101 Kumu::Result_t r = reader_A.OpenRead (file()->string().c_str());
102 if (ASDCP_FAILURE (r)) {
103 boost::throw_exception (MXFFileError ("could not open MXF file for reading", file()->string(), r));
106 ASDCP::PCM::MXFReader reader_B;
107 r = reader_B.OpenRead (other->file()->string().c_str());
108 if (ASDCP_FAILURE (r)) {
109 boost::throw_exception (MXFFileError ("could not open MXF file for reading", other->file()->string(), r));
112 ASDCP::PCM::AudioDescriptor desc_A;
113 if (ASDCP_FAILURE (reader_A.FillAudioDescriptor (desc_A))) {
114 boost::throw_exception (DCPReadError ("could not read audio MXF information"));
116 ASDCP::PCM::AudioDescriptor desc_B;
117 if (ASDCP_FAILURE (reader_B.FillAudioDescriptor (desc_B))) {
118 boost::throw_exception (DCPReadError ("could not read audio MXF information"));
121 if (desc_A.EditRate != desc_B.EditRate) {
125 "audio edit rates differ: %1/%2 cf %3/%4",
126 desc_A.EditRate.Numerator, desc_A.EditRate.Denominator, desc_B.EditRate.Numerator, desc_B.EditRate.Denominator
130 } else if (desc_A.AudioSamplingRate != desc_B.AudioSamplingRate) {
134 "audio sampling rates differ: %1 cf %2",
135 desc_A.AudioSamplingRate.Numerator, desc_A.AudioSamplingRate.Denominator,
136 desc_B.AudioSamplingRate.Numerator, desc_B.AudioSamplingRate.Numerator
140 } else if (desc_A.Locked != desc_B.Locked) {
141 note (DCP_ERROR, String::compose ("audio locked flags differ: %1 cf %2", desc_A.Locked, desc_B.Locked));
143 } else if (desc_A.ChannelCount != desc_B.ChannelCount) {
144 note (DCP_ERROR, String::compose ("audio channel counts differ: %1 cf %2", desc_A.ChannelCount, desc_B.ChannelCount));
146 } else if (desc_A.QuantizationBits != desc_B.QuantizationBits) {
147 note (DCP_ERROR, String::compose ("audio bits per sample differ: %1 cf %2", desc_A.QuantizationBits, desc_B.QuantizationBits));
149 } else if (desc_A.BlockAlign != desc_B.BlockAlign) {
150 note (DCP_ERROR, String::compose ("audio bytes per sample differ: %1 cf %2", desc_A.BlockAlign, desc_B.BlockAlign));
152 } else if (desc_A.AvgBps != desc_B.AvgBps) {
153 note (DCP_ERROR, String::compose ("audio average bps differ: %1 cf %2", desc_A.AvgBps, desc_B.AvgBps));
155 } else if (desc_A.LinkedTrackID != desc_B.LinkedTrackID) {
156 note (DCP_ERROR, String::compose ("audio linked track IDs differ: %1 cf %2", desc_A.LinkedTrackID, desc_B.LinkedTrackID));
158 } else if (desc_A.ContainerDuration != desc_B.ContainerDuration) {
159 note (DCP_ERROR, String::compose ("audio container durations differ: %1 cf %2", desc_A.ContainerDuration, desc_B.ContainerDuration));
161 } else if (desc_A.ChannelFormat != desc_B.ChannelFormat) {
165 shared_ptr<const SoundAsset> other_sound = dynamic_pointer_cast<const SoundAsset> (other);
167 shared_ptr<const SoundAssetReader> reader = start_read ();
168 shared_ptr<const SoundAssetReader> other_reader = other_sound->start_read ();
170 for (int i = 0; i < _intrinsic_duration; ++i) {
172 shared_ptr<const SoundFrame> frame_A = reader->get_frame (i);
173 shared_ptr<const SoundFrame> frame_B = other_reader->get_frame (i);
175 if (frame_A->size() != frame_B->size()) {
176 note (DCP_ERROR, String::compose ("sizes of audio data for frame %1 differ", i));
180 if (memcmp (frame_A->data(), frame_B->data(), frame_A->size()) != 0) {
181 for (int i = 0; i < frame_A->size(); ++i) {
182 int const d = abs (frame_A->data()[i] - frame_B->data()[i]);
183 if (d > opt.max_audio_sample_error) {
184 note (DCP_ERROR, String::compose ("PCM data difference of %1", d));
194 shared_ptr<SoundAssetWriter>
195 SoundAsset::start_write (boost::filesystem::path file, Standard standard)
197 /* XXX: can't we use a shared_ptr here? */
198 return shared_ptr<SoundAssetWriter> (new SoundAssetWriter (this, file, standard));
201 shared_ptr<SoundAssetReader>
202 SoundAsset::start_read () const
204 return shared_ptr<SoundAssetReader> (new SoundAssetReader (this, key ()));
208 SoundAsset::pkl_type (Standard standard) const
212 return "application/x-smpte-mxf;asdcpKind=Sound";
214 return "application/mxf";
221 SoundAsset::valid_mxf (boost::filesystem::path file)
223 ASDCP::PCM::MXFReader reader;
224 Kumu::Result_t r = reader.OpenRead (file.string().c_str ());
225 return !ASDCP_FAILURE (r);