#include "exceptions.h"
#include "config.h"
#include "frame_rate_change.h"
+#include "audio_processor.h"
#include "i18n.h"
int const AudioContentProperty::AUDIO_GAIN = 203;
int const AudioContentProperty::AUDIO_DELAY = 204;
int const AudioContentProperty::AUDIO_MAPPING = 205;
+int const AudioContentProperty::AUDIO_PROCESSOR = 206;
AudioContent::AudioContent (shared_ptr<const Film> f)
: Content (f)
, _audio_gain (0)
, _audio_delay (Config::instance()->default_audio_delay ())
+ , _audio_processor (0)
{
}
: Content (f, s)
, _audio_gain (0)
, _audio_delay (Config::instance()->default_audio_delay ())
+ , _audio_processor (0)
{
}
: Content (f, p)
, _audio_gain (0)
, _audio_delay (Config::instance()->default_audio_delay ())
+ , _audio_processor (0)
{
}
AudioContent::AudioContent (shared_ptr<const Film> f, cxml::ConstNodePtr node)
: Content (f, node)
+ , _audio_processor (0)
{
_audio_gain = node->number_child<float> ("AudioGain");
_audio_delay = node->number_child<int> ("AudioDelay");
+ if (node->optional_string_child ("AudioProcessor")) {
+ _audio_processor = AudioProcessor::from_id (node->string_child ("AudioProcessor"));
+ }
}
AudioContent::AudioContent (shared_ptr<const Film> f, vector<shared_ptr<Content> > c)
_audio_gain = ref->audio_gain ();
_audio_delay = ref->audio_delay ();
+ _audio_processor = ref->audio_processor ();
}
void
boost::mutex::scoped_lock lm (_mutex);
node->add_child("AudioGain")->add_child_text (raw_convert<string> (_audio_gain));
node->add_child("AudioDelay")->add_child_text (raw_convert<string> (_audio_delay));
+ if (_audio_processor) {
+ node->add_child("AudioProcessor")->add_child_text (_audio_processor->id ());
+ }
}
signal_changed (AudioContentProperty::AUDIO_DELAY);
}
+void
+AudioContent::set_audio_processor (AudioProcessor const * p)
+{
+ {
+ boost::mutex::scoped_lock lm (_mutex);
+ _audio_processor = p;
+ }
+
+ /* The channel count might have changed, so reset the mapping */
+ AudioMapping m (processed_audio_channels ());
+ m.make_default ();
+ set_audio_mapping (m);
+
+ signal_changed (AudioContentProperty::AUDIO_PROCESSOR);
+}
+
boost::signals2::connection
AudioContent::analyse_audio (boost::function<void()> finished)
{
return rint (t);
}
+
+int
+AudioContent::processed_audio_channels () const
+{
+ if (!audio_processor ()) {
+ return audio_channels ();
+ }
+
+ return audio_processor()->out_channels (audio_channels ());
+}
+
class Node;
}
+class AudioProcessor;
+
class AudioContentProperty
{
public:
static int const AUDIO_GAIN;
static int const AUDIO_DELAY;
static int const AUDIO_MAPPING;
+ static int const AUDIO_PROCESSOR;
};
/** @class AudioContent
virtual boost::filesystem::path audio_analysis_path () const;
int resampled_audio_frame_rate () const;
+ int processed_audio_channels () const;
boost::signals2::connection analyse_audio (boost::function<void()>);
void set_audio_gain (double);
void set_audio_delay (int);
+ void set_audio_processor (AudioProcessor const *);
double audio_gain () const {
boost::mutex::scoped_lock lm (_mutex);
return _audio_delay;
}
+ AudioProcessor const * audio_processor () const {
+ boost::mutex::scoped_lock lm (_mutex);
+ return _audio_processor;
+ }
+
private:
/** Gain to apply to audio in dB */
double _audio_gain;
/** Delay to apply to audio (positive moves audio later) in milliseconds */
int _audio_delay;
+ AudioProcessor const * _audio_processor;
};
#endif
#include "resampler.h"
#include "util.h"
#include "film.h"
+#include "audio_processor.h"
#include "i18n.h"
_resampler.reset (new Resampler (content->audio_frame_rate(), content->resampled_audio_frame_rate(), content->audio_channels ()));
}
+ if (content->audio_processor ()) {
+ _processor = content->audio_processor()->clone ();
+ }
+
reset_decoded_audio ();
}
void
AudioDecoder::reset_decoded_audio ()
{
- _decoded_audio = ContentAudio (shared_ptr<AudioBuffers> (new AudioBuffers (_audio_content->audio_channels(), 0)), 0);
+ _decoded_audio = ContentAudio (shared_ptr<AudioBuffers> (new AudioBuffers (_audio_content->processed_audio_channels(), 0)), 0);
}
shared_ptr<ContentAudio>
data = _resampler->run (data);
}
+ if (_processor) {
+ data = _processor->run (data);
+ }
+
AudioFrame const frame_rate = _audio_content->resampled_audio_frame_rate ();
if (_seek_reference) {
boost::shared_ptr<const AudioContent> _audio_content;
boost::shared_ptr<Resampler> _resampler;
+ boost::shared_ptr<AudioProcessor> _processor;
boost::optional<AudioFrame> _audio_position;
/** Currently-available decoded audio data */
ContentAudio _decoded_audio;
}
-/** Create a default AudioMapping for a given channel count.
+/** Create an empty AudioMapping for a given channel count.
* @param channels Number of channels.
*/
AudioMapping::AudioMapping (int channels)
--- /dev/null
+/*
+ Copyright (C) 2014 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_processor.h"
+#include "mid_side_decoder.h"
+
+using std::string;
+using std::list;
+
+list<AudioProcessor const *> AudioProcessor::_all;
+
+void
+AudioProcessor::setup_audio_processors ()
+{
+ _all.push_back (new MidSideDecoder ());
+}
+
+AudioProcessor const *
+AudioProcessor::from_id (string id)
+{
+ for (list<AudioProcessor const *>::const_iterator i = _all.begin(); i != _all.end(); ++i) {
+ if ((*i)->id() == id) {
+ return *i;
+ }
+ }
+
+ return 0;
+}
+
+list<AudioProcessor const *>
+AudioProcessor::all ()
+{
+ return _all;
+}
--- /dev/null
+/*
+ Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef DCPOMATIC_AUDIO_PROCESSOR_H
+#define DCPOMATIC_AUDIO_PROCESSOR_H
+
+#include <list>
+#include <string>
+#include <boost/shared_ptr.hpp>
+#include "channel_count.h"
+
+class AudioBuffers;
+
+class AudioProcessor
+{
+public:
+ virtual ~AudioProcessor () {}
+
+ virtual std::string name () const = 0;
+ virtual std::string id () const = 0;
+ virtual ChannelCount in_channels () const = 0;
+ virtual int out_channels (int) const = 0;
+ virtual boost::shared_ptr<AudioProcessor> clone () const = 0;
+ virtual boost::shared_ptr<AudioBuffers> run (boost::shared_ptr<const AudioBuffers>) = 0;
+
+ static std::list<AudioProcessor const *> all ();
+ static void setup_audio_processors ();
+ static AudioProcessor const * from_id (std::string);
+
+private:
+ static std::list<AudioProcessor const *> _all;
+};
+
+#endif
--- /dev/null
+/*
+ Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef DCPOMATIC_CHANNEL_COUNT_H
+#define DCPOMATIC_CHANNEL_COUNT_H
+
+class ChannelCount
+{
+public:
+ ChannelCount ()
+ : min (0)
+ , max (0)
+ {}
+
+ ChannelCount (int n)
+ : min (n)
+ , max (n)
+ {}
+
+ ChannelCount (int min_, int max_)
+ : min (min_)
+ , max (max_)
+ {}
+
+ bool includes (int c) {
+ return min <= c && c <= max;
+ }
+
+ int min;
+ int max;
+};
+
+#endif
--- /dev/null
+/*
+ Copyright (C) 2014 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 "mid_side_decoder.h"
+#include "audio_buffers.h"
+
+#include "i18n.h"
+
+using std::string;
+using boost::shared_ptr;
+
+string
+MidSideDecoder::name () const
+{
+ return _("Mid-side decoder");
+}
+
+string
+MidSideDecoder::id () const
+{
+ return N_("mid-side-decoder");
+}
+
+ChannelCount
+MidSideDecoder::in_channels () const
+{
+ return ChannelCount (2);
+}
+
+int
+MidSideDecoder::out_channels (int) const
+{
+ return 3;
+}
+
+shared_ptr<AudioProcessor>
+MidSideDecoder::clone () const
+{
+ return shared_ptr<AudioProcessor> (new MidSideDecoder ());
+}
+
+shared_ptr<AudioBuffers>
+MidSideDecoder::run (shared_ptr<const AudioBuffers> in)
+{
+ shared_ptr<AudioBuffers> out (new AudioBuffers (3, in->frames ()));
+ for (int i = 0; i < in->frames(); ++i) {
+ float const left = in->data()[0][i];
+ float const right = in->data()[1][i];
+ float const mid = (left + right) / 2;
+ out->data()[0][i] = left - mid;
+ out->data()[1][i] = right - mid;
+ out->data()[2][i] = mid;
+ }
+
+ return out;
+}
--- /dev/null
+/*
+ Copyright (C) 2014 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_processor.h"
+
+class MidSideDecoder : public AudioProcessor
+{
+public:
+ std::string name () const;
+ std::string id () const;
+ ChannelCount in_channels () const;
+ int out_channels (int) const;
+ boost::shared_ptr<AudioProcessor> clone () const;
+ boost::shared_ptr<AudioBuffers> run (boost::shared_ptr<const AudioBuffers>);
+};
+
+
#include "video_content.h"
#include "rect.h"
#include "md5_digester.h"
+#include "audio_processor.h"
#ifdef DCPOMATIC_WINDOWS
#include "stack.hpp"
#endif
Scaler::setup_scalers ();
Filter::setup_filters ();
SoundProcessor::setup_sound_processors ();
+ AudioProcessor::setup_audio_processors ();
ui_thread = boost::this_thread::get_id ();
}
audio_content.cc
audio_decoder.cc
audio_mapping.cc
+ audio_processor.cc
cinema.cc
colour_conversion.cc
config.cc
log.cc
magick_image_proxy.cc
md5_digester.cc
+ mid_side_decoder.cc
player.cc
player_video.cc
playlist.cc
#include "lib/sound_processor.h"
#include "lib/ffmpeg_content.h"
#include "lib/ffmpeg_audio_stream.h"
+#include "lib/audio_processor.h"
#include "audio_dialog.h"
#include "audio_panel.h"
#include "audio_mapping_view.h"
using std::vector;
using std::cout;
using std::string;
+using std::list;
using boost::dynamic_pointer_cast;
using boost::lexical_cast;
using boost::shared_ptr;
grid->Add (_stream, wxGBPosition (r, 1));
_description = add_label_to_grid_bag_sizer (grid, this, "", false, wxGBPosition (r, 3));
++r;
+
+ add_label_to_grid_bag_sizer (grid, this, _("Process with"), true, wxGBPosition (r, 0));
+ _processor = new wxChoice (this, wxID_ANY);
+ setup_processors ();
+ grid->Add (_processor, wxGBPosition (r, 1));
+ ++r;
_mapping = new AudioMappingView (this);
_sizer->Add (_mapping, 1, wxEXPAND | wxALL, 6);
_gain->wrapped()->SetIncrement (0.5);
_delay->wrapped()->SetRange (-1000, 1000);
- _stream->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&AudioPanel::stream_changed, this));
- _show->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&AudioPanel::show_clicked, this));
- _gain_calculate_button->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&AudioPanel::gain_calculate_button_clicked, this));
+ _stream->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&AudioPanel::stream_changed, this));
+ _show->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&AudioPanel::show_clicked, this));
+ _gain_calculate_button->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&AudioPanel::gain_calculate_button_clicked, this));
+ _processor->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&AudioPanel::processor_changed, this));
_mapping->Changed.connect (boost::bind (&AudioPanel::mapping_changed, this, _1));
}
setup_stream_description ();
}
}
+ } else if (property == AudioContentProperty::AUDIO_PROCESSOR) {
+ if (acs) {
+ checked_set (_processor, acs->audio_processor() ? acs->audio_processor()->id() : N_("none"));
+ } else {
+ checked_set (_processor, N_("none"));
+ }
}
}
}
}
+void
+AudioPanel::processor_changed ()
+{
+ string const s = string_client_data (_processor->GetClientObject (_processor->GetSelection ()));
+ AudioProcessor const * p = 0;
+ if (s != wx_to_std (N_("none"))) {
+ p = AudioProcessor::from_id (s);
+ }
+
+ AudioContentList c = _parent->selected_audio ();
+ for (AudioContentList::const_iterator i = c.begin(); i != c.end(); ++i) {
+ (*i)->set_audio_processor (p);
+ }
+}
+
void
AudioPanel::mapping_changed (AudioMapping m)
{
_gain->set_content (sel);
_delay->set_content (sel);
+ _gain_calculate_button->Enable (sel.size() == 1);
_show->Enable (sel.size() == 1);
_stream->Enable (sel.size() == 1);
+ _processor->Enable (!sel.empty());
_mapping->Enable (sel.size() == 1);
+ setup_processors ();
+
film_content_changed (AudioContentProperty::AUDIO_MAPPING);
+ film_content_changed (AudioContentProperty::AUDIO_PROCESSOR);
film_content_changed (FFmpegContentProperty::AUDIO_STREAM);
film_content_changed (FFmpegContentProperty::AUDIO_STREAMS);
}
+
+void
+AudioPanel::setup_processors ()
+{
+ AudioContentList sel = _parent->selected_audio ();
+
+ _processor->Clear ();
+ list<AudioProcessor const *> ap = AudioProcessor::all ();
+ _processor->Append (_("None"), new wxStringClientData (N_("none")));
+ for (list<AudioProcessor const *>::const_iterator i = ap.begin(); i != ap.end(); ++i) {
+
+ AudioContentList::const_iterator j = sel.begin();
+ while (j != sel.end() && (*i)->in_channels().includes ((*j)->audio_channels ())) {
+ ++j;
+ }
+
+ if (j == sel.end ()) {
+ _processor->Append (std_to_wx ((*i)->name ()), new wxStringClientData (std_to_wx ((*i)->id ())));
+ }
+ }
+}
void stream_changed ();
void mapping_changed (AudioMapping);
void setup_stream_description ();
+ void processor_changed ();
+ void setup_processors ();
ContentSpinCtrlDouble<AudioContent>* _gain;
wxButton* _gain_calculate_button;
ContentSpinCtrl<AudioContent>* _delay;
wxChoice* _stream;
wxStaticText* _description;
+ wxChoice* _processor;
AudioMappingView* _mapping;
AudioDialog* _audio_dialog;
};