Merge master.
authorCarl Hetherington <cth@carlh.net>
Fri, 1 Mar 2013 20:13:03 +0000 (20:13 +0000)
committerCarl Hetherington <cth@carlh.net>
Fri, 1 Mar 2013 20:13:03 +0000 (20:13 +0000)
14 files changed:
1  2 
src/lib/analyse_audio_job.cc
src/lib/encoder.cc
src/lib/ffmpeg_decoder.cc
src/lib/film.cc
src/lib/job.cc
src/lib/util.cc
src/lib/writer.cc
src/lib/wscript
src/tools/dvdomatic.cc
src/wx/audio_dialog.cc
src/wx/audio_plot.cc
src/wx/film_editor.cc
src/wx/film_viewer.cc
src/wx/wscript

index bcabb6c91e8697b96c3f162ff7364c77faaf193c,0000000000000000000000000000000000000000..ca316f70ebe80d2ce4ccc2734dbd9f99459cb570
mode 100644,000000..100644
--- /dev/null
@@@ -1,108 -1,0 +1,110 @@@
-       return String::compose ("Analyse audio of %1", _film->name());
 +/*
 +    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
 +
 +    This program is free software; you can redistribute it and/or modify
 +    it under the terms of the GNU General Public License as published by
 +    the Free Software Foundation; either version 2 of the License, or
 +    (at your option) any later version.
 +
 +    This program is distributed in the hope that it will be useful,
 +    but WITHOUT ANY WARRANTY; without even the implied warranty of
 +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +    GNU General Public License for more details.
 +
 +    You should have received a copy of the GNU General Public License
 +    along with this program; if not, write to the Free Software
 +    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 +
 +*/
 +
 +#include "audio_analysis.h"
 +#include "analyse_audio_job.h"
 +#include "compose.hpp"
 +#include "film.h"
 +#include "options.h"
 +#include "decoder_factory.h"
 +#include "audio_decoder.h"
 +
++#include "i18n.h"
++
 +using std::string;
 +using std::max;
 +using std::cout;
 +using boost::shared_ptr;
 +
 +int const AnalyseAudioJob::_num_points = 1024;
 +
 +AnalyseAudioJob::AnalyseAudioJob (shared_ptr<Film> f)
 +      : Job (f)
 +      , _done (0)
 +      , _samples_per_point (1)
 +{
 +
 +}
 +
 +string
 +AnalyseAudioJob::name () const
 +{
++      return String::compose (_("Analyse audio of %1"), _film->name());
 +}
 +
 +void
 +AnalyseAudioJob::run ()
 +{
 +      if (!_film->audio_stream () || !_film->length()) {
 +              set_progress (1);
 +              set_state (FINISHED_ERROR);
 +              return;
 +      }
 +              
 +      DecodeOptions options;
 +      options.decode_video = false;
 +
 +      Decoders decoders = decoder_factory (_film, options);
 +      assert (decoders.audio);
 +      
 +      decoders.audio->set_audio_stream (_film->audio_stream ());
 +      decoders.audio->Audio.connect (bind (&AnalyseAudioJob::audio, this, _1));
 +
 +      int64_t total_audio_frames = video_frames_to_audio_frames (_film->length().get(), _film->audio_stream()->sample_rate(), _film->frames_per_second());
 +      _samples_per_point = total_audio_frames / _num_points;
 +
 +      _current.resize (_film->audio_stream()->channels ());
 +      _analysis.reset (new AudioAnalysis (_film->audio_stream()->channels()));
 +                       
 +      while (!decoders.audio->pass()) {
 +              set_progress (float (_done) / total_audio_frames);
 +      }
 +
 +      _analysis->write (_film->audio_analysis_path ());
 +      
 +      set_progress (1);
 +      set_state (FINISHED_OK);
 +}
 +
 +void
 +AnalyseAudioJob::audio (shared_ptr<AudioBuffers> b)
 +{
 +      for (int i = 0; i < b->frames(); ++i) {
 +              for (int j = 0; j < b->channels(); ++j) {
 +                      float s = b->data(j)[i];
 +                      if (fabsf (s) < 10e-7) {
 +                              /* stringstream can't serialise and recover inf or -inf, so prevent such
 +                                 values by replacing with this (140dB down) */
 +                              s = 10e-7;
 +                      }
 +                      _current[j][AudioPoint::RMS] += pow (s, 2);
 +                      _current[j][AudioPoint::PEAK] = max (_current[j][AudioPoint::PEAK], fabsf (s));
 +
 +                      if ((_done % _samples_per_point) == 0) {
 +                              _current[j][AudioPoint::RMS] = sqrt (_current[j][AudioPoint::RMS] / _samples_per_point);
 +                              _analysis->add_point (j, _current[j]);
 +                              
 +                              _current[j] = AudioPoint ();
 +                      }
 +              }
 +
 +              ++_done;
 +      }
 +}
 +
