2 Copyright (C) 2004-2011 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
35 #include "pbd/cartesian.h"
36 #include "pbd/boost_debug.h"
37 #include "pbd/convert.h"
38 #include "pbd/error.h"
39 #include "pbd/failed_constructor.h"
40 #include "pbd/xml++.h"
41 #include "pbd/enumwriter.h"
43 #include "evoral/Curve.hpp"
45 #include "ardour/audio_buffer.h"
46 #include "ardour/audio_buffer.h"
47 #include "ardour/automatable.h"
48 #include "ardour/buffer_set.h"
49 #include "ardour/debug.h"
50 #include "ardour/pannable.h"
51 #include "ardour/panner.h"
52 #include "ardour/panner_manager.h"
53 #include "ardour/panner_shell.h"
54 #include "ardour/runtime_functions.h"
55 #include "ardour/session.h"
56 #include "ardour/utils.h"
60 #include "pbd/mathfix.h"
63 using namespace ARDOUR
;
66 PannerShell::PannerShell (string name
, Session
& s
, boost::shared_ptr
<Pannable
> p
)
67 : SessionObject (s
, name
)
73 PannerShell::~PannerShell ()
75 DEBUG_TRACE(DEBUG::Destruction
, string_compose ("panner shell for %1 destructor, pannable is %2\n", _name
, _pannable
));
79 PannerShell::configure_io (ChanCount in
, ChanCount out
)
81 uint32_t nouts
= out
.n_audio();
82 uint32_t nins
= in
.n_audio();
84 /* if new and old config don't need panning, or if
85 the config hasn't changed, we're done.
88 if (_panner
&& (_panner
->in().n_audio() == nins
) && (_panner
->out().n_audio() == nouts
)) {
92 if (nouts
< 2 || nins
== 0) {
93 /* no need for panning with less than 2 outputs or no inputs */
96 Changed (); /* EMIT SIGNAL */
101 PannerInfo
* pi
= PannerManager::instance().select_panner (in
, out
);
107 boost::shared_ptr
<Speakers
> speakers
= _session
.get_speakers ();
109 if (nouts
!= speakers
->size()) {
110 /* hmm, output count doesn't match session speaker count so
111 create a new speaker set.
113 Speakers
* s
= new Speakers ();
114 s
->setup_default_speakers (nouts
);
118 Panner
* p
= pi
->descriptor
.factory (_pannable
, speakers
);
119 boost_debug_shared_ptr_mark_interesting (p
, "Panner");
121 _panner
->configure_io (in
, out
);
123 Changed (); /* EMIT SIGNAL */
127 PannerShell::get_state (void)
133 PannerShell::state (bool full
)
135 XMLNode
* node
= new XMLNode ("PannerShell");
138 node
->add_child_nocopy (_panner
->state (full
));
145 PannerShell::set_state (const XMLNode
& node
, int version
)
147 XMLNodeList nlist
= node
.children ();
148 XMLNodeConstIterator niter
;
149 const XMLProperty
*prop
;
150 LocaleGuard
lg (X_("POSIX"));
154 for (niter
= nlist
.begin(); niter
!= nlist
.end(); ++niter
) {
156 if ((*niter
)->name() == X_("Panner")) {
158 if ((prop
= (*niter
)->property (X_("type")))) {
160 list
<PannerInfo
*>::iterator p
;
161 PannerManager
& pm (PannerManager::instance());
163 for (p
= pm
.panner_info
.begin(); p
!= pm
.panner_info
.end(); ++p
) {
164 if (prop
->value() == (*p
)->descriptor
.name
) {
166 /* note that we assume that all the stream panners
167 are of the same type. pretty good
168 assumption, but it's still an assumption.
171 _panner
.reset ((*p
)->descriptor
.factory (_pannable
, _session
.get_speakers ()));
173 if (_panner
->set_state (**niter
, version
) == 0) {
181 if (p
== pm
.panner_info
.end()) {
182 error
<< string_compose (_("Unknown panner plugin \"%1\" found in pan state - ignored"),
188 error
<< _("panner plugin node has no type information!")
200 PannerShell::distribute_no_automation (BufferSet
& inbufs
, BufferSet
& outbufs
, pframes_t nframes
, gain_t gain_coeff
)
202 if (outbufs
.count().n_audio() == 0) {
203 // Don't want to lose audio...
204 assert(inbufs
.count().n_audio() == 0);
208 if (outbufs
.count().n_audio() == 1) {
210 /* just one output: no real panning going on */
212 AudioBuffer
& dst
= outbufs
.get_audio(0);
214 if (gain_coeff
== 0.0f
) {
216 /* gain was zero, so make it silent */
218 dst
.silence (nframes
);
220 } else if (gain_coeff
== 1.0f
){
222 /* mix all input buffers into the output */
225 dst
.read_from(inbufs
.get_audio(0), nframes
);
227 // accumulate starting with the second
228 if (inbufs
.count().n_audio() > 0) {
229 BufferSet::audio_iterator i
= inbufs
.audio_begin();
230 for (++i
; i
!= inbufs
.audio_end(); ++i
) {
231 dst
.merge_from(*i
, nframes
);
237 /* mix all buffers into the output, scaling them all by the gain */
240 dst
.read_from(inbufs
.get_audio(0), nframes
);
242 // accumulate (with gain) starting with the second
243 if (inbufs
.count().n_audio() > 0) {
244 BufferSet::audio_iterator i
= inbufs
.audio_begin();
245 for (++i
; i
!= inbufs
.audio_end(); ++i
) {
246 dst
.accumulate_with_gain_from(*i
, nframes
, gain_coeff
);
255 /* multiple outputs ... we must have a panner */
259 /* setup silent buffers so that we can mix into the outbuffers (slightly suboptimal -
260 better to copy the first set of data then mix after that, but hey, its 2011)
263 for (BufferSet::audio_iterator b
= outbufs
.audio_begin(); b
!= outbufs
.audio_end(); ++b
) {
264 (*b
).silence (nframes
);
267 _panner
->distribute (inbufs
, outbufs
, gain_coeff
, nframes
);
271 PannerShell::run (BufferSet
& inbufs
, BufferSet
& outbufs
, framepos_t start_frame
, framepos_t end_frame
, pframes_t nframes
)
273 if (outbufs
.count().n_audio() == 0) {
274 // Failing to deliver audio we were asked to deliver is a bug
275 assert(inbufs
.count().n_audio() == 0);
279 if (outbufs
.count().n_audio() == 1) {
281 /* one output only: no panner */
283 AudioBuffer
& dst
= outbufs
.get_audio(0);
285 // FIXME: apply gain automation?
288 dst
.read_from (inbufs
.get_audio(0), nframes
);
290 // accumulate starting with the second
291 BufferSet::audio_iterator i
= inbufs
.audio_begin();
292 for (++i
; i
!= inbufs
.audio_end(); ++i
) {
293 dst
.merge_from (*i
, nframes
);
299 // More than 1 output
301 AutoState as
= _panner
->automation_state ();
303 // If we shouldn't play automation defer to distribute_no_automation
305 if (!(as
& Play
|| ((as
& Touch
) && !_panner
->touching()))) {
308 gain_t gain_coeff
= 1.0;
310 if (fabsf(_session
.transport_speed()) > 1.5f
&& Config
->get_quieten_at_speed ()) {
311 gain_coeff
= speed_quietning
;
314 distribute_no_automation (inbufs
, outbufs
, nframes
, gain_coeff
);
318 /* setup the terrible silence so that we can mix into the outbuffers (slightly suboptimal -
319 better to copy the first set of data then mix after that, but hey, its 2011)
321 for (BufferSet::audio_iterator i
= outbufs
.audio_begin(); i
!= outbufs
.audio_end(); ++i
) {
325 _panner
->distribute_automated (inbufs
, outbufs
, start_frame
, end_frame
, nframes
, _session
.pan_automation_buffer());