summaryrefslogtreecommitdiff
path: root/src/lib
diff options
context:
space:
mode:
authorCarl Hetherington <cth@carlh.net>2013-03-01 23:36:22 +0000
committerCarl Hetherington <cth@carlh.net>2013-03-01 23:36:22 +0000
commitab1a666e724911c41dfe08fc96748b38ace839c1 (patch)
tree3b9bd2f9fe80156e5dfaf4e5cb72080e21ba9816 /src/lib
parentb1b3e2e30ce8152b03c9cd3502a00b1814c654ea (diff)
parentff950a024d0bfd892354d53e851c3915a68d2c89 (diff)
Merge.
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/analyse_audio_job.cc110
-rw-r--r--src/lib/analyse_audio_job.h44
-rw-r--r--src/lib/audio_analysis.cc125
-rw-r--r--src/lib/audio_analysis.h67
-rw-r--r--src/lib/encoder.cc26
-rw-r--r--src/lib/ffmpeg_decoder.cc14
-rw-r--r--src/lib/film.cc46
-rw-r--r--src/lib/film.h8
-rw-r--r--src/lib/job.cc1
-rw-r--r--src/lib/job.h1
-rw-r--r--src/lib/options.h6
-rw-r--r--src/lib/util.cc85
-rw-r--r--src/lib/util.h16
-rw-r--r--src/lib/writer.cc6
-rw-r--r--src/lib/wscript2
15 files changed, 513 insertions, 44 deletions
diff --git a/src/lib/analyse_audio_job.cc b/src/lib/analyse_audio_job.cc
new file mode 100644
index 000000000..ca316f70e
--- /dev/null
+++ b/src/lib/analyse_audio_job.cc
@@ -0,0 +1,110 @@
+/*
+ Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "audio_analysis.h"
+#include "analyse_audio_job.h"
+#include "compose.hpp"
+#include "film.h"
+#include "options.h"
+#include "decoder_factory.h"
+#include "audio_decoder.h"
+
+#include "i18n.h"
+
+using std::string;
+using std::max;
+using std::cout;
+using boost::shared_ptr;
+
+int const AnalyseAudioJob::_num_points = 1024;
+
+AnalyseAudioJob::AnalyseAudioJob (shared_ptr<Film> f)
+ : Job (f)
+ , _done (0)
+ , _samples_per_point (1)
+{
+
+}
+
+string
+AnalyseAudioJob::name () const
+{
+ return String::compose (_("Analyse audio of %1"), _film->name());
+}
+
+void
+AnalyseAudioJob::run ()
+{
+ if (!_film->audio_stream () || !_film->length()) {
+ set_progress (1);
+ set_state (FINISHED_ERROR);
+ return;
+ }
+
+ DecodeOptions options;
+ options.decode_video = false;
+
+ Decoders decoders = decoder_factory (_film, options);
+ assert (decoders.audio);
+
+ decoders.audio->set_audio_stream (_film->audio_stream ());
+ decoders.audio->Audio.connect (bind (&AnalyseAudioJob::audio, this, _1));
+
+ int64_t total_audio_frames = video_frames_to_audio_frames (_film->length().get(), _film->audio_stream()->sample_rate(), _film->frames_per_second());
+ _samples_per_point = total_audio_frames / _num_points;
+
+ _current.resize (_film->audio_stream()->channels ());
+ _analysis.reset (new AudioAnalysis (_film->audio_stream()->channels()));
+
+ while (!decoders.audio->pass()) {
+ set_progress (float (_done) / total_audio_frames);
+ }
+
+ _analysis->write (_film->audio_analysis_path ());
+
+ set_progress (1);
+ set_state (FINISHED_OK);
+}
+
+void
+AnalyseAudioJob::audio (shared_ptr<AudioBuffers> b)
+{
+ for (int i = 0; i < b->frames(); ++i) {
+ for (int j = 0; j < b->channels(); ++j) {
+ float s = b->data(j)[i];
+ if (fabsf (s) < 10e-7) {
+ /* stringstream can't serialise and recover inf or -inf, so prevent such
+ values by replacing with this (140dB down) */
+ s = 10e-7;
+ }
+ _current[j][AudioPoint::RMS] += pow (s, 2);
+ _current[j][AudioPoint::PEAK] = max (_current[j][AudioPoint::PEAK], fabsf (s));
+
+ if ((_done % _samples_per_point) == 0) {
+ _current[j][AudioPoint::RMS] = sqrt (_current[j][AudioPoint::RMS] / _samples_per_point);
+ _analysis->add_point (j, _current[j]);
+
+ _current[j] = AudioPoint ();
+ }
+ }
+
+ ++_done;
+ }
+}
+
diff --git a/src/lib/analyse_audio_job.h b/src/lib/analyse_audio_job.h
new file mode 100644
index 000000000..dc1e073ee
--- /dev/null
+++ b/src/lib/analyse_audio_job.h
@@ -0,0 +1,44 @@
+/*
+ Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "job.h"
+#include "audio_analysis.h"
+
+class AudioBuffers;
+
+class AnalyseAudioJob : public Job
+{
+public:
+ AnalyseAudioJob (boost::shared_ptr<Film> f);
+
+ std::string name () const;
+ void run ();
+
+private:
+ void audio (boost::shared_ptr<AudioBuffers>);
+
+ int64_t _done;
+ int64_t _samples_per_point;
+ std::vector<AudioPoint> _current;
+
+ boost::shared_ptr<AudioAnalysis> _analysis;
+
+ static const int _num_points;
+};
+
diff --git a/src/lib/audio_analysis.cc b/src/lib/audio_analysis.cc
new file mode 100644
index 000000000..9d708bbfd
--- /dev/null
+++ b/src/lib/audio_analysis.cc
@@ -0,0 +1,125 @@
+/*
+ Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <stdint.h>
+#include <cmath>
+#include <cassert>
+#include <fstream>
+#include <boost/filesystem.hpp>
+#include "audio_analysis.h"
+
+using std::ostream;
+using std::istream;
+using std::string;
+using std::ofstream;
+using std::ifstream;
+using std::vector;
+using std::cout;
+using std::max;
+using std::list;
+
+AudioPoint::AudioPoint ()
+{
+ for (int i = 0; i < COUNT; ++i) {
+ _data[i] = 0;
+ }
+}
+
+AudioPoint::AudioPoint (istream& s)
+{
+ for (int i = 0; i < COUNT; ++i) {
+ s >> _data[i];
+ }
+}
+
+void
+AudioPoint::write (ostream& s) const
+{
+ for (int i = 0; i < COUNT; ++i) {
+ s << _data[i] << "\n";
+ }
+}
+
+
+AudioAnalysis::AudioAnalysis (int channels)
+{
+ _data.resize (channels);
+}
+
+AudioAnalysis::AudioAnalysis (string filename)
+{
+ ifstream f (filename.c_str ());
+
+ int channels;
+ f >> channels;
+ _data.resize (channels);
+
+ for (int i = 0; i < channels; ++i) {
+ int points;
+ f >> points;
+ for (int j = 0; j < points; ++j) {
+ _data[i].push_back (AudioPoint (f));
+ }
+ }
+}
+
+void
+AudioAnalysis::add_point (int c, AudioPoint const & p)
+{
+ assert (c < channels ());
+ _data[c].push_back (p);
+}
+
+AudioPoint
+AudioAnalysis::get_point (int c, int p) const
+{
+ assert (p < points (c));
+ return _data[c][p];
+}
+
+int
+AudioAnalysis::channels () const
+{
+ return _data.size ();
+}
+
+int
+AudioAnalysis::points (int c) const
+{
+ assert (c < channels ());
+ return _data[c].size ();
+}
+
+void
+AudioAnalysis::write (string filename)
+{
+ string tmp = filename + ".tmp";
+
+ ofstream f (tmp.c_str ());
+ f << _data.size() << "\n";
+ for (vector<vector<AudioPoint> >::iterator i = _data.begin(); i != _data.end(); ++i) {
+ f << i->size () << "\n";
+ for (vector<AudioPoint>::iterator j = i->begin(); j != i->end(); ++j) {
+ j->write (f);
+ }
+ }
+
+ f.close ();
+ boost::filesystem::rename (tmp, filename);
+}
diff --git a/src/lib/audio_analysis.h b/src/lib/audio_analysis.h
new file mode 100644
index 000000000..6e0e2b78a
--- /dev/null
+++ b/src/lib/audio_analysis.h
@@ -0,0 +1,67 @@
+/*
+ Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef DVDOMATIC_AUDIO_ANALYSIS_H
+#define DVDOMATIC_AUDIO_ANALYSIS_H
+
+#include <iostream>
+#include <vector>
+#include <list>
+
+class AudioPoint
+{
+public:
+ enum Type {
+ PEAK,
+ RMS,
+ COUNT
+ };
+
+ AudioPoint ();
+ AudioPoint (std::istream &);
+
+ void write (std::ostream &) const;
+
+ float& operator[] (int t) {
+ return _data[t];
+ }
+
+private:
+ float _data[COUNT];
+};
+
+class AudioAnalysis
+{
+public:
+ AudioAnalysis (int c);
+ AudioAnalysis (std::string);
+
+ void add_point (int c, AudioPoint const & p);
+
+ AudioPoint get_point (int c, int p) const;
+ int points (int c) const;
+ int channels () const;
+
+ void write (std::string);
+
+private:
+ std::vector<std::vector<AudioPoint> > _data;
+};
+
+#endif
diff --git a/src/lib/encoder.cc b/src/lib/encoder.cc
index d4a27d01b..3cc643cd6 100644
--- a/src/lib/encoder.cc
+++ b/src/lib/encoder.cc
@@ -425,18 +425,20 @@ Encoder::encoder_thread (ServerDescription* server)
void
Encoder::write_audio (shared_ptr<const AudioBuffers> data)
{
- if (_film->audio_channels() == 1) {
- /* We need to switch things around so that the mono channel is on
- the centre channel of a 5.1 set (with other channels silent).
- */
-
- shared_ptr<AudioBuffers> b (new AudioBuffers (6, data->frames ()));
- b->make_silent (libdcp::LEFT);
- b->make_silent (libdcp::RIGHT);
- memcpy (b->data()[libdcp::CENTRE], data->data()[0], data->frames() * sizeof(float));
- b->make_silent (libdcp::LFE);
- b->make_silent (libdcp::LS);
- b->make_silent (libdcp::RS);
+ AudioMapping m (_film->audio_channels ());
+ if (m.dcp_channels() != _film->audio_channels()) {
+
+ /* Remap (currently just for mono -> 5.1) */
+
+ shared_ptr<AudioBuffers> b (new AudioBuffers (m.dcp_channels(), data->frames ()));
+ for (int i = 0; i < m.dcp_channels(); ++i) {
+ optional<int> s = m.dcp_to_source (static_cast<libdcp::Channel> (i));
+ if (!s) {
+ b->make_silent (i);
+ } else {
+ memcpy (b->data()[i], data->data()[s.get()], data->frames() * sizeof(float));
+ }
+ }
data = b;
}
diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc
index 8834f28ed..462db283a 100644
--- a/src/lib/ffmpeg_decoder.cc
+++ b/src/lib/ffmpeg_decoder.cc
@@ -199,7 +199,7 @@ FFmpegDecoder::setup_audio ()
void
FFmpegDecoder::setup_subtitle ()
{
- if (!_subtitle_stream) {
+ if (!_subtitle_stream || _subtitle_stream->id() >= int (_format_context->nb_streams)) {
return;
}
@@ -238,8 +238,10 @@ FFmpegDecoder::pass ()
int frame_finished;
- while (avcodec_decode_video2 (_video_codec_context, _frame, &frame_finished, &_packet) >= 0 && frame_finished) {
- filter_and_emit_video (_frame);
+ if (_opt.decode_video) {
+ while (avcodec_decode_video2 (_video_codec_context, _frame, &frame_finished, &_packet) >= 0 && frame_finished) {
+ filter_and_emit_video (_frame);
+ }
}
if (_audio_stream && _opt.decode_audio) {
@@ -260,7 +262,7 @@ FFmpegDecoder::pass ()
shared_ptr<FFmpegAudioStream> ffa = dynamic_pointer_cast<FFmpegAudioStream> (_audio_stream);
- if (_packet.stream_index == _video_stream) {
+ if (_packet.stream_index == _video_stream && _opt.decode_video) {
int frame_finished;
int const r = avcodec_decode_video2 (_video_codec_context, _frame, &frame_finished, &_packet);
@@ -290,9 +292,9 @@ FFmpegDecoder::pass ()
was before this packet. Until then audio is thrown away.
*/
- if (_first_video && _first_video.get() <= source_pts_seconds) {
+ if ((_first_video && _first_video.get() <= source_pts_seconds) || !_opt.decode_video) {
- if (!_first_audio) {
+ if (!_first_audio && _opt.decode_video) {
_first_audio = source_pts_seconds;
/* This is our first audio frame, and if we've arrived here we must have had our
diff --git a/src/lib/film.cc b/src/lib/film.cc
index c119f1515..510158e94 100644
--- a/src/lib/film.cc
+++ b/src/lib/film.cc
@@ -51,6 +51,7 @@
#include "video_decoder.h"
#include "audio_decoder.h"
#include "external_audio_decoder.h"
+#include "analyse_audio_job.h"
#include "i18n.h"
@@ -239,6 +240,15 @@ Film::video_mxf_filename () const
return video_state_identifier() + N_(".mxf");
}
+string
+Film::audio_analysis_path () const
+{
+ boost::filesystem::path p;
+ p /= "analysis";
+ p /= content_digest();
+ return file (p.string ());
+}
+
/** Add suitable Jobs to the JobManager to create a DCP for this Film */
void
Film::make_dcp ()
@@ -305,6 +315,19 @@ Film::make_dcp ()
}
}
+/** Start a job to analyse the audio of our content file */
+void
+Film::analyse_audio ()
+{
+ if (_analyse_audio_job) {
+ return;
+ }
+
+ _analyse_audio_job.reset (new AnalyseAudioJob (shared_from_this()));
+ _analyse_audio_job->Finished.connect (bind (&Film::analyse_audio_finished, this));
+ JobManager::instance()->add (_analyse_audio_job);
+}
+
/** Start a job to examine our content file */
void
Film::examine_content ()
@@ -319,6 +342,15 @@ Film::examine_content ()
}
void
+Film::analyse_audio_finished ()
+{
+ ensure_ui_thread ();
+ _analyse_audio_job.reset ();
+
+ AudioAnalysisFinished ();
+}
+
+void
Film::examine_content_finished ()
{
_examine_content_job.reset ();
@@ -872,6 +904,13 @@ Film::set_content (string c)
set_content_audio_streams (d.audio->audio_streams ());
}
+ {
+ boost::mutex::scoped_lock lm (_state_mutex);
+ _content = c;
+ }
+
+ signal_changed (CONTENT);
+
/* Start off with the first audio and subtitle streams */
if (d.audio && !d.audio->audio_streams().empty()) {
set_content_audio_stream (d.audio->audio_streams().front());
@@ -881,13 +920,6 @@ Film::set_content (string c)
set_subtitle_stream (d.video->subtitle_streams().front());
}
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _content = c;
- }
-
- signal_changed (CONTENT);
-
examine_content ();
} catch (...) {
diff --git a/src/lib/film.h b/src/lib/film.h
index 04a483998..847ab434e 100644
--- a/src/lib/film.h
+++ b/src/lib/film.h
@@ -45,6 +45,7 @@ class Job;
class Filter;
class Log;
class ExamineContentJob;
+class AnalyseAudioJob;
class ExternalAudioStream;
/** @class Film
@@ -65,8 +66,10 @@ public:
std::string info_path (int f) const;
std::string video_mxf_dir () const;
std::string video_mxf_filename () const;
+ std::string audio_analysis_path () const;
void examine_content ();
+ void analyse_audio ();
void send_dcp_to_tms ();
void make_dcp ();
@@ -364,6 +367,8 @@ public:
/** Emitted when some property has changed */
mutable boost::signals2::signal<void (Property)> Changed;
+ boost::signals2::signal<void ()> AudioAnalysisFinished;
+
/** Current version number of the state file */
static int const state_version;
@@ -374,9 +379,12 @@ private:
/** Any running ExamineContentJob, or 0 */
boost::shared_ptr<ExamineContentJob> _examine_content_job;
+ /** Any running AnalyseAudioJob, or 0 */
+ boost::shared_ptr<AnalyseAudioJob> _analyse_audio_job;
void signal_changed (Property);
void examine_content_finished ();
+ void analyse_audio_finished ();
std::string video_state_identifier () const;
/** Complete path to directory containing the film metadata;
diff --git a/src/lib/job.cc b/src/lib/job.cc
index 77d367136..8c1612a55 100644
--- a/src/lib/job.cc
+++ b/src/lib/job.cc
@@ -150,7 +150,6 @@ Job::set_state (State s)
if (_state == FINISHED_OK || _state == FINISHED_ERROR) {
_ran_for = elapsed_time ();
- Finished ();
}
}
diff --git a/src/lib/job.h b/src/lib/job.h
index 1538e2779..c98dbaea1 100644
--- a/src/lib/job.h
+++ b/src/lib/job.h
@@ -65,6 +65,7 @@ public:
void descend (float);
float overall_progress () const;
+ /** Emitted by the JobManagerView from the UI thread */
boost::signals2::signal<void()> Finished;
protected:
diff --git a/src/lib/options.h b/src/lib/options.h
index 2cd7dffde..0d2c07fd5 100644
--- a/src/lib/options.h
+++ b/src/lib/options.h
@@ -28,11 +28,13 @@ class DecodeOptions
{
public:
DecodeOptions ()
- : decode_audio (true)
+ : decode_video (true)
+ , decode_audio (true)
, decode_subtitles (false)
, video_sync (true)
{}
-
+
+ bool decode_video;
bool decode_audio;
bool decode_subtitles;
bool video_sync;
diff --git a/src/lib/util.cc b/src/lib/util.cc
index 3d70a3122..de69636da 100644
--- a/src/lib/util.cc
+++ b/src/lib/util.cc
@@ -450,19 +450,6 @@ dcp_audio_sample_rate (int fs)
return 96000;
}
-int
-dcp_audio_channels (int f)
-{
- if (f == 1) {
- /* The source is mono, so to put the mono channel into
- the centre we need to generate a 5.1 soundtrack.
- */
- return 6;
- }
-
- return f;
-}
-
bool operator== (Crop const & a, Crop const & b)
{
return (a.left == b.left && a.right == b.right && a.top == b.top && a.bottom == b.bottom);
@@ -903,3 +890,75 @@ cpu_info ()
return info;
}
+
+string
+audio_channel_name (int c)
+{
+ assert (MAX_AUDIO_CHANNELS == 6);
+
+ /* TRANSLATORS: these are the names of audio channels; Lfe (sub) is the low-frequency
+ enhancement channel (sub-woofer)./
+ */
+ string const channels[] = {
+ "Left",
+ "Right",
+ "Centre",
+ "Lfe (sub)",
+ "Left surround",
+ "Right surround",
+ };
+
+ return channels[c];
+}
+
+AudioMapping::AudioMapping (int c)
+ : _source_channels (c)
+{
+
+}
+
+optional<libdcp::Channel>
+AudioMapping::source_to_dcp (int c) const
+{
+ if (c >= _source_channels) {
+ return optional<libdcp::Channel> ();
+ }
+
+ if (_source_channels == 1) {
+ /* mono sources to centre */
+ return libdcp::CENTRE;
+ }
+
+ return static_cast<libdcp::Channel> (c);
+}
+
+optional<int>
+AudioMapping::dcp_to_source (libdcp::Channel c) const
+{
+ if (_source_channels == 1) {
+ if (c == libdcp::CENTRE) {
+ return 0;
+ } else {
+ return optional<int> ();
+ }
+ }
+
+ if (static_cast<int> (c) >= _source_channels) {
+ return optional<int> ();
+ }
+
+ return static_cast<int> (c);
+}
+
+int
+AudioMapping::dcp_channels () const
+{
+ if (_source_channels == 1) {
+ /* The source is mono, so to put the mono channel into
+ the centre we need to generate a 5.1 soundtrack.
+ */
+ return 6;
+ }
+
+ return _source_channels;
+}
diff --git a/src/lib/util.h b/src/lib/util.h
index 87735ea8e..22c6ea95b 100644
--- a/src/lib/util.h
+++ b/src/lib/util.h
@@ -29,6 +29,7 @@
#include <vector>
#include <boost/shared_ptr.hpp>
#include <boost/asio.hpp>
+#include <boost/optional.hpp>
#include <libdcp/util.h>
extern "C" {
#include <libavcodec/avcodec.h>
@@ -57,6 +58,7 @@ extern std::vector<std::string> split_at_spaces_considering_quotes (std::string)
extern std::string md5_digest (std::string);
extern std::string md5_digest (void const *, int);
extern void ensure_ui_thread ();
+extern std::string audio_channel_name (int);
typedef int SourceFrame;
@@ -178,7 +180,6 @@ struct Rect
extern std::string crop_string (Position, libdcp::Size);
extern int dcp_audio_sample_rate (int);
-extern int dcp_audio_channels (int);
extern std::string colour_lut_index_to_name (int index);
extern int stride_round_up (int, int const *, int);
extern int stride_lookup (int c, int const * stride);
@@ -268,6 +269,19 @@ private:
float** _data;
};
+class AudioMapping
+{
+public:
+ AudioMapping (int);
+
+ boost::optional<libdcp::Channel> source_to_dcp (int c) const;
+ boost::optional<int> dcp_to_source (libdcp::Channel c) const;
+ int dcp_channels () const;
+
+private:
+ int _source_channels;
+};
+
extern int64_t video_frames_to_audio_frames (SourceFrame v, float audio_sample_rate, float frames_per_second);
extern bool still_image_file (std::string);
extern std::pair<std::string, int> cpu_info ();
diff --git a/src/lib/writer.cc b/src/lib/writer.cc
index d480d502a..334ecec65 100644
--- a/src/lib/writer.cc
+++ b/src/lib/writer.cc
@@ -74,13 +74,15 @@ Writer::Writer (shared_ptr<Film> f)
_picture_asset_writer = _picture_asset->start_write (_first_nonexistant_frame > 0);
- if (dcp_audio_channels (_film->audio_channels()) > 0) {
+ AudioMapping m (_film->audio_channels ());
+
+ if (m.dcp_channels() > 0) {
_sound_asset.reset (
new libdcp::SoundAsset (
_film->dir (_film->dcp_name()),
N_("audio.mxf"),
DCPFrameRate (_film->frames_per_second()).frames_per_second,
- dcp_audio_channels (_film->audio_channels()),
+ m.dcp_channels (),
dcp_audio_sample_rate (_film->audio_stream()->sample_rate())
)
);
diff --git a/src/lib/wscript b/src/lib/wscript
index 6ddb94851..d36a24e7a 100644
--- a/src/lib/wscript
+++ b/src/lib/wscript
@@ -4,6 +4,8 @@ import i18n
sources = """
ab_transcode_job.cc
ab_transcoder.cc
+ analyse_audio_job.cc
+ audio_analysis.cc
audio_decoder.cc
audio_source.cc
config.cc