Simple merge
Simple merge
diff --cc src/lib/film.cc
index ab636bdfcc3a651fe79cd1b87dd94d15693e93ed,c119f1515fb469dadb0abc709674f5076e5eb5e7..510158e942ad010e3ffcf87aa56a91e7ae656e96
  #include "video_decoder.h"
  #include "audio_decoder.h"
  #include "external_audio_decoder.h"
 +#include "analyse_audio_job.h"
  
+ #include "i18n.h"
  using std::string;
  using std::stringstream;
  using std::multimap;
@@@ -235,18 -236,9 +237,18 @@@ Film::video_mxf_dir () cons
  string
  Film::video_mxf_filename () const
  {
-       return video_state_identifier() + ".mxf";
+       return video_state_identifier() + N_(".mxf");
  }
  
 +string
 +Film::audio_analysis_path () const
 +{
 +      boost::filesystem::path p;
 +      p /= "analysis";
 +      p /= content_digest();
 +      return file (p.string ());
 +}
 +
  /** Add suitable Jobs to the JobManager to create a DCP for this Film */
  void
  Film::make_dcp ()
diff --cc src/lib/job.cc
Simple merge
diff --cc src/lib/util.cc
Simple merge
index 1dad4357d9381f02f5a9b9e3382c22c0a6a71153,d480d502afdd6ef7899f6c51da2b739427998ba8..334ecec6581ce60c060a5e8154c2f65502fbf1c5
@@@ -78,9 -78,9 +80,9 @@@ Writer::Writer (shared_ptr<Film> f
                _sound_asset.reset (
                        new libdcp::SoundAsset (
                                _film->dir (_film->dcp_name()),
-                               "audio.mxf",
+                               N_("audio.mxf"),
                                DCPFrameRate (_film->frames_per_second()).frames_per_second,
 -                              dcp_audio_channels (_film->audio_channels()),
 +                              m.dcp_channels (),
                                dcp_audio_sample_rate (_film->audio_stream()->sample_rate())
                                )
                        );
diff --cc src/lib/wscript
index c2b46112cbe0de658ed5c9906661378bfcffd2fe,6ddb948515e53c2e8b872bd7c8696093be01c479..d36a24e7a100f220427a2827b6e4190e74134e19
@@@ -1,3 -1,55 +1,57 @@@
+ import os
+ import i18n
+ sources = """
+           ab_transcode_job.cc
+         ab_transcoder.cc
++          analyse_audio_job.cc
++          audio_analysis.cc
+           audio_decoder.cc
+           audio_source.cc
+           config.cc
+           combiner.cc
+           cross.cc
+           dci_metadata.cc
+           dcp_content_type.cc
+           dcp_video_frame.cc
+           decoder.cc
+           decoder_factory.cc
+           delay_line.cc
+           dolby_cp750.cc
+           encoder.cc
+           examine_content_job.cc
+           external_audio_decoder.cc
+           filter_graph.cc
+           ffmpeg_compatibility.cc
+           ffmpeg_decoder.cc
+           film.cc
+           filter.cc
+           format.cc
+           gain.cc
+           image.cc
+           imagemagick_decoder.cc
+           job.cc
+           job_manager.cc
+           log.cc
+           lut.cc
+           matcher.cc
+           scp_dcp_job.cc
+           scaler.cc
+           server.cc
+           sound_processor.cc
+           stream.cc
+           subtitle.cc
+           timer.cc
+           transcode_job.cc
+           transcoder.cc
+           ui_signaller.cc
+           util.cc
+           version.cc
+           video_decoder.cc
+           video_source.cc
+           writer.cc
+           """
  def build(bld):
      if bld.env.STATIC:
          obj = bld(features = 'cxx cxxstlib')
index 4f380b5ad87e6cc7e218415c03b91930c06691b7,ab34924aa56726e0164fe2a62393a67b9fdc858d..f5d9bdf181a3e82b3a40dac108c75bc129cca020
@@@ -153,27 -157,27 +157,27 @@@ voi
  setup_menu (wxMenuBar* m)
  {
        wxMenu* file = new wxMenu;
-       add_item (file, "New...", ID_file_new, ALWAYS);
-       add_item (file, "&Open...", ID_file_open, ALWAYS);
+       add_item (file, _("New..."), ID_file_new, ALWAYS);
+       add_item (file, _("&Open..."), ID_file_open, ALWAYS);
        file->AppendSeparator ();
-       add_item (file, "&Save", ID_file_save, NEEDS_FILM);
+       add_item (file, _("&Save"), ID_file_save, NEEDS_FILM);
        file->AppendSeparator ();
-       add_item (file, "&Properties...", ID_file_properties, NEEDS_FILM);
+       add_item (file, _("&Properties..."), ID_file_properties, NEEDS_FILM);
        file->AppendSeparator ();
-       add_item (file, "&Quit", ID_file_quit, ALWAYS);
+       add_item (file, _("&Quit"), ID_file_quit, ALWAYS);
  
        wxMenu* edit = new wxMenu;
-       add_item (edit, "&Preferences...", ID_edit_preferences, ALWAYS);
+       add_item (edit, _("&Preferences..."), ID_edit_preferences, ALWAYS);
  
        jobs_menu = new wxMenu;
-       add_item (jobs_menu, "&Make DCP", ID_jobs_make_dcp, NEEDS_FILM);
-       add_item (jobs_menu, "&Send DCP to TMS", ID_jobs_send_dcp_to_tms, NEEDS_FILM);
-       add_item (jobs_menu, "S&how DCP", ID_jobs_show_dcp, NEEDS_FILM);
+       add_item (jobs_menu, _("&Make DCP"), ID_jobs_make_dcp, NEEDS_FILM);
+       add_item (jobs_menu, _("&Send DCP to TMS"), ID_jobs_send_dcp_to_tms, NEEDS_FILM);
+       add_item (jobs_menu, _("S&how DCP"), ID_jobs_show_dcp, NEEDS_FILM);
        jobs_menu->AppendSeparator ();
-       add_item (jobs_menu, "&Analyse audio", ID_jobs_analyse_audio, NEEDS_FILM);
 -      add_item (jobs_menu, _("&Examine content"), ID_jobs_examine_content, NEEDS_FILM);
++      add_item (jobs_menu, _("&Analyse audio"), ID_jobs_analyse_audio, NEEDS_FILM);
  
        wxMenu* help = new wxMenu;
-       add_item (help, "About", ID_help_about, ALWAYS);
+       add_item (help, _("About"), ID_help_about, ALWAYS);
  
        m->Append (file, _("&File"));
        m->Append (edit, _("&Edit"));
index 74586fa132584f0c52ba51f661358066cef8c875,0000000000000000000000000000000000000000..5bac8eabeb76c21738c64990a5be38f5c29a23b3
mode 100644,000000..100644
--- /dev/null
@@@ -1,201 -1,0 +1,201 @@@
-       SetTitle (std_to_wx (String::compose ("DVD-o-matic audio - %1", _film->name())));
 +/*
 +    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
 +
 +    This program is free software; you can redistribute it and/or modify
 +    it under the terms of the GNU General Public License as published by
 +    the Free Software Foundation; either version 2 of the License, or
 +    (at your option) any later version.
 +
 +    This program is distributed in the hope that it will be useful,
 +    but WITHOUT ANY WARRANTY; without even the implied warranty of
 +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +    GNU General Public License for more details.
 +
 +    You should have received a copy of the GNU General Public License
 +    along with this program; if not, write to the Free Software
 +    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 +
 +*/
 +
 +#include <boost/filesystem.hpp>
 +#include "audio_dialog.h"
 +#include "audio_plot.h"
 +#include "audio_analysis.h"
 +#include "film.h"
 +#include "wx_util.h"
 +
 +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)
 +      , _plot (0)
 +{
 +      wxBoxSizer* sizer = new wxBoxSizer (wxHORIZONTAL);
 +
 +      _plot = new AudioPlot (this);
 +      sizer->Add (_plot, 1, wxALL, 12);
 +
 +      wxBoxSizer* side = new wxBoxSizer (wxVERTICAL);
 +
 +      {
 +              wxStaticText* m = new wxStaticText (this, wxID_ANY, _("Channels"));
 +              side->Add (m, 1, wxALIGN_CENTER_VERTICAL | wxTOP, 16);
 +      }
 +      
 +
 +      for (int i = 0; i < MAX_AUDIO_CHANNELS; ++i) {
 +              _channel_checkbox[i] = new wxCheckBox (this, wxID_ANY, std_to_wx (audio_channel_name (i)));
 +              side->Add (_channel_checkbox[i], 1, wxEXPAND | wxALL, 3);
 +              _channel_checkbox[i]->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler (AudioDialog::channel_clicked), 0, this);
 +      }
 +
 +      {
 +              wxStaticText* m = new wxStaticText (this, wxID_ANY, _("Type"));
 +              side->Add (m, 1, wxALIGN_CENTER_VERTICAL | wxTOP, 16);
 +      }
 +      
 +      wxString const types[] = {
 +              _("Peak"),
 +              _("RMS")
 +      };
 +
 +      for (int i = 0; i < AudioPoint::COUNT; ++i) {
 +              _type_checkbox[i] = new wxCheckBox (this, wxID_ANY, types[i]);
 +              side->Add (_type_checkbox[i], 1, wxEXPAND | wxALL, 3);
 +              _type_checkbox[i]->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler (AudioDialog::type_clicked), 0, this);
 +      }
 +
 +      {
 +              wxStaticText* m = new wxStaticText (this, wxID_ANY, _("Smoothing"));
 +              side->Add (m, 1, wxALIGN_CENTER_VERTICAL | wxTOP, 16);
 +      }
 +      
 +      _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);
 +      side->Add (_smoothing, 1, wxEXPAND);
 +
 +      sizer->Add (side, 0, wxALL, 12);
 +
 +      SetSizer (sizer);
 +      sizer->Layout ();
 +      sizer->SetSizeHints (this);
 +}
 +
 +void
 +AudioDialog::set_film (boost::shared_ptr<Film> f)
 +{
 +      _film_changed_connection.disconnect ();
 +      _film_audio_analysis_finished_connection.disconnect ();
 +      
 +      _film = f;
 +
 +      try_to_load_analysis ();
 +      setup_channels ();
 +      _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));
 +
