From cf1e212c30ec7419b96388e4f78b44cb55bf34c5 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Sun, 24 Feb 2013 22:15:50 +0000 Subject: Basic stuff to analyse audio (job). --- src/lib/analyse_audio_job.cc | 108 +++++++++++++++++++++++++++++++++++++++++++ src/lib/analyse_audio_job.h | 45 ++++++++++++++++++ src/lib/audio_analysis.cc | 69 +++++++++++++++++++++++++++ src/lib/audio_analysis.h | 59 +++++++++++++++++++++++ src/lib/ffmpeg_decoder.cc | 12 +++-- src/lib/film.cc | 29 ++++++++++++ src/lib/film.h | 6 +++ src/lib/options.h | 6 ++- src/lib/wscript | 2 + 9 files changed, 329 insertions(+), 7 deletions(-) create mode 100644 src/lib/analyse_audio_job.cc create mode 100644 src/lib/analyse_audio_job.h create mode 100644 src/lib/audio_analysis.cc create mode 100644 src/lib/audio_analysis.h (limited to 'src/lib') diff --git a/src/lib/analyse_audio_job.cc b/src/lib/analyse_audio_job.cc new file mode 100644 index 000000000..5623fdfcc --- /dev/null +++ b/src/lib/analyse_audio_job.cc @@ -0,0 +1,108 @@ +/* + Copyright (C) 2012 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 + 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" + +using std::string; +using std::max; +using boost::shared_ptr; + +int const AnalyseAudioJob::_num_points = 1024; + +AnalyseAudioJob::AnalyseAudioJob (shared_ptr f) + : Job (f) + , _done_for_this_point (0) + , _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 b) +{ + for (int i = 0; i < b->frames(); ++i) { + for (int j = 0; j < b->channels(); ++j) { + float const s = b->data(j)[i]; + _current[j][AudioPoint::RMS] += pow (s, 2); + _current[j][AudioPoint::PEAK] = max (_current[j][AudioPoint::PEAK], fabsf (s)); + + if (_done_for_this_point == _samples_per_point) { + _current[j][AudioPoint::RMS] = 20 * log10 (sqrt (_current[j][AudioPoint::RMS] / _samples_per_point)); + _current[j][AudioPoint::PEAK] = 20 * log10 (_current[j][AudioPoint::PEAK]); + + _analysis->add_point (j, _current[j]); + + _done_for_this_point = 0; + _current[j] = AudioPoint (); + } + } + + ++_done_for_this_point; + } + + _done += b->frames (); +} + diff --git a/src/lib/analyse_audio_job.h b/src/lib/analyse_audio_job.h new file mode 100644 index 000000000..1e7229ce6 --- /dev/null +++ b/src/lib/analyse_audio_job.h @@ -0,0 +1,45 @@ +/* + Copyright (C) 2012 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 + 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 f); + + std::string name () const; + void run (); + +private: + void audio (boost::shared_ptr); + + int64_t _done_for_this_point; + int64_t _done; + int64_t _samples_per_point; + std::vector _current; + + boost::shared_ptr _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..4a710f4c1 --- /dev/null +++ b/src/lib/audio_analysis.cc @@ -0,0 +1,69 @@ +/* + Copyright (C) 2012 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 + 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 +#include +#include +#include "audio_analysis.h" + +using std::ostream; +using std::string; +using std::ofstream; +using std::vector; + +AudioPoint::AudioPoint () +{ + for (int i = 0; i < COUNT; ++i) { + _data[i] = 0; + } +} + +void +AudioPoint::write (ostream& s) const +{ + for (int i = 0; i < COUNT; ++i) { + s << _data[i] << "\n"; + } +} + + +AudioAnalysis::AudioAnalysis (int channels) +{ + _data.resize (channels); +} + +void +AudioAnalysis::add_point (int c, AudioPoint const & p) +{ + assert (c < int (_data.size ())); + _data[c].push_back (p); +} + +void +AudioAnalysis::write (string filename) +{ + ofstream f (filename.c_str ()); + f << _data.size() << "\n"; + for (vector >::iterator i = _data.begin(); i != _data.end(); ++i) { + f << i->size () << "\n"; + for (vector::iterator j = i->begin(); j != i->end(); ++j) { + j->write (f); + } + } +} diff --git a/src/lib/audio_analysis.h b/src/lib/audio_analysis.h new file mode 100644 index 000000000..1c668b9c2 --- /dev/null +++ b/src/lib/audio_analysis.h @@ -0,0 +1,59 @@ +/* + Copyright (C) 2012 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 + 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 +#include + +class AudioPoint +{ +public: + enum Type { + PEAK, + RMS, + COUNT + }; + + AudioPoint (); + + void write (std::ostream &) const; + + float& operator[] (Type t) { + return _data[t]; + } + +private: + float _data[COUNT]; +}; + +class AudioAnalysis +{ +public: + AudioAnalysis (int c); + + void add_point (int c, AudioPoint const & p); + void write (std::string); + +private: + std::vector > _data; +}; + +#endif diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc index d4ed76e37..58c7317ac 100644 --- a/src/lib/ffmpeg_decoder.cc +++ b/src/lib/ffmpeg_decoder.cc @@ -236,8 +236,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) { @@ -258,7 +260,7 @@ FFmpegDecoder::pass () shared_ptr ffa = dynamic_pointer_cast (_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); @@ -288,9 +290,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 1cf161259..fb3fb4cde 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" using std::string; using std::stringstream; @@ -237,6 +238,15 @@ Film::video_mxf_filename () const return video_state_identifier() + ".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 () @@ -303,6 +313,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 () @@ -316,6 +339,12 @@ Film::examine_content () JobManager::instance()->add (_examine_content_job); } +void +Film::analyse_audio_finished () +{ + _analyse_audio_job.reset (); +} + void Film::examine_content_finished () { diff --git a/src/lib/film.h b/src/lib/film.h index 04a483998..c268d3eac 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 (); @@ -374,9 +377,12 @@ private: /** Any running ExamineContentJob, or 0 */ boost::shared_ptr _examine_content_job; + /** Any running AnalyseAudioJob, or 0 */ + boost::shared_ptr _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/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/wscript b/src/lib/wscript index eee04190c..c2b46112c 100644 --- a/src/lib/wscript +++ b/src/lib/wscript @@ -16,6 +16,8 @@ def build(bld): obj.source = """ ab_transcode_job.cc ab_transcoder.cc + analyse_audio_job.cc + audio_analysis.cc audio_decoder.cc audio_source.cc config.cc -- cgit v1.2.3 From dcd968d6d64d645816af0efbcd2f928128c95b9f Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Sun, 24 Feb 2013 22:40:57 +0000 Subject: Basic UI. --- src/lib/analyse_audio_job.cc | 7 ++++- src/lib/audio_analysis.cc | 43 ++++++++++++++++++++++++++ src/lib/audio_analysis.h | 9 +++++- src/wx/audio_dialog.cc | 27 +++++++++++++++++ src/wx/audio_dialog.h | 14 +++++++++ src/wx/audio_plot.cc | 72 ++++++++++++++++++++++++++++++++++++++++++++ src/wx/audio_plot.h | 36 ++++++++++++++++++++++ src/wx/film_editor.cc | 8 ++--- 8 files changed, 210 insertions(+), 6 deletions(-) create mode 100644 src/wx/audio_dialog.cc create mode 100644 src/wx/audio_dialog.h create mode 100644 src/wx/audio_plot.cc create mode 100644 src/wx/audio_plot.h (limited to 'src/lib') diff --git a/src/lib/analyse_audio_job.cc b/src/lib/analyse_audio_job.cc index 5623fdfcc..190d2a5d9 100644 --- a/src/lib/analyse_audio_job.cc +++ b/src/lib/analyse_audio_job.cc @@ -85,7 +85,12 @@ AnalyseAudioJob::audio (shared_ptr b) { for (int i = 0; i < b->frames(); ++i) { for (int j = 0; j < b->channels(); ++j) { - float const s = b->data(j)[i]; + 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)); diff --git a/src/lib/audio_analysis.cc b/src/lib/audio_analysis.cc index 4a710f4c1..39c1ba226 100644 --- a/src/lib/audio_analysis.cc +++ b/src/lib/audio_analysis.cc @@ -18,14 +18,18 @@ */ #include +#include #include #include #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; AudioPoint::AudioPoint () { @@ -34,6 +38,13 @@ AudioPoint::AudioPoint () } } +AudioPoint::AudioPoint (istream& s) +{ + for (int i = 0; i < COUNT; ++i) { + s >> _data[i]; + } +} + void AudioPoint::write (ostream& s) const { @@ -48,6 +59,23 @@ 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) { @@ -55,6 +83,21 @@ AudioAnalysis::add_point (int c, AudioPoint const & p) _data[c].push_back (p); } +AudioPoint +AudioAnalysis::get_point (int c, int p) const +{ + assert (c < int (_data.size ())); + assert (p < int (_data[c].size ())); + return _data[c][p]; +} + +int +AudioAnalysis::points (int c) const +{ + assert (c < int (_data.size ())); + return _data[c].size (); +} + void AudioAnalysis::write (string filename) { diff --git a/src/lib/audio_analysis.h b/src/lib/audio_analysis.h index 1c668b9c2..c26c0584c 100644 --- a/src/lib/audio_analysis.h +++ b/src/lib/audio_analysis.h @@ -33,10 +33,11 @@ public: }; AudioPoint (); + AudioPoint (std::istream &); void write (std::ostream &) const; - float& operator[] (Type t) { + float& operator[] (int t) { return _data[t]; } @@ -48,10 +49,16 @@ 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; + void write (std::string); + private: std::vector > _data; }; diff --git a/src/wx/audio_dialog.cc b/src/wx/audio_dialog.cc new file mode 100644 index 000000000..7f87ee5fd --- /dev/null +++ b/src/wx/audio_dialog.cc @@ -0,0 +1,27 @@ +#include "audio_dialog.h" +#include "audio_plot.h" +#include "audio_analysis.h" +#include "film.h" + +using boost::shared_ptr; + +AudioDialog::AudioDialog (wxWindow* parent, boost::shared_ptr film) + : wxDialog (parent, wxID_ANY, _("Audio"), wxDefaultPosition, wxSize (640, 512), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) +{ + wxBoxSizer* sizer = new wxBoxSizer (wxVERTICAL); + + shared_ptr a; + + try { + a.reset (new AudioAnalysis (film->audio_analysis_path ())); + _plot = new AudioPlot (this, a, 0); + sizer->Add (_plot, 1); + } catch (...) { + + } + + SetSizer (sizer); + sizer->Layout (); + sizer->SetSizeHints (this); +} + diff --git a/src/wx/audio_dialog.h b/src/wx/audio_dialog.h new file mode 100644 index 000000000..324a1ec99 --- /dev/null +++ b/src/wx/audio_dialog.h @@ -0,0 +1,14 @@ +#include +#include + +class AudioPlot; +class Film; + +class AudioDialog : public wxDialog +{ +public: + AudioDialog (wxWindow *, boost::shared_ptr); + +private: + AudioPlot* _plot; +}; diff --git a/src/wx/audio_plot.cc b/src/wx/audio_plot.cc new file mode 100644 index 000000000..4fda3227d --- /dev/null +++ b/src/wx/audio_plot.cc @@ -0,0 +1,72 @@ +#include +#include +#include +#include "audio_plot.h" +#include "lib/decoder_factory.h" +#include "lib/audio_decoder.h" +#include "lib/audio_analysis.h" +#include "wx/wx_util.h" + +using std::cout; +using std::vector; +using std::max; +using std::min; +using boost::bind; +using boost::shared_ptr; + +AudioPlot::AudioPlot (wxWindow* parent, shared_ptr a, int c) + : wxPanel (parent) + , _analysis (a) + , _channel (c) +{ + Connect (wxID_ANY, wxEVT_PAINT, wxPaintEventHandler (AudioPlot::paint), 0, this); + + SetMinSize (wxSize (640, 512)); +} + +void +AudioPlot::paint (wxPaintEvent &) +{ + wxPaintDC dc (this); + wxGraphicsContext* gc = wxGraphicsContext::Create (dc); + if (!gc) { + return; + } + + int const width = GetSize().GetWidth(); + float const xs = width / float (_analysis->points (_channel)); + int const height = GetSize().GetHeight (); + float const ys = height / 60; + + wxGraphicsPath grid = gc->CreatePath (); + gc->SetFont (gc->CreateFont (*wxSMALL_FONT)); + for (int i = -60; i <= 0; i += 10) { + int const y = height - (i + 60) * ys; + grid.MoveToPoint (0, y); + grid.AddLineToPoint (width, y); + gc->DrawText (std_to_wx (String::compose ("%1dB", i)), width - 32, y - 12); + } + gc->SetPen (*wxLIGHT_GREY_PEN); + gc->StrokePath (grid); + + wxGraphicsPath path[AudioPoint::COUNT]; + + for (int i = 0; i < AudioPoint::COUNT; ++i) { + path[i] = gc->CreatePath (); + path[i].MoveToPoint (0, height - (max (_analysis->get_point(_channel, 0)[i], -60.0f) + 60) * ys); + } + + for (int i = 0; i < _analysis->points(_channel); ++i) { + for (int j = 0; j < AudioPoint::COUNT; ++j) { + path[j].AddLineToPoint (i * xs, height - (max (_analysis->get_point(_channel, i)[j], -60.0f) + 60) * ys); + } + } + + gc->SetPen (*wxBLUE_PEN); + gc->StrokePath (path[AudioPoint::RMS]); + + gc->SetPen (*wxRED_PEN); + gc->StrokePath (path[AudioPoint::PEAK]); + + delete gc; +} diff --git a/src/wx/audio_plot.h b/src/wx/audio_plot.h new file mode 100644 index 000000000..565997f7e --- /dev/null +++ b/src/wx/audio_plot.h @@ -0,0 +1,36 @@ +/* + Copyright (C) 2012 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 + 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 +#include +#include + +class AudioAnalysis; + +class AudioPlot : public wxPanel +{ +public: + AudioPlot (wxWindow *, boost::shared_ptr, int); + +private: + void paint (wxPaintEvent &); + + boost::shared_ptr _analysis; + int _channel; +}; diff --git a/src/wx/film_editor.cc b/src/wx/film_editor.cc index 4a67624db..0a9b6d87c 100644 --- a/src/wx/film_editor.cc +++ b/src/wx/film_editor.cc @@ -305,6 +305,10 @@ FilmEditor::make_audio_panel () wxFlexGridSizer* grid = new wxFlexGridSizer (2, 4, 4); _audio_sizer->Add (grid, 0, wxALL, 8); + _show_audio = new wxButton (_audio_panel, wxID_ANY, _("Show Audio...")); + grid->Add (_show_audio, 1); + grid->AddSpacer (0); + { video_control (add_label_to_sizer (grid, _audio_panel, _("Audio Gain"))); wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL); @@ -317,10 +321,6 @@ FilmEditor::make_audio_panel () grid->Add (s); } - _show_audio = new wxButton (_audio_panel, wxID_ANY, _("Show Audio...")); - grid->AddSpacer (0); - grid->Add (_show_audio); - { video_control (add_label_to_sizer (grid, _audio_panel, _("Audio Delay"))); wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL); -- cgit v1.2.3 From 040a227d300033f8a103dc6eb67847286131d9b7 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Mon, 25 Feb 2013 00:11:49 +0000 Subject: Some tidying up, add channel selector to Audio dialog. --- src/lib/analyse_audio_job.cc | 14 ++++----- src/lib/analyse_audio_job.h | 1 - src/lib/ffmpeg_decoder.cc | 2 +- src/lib/util.cc | 20 +++++++++++++ src/lib/util.h | 1 + src/wx/audio_dialog.cc | 68 ++++++++++++++++++++++++++++++++++++++------ src/wx/audio_dialog.h | 24 ++++++++++++++++ src/wx/audio_plot.cc | 44 ++++++++++++++++++++++++++-- src/wx/audio_plot.h | 5 +++- src/wx/film_editor.cc | 32 +++++++++------------ src/wx/film_editor.h | 3 +- 11 files changed, 172 insertions(+), 42 deletions(-) (limited to 'src/lib') diff --git a/src/lib/analyse_audio_job.cc b/src/lib/analyse_audio_job.cc index 190d2a5d9..fb5f2868f 100644 --- a/src/lib/analyse_audio_job.cc +++ b/src/lib/analyse_audio_job.cc @@ -27,13 +27,13 @@ using std::string; using std::max; +using std::cout; using boost::shared_ptr; -int const AnalyseAudioJob::_num_points = 1024; +int const AnalyseAudioJob::_num_points = 128; AnalyseAudioJob::AnalyseAudioJob (shared_ptr f) : Job (f) - , _done_for_this_point (0) , _done (0) , _samples_per_point (1) { @@ -94,20 +94,16 @@ AnalyseAudioJob::audio (shared_ptr b) _current[j][AudioPoint::RMS] += pow (s, 2); _current[j][AudioPoint::PEAK] = max (_current[j][AudioPoint::PEAK], fabsf (s)); - if (_done_for_this_point == _samples_per_point) { + if ((_done % _samples_per_point) == 0) { _current[j][AudioPoint::RMS] = 20 * log10 (sqrt (_current[j][AudioPoint::RMS] / _samples_per_point)); _current[j][AudioPoint::PEAK] = 20 * log10 (_current[j][AudioPoint::PEAK]); - _analysis->add_point (j, _current[j]); - _done_for_this_point = 0; _current[j] = AudioPoint (); } } - - ++_done_for_this_point; - } - _done += b->frames (); + ++_done; + } } diff --git a/src/lib/analyse_audio_job.h b/src/lib/analyse_audio_job.h index 1e7229ce6..dc1e073ee 100644 --- a/src/lib/analyse_audio_job.h +++ b/src/lib/analyse_audio_job.h @@ -33,7 +33,6 @@ public: private: void audio (boost::shared_ptr); - int64_t _done_for_this_point; int64_t _done; int64_t _samples_per_point; std::vector _current; diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc index 58c7317ac..148764162 100644 --- a/src/lib/ffmpeg_decoder.cc +++ b/src/lib/ffmpeg_decoder.cc @@ -197,7 +197,7 @@ FFmpegDecoder::setup_audio () void FFmpegDecoder::setup_subtitle () { - if (!_subtitle_stream) { + if (!_subtitle_stream || _subtitle_stream->id() >= _format_context->nb_streams) { return; } diff --git a/src/lib/util.cc b/src/lib/util.cc index 4ee304600..f807bf329 100644 --- a/src/lib/util.cc +++ b/src/lib/util.cc @@ -898,3 +898,23 @@ 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]; +} diff --git a/src/lib/util.h b/src/lib/util.h index 87735ea8e..b76aead41 100644 --- a/src/lib/util.h +++ b/src/lib/util.h @@ -57,6 +57,7 @@ extern std::vector 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; diff --git a/src/wx/audio_dialog.cc b/src/wx/audio_dialog.cc index 7f87ee5fd..11efc153c 100644 --- a/src/wx/audio_dialog.cc +++ b/src/wx/audio_dialog.cc @@ -1,27 +1,79 @@ +/* + Copyright (C) 2013 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 + 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_dialog.h" #include "audio_plot.h" #include "audio_analysis.h" #include "film.h" +#include "wx_util.h" using boost::shared_ptr; AudioDialog::AudioDialog (wxWindow* parent, boost::shared_ptr film) : wxDialog (parent, wxID_ANY, _("Audio"), wxDefaultPosition, wxSize (640, 512), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) + , _plot (0) { - wxBoxSizer* sizer = new wxBoxSizer (wxVERTICAL); + wxBoxSizer* sizer = new wxBoxSizer (wxHORIZONTAL); + + _plot = new AudioPlot (this); + sizer->Add (_plot, 1); + + wxFlexGridSizer* table = new wxFlexGridSizer (2, 6, 6); + + add_label_to_sizer (table, this, _("Channel")); + _channel = new wxChoice (this, wxID_ANY); + table->Add (_channel, 1, wxEXPAND | wxALL, 6); + + sizer->Add (table); + + set_film (film); + + SetSizer (sizer); + sizer->Layout (); + sizer->SetSizeHints (this); + + _channel->Connect (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler (AudioDialog::channel_changed), 0, this); +} +void +AudioDialog::set_film (boost::shared_ptr f) +{ shared_ptr a; - + try { - a.reset (new AudioAnalysis (film->audio_analysis_path ())); - _plot = new AudioPlot (this, a, 0); - sizer->Add (_plot, 1); + a.reset (new AudioAnalysis (f->audio_analysis_path ())); } catch (...) { + + } + _plot->set_analysis (a); + + _channel->Clear (); + for (int i = 0; i < f->audio_stream()->channels(); ++i) { + _channel->Append (audio_channel_name (i)); } - SetSizer (sizer); - sizer->Layout (); - sizer->SetSizeHints (this); + _channel->SetSelection (0); } +void +AudioDialog::channel_changed (wxCommandEvent &) +{ + _plot->set_channel (_channel->GetSelection ()); +} diff --git a/src/wx/audio_dialog.h b/src/wx/audio_dialog.h index 324a1ec99..1e4563972 100644 --- a/src/wx/audio_dialog.h +++ b/src/wx/audio_dialog.h @@ -1,3 +1,22 @@ +/* + Copyright (C) 2013 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 + 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 #include @@ -9,6 +28,11 @@ class AudioDialog : public wxDialog public: AudioDialog (wxWindow *, boost::shared_ptr); + void set_film (boost::shared_ptr); + private: + void channel_changed (wxCommandEvent &); + AudioPlot* _plot; + wxChoice* _channel; }; diff --git a/src/wx/audio_plot.cc b/src/wx/audio_plot.cc index 4fda3227d..cc2d8f6b4 100644 --- a/src/wx/audio_plot.cc +++ b/src/wx/audio_plot.cc @@ -1,3 +1,22 @@ +/* + Copyright (C) 2013 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 + 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 #include #include @@ -14,20 +33,39 @@ using std::min; using boost::bind; using boost::shared_ptr; -AudioPlot::AudioPlot (wxWindow* parent, shared_ptr a, int c) +AudioPlot::AudioPlot (wxWindow* parent) : wxPanel (parent) - , _analysis (a) - , _channel (c) + , _channel (0) { Connect (wxID_ANY, wxEVT_PAINT, wxPaintEventHandler (AudioPlot::paint), 0, this); SetMinSize (wxSize (640, 512)); } +void +AudioPlot::set_analysis (shared_ptr a) +{ + _analysis = a; + _channel = 0; + Refresh (); +} + +void +AudioPlot::set_channel (int c) +{ + _channel = c; + Refresh (); +} + void AudioPlot::paint (wxPaintEvent &) { wxPaintDC dc (this); + + if (!_analysis) { + return; + } + wxGraphicsContext* gc = wxGraphicsContext::Create (dc); if (!gc) { return; diff --git a/src/wx/audio_plot.h b/src/wx/audio_plot.h index 565997f7e..03bd79323 100644 --- a/src/wx/audio_plot.h +++ b/src/wx/audio_plot.h @@ -26,7 +26,10 @@ class AudioAnalysis; class AudioPlot : public wxPanel { public: - AudioPlot (wxWindow *, boost::shared_ptr, int); + AudioPlot (wxWindow *); + + void set_analysis (boost::shared_ptr); + void set_channel (int c); private: void paint (wxPaintEvent &); diff --git a/src/wx/film_editor.cc b/src/wx/film_editor.cc index 0a9b6d87c..725f2d1b3 100644 --- a/src/wx/film_editor.cc +++ b/src/wx/film_editor.cc @@ -63,6 +63,7 @@ FilmEditor::FilmEditor (shared_ptr f, wxWindow* parent) : wxPanel (parent) , _film (f) , _generally_sensitive (true) + , _audio_dialog (0) { wxBoxSizer* s = new wxBoxSizer (wxVERTICAL); SetSizer (s); @@ -346,22 +347,8 @@ FilmEditor::make_audio_panel () grid->Add (_use_external_audio); grid->AddSpacer (0); - assert (MAX_AUDIO_CHANNELS == 6); - - /* TRANSLATORS: these are the names of audio channels; Lfe (sub) is the low-frequency - enhancement channel (sub-woofer)./ - */ - wxString const channels[] = { - _("Left"), - _("Right"), - _("Centre"), - _("Lfe (sub)"), - _("Left surround"), - _("Right surround"), - }; - for (int i = 0; i < MAX_AUDIO_CHANNELS; ++i) { - add_label_to_sizer (grid, _audio_panel, channels[i]); + add_label_to_sizer (grid, _audio_panel, audio_channel_name (i)); _external_audio[i] = new wxFilePickerCtrl (_audio_panel, wxID_ANY, wxT (""), _("Select Audio File"), wxT ("*.wav")); grid->Add (_external_audio[i], 1, wxEXPAND); } @@ -762,6 +749,10 @@ FilmEditor::set_film (shared_ptr f) } else { FileChanged (""); } + + if (_audio_dialog) { + _audio_dialog->set_film (_film); + } film_changed (Film::NAME); film_changed (Film::USE_DCI_NAME); @@ -823,6 +814,7 @@ FilmEditor::set_things_sensitive (bool s) _j2k_bandwidth->Enable (s); _audio_gain->Enable (s); _audio_gain_calculate_button->Enable (s); + _show_audio->Enable (s); _audio_delay->Enable (s); _still_duration->Enable (s); @@ -1185,7 +1177,11 @@ FilmEditor::setup_dcp_name () void FilmEditor::show_audio_clicked (wxCommandEvent &) { - AudioDialog* d = new AudioDialog (this, _film); - d->ShowModal (); - d->Destroy (); + if (_audio_dialog) { + _audio_dialog->Destroy (); + _audio_dialog = 0; + } + + _audio_dialog = new AudioDialog (this, _film); + _audio_dialog->Show (); } diff --git a/src/wx/film_editor.h b/src/wx/film_editor.h index bd0300aed..2af747bb0 100644 --- a/src/wx/film_editor.h +++ b/src/wx/film_editor.h @@ -29,8 +29,8 @@ #include "lib/film.h" class wxNotebook; - class Film; +class AudioDialog; /** @class FilmEditor * @brief A wx widget to edit a film's metadata, and perform various functions. @@ -179,4 +179,5 @@ private: std::vector _formats; bool _generally_sensitive; + AudioDialog* _audio_dialog; }; -- cgit v1.2.3 From 32ae1f11a9d3e1530c3939190690b5a524997ccb Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Mon, 25 Feb 2013 00:19:06 +0000 Subject: Set up streams after changing content, otherwise we are anticipating things that have not happened yet. --- src/lib/film.cc | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'src/lib') diff --git a/src/lib/film.cc b/src/lib/film.cc index fb3fb4cde..f36614689 100644 --- a/src/lib/film.cc +++ b/src/lib/film.cc @@ -899,6 +899,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()); @@ -908,13 +915,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 (...) { -- cgit v1.2.3 From d371988d26f8c9c4240dc3794df044cbe95d5d0d Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Mon, 25 Feb 2013 18:56:25 +0000 Subject: Fix warning. --- src/lib/ffmpeg_decoder.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/lib') diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc index 148764162..fa2355cc2 100644 --- a/src/lib/ffmpeg_decoder.cc +++ b/src/lib/ffmpeg_decoder.cc @@ -197,7 +197,7 @@ FFmpegDecoder::setup_audio () void FFmpegDecoder::setup_subtitle () { - if (!_subtitle_stream || _subtitle_stream->id() >= _format_context->nb_streams) { + if (!_subtitle_stream || _subtitle_stream->id() >= int (_format_context->nb_streams)) { return; } -- cgit v1.2.3 From 8bfb6ae0780b0bf3318c345df78518ad3fabc9fc Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Mon, 25 Feb 2013 19:27:58 +0000 Subject: Tidy up creation of analysis a bit. --- src/lib/audio_analysis.cc | 8 +++++- src/lib/film.cc | 3 +++ src/lib/film.h | 2 ++ src/lib/job.cc | 1 - src/lib/job.h | 1 + src/wx/audio_dialog.cc | 62 ++++++++++++++++++++++++++++++++++------------ src/wx/audio_dialog.h | 7 ++++-- src/wx/audio_plot.cc | 10 +++++--- src/wx/film_editor.cc | 3 ++- src/wx/job_manager_view.cc | 1 + 10 files changed, 73 insertions(+), 25 deletions(-) (limited to 'src/lib') diff --git a/src/lib/audio_analysis.cc b/src/lib/audio_analysis.cc index 39c1ba226..fffafc4d4 100644 --- a/src/lib/audio_analysis.cc +++ b/src/lib/audio_analysis.cc @@ -21,6 +21,7 @@ #include #include #include +#include #include "audio_analysis.h" using std::ostream; @@ -101,7 +102,9 @@ AudioAnalysis::points (int c) const void AudioAnalysis::write (string filename) { - ofstream f (filename.c_str ()); + string tmp = filename + ".tmp"; + + ofstream f (tmp.c_str ()); f << _data.size() << "\n"; for (vector >::iterator i = _data.begin(); i != _data.end(); ++i) { f << i->size () << "\n"; @@ -109,4 +112,7 @@ AudioAnalysis::write (string filename) j->write (f); } } + + f.close (); + boost::filesystem::rename (tmp, filename); } diff --git a/src/lib/film.cc b/src/lib/film.cc index f36614689..ab636bdfc 100644 --- a/src/lib/film.cc +++ b/src/lib/film.cc @@ -342,7 +342,10 @@ Film::examine_content () void Film::analyse_audio_finished () { + ensure_ui_thread (); _analyse_audio_job.reset (); + + AudioAnalysisFinished (); } void diff --git a/src/lib/film.h b/src/lib/film.h index c268d3eac..847ab434e 100644 --- a/src/lib/film.h +++ b/src/lib/film.h @@ -367,6 +367,8 @@ public: /** Emitted when some property has changed */ mutable boost::signals2::signal Changed; + boost::signals2::signal AudioAnalysisFinished; + /** Current version number of the state file */ static int const state_version; diff --git a/src/lib/job.cc b/src/lib/job.cc index bfad65a0a..6a53e629c 100644 --- a/src/lib/job.cc +++ b/src/lib/job.cc @@ -147,7 +147,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 Finished; protected: diff --git a/src/wx/audio_dialog.cc b/src/wx/audio_dialog.cc index 32864ca15..89b04409a 100644 --- a/src/wx/audio_dialog.cc +++ b/src/wx/audio_dialog.cc @@ -17,6 +17,7 @@ */ +#include #include "audio_dialog.h" #include "audio_plot.h" #include "audio_analysis.h" @@ -24,8 +25,9 @@ #include "wx_util.h" using boost::shared_ptr; +using boost::bind; -AudioDialog::AudioDialog (wxWindow* parent, boost::shared_ptr film) +AudioDialog::AudioDialog (wxWindow* parent) : wxDialog (parent, wxID_ANY, _("Audio"), wxDefaultPosition, wxSize (640, 512), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) , _plot (0) { @@ -42,8 +44,6 @@ AudioDialog::AudioDialog (wxWindow* parent, boost::shared_ptr film) sizer->Add (table); - set_film (film); - SetSizer (sizer); sizer->Layout (); sizer->SetSizeHints (this); @@ -54,27 +54,48 @@ AudioDialog::AudioDialog (wxWindow* parent, boost::shared_ptr film) void AudioDialog::set_film (boost::shared_ptr f) { - _film_connection.disconnect (); - _film = f; + _film_changed_connection.disconnect (); + _film_audio_analysis_finished_connection.disconnect (); - shared_ptr a; + _film = f; - try { - a.reset (new AudioAnalysis (f->audio_analysis_path ())); - } catch (...) { + try_to_load_analysis (); + setup_channels (); - } - - _plot->set_analysis (a); + _channel->SetSelection (0); + _film_changed_connection = _film->Changed.connect (bind (&AudioDialog::film_changed, this, _1)); + _film_audio_analysis_finished_connection = _film->AudioAnalysisFinished.connect (bind (&AudioDialog::try_to_load_analysis, this)); +} + +void +AudioDialog::setup_channels () +{ _channel->Clear (); - for (int i = 0; i < f->audio_stream()->channels(); ++i) { + + if (!_film->audio_stream()) { + return; + } + + for (int i = 0; i < _film->audio_stream()->channels(); ++i) { _channel->Append (audio_channel_name (i)); } +} - _channel->SetSelection (0); +void +AudioDialog::try_to_load_analysis () +{ + shared_ptr a; - _film_connection = f->Changed.connect (bind (&AudioDialog::film_changed, this, _1)); + if (boost::filesystem::exists (_film->audio_analysis_path())) { + a.reset (new AudioAnalysis (_film->audio_analysis_path ())); + } else { + if (IsShown ()) { + _film->analyse_audio (); + } + } + + _plot->set_analysis (a); } void @@ -86,7 +107,16 @@ AudioDialog::channel_changed (wxCommandEvent &) void AudioDialog::film_changed (Film::Property p) { - if (p == Film::AUDIO_GAIN) { + switch (p) { + case Film::AUDIO_GAIN: _plot->set_gain (_film->audio_gain ()); + break; + case Film::CONTENT_AUDIO_STREAM: + case Film::EXTERNAL_AUDIO: + case Film::USE_CONTENT_AUDIO: + setup_channels (); + break; + default: + break; } } diff --git a/src/wx/audio_dialog.h b/src/wx/audio_dialog.h index 968fd0a12..623c9a067 100644 --- a/src/wx/audio_dialog.h +++ b/src/wx/audio_dialog.h @@ -28,16 +28,19 @@ class Film; class AudioDialog : public wxDialog { public: - AudioDialog (wxWindow *, boost::shared_ptr); + AudioDialog (wxWindow *); void set_film (boost::shared_ptr); private: void film_changed (Film::Property); void channel_changed (wxCommandEvent &); + void try_to_load_analysis (); + void setup_channels (); boost::shared_ptr _film; AudioPlot* _plot; wxChoice* _channel; - boost::signals2::scoped_connection _film_connection; + boost::signals2::scoped_connection _film_changed_connection; + boost::signals2::scoped_connection _film_audio_analysis_finished_connection; }; diff --git a/src/wx/audio_plot.cc b/src/wx/audio_plot.cc index 1ad07fcce..41d074dad 100644 --- a/src/wx/audio_plot.cc +++ b/src/wx/audio_plot.cc @@ -65,15 +65,17 @@ AudioPlot::paint (wxPaintEvent &) { wxPaintDC dc (this); - if (!_analysis) { - return; - } - wxGraphicsContext* gc = wxGraphicsContext::Create (dc); if (!gc) { return; } + if (!_analysis) { + gc->SetFont (gc->CreateFont (*wxNORMAL_FONT)); + gc->DrawText (_("Please wait; audio is being analysed..."), 32, 32); + return; + } + int const width = GetSize().GetWidth(); float const xs = width / float (_analysis->points (_channel)); int const height = GetSize().GetHeight (); diff --git a/src/wx/film_editor.cc b/src/wx/film_editor.cc index 725f2d1b3..c9f83677c 100644 --- a/src/wx/film_editor.cc +++ b/src/wx/film_editor.cc @@ -1182,6 +1182,7 @@ FilmEditor::show_audio_clicked (wxCommandEvent &) _audio_dialog = 0; } - _audio_dialog = new AudioDialog (this, _film); + _audio_dialog = new AudioDialog (this); _audio_dialog->Show (); + _audio_dialog->set_film (_film); } diff --git a/src/wx/job_manager_view.cc b/src/wx/job_manager_view.cc index 7537da287..7361f29a8 100644 --- a/src/wx/job_manager_view.cc +++ b/src/wx/job_manager_view.cc @@ -109,6 +109,7 @@ JobManagerView::update () if ((*i)->finished() && !_job_records[*i].finalised) { _job_records[*i].gauge->SetValue (100); checked_set (_job_records[*i].message, st); + (*i)->Finished (); _job_records[*i].finalised = true; if (!(*i)->error_details().empty ()) { _job_records[*i].details->Enable (true); -- cgit v1.2.3 From 6e0f2a39c9deeb51f05c0c8c9bd46632c2c6483a Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Mon, 25 Feb 2013 20:21:29 +0000 Subject: Multiple simultaneous plots. --- src/lib/audio_analysis.cc | 13 ++++--- src/lib/audio_analysis.h | 1 + src/wx/audio_dialog.cc | 68 +++++++++++++++++++++++++++++------- src/wx/audio_dialog.h | 7 ++-- src/wx/audio_plot.cc | 88 +++++++++++++++++++++++++++++++++++++---------- src/wx/audio_plot.h | 12 ++++--- 6 files changed, 148 insertions(+), 41 deletions(-) (limited to 'src/lib') diff --git a/src/lib/audio_analysis.cc b/src/lib/audio_analysis.cc index fffafc4d4..b29ed1707 100644 --- a/src/lib/audio_analysis.cc +++ b/src/lib/audio_analysis.cc @@ -80,22 +80,27 @@ AudioAnalysis::AudioAnalysis (string filename) void AudioAnalysis::add_point (int c, AudioPoint const & p) { - assert (c < int (_data.size ())); + assert (c < channels ()); _data[c].push_back (p); } AudioPoint AudioAnalysis::get_point (int c, int p) const { - assert (c < int (_data.size ())); - assert (p < int (_data[c].size ())); + assert (p < points (c)); return _data[c][p]; } +int +AudioAnalysis::channels () const +{ + return _data.size (); +} + int AudioAnalysis::points (int c) const { - assert (c < int (_data.size ())); + assert (c < channels ()); return _data[c].size (); } diff --git a/src/lib/audio_analysis.h b/src/lib/audio_analysis.h index c26c0584c..c2d8db876 100644 --- a/src/lib/audio_analysis.h +++ b/src/lib/audio_analysis.h @@ -55,6 +55,7 @@ public: AudioPoint get_point (int c, int p) const; int points (int c) const; + int channels () const; void write (std::string); diff --git a/src/wx/audio_dialog.cc b/src/wx/audio_dialog.cc index 89b04409a..dc297e246 100644 --- a/src/wx/audio_dialog.cc +++ b/src/wx/audio_dialog.cc @@ -38,17 +38,33 @@ AudioDialog::AudioDialog (wxWindow* parent) wxFlexGridSizer* table = new wxFlexGridSizer (2, 6, 6); - add_label_to_sizer (table, this, _("Channel")); - _channel = new wxChoice (this, wxID_ANY); - table->Add (_channel, 1, wxEXPAND | wxALL, 6); + for (int i = 0; i < MAX_AUDIO_CHANNELS; ++i) { + _channel_checkbox[i] = new wxCheckBox (this, wxID_ANY, audio_channel_name (i)); + table->Add (_channel_checkbox[i], 1, wxEXPAND); + table->AddSpacer (0); + _channel_checkbox[i]->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler (AudioDialog::channel_clicked), 0, this); + } + + table->AddSpacer (0); + table->AddSpacer (0); + + wxString const types[] = { + _("Peak"), + _("RMS") + }; + + for (int i = 0; i < AudioPoint::COUNT; ++i) { + _type_checkbox[i] = new wxCheckBox (this, wxID_ANY, types[i]); + table->Add (_type_checkbox[i], 1, wxEXPAND); + table->AddSpacer (0); + _type_checkbox[i]->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler (AudioDialog::type_clicked), 0, this); + } sizer->Add (table); SetSizer (sizer); sizer->Layout (); sizer->SetSizeHints (this); - - _channel->Connect (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler (AudioDialog::channel_changed), 0, this); } void @@ -61,8 +77,7 @@ AudioDialog::set_film (boost::shared_ptr f) try_to_load_analysis (); setup_channels (); - - _channel->SetSelection (0); + _plot->set_gain (_film->audio_gain ()); _film_changed_connection = _film->Changed.connect (bind (&AudioDialog::film_changed, this, _1)); _film_audio_analysis_finished_connection = _film->AudioAnalysisFinished.connect (bind (&AudioDialog::try_to_load_analysis, this)); @@ -71,14 +86,16 @@ AudioDialog::set_film (boost::shared_ptr f) void AudioDialog::setup_channels () { - _channel->Clear (); - if (!_film->audio_stream()) { return; } for (int i = 0; i < _film->audio_stream()->channels(); ++i) { - _channel->Append (audio_channel_name (i)); + _channel_checkbox[i]->Show (); + } + + for (int i = _film->audio_stream()->channels(); i < MAX_AUDIO_CHANNELS; ++i) { + _channel_checkbox[i]->Hide (); } } @@ -96,12 +113,26 @@ AudioDialog::try_to_load_analysis () } _plot->set_analysis (a); + _channel_checkbox[0]->SetValue (true); + _plot->set_channel_visible (0, true); + + for (int i = 0; i < AudioPoint::COUNT; ++i) { + _type_checkbox[i]->SetValue (true); + _plot->set_type_visible (i, true); + } } void -AudioDialog::channel_changed (wxCommandEvent &) +AudioDialog::channel_clicked (wxCommandEvent& ev) { - _plot->set_channel (_channel->GetSelection ()); + int c = 0; + while (c < MAX_AUDIO_CHANNELS && ev.GetEventObject() != _channel_checkbox[c]) { + ++c; + } + + assert (c < MAX_AUDIO_CHANNELS); + + _plot->set_channel_visible (c, _channel_checkbox[c]->GetValue ()); } void @@ -120,3 +151,16 @@ AudioDialog::film_changed (Film::Property p) break; } } + +void +AudioDialog::type_clicked (wxCommandEvent& ev) +{ + int t = 0; + while (t < AudioPoint::COUNT && ev.GetEventObject() != _type_checkbox[t]) { + ++t; + } + + assert (t < AudioPoint::COUNT); + + _plot->set_type_visible (t, _type_checkbox[t]->GetValue ()); +} diff --git a/src/wx/audio_dialog.h b/src/wx/audio_dialog.h index 623c9a067..c3875023f 100644 --- a/src/wx/audio_dialog.h +++ b/src/wx/audio_dialog.h @@ -21,6 +21,7 @@ #include #include #include "lib/film.h" +#include "lib/audio_analysis.h" class AudioPlot; class Film; @@ -34,13 +35,15 @@ public: private: void film_changed (Film::Property); - void channel_changed (wxCommandEvent &); + void channel_clicked (wxCommandEvent &); + void type_clicked (wxCommandEvent &); void try_to_load_analysis (); void setup_channels (); boost::shared_ptr _film; AudioPlot* _plot; - wxChoice* _channel; + wxCheckBox* _channel_checkbox[MAX_AUDIO_CHANNELS]; + wxCheckBox* _type_checkbox[AudioPoint::COUNT]; boost::signals2::scoped_connection _film_changed_connection; boost::signals2::scoped_connection _film_audio_analysis_finished_connection; }; diff --git a/src/wx/audio_plot.cc b/src/wx/audio_plot.cc index 41d074dad..cccdaed34 100644 --- a/src/wx/audio_plot.cc +++ b/src/wx/audio_plot.cc @@ -37,11 +37,25 @@ int const AudioPlot::_minimum = -90; AudioPlot::AudioPlot (wxWindow* parent) : wxPanel (parent) - , _channel (0) , _gain (0) { - Connect (wxID_ANY, wxEVT_PAINT, wxPaintEventHandler (AudioPlot::paint), 0, this); + for (int i = 0; i < MAX_AUDIO_CHANNELS; ++i) { + _channel_visible[i] = false; + } + for (int i = 0; i < AudioPoint::COUNT; ++i) { + _type_visible[i] = false; + } + + _colours.push_back (wxColour ( 0, 0, 0)); + _colours.push_back (wxColour (255, 0, 0)); + _colours.push_back (wxColour ( 0, 255, 0)); + _colours.push_back (wxColour (139, 0, 204)); + _colours.push_back (wxColour ( 0, 0, 255)); + _colours.push_back (wxColour (100, 100, 100)); + + Connect (wxID_ANY, wxEVT_PAINT, wxPaintEventHandler (AudioPlot::paint), 0, this); + SetMinSize (wxSize (640, 512)); } @@ -49,14 +63,29 @@ void AudioPlot::set_analysis (shared_ptr a) { _analysis = a; - _channel = 0; + + for (int i = 0; i < MAX_AUDIO_CHANNELS; ++i) { + _channel_visible[i] = false; + } + + for (int i = 0; i < AudioPoint::COUNT; ++i) { + _type_visible[i] = false; + } + Refresh (); } void -AudioPlot::set_channel (int c) +AudioPlot::set_channel_visible (int c, bool v) { - _channel = c; + _channel_visible[c] = v; + Refresh (); +} + +void +AudioPlot::set_type_visible (int t, bool v) +{ + _type_visible[t] = v; Refresh (); } @@ -77,7 +106,8 @@ AudioPlot::paint (wxPaintEvent &) } int const width = GetSize().GetWidth(); - float const xs = width / float (_analysis->points (_channel)); + /* Assume all channels have the same number of points */ + float const xs = width / float (_analysis->points (0)); int const height = GetSize().GetHeight (); float const ys = height / -_minimum; @@ -92,24 +122,44 @@ AudioPlot::paint (wxPaintEvent &) gc->SetPen (*wxLIGHT_GREY_PEN); gc->StrokePath (grid); - wxGraphicsPath path[AudioPoint::COUNT]; + for (int c = 0; c < MAX_AUDIO_CHANNELS; ++c) { + if (!_channel_visible[c] || c >= _analysis->channels()) { + continue; + } - for (int i = 0; i < AudioPoint::COUNT; ++i) { - path[i] = gc->CreatePath (); - path[i].MoveToPoint (0, height - (max (_analysis->get_point(_channel, 0)[i], float (_minimum)) - _minimum + _gain) * ys); - } + wxGraphicsPath path[AudioPoint::COUNT]; + + for (int i = 0; i < AudioPoint::COUNT; ++i) { + if (!_type_visible[i]) { + continue; + } + + path[i] = gc->CreatePath (); + path[i].MoveToPoint (0, height - (max (_analysis->get_point(c, 0)[i], float (_minimum)) - _minimum + _gain) * ys); + } - for (int i = 0; i < _analysis->points(_channel); ++i) { - for (int j = 0; j < AudioPoint::COUNT; ++j) { - path[j].AddLineToPoint (i * xs, height - (max (_analysis->get_point(_channel, i)[j], float (_minimum)) - _minimum + _gain) * ys); + for (int i = 0; i < _analysis->points(c); ++i) { + for (int j = 0; j < AudioPoint::COUNT; ++j) { + if (!_type_visible[j]) { + continue; + } + + path[j].AddLineToPoint (i * xs, height - (max (_analysis->get_point(c, i)[j], float (_minimum)) - _minimum + _gain) * ys); + } } - } - gc->SetPen (*wxBLUE_PEN); - gc->StrokePath (path[AudioPoint::RMS]); + wxColour const col = _colours[c]; - gc->SetPen (*wxRED_PEN); - gc->StrokePath (path[AudioPoint::PEAK]); + if (_type_visible[AudioPoint::RMS]) { + gc->SetPen (*wxThePenList->FindOrCreatePen (col)); + gc->StrokePath (path[AudioPoint::RMS]); + } + + if (_type_visible[AudioPoint::PEAK]) { + gc->SetPen (*wxThePenList->FindOrCreatePen (wxColour (col.Red(), col.Green(), col.Blue(), col.Alpha() / 2))); + gc->StrokePath (path[AudioPoint::PEAK]); + } + } delete gc; } diff --git a/src/wx/audio_plot.h b/src/wx/audio_plot.h index b2ae139d1..4ac7f848c 100644 --- a/src/wx/audio_plot.h +++ b/src/wx/audio_plot.h @@ -20,8 +20,8 @@ #include #include #include - -class AudioAnalysis; +#include "util.h" +#include "audio_analysis.h" class AudioPlot : public wxPanel { @@ -29,16 +29,20 @@ public: AudioPlot (wxWindow *); void set_analysis (boost::shared_ptr); - void set_channel (int c); + void set_channel_visible (int c, bool v); + void set_type_visible (int t, bool v); void set_gain (float); private: void paint (wxPaintEvent &); boost::shared_ptr _analysis; - int _channel; + bool _channel_visible[MAX_AUDIO_CHANNELS]; + bool _type_visible[AudioPoint::COUNT]; /** gain to apply in dB */ float _gain; + std::vector _colours; + static const int _minimum; }; -- cgit v1.2.3 From eac941aed6e6eed7f0329e42c7a036ed620f8df0 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Mon, 25 Feb 2013 20:52:20 +0000 Subject: Try to tidy up channel mapping a bit. --- src/lib/encoder.cc | 26 ++++++++++---------- src/lib/util.cc | 65 ++++++++++++++++++++++++++++++++++++++++---------- src/lib/util.h | 15 +++++++++++- src/lib/writer.cc | 6 +++-- src/wx/audio_dialog.cc | 32 +++++++++++++++++-------- 5 files changed, 106 insertions(+), 38 deletions(-) (limited to 'src/lib') diff --git a/src/lib/encoder.cc b/src/lib/encoder.cc index f25256379..07139968d 100644 --- a/src/lib/encoder.cc +++ b/src/lib/encoder.cc @@ -423,18 +423,20 @@ Encoder::encoder_thread (ServerDescription* server) void Encoder::write_audio (shared_ptr 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 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 b (new AudioBuffers (m.dcp_channels(), data->frames ())); + for (int i = 0; i < m.dcp_channels(); ++i) { + optional s = m.dcp_to_source (static_cast (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/util.cc b/src/lib/util.cc index f807bf329..8ad980361 100644 --- a/src/lib/util.cc +++ b/src/lib/util.cc @@ -445,19 +445,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); @@ -918,3 +905,55 @@ audio_channel_name (int c) return channels[c]; } + +AudioMapping::AudioMapping (int c) + : _source_channels (c) +{ + +} + +optional +AudioMapping::source_to_dcp (int c) const +{ + if (c >= _source_channels) { + return optional (); + } + + if (_source_channels == 1) { + /* mono sources to centre */ + return libdcp::CENTRE; + } + + return static_cast (c); +} + +optional +AudioMapping::dcp_to_source (libdcp::Channel c) const +{ + if (_source_channels == 1) { + if (c == libdcp::CENTRE) { + return 0; + } else { + return optional (); + } + } + + if (static_cast (c) >= _source_channels) { + return optional (); + } + + return static_cast (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 b76aead41..22c6ea95b 100644 --- a/src/lib/util.h +++ b/src/lib/util.h @@ -29,6 +29,7 @@ #include #include #include +#include #include extern "C" { #include @@ -179,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); @@ -269,6 +269,19 @@ private: float** _data; }; +class AudioMapping +{ +public: + AudioMapping (int); + + boost::optional source_to_dcp (int c) const; + boost::optional 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 cpu_info (); diff --git a/src/lib/writer.cc b/src/lib/writer.cc index c2cc00328..1dad4357d 100644 --- a/src/lib/writer.cc +++ b/src/lib/writer.cc @@ -72,13 +72,15 @@ Writer::Writer (shared_ptr 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()), "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/wx/audio_dialog.cc b/src/wx/audio_dialog.cc index dc297e246..a422e4e46 100644 --- a/src/wx/audio_dialog.cc +++ b/src/wx/audio_dialog.cc @@ -26,6 +26,7 @@ using boost::shared_ptr; using boost::bind; +using boost::optional; AudioDialog::AudioDialog (wxWindow* parent) : wxDialog (parent, wxID_ANY, _("Audio"), wxDefaultPosition, wxSize (640, 512), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) @@ -89,13 +90,15 @@ AudioDialog::setup_channels () if (!_film->audio_stream()) { return; } - - for (int i = 0; i < _film->audio_stream()->channels(); ++i) { - _channel_checkbox[i]->Show (); - } - for (int i = _film->audio_stream()->channels(); i < MAX_AUDIO_CHANNELS; ++i) { - _channel_checkbox[i]->Hide (); + AudioMapping m (_film->audio_stream()->channels ()); + + for (int i = 0; i < MAX_AUDIO_CHANNELS; ++i) { + if (m.dcp_to_source(static_cast(i))) { + _channel_checkbox[i]->Show (); + } else { + _channel_checkbox[i]->Hide (); + } } } @@ -113,8 +116,13 @@ AudioDialog::try_to_load_analysis () } _plot->set_analysis (a); - _channel_checkbox[0]->SetValue (true); - _plot->set_channel_visible (0, true); + + AudioMapping m (_film->audio_stream()->channels ()); + optional c = m.source_to_dcp (0); + if (c) { + _channel_checkbox[c.get()]->SetValue (true); + _plot->set_channel_visible (0, true); + } for (int i = 0; i < AudioPoint::COUNT; ++i) { _type_checkbox[i]->SetValue (true); @@ -131,8 +139,12 @@ AudioDialog::channel_clicked (wxCommandEvent& ev) } assert (c < MAX_AUDIO_CHANNELS); - - _plot->set_channel_visible (c, _channel_checkbox[c]->GetValue ()); + + AudioMapping m (_film->audio_stream()->channels ()); + optional s = m.dcp_to_source (static_cast (c)); + if (s) { + _plot->set_channel_visible (s.get(), _channel_checkbox[c]->GetValue ()); + } } void -- cgit v1.2.3 From 36fdb78ea9973d1a797171d762802e707577c960 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Mon, 25 Feb 2013 23:40:08 +0000 Subject: Tweak number of points and minimum dB for display. --- src/lib/analyse_audio_job.cc | 2 +- src/wx/audio_plot.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src/lib') diff --git a/src/lib/analyse_audio_job.cc b/src/lib/analyse_audio_job.cc index fb5f2868f..588e9fc3d 100644 --- a/src/lib/analyse_audio_job.cc +++ b/src/lib/analyse_audio_job.cc @@ -30,7 +30,7 @@ using std::max; using std::cout; using boost::shared_ptr; -int const AnalyseAudioJob::_num_points = 128; +int const AnalyseAudioJob::_num_points = 1024; AnalyseAudioJob::AnalyseAudioJob (shared_ptr f) : Job (f) diff --git a/src/wx/audio_plot.cc b/src/wx/audio_plot.cc index 8d5184d48..ad69b6e1d 100644 --- a/src/wx/audio_plot.cc +++ b/src/wx/audio_plot.cc @@ -33,7 +33,7 @@ using std::min; using boost::bind; using boost::shared_ptr; -int const AudioPlot::_minimum = -90; +int const AudioPlot::_minimum = -70; AudioPlot::AudioPlot (wxWindow* parent) : wxPanel (parent) -- cgit v1.2.3 From 1fadfdf60bb2c02086c2c9689ea44c73bed41571 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Tue, 26 Feb 2013 08:15:51 +0000 Subject: Pretty dumb smoothing. --- src/lib/analyse_audio_job.cc | 3 +-- src/lib/audio_analysis.cc | 28 ++++++++++++++++++++++++++++ src/lib/audio_analysis.h | 2 ++ src/wx/audio_dialog.cc | 11 +++++++++++ src/wx/audio_dialog.h | 2 ++ src/wx/audio_plot.cc | 25 +++++++++++++++++++++++-- src/wx/audio_plot.h | 2 ++ 7 files changed, 69 insertions(+), 4 deletions(-) (limited to 'src/lib') diff --git a/src/lib/analyse_audio_job.cc b/src/lib/analyse_audio_job.cc index 588e9fc3d..bcabb6c91 100644 --- a/src/lib/analyse_audio_job.cc +++ b/src/lib/analyse_audio_job.cc @@ -95,8 +95,7 @@ AnalyseAudioJob::audio (shared_ptr b) _current[j][AudioPoint::PEAK] = max (_current[j][AudioPoint::PEAK], fabsf (s)); if ((_done % _samples_per_point) == 0) { - _current[j][AudioPoint::RMS] = 20 * log10 (sqrt (_current[j][AudioPoint::RMS] / _samples_per_point)); - _current[j][AudioPoint::PEAK] = 20 * log10 (_current[j][AudioPoint::PEAK]); + _current[j][AudioPoint::RMS] = sqrt (_current[j][AudioPoint::RMS] / _samples_per_point); _analysis->add_point (j, _current[j]); _current[j] = AudioPoint (); diff --git a/src/lib/audio_analysis.cc b/src/lib/audio_analysis.cc index b29ed1707..0cf08c5bd 100644 --- a/src/lib/audio_analysis.cc +++ b/src/lib/audio_analysis.cc @@ -31,6 +31,8 @@ using std::ofstream; using std::ifstream; using std::vector; using std::cout; +using std::max; +using std::list; AudioPoint::AudioPoint () { @@ -121,3 +123,29 @@ AudioAnalysis::write (string filename) f.close (); boost::filesystem::rename (tmp, filename); } + +float +AudioAnalysis::smooth (list const & data, AudioPoint::Type t) +{ + float val; + + switch (t) { + case AudioPoint::PEAK: + /* XXX: fall-off, or something...? */ + val = -200; + for (list::const_iterator i = data.begin(); i != data.end(); ++i) { + val = max (val, *i); + } + return val; + case AudioPoint::RMS: + val = 0; + for (list::const_iterator i = data.begin(); i != data.end(); ++i) { + val += pow (*i, 2); + } + return sqrt (val / data.size()); + default: + assert (false); + } + + return 0; +} diff --git a/src/lib/audio_analysis.h b/src/lib/audio_analysis.h index c2d8db876..a8cfbdeca 100644 --- a/src/lib/audio_analysis.h +++ b/src/lib/audio_analysis.h @@ -22,6 +22,7 @@ #include #include +#include class AudioPoint { @@ -59,6 +60,7 @@ public: void write (std::string); + static float smooth (std::list const &, AudioPoint::Type); private: std::vector > _data; diff --git a/src/wx/audio_dialog.cc b/src/wx/audio_dialog.cc index 701c8263a..bcec01332 100644 --- a/src/wx/audio_dialog.cc +++ b/src/wx/audio_dialog.cc @@ -61,6 +61,11 @@ AudioDialog::AudioDialog (wxWindow* parent) _type_checkbox[i]->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler (AudioDialog::type_clicked), 0, this); } + _smoothing = new wxSlider (this, wxID_ANY, 1, 1, 128); + _smoothing->Connect (wxID_ANY, wxEVT_SCROLL_THUMBTRACK, wxScrollEventHandler (AudioDialog::smoothing_changed), 0, this); + table->Add (_smoothing, 1, wxEXPAND); + table->AddSpacer (0); + sizer->Add (table, 0, wxALL, 12); SetSizer (sizer); @@ -178,3 +183,9 @@ AudioDialog::type_clicked (wxCommandEvent& ev) _plot->set_type_visible (t, _type_checkbox[t]->GetValue ()); } + +void +AudioDialog::smoothing_changed (wxScrollEvent &) +{ + _plot->set_smoothing (_smoothing->GetValue ()); +} diff --git a/src/wx/audio_dialog.h b/src/wx/audio_dialog.h index c3875023f..16cb356fe 100644 --- a/src/wx/audio_dialog.h +++ b/src/wx/audio_dialog.h @@ -37,6 +37,7 @@ private: void film_changed (Film::Property); void channel_clicked (wxCommandEvent &); void type_clicked (wxCommandEvent &); + void smoothing_changed (wxScrollEvent &); void try_to_load_analysis (); void setup_channels (); @@ -44,6 +45,7 @@ private: AudioPlot* _plot; wxCheckBox* _channel_checkbox[MAX_AUDIO_CHANNELS]; wxCheckBox* _type_checkbox[AudioPoint::COUNT]; + wxSlider* _smoothing; boost::signals2::scoped_connection _film_changed_connection; boost::signals2::scoped_connection _film_audio_analysis_finished_connection; }; diff --git a/src/wx/audio_plot.cc b/src/wx/audio_plot.cc index ad69b6e1d..d938d0c27 100644 --- a/src/wx/audio_plot.cc +++ b/src/wx/audio_plot.cc @@ -28,6 +28,7 @@ using std::cout; using std::vector; +using std::list; using std::max; using std::min; using boost::bind; @@ -38,6 +39,7 @@ int const AudioPlot::_minimum = -70; AudioPlot::AudioPlot (wxWindow* parent) : wxPanel (parent) , _gain (0) + , _smoothing (1) { SetDoubleBuffered (true); @@ -149,21 +151,33 @@ AudioPlot::paint (wxPaintEvent &) } path[i] = gc->CreatePath (); + + float const val = 20 * log10 (_analysis->get_point(c, 0)[i]); + path[i].MoveToPoint ( db_label_width, - height - (max (_analysis->get_point(c, 0)[i], float (_minimum)) - _minimum + _gain) * ys - yo + height - (max (val, float (_minimum)) - _minimum + _gain) * ys - yo ); } + list smoothing[AudioPoint::COUNT]; + for (int i = 0; i < _analysis->points(c); ++i) { for (int j = 0; j < AudioPoint::COUNT; ++j) { if (!_type_visible[j]) { continue; } + + smoothing[j].push_back (_analysis->get_point(c, i)[j]); + if (int(smoothing[j].size()) > _smoothing) { + smoothing[j].pop_front (); + } + + float const val = 20 * log10 (_analysis->smooth (smoothing[j], static_cast (j))); path[j].AddLineToPoint ( i * xs + db_label_width, - height - (max (_analysis->get_point(c, i)[j], float (_minimum)) - _minimum + _gain) * ys - yo + height - (max (val, float (_minimum)) - _minimum + _gain) * ys - yo ); } } @@ -197,3 +211,10 @@ AudioPlot::set_gain (float g) _gain = g; Refresh (); } + +void +AudioPlot::set_smoothing (int s) +{ + _smoothing = s; + Refresh (); +} diff --git a/src/wx/audio_plot.h b/src/wx/audio_plot.h index 4ac7f848c..fe8862d54 100644 --- a/src/wx/audio_plot.h +++ b/src/wx/audio_plot.h @@ -32,6 +32,7 @@ public: void set_channel_visible (int c, bool v); void set_type_visible (int t, bool v); void set_gain (float); + void set_smoothing (int); private: void paint (wxPaintEvent &); @@ -41,6 +42,7 @@ private: bool _type_visible[AudioPoint::COUNT]; /** gain to apply in dB */ float _gain; + int _smoothing; std::vector _colours; -- cgit v1.2.3 From ab0ef1016c94d583d00ccb734373d7d07faf0e24 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 27 Feb 2013 00:25:37 +0000 Subject: Improve smoothing behaviour. --- src/lib/audio_analysis.cc | 26 -------- src/lib/audio_analysis.h | 2 - src/wx/audio_dialog.cc | 2 +- src/wx/audio_plot.cc | 149 ++++++++++++++++++++++++++-------------------- src/wx/audio_plot.h | 13 +++- 5 files changed, 97 insertions(+), 95 deletions(-) (limited to 'src/lib') diff --git a/src/lib/audio_analysis.cc b/src/lib/audio_analysis.cc index 0cf08c5bd..9d708bbfd 100644 --- a/src/lib/audio_analysis.cc +++ b/src/lib/audio_analysis.cc @@ -123,29 +123,3 @@ AudioAnalysis::write (string filename) f.close (); boost::filesystem::rename (tmp, filename); } - -float -AudioAnalysis::smooth (list const & data, AudioPoint::Type t) -{ - float val; - - switch (t) { - case AudioPoint::PEAK: - /* XXX: fall-off, or something...? */ - val = -200; - for (list::const_iterator i = data.begin(); i != data.end(); ++i) { - val = max (val, *i); - } - return val; - case AudioPoint::RMS: - val = 0; - for (list::const_iterator i = data.begin(); i != data.end(); ++i) { - val += pow (*i, 2); - } - return sqrt (val / data.size()); - default: - assert (false); - } - - return 0; -} diff --git a/src/lib/audio_analysis.h b/src/lib/audio_analysis.h index a8cfbdeca..6e0e2b78a 100644 --- a/src/lib/audio_analysis.h +++ b/src/lib/audio_analysis.h @@ -60,8 +60,6 @@ public: void write (std::string); - static float smooth (std::list const &, AudioPoint::Type); - private: std::vector > _data; }; diff --git a/src/wx/audio_dialog.cc b/src/wx/audio_dialog.cc index bcec01332..b7736f664 100644 --- a/src/wx/audio_dialog.cc +++ b/src/wx/audio_dialog.cc @@ -61,7 +61,7 @@ AudioDialog::AudioDialog (wxWindow* parent) _type_checkbox[i]->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler (AudioDialog::type_clicked), 0, this); } - _smoothing = new wxSlider (this, wxID_ANY, 1, 1, 128); + _smoothing = new wxSlider (this, wxID_ANY, AudioPlot::max_smoothing / 2, 1, AudioPlot::max_smoothing); _smoothing->Connect (wxID_ANY, wxEVT_SCROLL_THUMBTRACK, wxScrollEventHandler (AudioDialog::smoothing_changed), 0, this); table->Add (_smoothing, 1, wxEXPAND); table->AddSpacer (0); diff --git a/src/wx/audio_plot.cc b/src/wx/audio_plot.cc index d938d0c27..23cbabcdc 100644 --- a/src/wx/audio_plot.cc +++ b/src/wx/audio_plot.cc @@ -35,11 +35,12 @@ using boost::bind; using boost::shared_ptr; int const AudioPlot::_minimum = -70; +int const AudioPlot::max_smoothing = 128; AudioPlot::AudioPlot (wxWindow* parent) : wxPanel (parent) , _gain (0) - , _smoothing (1) + , _smoothing (max_smoothing / 2) { SetDoubleBuffered (true); @@ -111,100 +112,118 @@ AudioPlot::paint (wxPaintEvent &) wxGraphicsPath grid = gc->CreatePath (); gc->SetFont (gc->CreateFont (*wxSMALL_FONT)); - wxDouble db_label_width; wxDouble db_label_height; wxDouble db_label_descent; wxDouble db_label_leading; - gc->GetTextExtent (_("-80dB"), &db_label_width, &db_label_height, &db_label_descent, &db_label_leading); + gc->GetTextExtent (_("-80dB"), &_db_label_width, &db_label_height, &db_label_descent, &db_label_leading); - db_label_width += 8; + _db_label_width += 8; - int const data_width = GetSize().GetWidth() - db_label_width; + int const data_width = GetSize().GetWidth() - _db_label_width; /* Assume all channels have the same number of points */ - float const xs = data_width / float (_analysis->points (0)); - int const height = GetSize().GetHeight (); - int const yo = 32; - float const ys = (height - yo) / -_minimum; + _x_scale = data_width / float (_analysis->points (0)); + _height = GetSize().GetHeight (); + _y_origin = 32; + _y_scale = (_height - _y_origin) / -_minimum; for (int i = _minimum; i <= 0; i += 10) { - int const y = (height - (i - _minimum) * ys) - yo; - grid.MoveToPoint (db_label_width - 4, y); - grid.AddLineToPoint (db_label_width + data_width, y); + int const y = (_height - (i - _minimum) * _y_scale) - _y_origin; + grid.MoveToPoint (_db_label_width - 4, y); + grid.AddLineToPoint (_db_label_width + data_width, y); gc->DrawText (std_to_wx (String::compose ("%1dB", i)), 0, y - (db_label_height / 2)); } gc->SetPen (*wxLIGHT_GREY_PEN); gc->StrokePath (grid); - gc->DrawText (_("Time"), data_width, height - yo + db_label_height / 2); + gc->DrawText (_("Time"), data_width, _height - _y_origin + db_label_height / 2); - for (int c = 0; c < MAX_AUDIO_CHANNELS; ++c) { - if (!_channel_visible[c] || c >= _analysis->channels()) { - continue; - } - - wxGraphicsPath path[AudioPoint::COUNT]; - - for (int i = 0; i < AudioPoint::COUNT; ++i) { - if (!_type_visible[i]) { - continue; + + if (_type_visible[AudioPoint::PEAK]) { + for (int c = 0; c < MAX_AUDIO_CHANNELS; ++c) { + wxGraphicsPath p = gc->CreatePath (); + if (_channel_visible[c] && c < _analysis->channels()) { + plot_peak (p, c); } - - path[i] = gc->CreatePath (); - - float const val = 20 * log10 (_analysis->get_point(c, 0)[i]); - - path[i].MoveToPoint ( - db_label_width, - height - (max (val, float (_minimum)) - _minimum + _gain) * ys - yo - ); + wxColour const col = _colours[c]; + gc->SetPen (*wxThePenList->FindOrCreatePen (wxColour (col.Red(), col.Green(), col.Blue(), col.Alpha() / 2))); + gc->StrokePath (p); } + } - list smoothing[AudioPoint::COUNT]; - - for (int i = 0; i < _analysis->points(c); ++i) { - for (int j = 0; j < AudioPoint::COUNT; ++j) { - if (!_type_visible[j]) { - continue; - } - - smoothing[j].push_back (_analysis->get_point(c, i)[j]); - if (int(smoothing[j].size()) > _smoothing) { - smoothing[j].pop_front (); - } - - float const val = 20 * log10 (_analysis->smooth (smoothing[j], static_cast (j))); - - path[j].AddLineToPoint ( - i * xs + db_label_width, - height - (max (val, float (_minimum)) - _minimum + _gain) * ys - yo - ); + if (_type_visible[AudioPoint::RMS]) { + for (int c = 0; c < MAX_AUDIO_CHANNELS; ++c) { + wxGraphicsPath p = gc->CreatePath (); + if (_channel_visible[c] && c < _analysis->channels()) { + plot_rms (p, c); } - } - - wxColour const col = _colours[c]; - - if (_type_visible[AudioPoint::RMS]) { + wxColour const col = _colours[c]; gc->SetPen (*wxThePenList->FindOrCreatePen (col)); - gc->StrokePath (path[AudioPoint::RMS]); - } - - if (_type_visible[AudioPoint::PEAK]) { - gc->SetPen (*wxThePenList->FindOrCreatePen (wxColour (col.Red(), col.Green(), col.Blue(), col.Alpha() / 2))); - gc->StrokePath (path[AudioPoint::PEAK]); + gc->StrokePath (p); } } wxGraphicsPath axes = gc->CreatePath (); - axes.MoveToPoint (db_label_width, 0); - axes.AddLineToPoint (db_label_width, height - yo); - axes.AddLineToPoint (db_label_width + data_width, height - yo); + axes.MoveToPoint (_db_label_width, 0); + axes.AddLineToPoint (_db_label_width, _height - _y_origin); + axes.AddLineToPoint (_db_label_width + data_width, _height - _y_origin); gc->SetPen (*wxBLACK_PEN); gc->StrokePath (axes); delete gc; } +float +AudioPlot::y_for_linear (float p) const +{ + return _height - (20 * log10(p) - _minimum + _gain) * _y_scale - _y_origin; +} + +void +AudioPlot::plot_peak (wxGraphicsPath& path, int channel) const +{ + path.MoveToPoint (_db_label_width, y_for_linear (_analysis->get_point(channel, 0)[AudioPoint::PEAK])); + + float peak = 0; + int const N = _analysis->points(channel); + for (int i = 0; i < N; ++i) { + float const p = _analysis->get_point(channel, i)[AudioPoint::PEAK]; + peak -= 0.01f * (1 - log10 (_smoothing) / log10 (max_smoothing)); + if (p > peak) { + peak = p; + } else if (peak < 0) { + peak = 0; + } + + path.AddLineToPoint (_db_label_width + i * _x_scale, y_for_linear (peak)); + } +} + +void +AudioPlot::plot_rms (wxGraphicsPath& path, int channel) const +{ + path.MoveToPoint (_db_label_width, y_for_linear (_analysis->get_point(channel, 0)[AudioPoint::RMS])); + + list smoothing; + int const N = _analysis->points(channel); + for (int i = 0; i < N; ++i) { + + smoothing.push_back (_analysis->get_point(channel, i)[AudioPoint::RMS]); + if (int(smoothing.size()) > _smoothing) { + smoothing.pop_front (); + } + + float p = 0; + for (list::const_iterator j = smoothing.begin(); j != smoothing.end(); ++j) { + p += pow (*j, 2); + } + + p = sqrt (p / smoothing.size ()); + + path.AddLineToPoint (_db_label_width + i * _x_scale, y_for_linear (p)); + } +} + void AudioPlot::set_gain (float g) { diff --git a/src/wx/audio_plot.h b/src/wx/audio_plot.h index fe8862d54..7b2955e27 100644 --- a/src/wx/audio_plot.h +++ b/src/wx/audio_plot.h @@ -34,6 +34,8 @@ public: void set_gain (float); void set_smoothing (int); + static const int max_smoothing; + private: void paint (wxPaintEvent &); @@ -43,8 +45,17 @@ private: /** gain to apply in dB */ float _gain; int _smoothing; - std::vector _colours; + void plot_peak (wxGraphicsPath &, int) const; + void plot_rms (wxGraphicsPath &, int) const; + float y_for_linear (float) const; + + double _db_label_width; + int _height; + int _y_origin; + float _x_scale; + float _y_scale; + static const int _minimum; }; -- cgit v1.2.3