Skip to content

Commit

Permalink
re-based delay-panner patch - closes #63
Browse files Browse the repository at this point in the history
  • Loading branch information
SReichelt authored and x42 committed Feb 27, 2014
1 parent ce4d125 commit cdace71
Show file tree
Hide file tree
Showing 25 changed files with 993 additions and 380 deletions.
38 changes: 38 additions & 0 deletions gtk2_ardour/session_option_editor.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
*/

#include "ardour/session.h"
#include "ardour/route.h"
#include "ardour/panner_shell.h"

#include "gui_thread.h"
#include "session_option_editor.h"
Expand All @@ -27,6 +29,7 @@
using namespace std;
using namespace ARDOUR;
using namespace Timecode;
using namespace Gtk;

SessionOptionEditor::SessionOptionEditor (Session* s)
: OptionEditor (&(s->config), _("Session Properties"))
Expand Down Expand Up @@ -272,6 +275,24 @@ SessionOptionEditor::SessionOptionEditor (Session* s)
sigc::mem_fun (*_session_config, &SessionConfiguration::set_glue_new_regions_to_bars_and_beats)
));

add_option (_("Misc"), new OptionEditorHeading (_("Panning")));

add_option (_("Misc"), new BoolOption (
"use-delay-panners",
_("Use delay-based (Haas effect) panners by default"),
sigc::mem_fun (*_session_config, &SessionConfiguration::get_use_delay_panners),
sigc::mem_fun (*this, &SessionOptionEditor::set_use_delay_panners)
));

Gtk::Adjustment *panning_delay_adjustment = manage (new Gtk::Adjustment (0, 0, 10, .1, 1));
add_option (_("Misc"), new HSliderOption (
"panning-delay",
_("Applied delay in relation to\n loudness difference [ms/100%]"),
panning_delay_adjustment,
sigc::mem_fun (*_session_config, &SessionConfiguration::get_panning_delay),
sigc::mem_fun (*_session_config, &SessionConfiguration::set_panning_delay)
));

add_option (_("Meterbridge"), new OptionEditorHeading (_("Route Display")));