++      SetTitle (std_to_wx (String::compose (wx_to_std (_("DVD-o-matic audio - %1")), _film->name())));
 +}
 +
 +void
 +AudioDialog::setup_channels ()
 +{
 +      if (!_film->audio_stream()) {
 +              return;
 +      }
 +
 +      AudioMapping m (_film->audio_stream()->channels ());
 +      
 +      for (int i = 0; i < MAX_AUDIO_CHANNELS; ++i) {
 +              if (m.dcp_to_source(static_cast<libdcp::Channel>(i))) {
 +                      _channel_checkbox[i]->Show ();
 +              } else {
 +                      _channel_checkbox[i]->Hide ();
 +              }
 +      }
 +}     
 +
 +void
 +AudioDialog::try_to_load_analysis ()
 +{
 +      shared_ptr<AudioAnalysis> a;
 +
 +      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);
 +
 +      AudioMapping m (_film->audio_stream()->channels ());
 +      optional<libdcp::Channel> 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);
 +              _plot->set_type_visible (i, true);
 +      }
 +}
 +
 +void
 +AudioDialog::channel_clicked (wxCommandEvent& ev)
 +{
 +      int c = 0;
 +      while (c < MAX_AUDIO_CHANNELS && ev.GetEventObject() != _channel_checkbox[c]) {
 +              ++c;
 +      }
 +
 +      assert (c < MAX_AUDIO_CHANNELS);
 +
 +      AudioMapping m (_film->audio_stream()->channels ());
 +      optional<int> s = m.dcp_to_source (static_cast<libdcp::Channel> (c));
 +      if (s) {
 +              _plot->set_channel_visible (s.get(), _channel_checkbox[c]->GetValue ());
 +      }
 +}
 +
 +void
 +AudioDialog::film_changed (Film::Property p)
 +{
 +      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;
 +      }
 +}
 +
 +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 ());
 +}
 +
 +void
 +AudioDialog::smoothing_changed (wxScrollEvent &)
 +{
 +      _plot->set_smoothing (_smoothing->GetValue ());
 +}
