From: Carl Hetherington Date: Wed, 2 Sep 2015 10:20:24 +0000 (+0100) Subject: Add option to analyse audio automatically when content is added (#673). X-Git-Tag: v2.2.1~13 X-Git-Url: https://main.carlh.net/gitweb/?p=dcpomatic.git;a=commitdiff_plain;h=d2bd0c628fd0616fe3b7dd02bd955b2c07ab48d5 Add option to analyse audio automatically when content is added (#673). --- diff --git a/src/lib/analyse_audio_job.h b/src/lib/analyse_audio_job.h index d484bff2c..d9b90ec66 100644 --- a/src/lib/analyse_audio_job.h +++ b/src/lib/analyse_audio_job.h @@ -28,6 +28,7 @@ class AudioBuffers; class AudioAnalysis; class Playlist; +class AudioPoint; /** @class AnalyseAudioJob * @brief A job to analyse the audio of a film and make a note of its @@ -46,6 +47,10 @@ public: std::string json_name () const; void run (); + boost::shared_ptr playlist () const { + return _playlist; + } + private: void analyse (boost::shared_ptr); diff --git a/src/lib/config.cc b/src/lib/config.cc index fc71a72f3..20386368e 100644 --- a/src/lib/config.cc +++ b/src/lib/config.cc @@ -97,6 +97,7 @@ Config::set_defaults () _check_for_test_updates = false; _maximum_j2k_bandwidth = 250000000; _log_types = Log::TYPE_GENERAL | Log::TYPE_WARNING | Log::TYPE_ERROR; + _automatic_audio_analysis = false; #ifdef DCPOMATIC_WINDOWS _win32_console = false; #endif @@ -231,6 +232,7 @@ Config::read () _allow_any_dcp_frame_rate = f.optional_bool_child ("AllowAnyDCPFrameRate"); _log_types = f.optional_number_child ("LogTypes").get_value_or (Log::TYPE_GENERAL | Log::TYPE_WARNING | Log::TYPE_ERROR); + _automatic_audio_analysis = f.optional_bool_child ("AutomaticAudioAnalysis").get_value_or (false); #ifdef DCPOMATIC_WINDOWS _win32_console = f.optional_bool_child ("Win32Console").get_value_or (false); #endif @@ -375,6 +377,7 @@ Config::write () const root->add_child("MaximumJ2KBandwidth")->add_child_text (raw_convert (_maximum_j2k_bandwidth)); root->add_child("AllowAnyDCPFrameRate")->add_child_text (_allow_any_dcp_frame_rate ? "1" : "0"); root->add_child("LogTypes")->add_child_text (raw_convert (_log_types)); + root->add_child("AutomaticAudioAnalysis")->add_child_text (_automatic_audio_analysis ? "1" : "0"); #ifdef DCPOMATIC_WINDOWS root->add_child("Win32Console")->add_child_text (_win32_console ? "1" : "0"); #endif diff --git a/src/lib/config.h b/src/lib/config.h index fc63c518c..2f68ea31c 100644 --- a/src/lib/config.h +++ b/src/lib/config.h @@ -226,6 +226,10 @@ public: return _log_types; } + bool automatic_audio_analysis () const { + return _automatic_audio_analysis; + } + #ifdef DCPOMATIC_WINDOWS bool win32_console () const { return _win32_console; @@ -407,6 +411,10 @@ public: maybe_set (_log_types, t); } + void set_automatic_audio_analysis (bool a) { + maybe_set (_automatic_audio_analysis, a); + } + #ifdef DCPOMATIC_WINDOWS void set_win32_console (bool c) { maybe_set (_win32_console, c); @@ -505,6 +513,7 @@ private: /** maximum allowed J2K bandwidth in bits per second */ int _maximum_j2k_bandwidth; int _log_types; + bool _automatic_audio_analysis; #ifdef DCPOMATIC_WINDOWS bool _win32_console; #endif diff --git a/src/lib/film.cc b/src/lib/film.cc index 685fc7658..231ac92e1 100644 --- a/src/lib/film.cc +++ b/src/lib/film.cc @@ -162,8 +162,12 @@ Film::Film (boost::filesystem::path dir, bool log) Film::~Film () { - for (list::const_iterator i = _job_connections.begin(); i != _job_connections.end(); ++i) { - i->disconnect (); + BOOST_FOREACH (boost::signals2::connection& i, _job_connections) { + i.disconnect (); + } + + BOOST_FOREACH (boost::signals2::connection& i, _audio_analysis_connections) { + i.disconnect (); } } @@ -947,8 +951,19 @@ Film::maybe_add_content (weak_ptr j, weak_ptr c) } shared_ptr content = c.lock (); - if (content) { - add_content (content); + if (!content) { + return; + } + + add_content (content); + if (Config::instance()->automatic_audio_analysis ()) { + shared_ptr playlist (new Playlist); + playlist->add (content); + boost::signals2::connection c; + JobManager::instance()->analyse_audio ( + shared_from_this (), playlist, c, bind (&Film::audio_analysis_finished, this) + ); + _audio_analysis_connections.push_back (c); } } @@ -1233,3 +1248,9 @@ Film::remove_content (ContentList c) { _playlist->remove (c); } + +void +Film::audio_analysis_finished () +{ + /* XXX */ +} diff --git a/src/lib/film.h b/src/lib/film.h index 2165eed23..aa7be939e 100644 --- a/src/lib/film.h +++ b/src/lib/film.h @@ -296,6 +296,7 @@ private: void playlist_changed (); void playlist_content_changed (boost::weak_ptr, int, bool frequent); void maybe_add_content (boost::weak_ptr, boost::weak_ptr); + void audio_analysis_finished (); /** Log to write to */ boost::shared_ptr _log; @@ -345,6 +346,7 @@ private: boost::signals2::scoped_connection _playlist_changed_connection; boost::signals2::scoped_connection _playlist_content_changed_connection; std::list _job_connections; + std::list _audio_analysis_connections; friend struct paths_test; friend struct film_metadata_test; diff --git a/src/lib/job.cc b/src/lib/job.cc index 784defc91..37bf462fc 100644 --- a/src/lib/job.cc +++ b/src/lib/job.cc @@ -39,6 +39,7 @@ using std::list; using std::cout; using boost::shared_ptr; using boost::optional; +using boost::function; #define LOG_ERROR_NC(...) _film->log()->log (__VA_ARGS__, Log::TYPE_ERROR); @@ -434,3 +435,15 @@ Job::resume () _pause_changed.notify_all (); } } + +void +Job::when_finished (boost::signals2::connection& connection, function finished) +{ + boost::mutex::scoped_lock lm (_state_mutex); + if (_state == FINISHED_OK || _state == FINISHED_ERROR || _state == FINISHED_CANCELLED) { + finished (); + } else { + connection = Finished.connect (finished); + } +} + diff --git a/src/lib/job.h b/src/lib/job.h index 1caa5a904..32e71d658 100644 --- a/src/lib/job.h +++ b/src/lib/job.h @@ -80,6 +80,8 @@ public: return _film; } + void when_finished (boost::signals2::connection& connection, boost::function finished); + boost::signals2::signal Progress; /** Emitted from the UI thread when the job is finished */ boost::signals2::signal Finished; diff --git a/src/lib/job_manager.cc b/src/lib/job_manager.cc index e3f91f4fc..3748fa353 100644 --- a/src/lib/job_manager.cc +++ b/src/lib/job_manager.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 Carl Hetherington + Copyright (C) 2012-2015 Carl Hetherington 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 @@ -21,23 +21,28 @@ * @brief A simple scheduler for jobs. */ -#include -#include #include "job_manager.h" #include "job.h" #include "cross.h" +#include "analyse_audio_job.h" +#include "film.h" +#include +#include +#include using std::string; using std::list; using std::cout; using boost::shared_ptr; using boost::weak_ptr; +using boost::function; +using boost::dynamic_pointer_cast; +using boost::optional; JobManager* JobManager::_instance = 0; JobManager::JobManager () : _terminate (false) - , _last_active_jobs (false) , _scheduler (0) { @@ -113,7 +118,7 @@ JobManager::scheduler () { while (true) { - bool active_jobs = false; + optional active_job; { boost::mutex::scoped_lock lm (_mutex); @@ -121,29 +126,28 @@ JobManager::scheduler () return; } - for (list >::iterator i = _jobs.begin(); i != _jobs.end(); ++i) { + BOOST_FOREACH (shared_ptr i, _jobs) { - if (!(*i)->finished ()) { - active_jobs = true; + if (!i->finished ()) { + active_job = i->json_name (); } - if ((*i)->running ()) { + if (i->running ()) { /* Something is already happening */ break; } - if ((*i)->is_new()) { - (*i)->start (); - + if (i->is_new()) { + i->start (); /* Only start one job at once */ break; } } } - if (active_jobs != _last_active_jobs) { - _last_active_jobs = active_jobs; - emit (boost::bind (boost::ref (ActiveJobsChanged), active_jobs)); + if (active_job != _last_active_job) { + _last_active_job = active_job; + emit (boost::bind (boost::ref (ActiveJobsChanged), active_job)); } dcpomatic_sleep (1); @@ -167,3 +171,32 @@ JobManager::drop () delete _instance; _instance = 0; } + +void +JobManager::analyse_audio ( + shared_ptr film, + shared_ptr playlist, + boost::signals2::connection& connection, + function ready + ) +{ + shared_ptr job; + + { + boost::mutex::scoped_lock lm (_mutex); + + BOOST_FOREACH (shared_ptr i, _jobs) { + shared_ptr a = dynamic_pointer_cast (i); + if (a && film->audio_analysis_path (a->playlist ()) == film->audio_analysis_path (playlist)) { + i->when_finished (connection, ready); + return; + } + } + + job.reset (new AnalyseAudioJob (film, playlist)); + connection = job->Finished.connect (ready); + _jobs.push_back (job); + } + + emit (boost::bind (boost::ref (JobAdded), weak_ptr (job))); +} diff --git a/src/lib/job_manager.h b/src/lib/job_manager.h index 3cd8be6d6..7de7862a1 100644 --- a/src/lib/job_manager.h +++ b/src/lib/job_manager.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 Carl Hetherington + Copyright (C) 2012-2015 Carl Hetherington 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 @@ -28,6 +28,9 @@ #include class Job; +class Film; +class Playlist; + extern void wait_for_jobs (); /** @class JobManager @@ -42,8 +45,15 @@ public: bool work_to_do () const; bool errors () const; + void analyse_audio ( + boost::shared_ptr film, + boost::shared_ptr playlist, + boost::signals2::connection& connection, + boost::function ready + ); + boost::signals2::signal)> JobAdded; - boost::signals2::signal ActiveJobsChanged; + boost::signals2::signal)> ActiveJobsChanged; static JobManager* instance (); static void drop (); @@ -61,7 +71,7 @@ private: std::list > _jobs; bool _terminate; - bool _last_active_jobs; + boost::optional _last_active_job; boost::thread* _scheduler; static JobManager* _instance; diff --git a/src/tools/dcpomatic.cc b/src/tools/dcpomatic.cc index 8692f4c83..5abf47182 100644 --- a/src/tools/dcpomatic.cc +++ b/src/tools/dcpomatic.cc @@ -44,7 +44,6 @@ #include "lib/signal_manager.h" #include "lib/log.h" #include "lib/job_manager.h" -#include "lib/transcode_job.h" #include "lib/exceptions.h" #include "lib/cinema.h" #include "lib/kdm.h" @@ -606,7 +605,7 @@ private: { list > jobs = JobManager::instance()->get (); list >::iterator i = jobs.begin(); - while (i != jobs.end() && dynamic_pointer_cast (*i) == 0) { + while (i != jobs.end() && (*i)->json_name() != "transcode") { ++i; } bool const dcp_creation = (i != jobs.end ()) && !(*i)->finished (); diff --git a/src/wx/audio_dialog.cc b/src/wx/audio_dialog.cc index a14498359..d4108f89c 100644 --- a/src/wx/audio_dialog.cc +++ b/src/wx/audio_dialog.cc @@ -133,14 +133,11 @@ AudioDialog::try_to_load_analysis () shared_ptr film = _film.lock (); DCPOMATIC_ASSERT (film); - boost::filesystem::path path = film->audio_analysis_path (_playlist); - + boost::filesystem::path const path = film->audio_analysis_path (_playlist); if (!boost::filesystem::exists (path)) { _plot->set_analysis (shared_ptr ()); _analysis.reset (); - shared_ptr job (new AnalyseAudioJob (film, _playlist)); - _analysis_finished_connection = job->Finished.connect (bind (&AudioDialog::analysis_finished, this)); - JobManager::instance()->add (job); + JobManager::instance()->analyse_audio (film, _playlist, _analysis_finished_connection, bind (&AudioDialog::analysis_finished, this)); return; } @@ -148,9 +145,7 @@ AudioDialog::try_to_load_analysis () _analysis.reset (new AudioAnalysis (path)); } catch (xmlpp::exception& e) { /* Probably an old-style analysis file: recreate it */ - shared_ptr job (new AnalyseAudioJob (film, _playlist)); - _analysis_finished_connection = job->Finished.connect (bind (&AudioDialog::analysis_finished, this)); - JobManager::instance()->add (job); + JobManager::instance()->analyse_audio (film, _playlist, _analysis_finished_connection, bind (&AudioDialog::analysis_finished, this)); return; } diff --git a/src/wx/config_dialog.cc b/src/wx/config_dialog.cc index 08b78bce1..4633552a6 100644 --- a/src/wx/config_dialog.cc +++ b/src/wx/config_dialog.cc @@ -183,6 +183,10 @@ private: table->Add (_num_local_encoding_threads, wxGBPosition (r, 1)); ++r; + _automatic_audio_analysis = new wxCheckBox (_panel, wxID_ANY, _("Automatically analyse content audio")); + table->Add (_automatic_audio_analysis, wxGBPosition (r, 0), wxGBSpan (1, 2)); + ++r; + _check_for_updates = new wxCheckBox (_panel, wxID_ANY, _("Check for updates on startup")); table->Add (_check_for_updates, wxGBPosition (r, 0), wxGBSpan (1, 2)); ++r; @@ -211,6 +215,7 @@ private: _num_local_encoding_threads->SetRange (1, 128); _num_local_encoding_threads->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&GeneralPage::num_local_encoding_threads_changed, this)); + _automatic_audio_analysis->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::automatic_audio_analysis_changed, this)); _check_for_updates->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::check_for_updates_changed, this)); _check_for_test_updates->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::check_for_test_updates_changed, this)); @@ -249,6 +254,7 @@ private: setup_language_sensitivity (); checked_set (_num_local_encoding_threads, config->num_local_encoding_threads ()); + checked_set (_automatic_audio_analysis, config->automatic_audio_analysis ()); checked_set (_check_for_updates, config->check_for_updates ()); checked_set (_check_for_test_updates, config->check_for_test_updates ()); checked_set (_issuer, config->dcp_issuer ()); @@ -306,6 +312,11 @@ private: } } + void automatic_audio_analysis_changed () + { + Config::instance()->set_automatic_audio_analysis (_automatic_audio_analysis->GetValue ()); + } + void check_for_updates_changed () { Config::instance()->set_check_for_updates (_check_for_updates->GetValue ()); @@ -334,6 +345,7 @@ private: wxCheckBox* _set_language; wxChoice* _language; wxSpinCtrl* _num_local_encoding_threads; + wxCheckBox* _automatic_audio_analysis; wxCheckBox* _check_for_updates; wxCheckBox* _check_for_test_updates; wxTextCtrl* _issuer; diff --git a/src/wx/film_editor.cc b/src/wx/film_editor.cc index 545eaf56e..b67c9612d 100644 --- a/src/wx/film_editor.cc +++ b/src/wx/film_editor.cc @@ -34,7 +34,9 @@ #include using std::cout; +using std::string; using boost::shared_ptr; +using boost::optional; /** @param f Film to edit */ FilmEditor::FilmEditor (wxWindow* parent, FilmViewer* viewer) @@ -136,7 +138,7 @@ FilmEditor::set_general_sensitivity (bool s) } void -FilmEditor::active_jobs_changed (bool a) +FilmEditor::active_jobs_changed (optional j) { - set_general_sensitivity (!a); + set_general_sensitivity (!j || *j == "analyse_audio"); } diff --git a/src/wx/film_editor.h b/src/wx/film_editor.h index 43a1214c2..e19fcabdf 100644 --- a/src/wx/film_editor.h +++ b/src/wx/film_editor.h @@ -58,7 +58,7 @@ public: void film_content_changed (int); void set_general_sensitivity (bool); - void active_jobs_changed (bool); + void active_jobs_changed (boost::optional); wxNotebook* _main_notebook; ContentPanel* _content_panel; diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc index 3f4bc6514..f00cdfe28 100644 --- a/src/wx/film_viewer.cc +++ b/src/wx/film_viewer.cc @@ -54,6 +54,7 @@ using std::exception; using boost::shared_ptr; using boost::dynamic_pointer_cast; using boost::weak_ptr; +using boost::optional; using dcp::Size; FilmViewer::FilmViewer (wxWindow* p) @@ -385,23 +386,12 @@ FilmViewer::update_position_label () } void -FilmViewer::active_jobs_changed (bool a) +FilmViewer::active_jobs_changed (optional j) { - if (a) { - list > jobs = JobManager::instance()->get (); - list >::iterator i = jobs.begin (); - while (i != jobs.end() && boost::dynamic_pointer_cast (*i) == 0) { - ++i; - } - - if (i == jobs.end() || (*i)->finished()) { - /* no examine content job running, so we're ok to use the viewer */ - a = false; - } - } - - _slider->Enable (!a); - _play_button->Enable (!a); + /* examine content is the only job which stops the viewer working */ + bool const a = !j || *j != "examine_content"; + _slider->Enable (a); + _play_button->Enable (a); } void diff --git a/src/wx/film_viewer.h b/src/wx/film_viewer.h index eb9d256d3..4776d24b4 100644 --- a/src/wx/film_viewer.h +++ b/src/wx/film_viewer.h @@ -56,7 +56,7 @@ private: void timer (); void calculate_sizes (); void check_play_state (); - void active_jobs_changed (bool); + void active_jobs_changed (boost::optional); void back_clicked (); void forward_clicked (); void player_changed (bool);