add_option (_("Meterbridge"), new BoolOption (
Expand Down Expand Up @@ -381,3 +402,20 @@ SessionOptionEditor::get_use_monitor_section ()
{
return _session->monitor_out() != 0;
}

bool
SessionOptionEditor::set_use_delay_panners (bool yn)
{
bool changed = _session_config->set_use_delay_panners (yn);

string txt = _("Would you like to reset all selectable panners to their new default?");
MessageDialog msg (txt, false, MESSAGE_QUESTION, BUTTONS_YES_NO, true);
if (msg.run() == RESPONSE_YES) {
boost::shared_ptr<RouteList> routes = _session->get_routes ();
for (RouteList::const_iterator i = routes->begin(); i != routes->end(); ++i) {
(*i)->panner_shell()->select_default_panner ();
}
}

return changed;
}
2 changes: 2 additions & 0 deletions gtk2_ardour/session_option_editor.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,7 @@ class SessionOptionEditor : public OptionEditor
bool set_use_monitor_section (bool);
bool get_use_monitor_section ();

bool set_use_delay_panners (bool);

ComboOption<float>* _vpu;
};
120 changes: 120 additions & 0 deletions libs/ardour/ardour/pan_delay_buffer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/*
Copyright (C) 2013-2014 Sebastian Reichelt
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 __libardour_pan_delay_buffer_h__
#define __libardour_pan_delay_buffer_h__

#include <cmath>

#include "ardour/session_handle.h"
#include "ardour/pan_distribution_buffer.h"

namespace ARDOUR {

class Session;

/** Buffer to add a delay to a panned channel.
*
* The delay is specified in the session properties, in ms/100%, where the
* percentage refers to the difference between the two channels (for example,
* L60R40 means 20% in this case). Only the position is relevant, not the
* width of the stereo panner. The delay is applied to the output channel with
* the lower percentage. (It might be nice if the width control affected the
* phase differences of the incoming stereo signal, but that is a different
* topic.)
*
* To keep things simple, the applied delay is always an integer number of
* frames. As long as this integer stays the same, the implementation matches
* a regular circular buffer. (We no longer use boost::circular_buffer because
* it does not offer a way to preallocate memory beyond its capacity.) Things
* become more complicated whenever the delay changes, as this requires
* non-integer interpolation between the old and new delay, to avoid minor
* clicks in the audio.
*/
class PanDelayBuffer : public PanDistributionBuffer, public SessionHandleRef
{
public:
PanDelayBuffer(Session &s);
virtual ~PanDelayBuffer();

/* Overridden to update _session_delay_coeff according to the delay
* specified in the session configuration. */
virtual void update_session_config();

protected:
/* Overridden to update the delay according to the given panner
* position. */
virtual void do_set_pan_position(float pan_position);

/* Overridden to append the @a input sample to the delay buffer and
* remove and returns the oldest sample in the buffer. */
virtual Sample do_process(Sample input);

/* Overridden to honor delay. */
virtual void do_mix_buffers(Sample *dst, const Sample *src, pframes_t nframes, float gain);

private:
/* The delay buffer, which is an array of size _buffer_size that is
* used as a circular buffer. */
Sample *_buffer;

/* Size of the _buffer array. */
pframes_t _buffer_size;

/* Position in the buffer where the next sample will be written.
* Increased by 1 for every sample, then wraps around at _buffer_size. */
pframes_t _buffer_write_pos;

/* Delay coefficient according to session configuration (in frames
* instead of ms). */
float _session_delay_coeff;

/* Current delay when interpolating. */
float _current_delay;

/* Desired delay; matches current delay if _interp_active is false. */
pframes_t _desired_delay;

/* Interpolation mode: See comment for _buffer. If true, _current_delay
* approaches _desired_delay in small steps; interpolation is finished
* as soon as they are equal. */
bool _interp_active;

/* Set to true on the first call to process() or an equivalent
* convenience method (and by update_session_config() if it returns
* false). As long as it is false, set_pan_position() sets the delay
* immediately without interpolation. */
bool _samples_processed;

/* Maximum delay, needed for memory preallocation. */
static const float _max_delay_in_ms = 10.0f;

/* Step size for _current_delay if _interp_active is true. */
static const float _interp_inc = 1.0f / 16;

/* Updates _session_delay_coeff and _active. */
void update_session_delay_coeff();

/* Called by do_process() if _interp_active is true. */
Sample interpolate(Sample input);
};

} // namespace

#endif /* __libardour_pan_delay_buffer_h__ */
115 changes: 115 additions & 0 deletions libs/ardour/ardour/pan_distribution_buffer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/*
Copyright (C) 2014 Sebastian Reichelt
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 __libardour_pan_distribution_buffer_h__
#define __libardour_pan_distribution_buffer_h__

#include "ardour/types.h"

namespace ARDOUR {

/** Helper class for panners to manage distribution of signals to outputs.
*
* The only method in this class that actually does something interesting
* is mix_buffers(). All others exist purely to be overridden by subclasses.
* (There is currently just one subclass called PanDelayBuffer.)
*
* Clients should call update_session_config() whenever the session
* configuration might have changed, then set_pan_position() whenever the
* position of the panner might have changed, and then process() for every
* sample. For convenience and performance, the two helper methods
* set_pan_position_and_process() and mix_buffers() can be used instead.
*
* Since set_pan_position() and process() are potentially called for each
* sample, their most common case is inlined. Subclasses can make sure that
* this inlined code is used by setting _active to false.
*
* For more information, see pan_delay_buffer.h.
*/
class PanDistributionBuffer
{
public:
PanDistributionBuffer();
virtual ~PanDistributionBuffer();

/** Updates internal data according to the session configuration. */
virtual void update_session_config();

/** Updates internal data according to the given panner position.
*
* @a pan_position should be a value between 0 and 1, and should not
* be a gain value that has been calculated according to the pan law.
* For a stereo output, the @a pan_position values of the left and
* right channel should sum to 1. */
void set_pan_position(float pan_position)
{
if (_active) {
do_set_pan_position(pan_position);
}
}

/** Processes one sample, and returns the sample that should actually
* be output. */
Sample process(Sample input)
{
if (_active) {
return do_process(input);
} else {
return input;
}
}

/** Same as set_pan_position() followed by process(). */
Sample set_pan_position_and_process(float pan_position, Sample input)
{
if (_active) {
do_set_pan_position(pan_position);
return do_process(input);
} else {
return input;
}
}

/** Same as calling process() for each sample in @a src multiplied by
* @a gain, and adding the result to @a dst. However, if @a prev_gain
* is different from @a gain, interpolates between gains for the
* first 64 samples.
*
* Implemented using mix_buffers_no_gain() and mix_buffers_with_gain()
* from runtime_functions.h. */
void mix_buffers(Sample *dst, const Sample *src, pframes_t nframes, float prev_gain, float gain);

protected:
/* If this is false, do_set_pan_position() and do_process() are assumed
* to be no-ops and are therefore skipped. Must be set by subclasses. */
bool _active;

virtual void do_set_pan_position(float pan_position);
virtual Sample do_process(Sample input);
virtual void do_mix_buffers(Sample *dst, const Sample *src, pframes_t nframes, float gain);

private:
/* Maximum number of frames to interpolate between gains (used by
* mix_buffers(); must be a multiple of 16). */
static const pframes_t _gain_interp_frames = 64;
};

} // namespace