index 068b74ffdf57c4c81d09d3d0dd04ebbd8f9fbe83,0000000000000000000000000000000000000000..af8460a1653ac7a74d9478b7c1d809d0a8462343
mode 100644,000000..100644
--- /dev/null
@@@ -1,272 -1,0 +1,272 @@@
-       gc->GetTextExtent (_("-80dB"), &_db_label_width, &db_label_height, &db_label_descent, &db_label_leading);
 +/*
 +    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
 +
 +    This program is free software; you can redistribute it and/or modify
 +    it under the terms of the GNU General Public License as published by
 +    the Free Software Foundation; either version 2 of the License, or
 +    (at your option) any later version.
 +
 +    This program is distributed in the hope that it will be useful,
 +    but WITHOUT ANY WARRANTY; without even the implied warranty of
 +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +    GNU General Public License for more details.
 +
 +    You should have received a copy of the GNU General Public License
 +    along with this program; if not, write to the Free Software
 +    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 +
 +*/
 +
 +#include <iostream>
 +#include <boost/bind.hpp>
 +#include <wx/graphics.h>
 +#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::list;
 +using std::max;
 +using std::min;
 +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 (max_smoothing / 2)
 +{
 +      SetDoubleBuffered (true);
 +
 +      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));
 +}
 +
 +void
 +AudioPlot::set_analysis (shared_ptr<AudioAnalysis> a)
 +{
 +      _analysis = a;
 +
 +      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_visible (int c, bool v)
 +{
 +      _channel_visible[c] = v;
 +      Refresh ();
 +}
 +
 +void
 +AudioPlot::set_type_visible (int t, bool v)
 +{
 +      _type_visible[t] = v;
 +      Refresh ();
 +}
 +
 +void
 +AudioPlot::paint (wxPaintEvent &)
 +{
 +      wxPaintDC dc (this);
 +
 +      wxGraphicsContext* gc = wxGraphicsContext::Create (dc);
 +      if (!gc) {
 +              return;
 +      }
 +
 +      if (!_analysis || _analysis->channels() == 0) {
 +              gc->SetFont (gc->CreateFont (*wxNORMAL_FONT));
 +              gc->DrawText (_("Please wait; audio is being analysed..."), 32, 32);
 +              return;
 +      }
 +
 +      wxGraphicsPath grid = gc->CreatePath ();
 +      gc->SetFont (gc->CreateFont (*wxSMALL_FONT));
 +      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);
 +
 +      _db_label_width += 8;
 +      
 +      int const data_width = GetSize().GetWidth() - _db_label_width;
 +      /* Assume all channels have the same number of points */
 +      _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) * _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 - _y_origin + db_label_height / 2);
 +
 +      
 +      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);
 +                      }
 +                      wxColour const col = _colours[c];
 +#if wxMAJOR_VERSION == 2 && wxMINOR_VERSION >= 9
 +                      gc->SetPen (*wxThePenList->FindOrCreatePen (wxColour (col.Red(), col.Green(), col.Blue(), col.Alpha() / 2), 1, wxPENSTYLE_SOLID));
 +#else                 
 +                      gc->SetPen (*wxThePenList->FindOrCreatePen (wxColour (col.Red(), col.Green(), col.Blue(), col.Alpha() / 2), 1, wxSOLID));
 +#endif
 +                      gc->StrokePath (p);
 +              }
 +      }
 +
 +      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 wxMAJOR_VERSION == 2 && wxMINOR_VERSION >= 9
 +                      gc->SetPen (*wxThePenList->FindOrCreatePen (col, 1, wxPENSTYLE_SOLID));
 +#else
 +                      gc->SetPen (*wxThePenList->FindOrCreatePen (col, 1, wxSOLID));
 +#endif                        
 +                      gc->StrokePath (p);
 +              }
 +      }
 +
 +      wxGraphicsPath axes = gc->CreatePath ();
 +      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<float> smoothing;
 +
 +      int const N = _analysis->points(channel);
 +
 +      float const first = _analysis->get_point(channel, 0)[AudioPoint::RMS];
 +      float const last = _analysis->get_point(channel, N - 1)[AudioPoint::RMS];
 +
 +      int const before = _smoothing / 2;
 +      int const after = _smoothing - before;
 +      
 +      /* Pre-load the smoothing list */
 +      for (int i = 0; i < before; ++i) {
 +              smoothing.push_back (first);
 +      }
 +      for (int i = 0; i < after; ++i) {
 +              if (i < N) {
 +                      smoothing.push_back (_analysis->get_point(channel, i)[AudioPoint::RMS]);
 +              } else {
 +                      smoothing.push_back (last);
 +              }
 +      }
 +      
 +      for (int i = 0; i < N; ++i) {
 +
 +              int const next_for_window = i + after;
 +
 +              if (next_for_window < N) {
 +                      smoothing.push_back (_analysis->get_point(channel, i)[AudioPoint::RMS]);
 +              } else {
 +                      smoothing.push_back (last);
 +              }
 +
 +              smoothing.pop_front ();
 +
 +              float p = 0;
 +              for (list<float>::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)
 +{
 +      _gain = g;
 +      Refresh ();
 +}
 +
 +void
 +AudioPlot::set_smoothing (int s)
 +{
 +      _smoothing = s;
 +      Refresh ();
 +}
index 499cb1a433e52e8b55d85dc319ff2aa23939e440,85046644506de7672131087d6ef1f83918a37dec..362243ef300fd227afef169450fc2cdae2867de4
@@@ -747,12 -753,8 +747,12 @@@ FilmEditor::set_film (shared_ptr<Film> 
        if (_film) {
                FileChanged (_film->directory ());
        } else {
-               FileChanged ("");
+               FileChanged (wx_to_std (N_("")));
        }
 +
 +      if (_audio_dialog) {
 +              _audio_dialog->set_film (_film);
 +      }
        
        film_changed (Film::NAME);
        film_changed (Film::USE_DCI_NAME);
Simple merge
diff --cc src/wx/wscript
index 3fa40f55a7f811d74e1bf1186d99d3ee0a53288a,92ff440cd49be89a50fddb930d2a94beac8589f1..cc303f5e801d0b4011cd41f9ed02a507e87350f5
@@@ -1,3 -1,26 +1,28 @@@
+ import os
+ import glob
+ from waflib import Logs
+ import i18n
+ sources = """
++          audio_dialog.cc
++          audio_plot.cc
+           config_dialog.cc
+           dci_metadata_dialog.cc
+           dir_picker_ctrl.cc
+           film_editor.cc
+           film_viewer.cc
+           filter_dialog.cc
+           filter_view.cc
+           gain_calculator_dialog.cc
+           job_manager_view.cc
+           job_wrapper.cc
+           new_film_dialog.cc
+           properties_dialog.cc
+           server_dialog.cc
+           wx_util.cc
+           wx_ui_signaller.cc
+           """
  def configure(conf):
      conf.check_cfg(package = '', path = conf.options.wx_config, args = '--cppflags --cxxflags --libs', uselib_store = 'WXWIDGETS', mandatory = True)