From 50cb31af16240b248700dab1484d7f07656c66df Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Thu, 20 Jun 2013 17:34:23 +0100 Subject: [PATCH] Various fixes to make audio analysis sort-of work. --- src/lib/analyse_audio_job.cc | 33 ++++++++++++++++------- src/lib/analyse_audio_job.h | 6 +++-- src/lib/audio_analysis.cc | 10 +++---- src/lib/audio_analysis.h | 5 ++-- src/lib/audio_buffers.cc | 6 +++++ src/lib/audio_content.cc | 27 +++++++++++++++++++ src/lib/audio_content.h | 5 ++-- src/lib/audio_decoder.cc | 5 ++-- src/lib/content.h | 2 -- src/lib/encoder.cc | 2 +- src/lib/encoder.h | 4 +-- src/lib/examine_content_job.cc | 3 +-- src/lib/examine_content_job.h | 2 +- src/lib/ffmpeg_decoder.cc | 6 +---- src/lib/film.cc | 48 ++++++++++++---------------------- src/lib/film.h | 9 ++----- src/lib/job.cc | 2 +- src/lib/job.h | 4 +-- src/lib/player.cc | 33 ++--------------------- src/lib/playlist.cc | 23 ---------------- src/lib/playlist.h | 1 - src/lib/scp_dcp_job.cc | 2 +- src/lib/scp_dcp_job.h | 2 +- src/lib/transcode_job.cc | 2 +- src/lib/transcode_job.h | 2 +- src/lib/transcoder.cc | 2 +- src/lib/transcoder.h | 2 +- src/lib/writer.cc | 2 +- src/lib/writer.h | 4 +-- src/tools/dcpomatic.cc | 9 ------- src/wx/about_dialog.cc | 4 +-- src/wx/audio_dialog.cc | 32 +++++++++-------------- src/wx/audio_dialog.h | 9 +++---- src/wx/audio_plot.cc | 10 +++++-- src/wx/film_editor.cc | 36 ++++++++++++------------- 35 files changed, 155 insertions(+), 199 deletions(-) diff --git a/src/lib/analyse_audio_job.cc b/src/lib/analyse_audio_job.cc index 3a44a408f..2848c1ed7 100644 --- a/src/lib/analyse_audio_job.cc +++ b/src/lib/analyse_audio_job.cc @@ -33,8 +33,9 @@ using boost::shared_ptr; int const AnalyseAudioJob::_num_points = 1024; -AnalyseAudioJob::AnalyseAudioJob (shared_ptr f) +AnalyseAudioJob::AnalyseAudioJob (shared_ptr f, shared_ptr c) : Job (f) + , _content (c) , _done (0) , _samples_per_point (1) { @@ -44,13 +45,25 @@ AnalyseAudioJob::AnalyseAudioJob (shared_ptr f) string AnalyseAudioJob::name () const { - return String::compose (_("Analyse audio of %1"), _film->name()); + shared_ptr content = _content.lock (); + if (!content) { + return ""; + } + + return String::compose (_("Analyse audio of %1"), content->file().filename()); } void AnalyseAudioJob::run () { - shared_ptr player = _film->player (); + shared_ptr content = _content.lock (); + if (!content) { + return; + } + + shared_ptr playlist (new Playlist); + playlist->add (content); + shared_ptr player (new Player (_film, playlist)); player->disable_video (); player->Audio.connect (bind (&AnalyseAudioJob::audio, this, _1, _2)); @@ -61,18 +74,18 @@ AnalyseAudioJob::run () _analysis.reset (new AudioAnalysis (_film->dcp_audio_channels ())); _done = 0; - while (player->pass ()) { - set_progress (double (_done) / _film->length ()); + while (!player->pass ()) { + set_progress (double (_film->audio_frames_to_time (_done)) / _film->length ()); } - _analysis->write (_film->audio_analysis_path ()); + _analysis->write (content->audio_analysis_path ()); set_progress (1); set_state (FINISHED_OK); } void -AnalyseAudioJob::audio (shared_ptr b, Time t) +AnalyseAudioJob::audio (shared_ptr b, Time) { for (int i = 0; i < b->frames(); ++i) { for (int j = 0; j < b->channels(); ++j) { @@ -88,12 +101,12 @@ AnalyseAudioJob::audio (shared_ptr b, Time t) 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 = t; + ++_done; + } } diff --git a/src/lib/analyse_audio_job.h b/src/lib/analyse_audio_job.h index a0786a017..3d4881983 100644 --- a/src/lib/analyse_audio_job.h +++ b/src/lib/analyse_audio_job.h @@ -22,11 +22,12 @@ #include "types.h" class AudioBuffers; +class AudioContent; class AnalyseAudioJob : public Job { public: - AnalyseAudioJob (boost::shared_ptr f); + AnalyseAudioJob (boost::shared_ptr, boost::shared_ptr); std::string name () const; void run (); @@ -34,7 +35,8 @@ public: private: void audio (boost::shared_ptr, Time); - Time _done; + boost::weak_ptr _content; + OutputAudioFrame _done; int64_t _samples_per_point; std::vector _current; diff --git a/src/lib/audio_analysis.cc b/src/lib/audio_analysis.cc index 9d708bbfd..e12516620 100644 --- a/src/lib/audio_analysis.cc +++ b/src/lib/audio_analysis.cc @@ -62,9 +62,9 @@ AudioAnalysis::AudioAnalysis (int channels) _data.resize (channels); } -AudioAnalysis::AudioAnalysis (string filename) +AudioAnalysis::AudioAnalysis (boost::filesystem::path filename) { - ifstream f (filename.c_str ()); + ifstream f (filename.string().c_str ()); int channels; f >> channels; @@ -107,10 +107,10 @@ AudioAnalysis::points (int c) const } void -AudioAnalysis::write (string filename) +AudioAnalysis::write (boost::filesystem::path filename) { - string tmp = filename + ".tmp"; - + string tmp = filename.string() + ".tmp"; + ofstream f (tmp.c_str ()); f << _data.size() << "\n"; for (vector >::iterator i = _data.begin(); i != _data.end(); ++i) { diff --git a/src/lib/audio_analysis.h b/src/lib/audio_analysis.h index ec6905105..d57eba90a 100644 --- a/src/lib/audio_analysis.h +++ b/src/lib/audio_analysis.h @@ -23,6 +23,7 @@ #include #include #include +#include class AudioPoint { @@ -50,7 +51,7 @@ class AudioAnalysis { public: AudioAnalysis (int c); - AudioAnalysis (std::string); + AudioAnalysis (boost::filesystem::path); void add_point (int c, AudioPoint const & p); @@ -58,7 +59,7 @@ public: int points (int c) const; int channels () const; - void write (std::string); + void write (boost::filesystem::path); private: std::vector > _data; diff --git a/src/lib/audio_buffers.cc b/src/lib/audio_buffers.cc index c3e89f130..403babaf7 100644 --- a/src/lib/audio_buffers.cc +++ b/src/lib/audio_buffers.cc @@ -206,6 +206,9 @@ AudioBuffers::accumulate_channel (AudioBuffers const * from, int from_channel, i } } +/** Ensure we have space for at least a certain number of frames. If we extend + * the buffers, fill the new space with silence. + */ void AudioBuffers::ensure_size (int frames) { @@ -218,6 +221,9 @@ AudioBuffers::ensure_size (int frames) if (!_data[i]) { throw bad_alloc (); } + for (int j = _allocated_frames; j < frames; ++j) { + _data[i][j] = 0; + } } _allocated_frames = frames; diff --git a/src/lib/audio_content.cc b/src/lib/audio_content.cc index 9940574f9..e93f348f4 100644 --- a/src/lib/audio_content.cc +++ b/src/lib/audio_content.cc @@ -19,11 +19,14 @@ #include #include "audio_content.h" +#include "analyse_audio_job.h" +#include "job_manager.h" #include "film.h" using std::string; using boost::shared_ptr; using boost::lexical_cast; +using boost::dynamic_pointer_cast; int const AudioContentProperty::AUDIO_CHANNELS = 200; int const AudioContentProperty::AUDIO_LENGTH = 201; @@ -93,3 +96,27 @@ AudioContent::set_audio_delay (int d) signal_changed (AudioContentProperty::AUDIO_DELAY); } + +void +AudioContent::analyse_audio (boost::function finished) +{ + shared_ptr film = _film.lock (); + if (!film) { + return; + } + + shared_ptr job (new AnalyseAudioJob (film, dynamic_pointer_cast (shared_from_this()))); + job->Finished.connect (finished); + JobManager::instance()->add (job); +} + +boost::filesystem::path +AudioContent::audio_analysis_path () const +{ + shared_ptr film = _film.lock (); + if (!film) { + return boost::filesystem::path (); + } + + return film->audio_analysis_path (dynamic_pointer_cast (shared_from_this ())); +} diff --git a/src/lib/audio_content.h b/src/lib/audio_content.h index 8fc658a76..73a00ca7d 100644 --- a/src/lib/audio_content.h +++ b/src/lib/audio_content.h @@ -1,5 +1,3 @@ -/* -*- c-basic-offset: 8; default-tab-width: 8; -*- */ - /* Copyright (C) 2013 Carl Hetherington @@ -57,6 +55,9 @@ public: virtual AudioMapping audio_mapping () const = 0; virtual void set_audio_mapping (AudioMapping) = 0; + void analyse_audio (boost::function); + boost::filesystem::path audio_analysis_path () const; + void set_audio_gain (float); void set_audio_delay (int); diff --git a/src/lib/audio_decoder.cc b/src/lib/audio_decoder.cc index bbd4ced6c..a9e01908c 100644 --- a/src/lib/audio_decoder.cc +++ b/src/lib/audio_decoder.cc @@ -59,10 +59,10 @@ AudioDecoder::AudioDecoder (shared_ptr f, shared_ptraudio_channels ()), AV_SAMPLE_FMT_FLTP, _audio_content->output_audio_frame_rate(), - av_get_default_channel_layout (MAX_AUDIO_CHANNELS), + av_get_default_channel_layout (_audio_content->audio_channels ()), AV_SAMPLE_FMT_FLTP, _audio_content->content_audio_frame_rate(), 0, 0 @@ -152,7 +152,6 @@ AudioDecoder::audio (shared_ptr data, Time time) } Audio (dcp_mapped, time); - cout << "bumping n.a. by " << data->frames() << " ie " << film->audio_frames_to_time(data->frames()) << "\n"; _next_audio = time + film->audio_frames_to_time (data->frames()); } diff --git a/src/lib/content.h b/src/lib/content.h index 5e8f98428..e33f517ab 100644 --- a/src/lib/content.h +++ b/src/lib/content.h @@ -1,5 +1,3 @@ -/* -*- c-basic-offset: 8; default-tab-width: 8; -*- */ - /* Copyright (C) 2013 Carl Hetherington diff --git a/src/lib/encoder.cc b/src/lib/encoder.cc index 4dff19ea6..8b2db0eb3 100644 --- a/src/lib/encoder.cc +++ b/src/lib/encoder.cc @@ -47,7 +47,7 @@ using boost::optional; int const Encoder::_history_size = 25; /** @param f Film that we are encoding */ -Encoder::Encoder (shared_ptr f, shared_ptr j) +Encoder::Encoder (shared_ptr f, shared_ptr j) : _film (f) , _job (j) , _video_frames_out (0) diff --git a/src/lib/encoder.h b/src/lib/encoder.h index 8f724525c..3fe707b51 100644 --- a/src/lib/encoder.h +++ b/src/lib/encoder.h @@ -58,7 +58,7 @@ class Job; class Encoder : public VideoSink, public AudioSink { public: - Encoder (boost::shared_ptr f, boost::shared_ptr); + Encoder (boost::shared_ptr f, boost::shared_ptr); virtual ~Encoder (); /** Called to indicate that a processing run is about to begin */ @@ -87,7 +87,7 @@ private: void terminate_threads (); /** Film that we are encoding */ - boost::shared_ptr _film; + boost::shared_ptr _film; boost::shared_ptr _job; /** Mutex for _time_history and _last_frame */ diff --git a/src/lib/examine_content_job.cc b/src/lib/examine_content_job.cc index f68a1eea0..3cab9716d 100644 --- a/src/lib/examine_content_job.cc +++ b/src/lib/examine_content_job.cc @@ -28,7 +28,7 @@ using std::string; using boost::shared_ptr; -ExamineContentJob::ExamineContentJob (shared_ptr f, shared_ptr c) +ExamineContentJob::ExamineContentJob (shared_ptr f, shared_ptr c) : Job (f) , _content (c) { @@ -49,7 +49,6 @@ void ExamineContentJob::run () { _content->examine (shared_from_this ()); - _film->add_content (_content); set_progress (1); set_state (FINISHED_OK); } diff --git a/src/lib/examine_content_job.h b/src/lib/examine_content_job.h index 86f1ab111..b6903b86b 100644 --- a/src/lib/examine_content_job.h +++ b/src/lib/examine_content_job.h @@ -26,7 +26,7 @@ class Log; class ExamineContentJob : public Job { public: - ExamineContentJob (boost::shared_ptr, boost::shared_ptr); + ExamineContentJob (boost::shared_ptr, boost::shared_ptr); ~ExamineContentJob (); std::string name () const; diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc index 36078a463..2e586b8f7 100644 --- a/src/lib/ffmpeg_decoder.cc +++ b/src/lib/ffmpeg_decoder.cc @@ -1,5 +1,3 @@ -/* -*- c-basic-offset: 8; default-tab-width: 8; -*- */ - /* Copyright (C) 2012 Carl Hetherington @@ -278,8 +276,6 @@ FFmpegDecoder::pass () } avsubtitle_free (&sub); } - } else { - cout << "[ffmpeg] other packet.\n"; } av_free_packet (&_packet); @@ -541,7 +537,7 @@ FFmpegDecoder::decode_audio_packet () ); assert (_audio_codec_context->channels == _ffmpeg_content->audio_channels()); - audio (deinterleave_audio (_frame->data, data_size), source_pts_seconds); + audio (deinterleave_audio (_frame->data, data_size), source_pts_seconds * TIME_HZ); } copy_packet.data += decode_result; diff --git a/src/lib/film.cc b/src/lib/film.cc index a29f6c331..ad565aca0 100644 --- a/src/lib/film.cc +++ b/src/lib/film.cc @@ -45,7 +45,6 @@ #include "config.h" #include "version.h" #include "ui_signaller.h" -#include "analyse_audio_job.h" #include "playlist.h" #include "player.h" #include "ffmpeg_content.h" @@ -72,6 +71,7 @@ using std::endl; using std::cout; using std::list; using boost::shared_ptr; +using boost::weak_ptr; using boost::lexical_cast; using boost::dynamic_pointer_cast; using boost::to_upper_copy; @@ -222,13 +222,12 @@ Film::filename_safe_name () const return o; } -string -Film::audio_analysis_path () const +boost::filesystem::path +Film::audio_analysis_path (shared_ptr c) const { - boost::filesystem::path p; - p /= "analysis"; - p /= _playlist->audio_digest(); - return file (p.string ()); + boost::filesystem::path p = dir ("analysis"); + p /= c->digest(); + return p; } /** Add suitable Jobs to the JobManager to create a DCP for this Film */ @@ -289,31 +288,6 @@ Film::make_dcp () JobManager::instance()->add (shared_ptr (new TranscodeJob (shared_from_this()))); } -/** Start a job to analyse the audio in our Playlist */ -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); -} - -void -Film::analyse_audio_finished () -{ - ensure_ui_thread (); - - if (_analyse_audio_job->finished_ok ()) { - AudioAnalysisSucceeded (); - } - - _analyse_audio_job.reset (); -} - /** Start a job to send our DCP to the configured TMS */ void Film::send_dcp_to_tms () @@ -782,9 +756,19 @@ void Film::examine_and_add_content (shared_ptr c) { shared_ptr j (new ExamineContentJob (shared_from_this(), c)); + j->Finished.connect (bind (&Film::add_content_weak, this, boost::weak_ptr (c))); JobManager::instance()->add (j); } +void +Film::add_content_weak (weak_ptr c) +{ + shared_ptr content = c.lock (); + if (content) { + add_content (content); + } +} + void Film::add_content (shared_ptr c) { diff --git a/src/lib/film.h b/src/lib/film.h index 5f06a1dc7..5bb9acf29 100644 --- a/src/lib/film.h +++ b/src/lib/film.h @@ -63,12 +63,11 @@ public: std::string info_path (int f) const; std::string internal_video_mxf_dir () const; std::string internal_video_mxf_filename () const; - std::string audio_analysis_path () const; + boost::filesystem::path audio_analysis_path (boost::shared_ptr) const; std::string dcp_video_mxf_filename () const; std::string dcp_audio_mxf_filename () const; - void analyse_audio (); void send_dcp_to_tms (); void make_dcp (); @@ -244,24 +243,20 @@ public: /** Emitted when some property of our content has changed */ mutable boost::signals2::signal, int)> ContentChanged; - boost::signals2::signal AudioAnalysisSucceeded; - /** Current version number of the state file */ static int const state_version; private: void signal_changed (Property); - void analyse_audio_finished (); std::string video_state_identifier () const; void playlist_changed (); void playlist_content_changed (boost::weak_ptr, int); std::string filename_safe_name () const; + void add_content_weak (boost::weak_ptr); /** Log to write to */ boost::shared_ptr _log; - /** Any running AnalyseAudioJob, or 0 */ - boost::shared_ptr _analyse_audio_job; boost::shared_ptr _playlist; /** Complete path to directory containing the film metadata; diff --git a/src/lib/job.cc b/src/lib/job.cc index 2e6385d62..4969c4099 100644 --- a/src/lib/job.cc +++ b/src/lib/job.cc @@ -35,7 +35,7 @@ using std::list; using std::stringstream; using boost::shared_ptr; -Job::Job (shared_ptr f) +Job::Job (shared_ptr f) : _film (f) , _thread (0) , _state (NEW) diff --git a/src/lib/job.h b/src/lib/job.h index 40e90b73c..5a4775180 100644 --- a/src/lib/job.h +++ b/src/lib/job.h @@ -38,7 +38,7 @@ class Film; class Job : public boost::enable_shared_from_this { public: - Job (boost::shared_ptr); + Job (boost::shared_ptr); virtual ~Job() {} /** @return user-readable name of this job */ @@ -91,7 +91,7 @@ protected: void set_state (State); void set_error (std::string s, std::string d); - boost::shared_ptr _film; + boost::shared_ptr _film; private: diff --git a/src/lib/player.cc b/src/lib/player.cc index 60686e781..c05897c23 100644 --- a/src/lib/player.cc +++ b/src/lib/player.cc @@ -95,11 +95,6 @@ Player::pass () shared_ptr earliest; for (list >::iterator i = _pieces.begin(); i != _pieces.end(); ++i) { - cout << "check " << (*i)->content->file() - << " start=" << (*i)->content->start() - << ", position=" << (*i)->decoder->position() - << ", end=" << (*i)->content->end() << "\n"; - if ((*i)->decoder->done ()) { continue; } @@ -110,7 +105,6 @@ Player::pass () Time const t = (*i)->content->start() + (*i)->decoder->position(); if (t < earliest_t) { - cout << "\t candidate; " << t << " " << (t / TIME_HZ) << ".\n"; earliest_t = t; earliest = *i; } @@ -121,23 +115,8 @@ Player::pass () return true; } - cout << "PASS:\n"; - cout << "\tpass " << earliest->content->file() << " "; - if (dynamic_pointer_cast (earliest->content)) { - cout << " FFmpeg.\n"; - } else if (dynamic_pointer_cast (earliest->content)) { - cout << " ImageMagickContent.\n"; - } else if (dynamic_pointer_cast (earliest->content)) { - cout << " SndfileContent.\n"; - } else if (dynamic_pointer_cast (earliest->decoder)) { - cout << " Black.\n"; - } else if (dynamic_pointer_cast (earliest->decoder)) { - cout << " Silence.\n"; - } - earliest->decoder->pass (); _position = earliest->content->start() + earliest->decoder->position (); - cout << "\tpassed to " << _position << " " << (_position / TIME_HZ) << "\n"; return false; } @@ -169,6 +148,8 @@ Player::process_audio (weak_ptr weak_content, shared_ptrstart (); + cout << "Player gets " << audio->frames() << " @ " << time << " cf " << _next_audio << "\n"; + if (time > _next_audio) { /* We can emit some audio from our buffers */ OutputAudioFrame const N = _film->time_to_audio_frames (time - _next_audio); @@ -216,13 +197,10 @@ Player::seek (Time t) return; } -// cout << "seek to " << t << " " << (t / TIME_HZ) << "\n"; - for (list >::iterator i = _pieces.begin(); i != _pieces.end(); ++i) { Time s = t - (*i)->content->start (); s = max (static_cast