#endif /* __libardour_pan_distribution_buffer_h__ */
4 changes: 4 additions & 0 deletions libs/ardour/ardour/panner.h
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,8 @@ class Panner : public PBD::Stateful, public PBD::ScopedConnectionList
pan_t** buffers, uint32_t which) = 0;

int32_t _frozen;

static const float _pan_law_scale = 2.0f - 4.0f * M_SQRT1_2; /* -3 dB */
};

} // namespace
Expand All @@ -183,6 +185,8 @@ struct PanPluginDescriptor {
int32_t out;
uint32_t priority;
ARDOUR::Panner* (*factory)(boost::shared_ptr<ARDOUR::Pannable>, boost::shared_ptr<ARDOUR::Speakers>);

static const uint32_t priority_delay_flag = 0x80000000;
};
}

Expand Down
2 changes: 1 addition & 1 deletion libs/ardour/ardour/panner_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class PannerManager : public ARDOUR::SessionHandlePtr
void discover_panners ();
std::list<PannerInfo*> panner_info;

PannerInfo* select_panner (ChanCount in, ChanCount out, std::string const uri = "");
PannerInfo* select_panner (ChanCount in, ChanCount out, bool use_delay_panners, std::string const uri = "");
PannerInfo* get_by_uri (std::string uri) const;
PannerUriMap get_available_panners(uint32_t const a_in, uint32_t const a_out) const;

Expand Down
2 changes: 2 additions & 0 deletions libs/ardour/ardour/panner_shell.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,12 @@ class PannerShell : public SessionObject

/* this function takes the process lock: */
bool select_panner_by_uri (std::string const uri);
void select_default_panner ();

private:
void distribute_no_automation (BufferSet& src, BufferSet& dest, pframes_t nframes, gain_t gain_coeff);
bool set_user_selected_panner_uri (std::string const uri);
void reselect_panner ();

boost::shared_ptr<Panner> _panner;

Expand Down
2 changes: 2 additions & 0 deletions libs/ardour/ardour/session_configuration_vars.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ CONFIG_VARIABLE (bool, midi_copy_is_fork, "midi-copy-is-fork", false)
CONFIG_VARIABLE (bool, glue_new_regions_to_bars_and_beats, "glue-new-regions-to-bars-and-beats", false)
CONFIG_VARIABLE (bool, use_video_file_fps, "use-video-file-fps", false)
CONFIG_VARIABLE (bool, videotimeline_pullup, "videotimeline-pullup", true)
CONFIG_VARIABLE (bool, use_delay_panners, "use-delay-panners", false)
CONFIG_VARIABLE (float, panning_delay, "panning-delay", 1.0f)
CONFIG_VARIABLE (bool, show_busses_on_meterbridge, "show-busses-on-meterbridge", false)
CONFIG_VARIABLE (bool, show_master_on_meterbridge, "show-master-on-meterbridge", true)
CONFIG_VARIABLE (bool, show_midi_on_meterbridge, "show-midi-on-meterbridge", true)
Expand Down
Loading

0 comments on commit cdace71

Please sign in to comment.