X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fpanner.cc;h=cb2ad8dd4a127aa04ed76869f311380eaa9d52c4;hb=75ede0dd6bda0136aef612b0e427ae25b208d0d0;hp=9d184d45776355096bb9c7655cad08df274fb037;hpb=d55c8816262ff7ae10bc9119b2e4914cdf9267ce;p=ardour.git diff --git a/libs/ardour/panner.cc b/libs/ardour/panner.cc index 9d184d4577..cb2ad8dd4a 100644 --- a/libs/ardour/panner.cc +++ b/libs/ardour/panner.cc @@ -75,7 +75,7 @@ static double direct_control_to_stereo_pan (double fract) StreamPanner::StreamPanner (Panner& p, Evoral::Parameter param) : parent (p) - , _control (new PanControllable (parent.session(), _("direction"), *this, param)) + , _control (new PanControllable (parent.session(), _("direction"), this, param)) { assert (param.type() != NullAutomation); @@ -98,22 +98,40 @@ StreamPanner::set_mono (bool yn) } } +double +StreamPanner::PanControllable::lower () const +{ + switch (parameter().id()) { + case 200: /* width */ + return -1.0; + default: + return 0.0; + } +} + void StreamPanner::PanControllable::set_value (double val) { + Panner& p (streampanner->get_parent()); switch (parameter().id()) { case 100: /* position */ - AutomationControl::set_value(val); - streampanner.get_parent().set_stereo_position (val); + val = max (min (val, 1.0), 0.0); + if (p.set_stereo_pan (val, p.width_control()->get_value())) { + AutomationControl::set_value(val); + } break; + case 200: /* width */ - AutomationControl::set_value(val); - streampanner.get_parent().set_stereo_width (val); + val = max (min (val, 1.0), -1.0); + if (p.set_stereo_pan (p.direction_control()->get_value(), val)) { + AutomationControl::set_value(val); + } break; + default: - streampanner.set_position (AngularVector (direct_control_to_stereo_pan (val), 0.0)); + streampanner->set_position (AngularVector (direct_control_to_stereo_pan (val), 0.0)); AutomationControl::set_value(val); break; } @@ -186,7 +204,7 @@ StreamPanner::get_state () } void -StreamPanner::distribute (AudioBuffer& src, BufferSet& obufs, gain_t gain_coeff, nframes_t nframes) +StreamPanner::distribute (AudioBuffer& src, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes) { if (_mono) { /* we're in mono mode, so just pan the input to all outputs equally */ @@ -202,7 +220,7 @@ StreamPanner::distribute (AudioBuffer& src, BufferSet& obufs, gain_t gain_coeff, void StreamPanner::distribute_automated (AudioBuffer& src, BufferSet& obufs, - nframes_t start, nframes_t end, nframes_t nframes, pan_t** buffers) + framepos_t start, framepos_t end, pframes_t nframes, pan_t** buffers) { if (_mono) { /* we're in mono mode, so just pan the input to all outputs equally */ @@ -242,7 +260,7 @@ BaseStereoPanner::load (istream& in, string path, uint32_t& linecnt) _control->list()->clear (); while (in.getline (line, sizeof (line), '\n')) { - nframes_t when; + framepos_t when; double value; ++linecnt; @@ -251,7 +269,7 @@ BaseStereoPanner::load (istream& in, string path, uint32_t& linecnt) break; } - if (sscanf (line, "%" PRIu32 " %lf", &when, &value) != 2) { + if (sscanf (line, "%" PRIu64 " %lf", &when, &value) != 2) { warning << string_compose(_("badly formatted pan automation event record at line %1 of %2 (ignored) [%3]"), linecnt, path, line) << endmsg; continue; } @@ -267,7 +285,7 @@ BaseStereoPanner::load (istream& in, string path, uint32_t& linecnt) } void -BaseStereoPanner::do_distribute (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coeff, nframes_t nframes) +BaseStereoPanner::do_distribute (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes) { assert(obufs.count().n_audio() == 2); @@ -290,8 +308,8 @@ BaseStereoPanner::do_distribute (AudioBuffer& srcbuf, BufferSet& obufs, gain_t g /* we've moving the pan by an appreciable amount, so we must interpolate over 64 frames or nframes, whichever is smaller */ - nframes_t const limit = min ((nframes_t)64, nframes); - nframes_t n; + pframes_t const limit = min ((pframes_t) 64, nframes); + pframes_t n; delta = -(delta / (float) (limit)); @@ -347,8 +365,8 @@ BaseStereoPanner::do_distribute (AudioBuffer& srcbuf, BufferSet& obufs, gain_t g /* we're moving the pan by an appreciable amount, so we must interpolate over 64 frames or nframes, whichever is smaller */ - nframes_t const limit = min ((nframes_t)64, nframes); - nframes_t n; + pframes_t const limit = min ((pframes_t) 64, nframes); + pframes_t n; delta = -(delta / (float) (limit)); @@ -442,7 +460,7 @@ EqualPowerStereoPanner::update () void EqualPowerStereoPanner::do_distribute_automated (AudioBuffer& srcbuf, BufferSet& obufs, - nframes_t start, nframes_t end, nframes_t nframes, + framepos_t start, framepos_t end, pframes_t nframes, pan_t** buffers) { assert (obufs.count().n_audio() == 2); @@ -478,7 +496,7 @@ EqualPowerStereoPanner::do_distribute_automated (AudioBuffer& srcbuf, BufferSet& const float pan_law_attenuation = -3.0f; const float scale = 2.0f - 4.0f * powf (10.0f,pan_law_attenuation/20.0f); - for (nframes_t n = 0; n < nframes; ++n) { + for (pframes_t n = 0; n < nframes; ++n) { float const panR = buffers[0][n]; float const panL = 1 - panR; @@ -492,7 +510,7 @@ EqualPowerStereoPanner::do_distribute_automated (AudioBuffer& srcbuf, BufferSet& dst = obufs.get_audio(0).data(); pbuf = buffers[0]; - for (nframes_t n = 0; n < nframes; ++n) { + for (pframes_t n = 0; n < nframes; ++n) { dst[n] += src[n] * pbuf[n]; } @@ -503,7 +521,7 @@ EqualPowerStereoPanner::do_distribute_automated (AudioBuffer& srcbuf, BufferSet& dst = obufs.get_audio(1).data(); pbuf = buffers[1]; - for (nframes_t n = 0; n < nframes; ++n) { + for (pframes_t n = 0; n < nframes; ++n) { dst[n] += src[n] * pbuf[n]; } @@ -1102,7 +1120,7 @@ Panner::set_position (const AngularVector& a, StreamPanner& orig) } void -Panner::distribute_no_automation (BufferSet& inbufs, BufferSet& outbufs, nframes_t nframes, gain_t gain_coeff) +Panner::distribute_no_automation (BufferSet& inbufs, BufferSet& outbufs, pframes_t nframes, gain_t gain_coeff) { if (outbufs.count().n_audio() == 0) { // Don't want to lose audio... @@ -1173,7 +1191,7 @@ Panner::distribute_no_automation (BufferSet& inbufs, BufferSet& outbufs, nframes } void -Panner::run (BufferSet& inbufs, BufferSet& outbufs, framepos_t start_frame, framepos_t end_frame, nframes_t nframes) +Panner::run (BufferSet& inbufs, BufferSet& outbufs, framepos_t start_frame, framepos_t end_frame, pframes_t nframes) { if (outbufs.count().n_audio() == 0) { // Failing to deliver audio we were asked to deliver is a bug @@ -1400,42 +1418,67 @@ void Panner::set_stereo_width (double val) { boost::shared_ptr dc = direction_control(); - - if (!dc) { - return; + if (dc) { + dc->set_value (val); } - - double d = dc->get_value (); - set_stereo_position (d); } void Panner::set_stereo_position (double val) { - /* val is the range 0..1 */ - - AngularVector p (BaseStereoPanner::lr_fract_to_azimuth (val), 0.0); boost::shared_ptr wc = width_control(); - - if (!wc) { - return; + if (wc) { + wc->set_value (val); } +} - double spread = width_control()->get_value () * 180.0; - double l_pos = p.azi + (spread/2.0); /* more left is "increasing degrees" */ - double r_pos = p.azi - (spread/2.0); /* more right is "decreasing degrees" */ +bool +Panner::set_stereo_pan (double direction_as_lr_fract, double width) +{ + AngularVector p (BaseStereoPanner::lr_fract_to_azimuth (direction_as_lr_fract), 0.0); + /* width parameter ranges from -1..+1 with 0.0 at center. we want 0..+1 plus knowing + whether its "reversed" (i.e. left signal pans right and right signal pans left). + full width = 180 degrees + */ + double spread = 2.0 * fabs(width/2.0) * 180.0; + double l_pos = p.azi + (spread/2.0); /* more left is "increasing degrees" */ + double r_pos = p.azi - (spread/2.0); /* more right is "decreasing degrees" */ + bool move_left = true; + bool move_right = true; + int l_index = 0; + int r_index = 1; - if (l_pos > 180.0 || r_pos < 0.0) { - /* already full panned, can't move any more */ - return; + assert (_streampanners.size() > 1); + + if (width < 0.0) { + swap (l_index, r_index); } - assert (_streampanners.size() > 1); + l_pos = max (min (l_pos, 180.0), 0.0); + r_pos = max (min (r_pos, 180.0), 0.0); + + /* if the new left position is less than or equal to 180 (hard left) and the left panner + is already there, we're not moving the left signal. + */ + + if (l_pos >= 180.0 &&_streampanners[l_index]->get_position().azi == 180.0) { + move_left = false; + } - cerr << "pos = " << BaseStereoPanner::lr_fract_to_azimuth (val) << " spread = " << width_control()->get_value() << " left = " << l_pos << " right = " << r_pos << endl; + /* if the new right position is less than or equal to zero (hard right) and the right panner + is already there, we're not moving the right signal. + */ + + if (r_pos <= 0.0 && _streampanners[r_index]->get_position().azi == 0.0) { + move_right = false; + } - _streampanners[0]->set_position (AngularVector (l_pos, 0.0)); - _streampanners[1]->set_position (AngularVector (r_pos, 0.0)); + if (move_left && move_right) { + _streampanners[l_index]->set_position (AngularVector (l_pos, 0.0)); + _streampanners[r_index]->set_position (AngularVector (r_pos, 0.0)); + } + + return move_left && move_right; } void @@ -1453,16 +1496,57 @@ Panner::setup_meta_controls () Evoral::Parameter lr_param (PanAutomation, 0, 100); Evoral::Parameter width_param (PanAutomation, 0, 200); - - if (!automation_control (lr_param)) { - boost::shared_ptr c (new StreamPanner::PanControllable (_session, _("lr"), *_streampanners.front(), lr_param)); - c->set_value (0.5); - add_control (c); + boost::shared_ptr dc = automation_control (lr_param); + boost::shared_ptr wc = automation_control (width_param); + + if (dc) { + /* reset parent StreamPanner as the current one may have been deleted */ + boost::shared_ptr p = boost::dynamic_pointer_cast (dc); + assert (p); + p->streampanner = _streampanners.front (); + } else { + dc.reset (new StreamPanner::PanControllable (_session, _("lr"), _streampanners.front(), lr_param)); + add_control (dc); } - if (!automation_control (width_param)) { - boost::shared_ptr c (new StreamPanner::PanControllable (_session, _("width"), *_streampanners.front(), width_param)); - c->set_value (1.0); // full width - add_control (c); + if (wc) { + /* reset parent as above */ + boost::shared_ptr p = boost::dynamic_pointer_cast (wc); + assert (p); + p->streampanner = _streampanners.front (); + } else { + wc.reset (new StreamPanner::PanControllable (_session, _("width"), _streampanners.front(), width_param)); + add_control (wc); + } + + dc->set_value (0.5); + wc->set_value (1.0); // full width +} + +string +Panner::describe_parameter (Evoral::Parameter param) +{ + if (param.type() == PanAutomation) { + switch (param.id()) { + case 100: + return "Pan:position"; + case 200: + return "Pan:width"; + default: + if (_streampanners.size() == 2) { + switch (param.id()) { + case 0: + return "Pan L"; + default: + return "Pan R"; + } + } else { + stringstream ss; + ss << "Pan " << param.id() + 1; + return ss.str (); + } + } } + + return Automatable::describe_parameter (param); }