40 #include "compose.hpp"
49 LIBDCP_DISABLE_WARNINGS
50 #include <asdcp/AS_DCP.h>
51 #include <asdcp/KM_fileio.h>
52 #include <asdcp/Metadata.h>
53 LIBDCP_ENABLE_WARNINGS
54 #include <libxml++/nodes/element.h>
55 #include <boost/filesystem.hpp>
62 using std::shared_ptr;
63 using std::dynamic_pointer_cast;
67 SoundAsset::SoundAsset (boost::filesystem::path file)
70 ASDCP::PCM::MXFReader reader;
71 auto r = reader.OpenRead (file.string().c_str());
72 if (ASDCP_FAILURE(r)) {
73 boost::throw_exception (
MXFFileError(
"could not open MXF file for reading", file.string(), r));
76 ASDCP::PCM::AudioDescriptor desc;
77 if (ASDCP_FAILURE (reader.FillAudioDescriptor(desc))) {
78 boost::throw_exception (
ReadError(
"could not read audio MXF information"));
81 _sampling_rate = desc.AudioSamplingRate.Numerator / desc.AudioSamplingRate.Denominator;
82 _channels = desc.ChannelCount;
83 _edit_rate =
Fraction (desc.EditRate.Numerator, desc.EditRate.Denominator);
85 _intrinsic_duration = desc.ContainerDuration;
87 ASDCP::WriterInfo info;
88 if (ASDCP_FAILURE (reader.FillWriterInfo(info))) {
89 boost::throw_exception (
ReadError(
"could not read audio MXF information"));
92 ASDCP::MXF::SoundfieldGroupLabelSubDescriptor* soundfield;
93 auto rr = reader.OP1aHeader().GetMDObjectByType(
94 asdcp_smpte_dict->ul(ASDCP::MDD_SoundfieldGroupLabelSubDescriptor),
95 reinterpret_cast<ASDCP::MXF::InterchangeObject**
>(&soundfield)
99 if (!soundfield->RFC5646SpokenLanguage.empty()) {
101 soundfield->RFC5646SpokenLanguage.get().EncodeString(buffer,
sizeof(buffer));
106 _id = read_writer_info (info);
110 SoundAsset::SoundAsset (
Fraction edit_rate,
int sampling_rate,
int channels,
LanguageTag language, Standard standard)
112 , _edit_rate (edit_rate)
113 , _channels (channels)
114 , _sampling_rate (sampling_rate)
115 , _language (language.to_string())
122 SoundAsset::equals (shared_ptr<const Asset> other,
EqualityOptions opt, NoteHandler note)
const
124 ASDCP::PCM::MXFReader reader_A;
126 auto r = reader_A.OpenRead (
file()->
string().c_str());
127 if (ASDCP_FAILURE(r)) {
128 boost::throw_exception (
MXFFileError(
"could not open MXF file for reading",
file()->
string(), r));
131 ASDCP::PCM::MXFReader reader_B;
132 r = reader_B.OpenRead (other->file()->string().c_str());
133 if (ASDCP_FAILURE (r)) {
134 boost::throw_exception (
MXFFileError(
"could not open MXF file for reading", other->file()->string(), r));
137 ASDCP::PCM::AudioDescriptor desc_A;
138 if (ASDCP_FAILURE (reader_A.FillAudioDescriptor(desc_A))) {
139 boost::throw_exception (
ReadError (
"could not read audio MXF information"));
141 ASDCP::PCM::AudioDescriptor desc_B;
142 if (ASDCP_FAILURE (reader_B.FillAudioDescriptor(desc_B))) {
143 boost::throw_exception (
ReadError (
"could not read audio MXF information"));
146 if (desc_A.EditRate != desc_B.EditRate) {
150 "audio edit rates differ: %1/%2 cf %3/%4",
151 desc_A.EditRate.Numerator, desc_A.EditRate.Denominator, desc_B.EditRate.Numerator, desc_B.EditRate.Denominator
155 }
else if (desc_A.AudioSamplingRate != desc_B.AudioSamplingRate) {
159 "audio sampling rates differ: %1 cf %2",
160 desc_A.AudioSamplingRate.Numerator, desc_A.AudioSamplingRate.Denominator,
161 desc_B.AudioSamplingRate.Numerator, desc_B.AudioSamplingRate.Numerator
165 }
else if (desc_A.Locked != desc_B.Locked) {
166 note (NoteType::ERROR, String::compose (
"audio locked flags differ: %1 cf %2", desc_A.Locked, desc_B.Locked));
168 }
else if (desc_A.ChannelCount != desc_B.ChannelCount) {
169 note (NoteType::ERROR, String::compose (
"audio channel counts differ: %1 cf %2", desc_A.ChannelCount, desc_B.ChannelCount));
171 }
else if (desc_A.QuantizationBits != desc_B.QuantizationBits) {
172 note (NoteType::ERROR, String::compose (
"audio bits per sample differ: %1 cf %2", desc_A.QuantizationBits, desc_B.QuantizationBits));
174 }
else if (desc_A.BlockAlign != desc_B.BlockAlign) {
175 note (NoteType::ERROR, String::compose (
"audio bytes per sample differ: %1 cf %2", desc_A.BlockAlign, desc_B.BlockAlign));
177 }
else if (desc_A.AvgBps != desc_B.AvgBps) {
178 note (NoteType::ERROR, String::compose (
"audio average bps differ: %1 cf %2", desc_A.AvgBps, desc_B.AvgBps));
180 }
else if (desc_A.LinkedTrackID != desc_B.LinkedTrackID) {
181 note (NoteType::ERROR, String::compose (
"audio linked track IDs differ: %1 cf %2", desc_A.LinkedTrackID, desc_B.LinkedTrackID));
183 }
else if (desc_A.ContainerDuration != desc_B.ContainerDuration) {
184 note (NoteType::ERROR, String::compose (
"audio container durations differ: %1 cf %2", desc_A.ContainerDuration, desc_B.ContainerDuration));
186 }
else if (desc_A.ChannelFormat != desc_B.ChannelFormat) {
190 auto other_sound = dynamic_pointer_cast<const SoundAsset> (other);
192 auto reader = start_read ();
193 auto other_reader = other_sound->start_read ();
197 auto frame_A = reader->get_frame (i);
198 auto frame_B = other_reader->get_frame (i);
200 if (frame_A->size() != frame_B->size()) {
201 note (NoteType::ERROR, String::compose (
"sizes of audio data for frame %1 differ", i));
205 if (memcmp (frame_A->data(), frame_B->data(), frame_A->size()) != 0) {
206 for (
int sample = 0; sample < frame_A->samples(); ++sample) {
207 for (
int channel = 0; channel < frame_A->channels(); ++channel) {
208 int32_t
const d = abs(frame_A->get(channel, sample) - frame_B->get(channel, sample));
210 note (NoteType::ERROR, String::compose(
"PCM data difference of %1 in frame %2, channel %3, sample %4", d, i, channel, sample));
222 shared_ptr<SoundAssetWriter>
223 SoundAsset::start_write (boost::filesystem::path file,
bool atmos_sync)
226 throw MiscError (
"Insufficient channels to write ATMOS sync (there must be at least 14)");
233 shared_ptr<SoundAssetReader>
234 SoundAsset::start_read ()
const
241 SoundAsset::static_pkl_type (Standard standard)
244 case Standard::INTEROP:
245 return "application/x-smpte-mxf;asdcpKind=Sound";
246 case Standard::SMPTE:
247 return "application/mxf";
255 SoundAsset::valid_mxf (boost::filesystem::path file)
257 ASDCP::PCM::MXFReader reader;
258 Kumu::Result_t r = reader.OpenRead (
file.string().c_str());
259 return !ASDCP_FAILURE (r);
Parent class for DCP assets, i.e. picture, sound, subtitles, closed captions, CPLs,...
boost::optional< boost::filesystem::path > file() const
A fraction (i.e. a thing with an integer numerator and an integer denominator).
An exception related to an MXF file.
Parent for classes which represent MXF files.
boost::optional< Key > key() const
A miscellaneous exception.
Any error that occurs when reading data from a DCP.
A helper class for writing to SoundAssets.
int _channels
number of channels
int64_t _intrinsic_duration
Exceptions thrown by libdcp.
Namespace for everything in libdcp.
SoundAssetReader typedef.
A class to describe what "equality" means for a particular test.
int max_audio_sample_error
Utility methods and classes.