X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fpanner.cc;h=e0dcf206345febb2efad2d256edd162382c47c20;hb=bbf41757133a29df0d37905f2fdce091878d2ffd;hp=83c9e6eb4d7f48854bb4cbc8246451172b52f9a9;hpb=fe13d08874f08b723df53116e5655c3d229a657e;p=ardour.git diff --git a/libs/ardour/panner.cc b/libs/ardour/panner.cc index 83c9e6eb4d..e0dcf20634 100644 --- a/libs/ardour/panner.cc +++ b/libs/ardour/panner.cc @@ -15,9 +15,11 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - $Id$ */ +#define __STDC_FORMAT_MACROS 1 +#include + #include #include #include @@ -33,12 +35,14 @@ #include #include #include +#include #include #include #include -#include +#include +#include #include "i18n.h" @@ -53,24 +57,26 @@ float Panner::current_automation_version_number = 1.0; string EqualPowerStereoPanner::name = "Equal Power Stereo"; string Multi2dPanner::name = "Multiple (2D)"; -/* this is a default mapper of MIDI control values to a pan position - others can be imagined. see Panner::set_midi_to_pan_function(). +/* this is a default mapper of control values to a pan position + others can be imagined. */ -static pan_t direct_midi_to_pan (double fract) { +static pan_t direct_control_to_pan (double fract) { return fract; } -static double direct_pan_to_midi (pan_t val) { +static double direct_pan_to_control (pan_t val) { return val; } StreamPanner::StreamPanner (Panner& p) : parent (p), - _midi_control (*this, (MIDI::Port*) 0) + _control (new PanControllable(p.session(), X_("panner"), *this)) { _muted = false; + parent.session().add_controllable (_control); + x = 0.5; y = 0.5; z = 0.5; @@ -80,84 +86,30 @@ StreamPanner::~StreamPanner () { } -StreamPanner::MIDIControl::MIDIControl (StreamPanner& s, MIDI::Port* port) - : MIDI::Controllable (port, 0), sp (s), setting(false) -{ - midi_to_pan = direct_midi_to_pan; - pan_to_midi = direct_pan_to_midi; - last_written = 0; /* XXX need a good out-of-bound-value */ -} - void -StreamPanner::MIDIControl::set_value (float val) +StreamPanner::PanControllable::set_value (float val) { - setting = true; - sp.set_position (midi_to_pan (val)); - setting = false; + panner.set_position (direct_control_to_pan (val)); } -void -StreamPanner::MIDIControl::send_feedback (pan_t value) +float +StreamPanner::PanControllable::get_value (void) const { - - if (!setting && get_midi_feedback() && pan_to_midi) { - MIDI::byte val = (MIDI::byte) (pan_to_midi (value) * 127.0f); - MIDI::channel_t ch = 0; - MIDI::eventType ev = MIDI::none; - MIDI::byte additional = 0; - MIDI::EventTwoBytes data; - - if (get_control_info (ch, ev, additional)) { - data.controller_number = additional; - data.value = val; - last_written = val; - - sp.get_parent().session().send_midi_message (get_port(), ev, ch, data); - } - - // send_midi_feedback (pan_to_midi (val)); - } - + float xpos; + panner.get_effective_position (xpos); + return direct_pan_to_control (xpos); } -MIDI::byte* -StreamPanner::MIDIControl::write_feedback (MIDI::byte* buf, int32_t& bufsize, pan_t val, bool force) -{ - if (get_midi_feedback() && pan_to_midi && bufsize > 2) { - MIDI::channel_t ch = 0; - MIDI::eventType ev = MIDI::none; - MIDI::byte additional = 0; - MIDI::byte pm; - if (get_control_info (ch, ev, additional)) { - - pm = (MIDI::byte) (pan_to_midi (val) * 127.0); - - if (pm != last_written || force) { - *buf++ = (0xF0 & ev) | (0xF & ch); - *buf++ = additional; /* controller number */ - *buf++ = pm; - last_written = pm; - bufsize -= 3; - } - } - } - - return buf; -} - - -void -StreamPanner::reset_midi_control (MIDI::Port* port, bool on) +bool +StreamPanner::PanControllable::can_send_feedback () const { - MIDI::channel_t chn; - MIDI::eventType ev; - MIDI::byte extra; + AutoState astate = panner.get_parent().automation_state (); - _midi_control.get_control_info (chn, ev, extra); - if (!on) { - chn = -1; + if ((astate == Play) || (astate == Touch && !panner.get_parent().touching())) { + return true; } - _midi_control.midi_rebind (port, chn); + + return false; } void @@ -180,10 +132,7 @@ StreamPanner::set_position (float xpos, bool link_call) x = xpos; update (); Changed (); - - if (parent.session().get_midi_feedback()) { - _midi_control.send_feedback (x); - } + _control->Changed (); } } @@ -224,42 +173,11 @@ StreamPanner::set_state (const XMLNode& node) { const XMLProperty* prop; XMLNodeConstIterator iter; - XMLNodeList midi_kids; if ((prop = node.property (X_("muted")))) { set_muted (prop->value() == "yes"); } - midi_kids = node.children ("MIDI"); - - for (iter = midi_kids.begin(); iter != midi_kids.end(); ++iter) { - - XMLNodeList kids; - XMLNodeConstIterator miter; - XMLNode* child; - - kids = (*iter)->children (); - - for (miter = kids.begin(); miter != kids.end(); ++miter) { - - child =* miter; - - if (child->name() == "pan") { - - MIDI::eventType ev = MIDI::on; /* initialize to keep gcc happy */ - MIDI::byte additional = 0; /* ditto */ - MIDI::channel_t chn = 0; /* ditto */ - - if (get_midi_node_info (child, ev, chn, additional)) { - _midi_control.set_control_type (chn, ev, additional); - } else { - error << _("MIDI pan control specification is incomplete, so it has been ignored") << endmsg; - } - } - } - } - - return 0; } @@ -267,74 +185,12 @@ void StreamPanner::add_state (XMLNode& node) { node.add_property (X_("muted"), (muted() ? "yes" : "no")); - - /* MIDI control */ - - MIDI::channel_t chn; - MIDI::eventType ev; - MIDI::byte additional; - XMLNode* midi_node = 0; - XMLNode* child; - - if (_midi_control.get_control_info (chn, ev, additional)) { - - midi_node = node.add_child ("MIDI"); - - child = midi_node->add_child ("pan"); - set_midi_node_info (child, ev, chn, additional); - } - -} - - -bool -StreamPanner::get_midi_node_info (XMLNode * node, MIDI::eventType & ev, MIDI::channel_t & chan, MIDI::byte & additional) -{ - bool ok = true; - const XMLProperty* prop; - int xx; - - if ((prop = node->property ("event")) != 0) { - sscanf (prop->value().c_str(), "0x%x", &xx); - ev = (MIDI::eventType) xx; - } else { - ok = false; - } - - if (ok && ((prop = node->property ("channel")) != 0)) { - sscanf (prop->value().c_str(), "%d", &xx); - chan = (MIDI::channel_t) xx; - } else { - ok = false; - } - - if (ok && ((prop = node->property ("additional")) != 0)) { - sscanf (prop->value().c_str(), "0x%x", &xx); - additional = (MIDI::byte) xx; - } - - return ok; -} - -bool -StreamPanner::set_midi_node_info (XMLNode * node, MIDI::eventType ev, MIDI::channel_t chan, MIDI::byte additional) -{ - char buf[32]; - - snprintf (buf, sizeof(buf), "0x%x", ev); - node->add_property ("event", buf); - snprintf (buf, sizeof(buf), "%d", chan); - node->add_property ("channel", buf); - snprintf (buf, sizeof(buf), "0x%x", additional); - node->add_property ("additional", buf); - - return true; } /*---------------------------------------------------------------------- */ BaseStereoPanner::BaseStereoPanner (Panner& p) - : StreamPanner (p), _automation (0.0, 1.0, 0.5) + : StreamPanner (p) { } @@ -342,81 +198,16 @@ BaseStereoPanner::~BaseStereoPanner () { } -void -BaseStereoPanner::snapshot (jack_nframes_t now) -{ - if (_automation.automation_state() == Write || _automation.automation_state() == Touch) { - _automation.rt_add (now, x); - } -} - -void -BaseStereoPanner::transport_stopped (jack_nframes_t frame) -{ - _automation.reposition_for_rt_add (frame); - - if (_automation.automation_state() != Off) { - - if (_automation.automation_write()) { - _automation.save_state (_("automation write pass")); - } - - set_position (_automation.eval (frame)); - } -} - -void -BaseStereoPanner::set_automation_style (AutoStyle style) -{ - _automation.set_automation_style (style); -} - -void -BaseStereoPanner::set_automation_state (AutoState state) -{ - if (state != _automation.automation_state()) { - - _automation.set_automation_state (state); - - if (state != Off) { - set_position (_automation.eval (parent.session().transport_frame())); - } - } -} - -int -BaseStereoPanner::save (ostream& out) const -{ - LocaleGuard lg (X_("POSIX")); - - /* force a single format for numeric data to ease session interchange - across national boundaries. - */ - - out << "begin" << endl; - - for (AutomationList::const_iterator i = _automation.const_begin(); i != _automation.const_end(); ++i) { - out << '\t' << (jack_nframes_t) floor ((*i)->when) << ' ' << (*i)->value << endl; - if (!out) { - error << string_compose (_("error writing pan automation file (%s)"), strerror (errno)) << endmsg; - return -1; - } - } - out << "end" << endl; - - return 0; -} - int BaseStereoPanner::load (istream& in, string path, uint32_t& linecnt) { char line[128]; LocaleGuard lg (X_("POSIX")); - _automation.clear (); + _control->list()->clear (); while (in.getline (line, sizeof (line), '\n')) { - jack_nframes_t when; + nframes_t when; double value; ++linecnt; @@ -430,20 +221,21 @@ BaseStereoPanner::load (istream& in, string path, uint32_t& linecnt) continue; } - _automation.add (when, value, true); + _control->list()->fast_simple_add (when, value); } /* now that we are done loading */ - _automation.save_state (_("loaded from disk")); - _automation.StateChanged (Change (0)); + _control->list()->StateChanged (); return 0; } void -BaseStereoPanner::distribute (Sample* src, Sample** obufs, gain_t gain_coeff, jack_nframes_t nframes) +BaseStereoPanner::distribute (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coeff, nframes_t nframes) { + assert(obufs.count().n_audio() == 2); + pan_t delta; Sample* dst; pan_t pan; @@ -451,17 +243,19 @@ BaseStereoPanner::distribute (Sample* src, Sample** obufs, gain_t gain_coeff, ja if (_muted) { return; } + + Sample* const src = srcbuf.data(); /* LEFT */ - dst = obufs[0]; + dst = obufs.get_audio(0).data(); if (fabsf ((delta = (left - desired_left))) > 0.002) { // about 1 degree of arc /* interpolate over 64 frames or nframes, whichever is smaller */ - jack_nframes_t limit = min ((jack_nframes_t)64, nframes); - jack_nframes_t n; + nframes_t limit = min ((nframes_t)64, nframes); + nframes_t n; delta = -(delta / (float) (limit)); @@ -473,7 +267,7 @@ BaseStereoPanner::distribute (Sample* src, Sample** obufs, gain_t gain_coeff, ja pan = left * gain_coeff; - Session::mix_buffers_with_gain(dst+n,src+n,nframes-n,pan); + mix_buffers_with_gain(dst+n,src+n,nframes-n,pan); } else { @@ -484,7 +278,7 @@ BaseStereoPanner::distribute (Sample* src, Sample** obufs, gain_t gain_coeff, ja if (pan != 0.0f) { - Session::mix_buffers_with_gain(dst,src,nframes,pan); + mix_buffers_with_gain(dst,src,nframes,pan); /* mark that we wrote into the buffer */ @@ -494,7 +288,7 @@ BaseStereoPanner::distribute (Sample* src, Sample** obufs, gain_t gain_coeff, ja } else { - Session::mix_buffers_no_gain(dst,src,nframes); + mix_buffers_no_gain(dst,src,nframes); /* mark that we wrote into the buffer */ @@ -504,14 +298,14 @@ BaseStereoPanner::distribute (Sample* src, Sample** obufs, gain_t gain_coeff, ja /* RIGHT */ - dst = obufs[1]; + dst = obufs.get_audio(1).data(); if (fabsf ((delta = (right - desired_right))) > 0.002) { // about 1 degree of arc /* interpolate over 64 frames or nframes, whichever is smaller */ - jack_nframes_t limit = min ((jack_nframes_t)64, nframes); - jack_nframes_t n; + nframes_t limit = min ((nframes_t)64, nframes); + nframes_t n; delta = -(delta / (float) (limit)); @@ -523,7 +317,7 @@ BaseStereoPanner::distribute (Sample* src, Sample** obufs, gain_t gain_coeff, ja pan = right * gain_coeff; - Session::mix_buffers_with_gain(dst+n,src+n,nframes-n,pan); + mix_buffers_with_gain(dst+n,src+n,nframes-n,pan); /* XXX it would be nice to mark the buffer as written to */ @@ -536,14 +330,14 @@ BaseStereoPanner::distribute (Sample* src, Sample** obufs, gain_t gain_coeff, ja if (pan != 0.0f) { - Session::mix_buffers_with_gain(dst,src,nframes,pan); + mix_buffers_with_gain(dst,src,nframes,pan); /* XXX it would be nice to mark the buffer as written to */ } } else { - Session::mix_buffers_no_gain(dst,src,nframes); + mix_buffers_no_gain(dst,src,nframes); /* XXX it would be nice to mark the buffer as written to */ } @@ -595,19 +389,22 @@ EqualPowerStereoPanner::update () } void -EqualPowerStereoPanner::distribute_automated (Sample* src, Sample** obufs, - jack_nframes_t start, jack_nframes_t end, jack_nframes_t nframes, +EqualPowerStereoPanner::distribute_automated (AudioBuffer& srcbuf, BufferSet& obufs, + nframes_t start, nframes_t end, nframes_t nframes, pan_t** buffers) { + assert(obufs.count().n_audio() == 2); + Sample* dst; pan_t* pbuf; + Sample* const src = srcbuf.data(); /* fetch positional data */ - if (!_automation.rt_safe_get_vector (start, end, buffers[0], nframes)) { + if (!_control->list()->curve().rt_safe_get_vector (start, end, buffers[0], nframes)) { /* fallback */ if (!_muted) { - distribute (src, obufs, 1.0, nframes); + distribute (srcbuf, obufs, 1.0, nframes); } return; } @@ -628,7 +425,7 @@ EqualPowerStereoPanner::distribute_automated (Sample* src, Sample** obufs, const float pan_law_attenuation = -3.0f; const float scale = 2.0f - 4.0f * powf (10.0f,pan_law_attenuation/20.0f); - for (jack_nframes_t n = 0; n < nframes; ++n) { + for (nframes_t n = 0; n < nframes; ++n) { float panR = buffers[0][n]; float panL = 1 - panR; @@ -639,25 +436,25 @@ EqualPowerStereoPanner::distribute_automated (Sample* src, Sample** obufs, /* LEFT */ - dst = obufs[0]; + dst = obufs.get_audio(0).data(); pbuf = buffers[0]; - for (jack_nframes_t n = 0; n < nframes; ++n) { + for (nframes_t n = 0; n < nframes; ++n) { dst[n] += src[n] * pbuf[n]; } - /* XXX it would be nice to mark the buffer as written to */ + /* XXX it would be nice to mark the buffer as written to */ /* RIGHT */ - dst = obufs[1]; + dst = obufs.get_audio(1).data(); pbuf = buffers[1]; - for (jack_nframes_t n = 0; n < nframes; ++n) { + for (nframes_t n = 0; n < nframes; ++n) { dst[n] += src[n] * pbuf[n]; } - /* XXX it would be nice to mark the buffer as written to */ + /* XXX it would be nice to mark the buffer as written to */ } StreamPanner* @@ -679,21 +476,18 @@ EqualPowerStereoPanner::state (bool full_state) char buf[64]; LocaleGuard lg (X_("POSIX")); - snprintf (buf, sizeof (buf), "%f", x); + snprintf (buf, sizeof (buf), "%.12g", x); root->add_property (X_("x"), buf); root->add_property (X_("type"), EqualPowerStereoPanner::name); - if (full_state) { - snprintf (buf, sizeof (buf), "0x%x", _automation.automation_state()); - } else { - /* never store automation states other than off in a template */ - snprintf (buf, sizeof (buf), "0x%x", ARDOUR::Off); - } - root->add_property (X_("automation-state"), buf); - snprintf (buf, sizeof (buf), "0x%x", _automation.automation_style()); - root->add_property (X_("automation-style"), buf); + + XMLNode* autonode = new XMLNode (X_("Automation")); + autonode->add_child_nocopy (_control->list()->state (full_state)); + root->add_child_nocopy (*autonode); StreamPanner::add_state (*root); + root->add_child_nocopy (_control->get_state ()); + return *root; } @@ -701,7 +495,6 @@ int EqualPowerStereoPanner::set_state (const XMLNode& node) { const XMLProperty* prop; - int x; float pos; LocaleGuard lg (X_("POSIX")); @@ -710,29 +503,32 @@ EqualPowerStereoPanner::set_state (const XMLNode& node) set_position (pos, true); } - if ((prop = node.property (X_("automation-state")))) { - sscanf (prop->value().c_str(), "0x%x", &x); - _automation.set_automation_state ((AutoState) x); + StreamPanner::set_state (node); - if (x != Off) { - set_position (_automation.eval (parent.session().transport_frame())); - } - } + for (XMLNodeConstIterator iter = node.children().begin(); iter != node.children().end(); ++iter) { + + if ((*iter)->name() == X_("controllable")) { + if ((prop = (*iter)->property("name")) != 0 && prop->value() == "panner") { + _control->set_state (**iter); + } - if ((prop = node.property (X_("automation-style")))) { - sscanf (prop->value().c_str(), "0x%x", &x); - _automation.set_automation_style ((AutoStyle) x); + } else if ((*iter)->name() == X_("Automation")) { + + _control->list()->set_state (*((*iter)->children().front())); + + if (_control->list()->automation_state() != Off) { + set_position (_control->list()->eval (parent.session().transport_frame())); + } + } } - StreamPanner::set_state (node); - return 0; } /*----------------------------------------------------------------------*/ Multi2dPanner::Multi2dPanner (Panner& p) - : StreamPanner (p), _automation (0.0, 1.0, 0.5) // XXX useless + : StreamPanner (p) { update (); } @@ -741,30 +537,6 @@ Multi2dPanner::~Multi2dPanner () { } -void -Multi2dPanner::snapshot (jack_nframes_t now) -{ - // how? -} - -void -Multi2dPanner::transport_stopped (jack_nframes_t frame) -{ - //what? -} - -void -Multi2dPanner::set_automation_style (AutoStyle style) -{ - //what? -} - -void -Multi2dPanner::set_automation_state (AutoState state) -{ - // what? -} - void Multi2dPanner::update () { @@ -798,7 +570,7 @@ Multi2dPanner::update () } void -Multi2dPanner::distribute (Sample* src, Sample** obufs, gain_t gain_coeff, jack_nframes_t nframes) +Multi2dPanner::distribute (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coeff, nframes_t nframes) { Sample* dst; pan_t pan; @@ -808,19 +580,21 @@ Multi2dPanner::distribute (Sample* src, Sample** obufs, gain_t gain_coeff, jack_ if (_muted) { return; } + + Sample* const src = srcbuf.data(); for (n = 0, o = parent.outputs.begin(); o != parent.outputs.end(); ++o, ++n) { - dst = obufs[n]; + dst = obufs.get_audio(n).data(); #ifdef CAN_INTERP if (fabsf ((delta = (left_interp - desired_left))) > 0.002) { // about 1 degree of arc /* interpolate over 64 frames or nframes, whichever is smaller */ - jack_nframes_t limit = min ((jack_nframes_t)64, nframes); - jack_nframes_t n; + nframes_t limit = min ((nframes_t)64, nframes); + nframes_t n; delta = -(delta / (float) (limit)); @@ -831,10 +605,7 @@ Multi2dPanner::distribute (Sample* src, Sample** obufs, gain_t gain_coeff, jack_ } pan = left * gain_coeff; - - for (; n < nframes; ++n) { - dst[n] += src[n] * pan; - } + mix_buffers_with_gain(dst+n,src+n,nframes-n,pan); } else { @@ -844,20 +615,10 @@ Multi2dPanner::distribute (Sample* src, Sample** obufs, gain_t gain_coeff, jack_ if ((pan *= gain_coeff) != 1.0f) { if (pan != 0.0f) { - - for (jack_nframes_t n = 0; n < nframes; ++n) { - dst[n] += src[n] * pan; - } - + mix_buffers_with_gain(dst,src,nframes,pan); } - - } else { - - for (jack_nframes_t n = 0; n < nframes; ++n) { - dst[n] += src[n]; - } - + mix_buffers_no_gain(dst,src,nframes); } #endif #ifdef CAN_INTERP @@ -869,8 +630,8 @@ Multi2dPanner::distribute (Sample* src, Sample** obufs, gain_t gain_coeff, jack_ } void -Multi2dPanner::distribute_automated (Sample* src, Sample** obufs, - jack_nframes_t start, jack_nframes_t end, jack_nframes_t nframes, +Multi2dPanner::distribute_automated (AudioBuffer& src, BufferSet& obufs, + nframes_t start, nframes_t end, nframes_t nframes, pan_t** buffers) { if (_muted) { @@ -894,12 +655,6 @@ Multi2dPanner::load (istream& in, string path, uint32_t& linecnt) return 0; } -int -Multi2dPanner::save (ostream& out) const -{ - return 0; -} - XMLNode& Multi2dPanner::get_state (void) { @@ -913,12 +668,14 @@ Multi2dPanner::state (bool full_state) char buf[64]; LocaleGuard lg (X_("POSIX")); - snprintf (buf, sizeof (buf), "%f", x); + snprintf (buf, sizeof (buf), "%.12g", x); root->add_property (X_("x"), buf); - snprintf (buf, sizeof (buf), "%f", y); + snprintf (buf, sizeof (buf), "%.12g", y); root->add_property (X_("y"), buf); root->add_property (X_("type"), Multi2dPanner::name); + /* XXX no meaningful automation yet */ + return *root; } @@ -956,11 +713,10 @@ Panner::Panner (string name, Session& s) : _session (s) { set_name (name); + _linked = false; _link_direction = SameDirection; _bypassed = false; - - reset_midi_control (_session.mmc_port(), _session.get_mmc_control()); } Panner::~Panner () @@ -987,17 +743,6 @@ Panner::set_link_direction (LinkDirection ld) } } -void -Panner::set_name (string str) -{ - automation_path = _session.automation_dir(); - automation_path += _session.snap_name(); - automation_path += "-pan-"; - automation_path += legalize_for_path (str); - automation_path += ".automation"; -} - - void Panner::set_bypassed (bool yn) { @@ -1014,7 +759,6 @@ Panner::reset (uint32_t nouts, uint32_t npans) uint32_t n; bool changed = false; - if (nouts < 2 || (nouts == outputs.size() && npans == size())) { return; } @@ -1107,8 +851,6 @@ Panner::reset (uint32_t nouts, uint32_t npans) (*x)->update (); } - reset_midi_control (_session.mmc_port(), _session.get_mmc_control()); - /* force hard left/right panning in a common case: 2in/2out */ @@ -1127,10 +869,10 @@ Panner::reset (uint32_t nouts, uint32_t npans) if (changed || ((left == 0.5) && (right == 0.5))) { front()->set_position (0.0); - front()->automation().reset_default (0.0); + front()->pan_control()->list()->reset_default (0.0); back()->set_position (1.0); - back()->automation().reset_default (1.0); + back()->pan_control()->list()->reset_default (1.0); changed = true; } @@ -1169,7 +911,7 @@ void Panner::set_automation_style (AutoStyle style) { for (vector::iterator i = begin(); i != end(); ++i) { - (*i)->set_automation_style (style); + (*i)->pan_control()->list()->set_automation_style (style); } _session.set_dirty (); } @@ -1178,7 +920,7 @@ void Panner::set_automation_state (AutoState state) { for (vector::iterator i = begin(); i != end(); ++i) { - (*i)->set_automation_state (state); + (*i)->pan_control()->list()->set_automation_state (state); } _session.set_dirty (); } @@ -1187,7 +929,7 @@ AutoState Panner::automation_state () const { if (!empty()) { - return front()->automation().automation_state (); + return front()->pan_control()->list()->automation_state (); } else { return Off; } @@ -1197,25 +939,27 @@ AutoStyle Panner::automation_style () const { if (!empty()) { - return front()->automation().automation_style (); + return front()->pan_control()->list()->automation_style (); } else { return Absolute; } } void -Panner::transport_stopped (jack_nframes_t frame) +Panner::transport_stopped (nframes_t frame) { for (vector::iterator i = begin(); i != end(); ++i) { - (*i)->transport_stopped (frame); + (*i)->pan_control()->list()->reposition_for_rt_add (frame); } } void -Panner::snapshot (jack_nframes_t now) +Panner::snapshot (nframes_t now) { for (vector::iterator i = begin(); i != end(); ++i) { - (*i)->snapshot (now); + boost::shared_ptr list = (*i)->pan_control()->list(); + if (list->automation_write()) + list->rt_add(now, (*i)->pan_control()->get_value()); } } @@ -1223,107 +967,11 @@ void Panner::clear_automation () { for (vector::iterator i = begin(); i != end(); ++i) { - (*i)->automation().clear (); + (*i)->pan_control()->list()->clear (); } _session.set_dirty (); } -int -Panner::save () const -{ - ofstream out (automation_path.c_str()); - - if (!out) { - error << string_compose (_("cannot open pan automation file \"%1\" for saving (%s)"), automation_path, strerror (errno)) - << endmsg; - return -1; - } - - out << X_("version ") << current_automation_version_number << endl; - - for (vector::const_iterator i = begin(); i != end(); ++i) { - if ((*i)->save (out)) { - return -1; - } - } - - return 0; -} - -int -Panner::load () -{ - char line[128]; - uint32_t linecnt = 0; - float version; - iterator sp; - LocaleGuard lg (X_("POSIX")); - - if (automation_path.length() == 0) { - return 0; - } - - if (access (automation_path.c_str(), F_OK)) { - return 0; - } - - ifstream in (automation_path.c_str()); - - if (!in) { - error << string_compose (_("cannot open pan automation file %1 (%2)"), - automation_path, strerror (errno)) - << endmsg; - return -1; - } - - sp = begin(); - - while (in.getline (line, sizeof(line), '\n')) { - - if (++linecnt == 1) { - if (memcmp (line, X_("version"), 7) == 0) { - if (sscanf (line, "version %f", &version) != 1) { - error << string_compose(_("badly formed version number in pan automation event file \"%1\""), automation_path) << endmsg; - return -1; - } - } else { - error << string_compose(_("no version information in pan automation event file \"%1\" (first line = %2)"), - automation_path, line) << endmsg; - return -1; - } - - if (version != current_automation_version_number) { - error << string_compose(_("mismatched pan automation event file version (%1)"), version) << endmsg; - return -1; - } - - continue; - } - - if (strlen (line) == 0 || line[0] == '#') { - continue; - } - - if (strcmp (line, "begin") == 0) { - - if (sp == end()) { - error << string_compose (_("too many panner states found in pan automation file %1"), - automation_path) - << endmsg; - return -1; - } - - if ((*sp)->load (in, automation_path, linecnt)) { - return -1; - } - - ++sp; - } - } - - return 0; -} - struct PanPlugins { string name; uint32_t nouts; @@ -1333,7 +981,7 @@ struct PanPlugins { PanPlugins pan_plugins[] = { { EqualPowerStereoPanner::name, 2, EqualPowerStereoPanner::factory }, { Multi2dPanner::name, 3, Multi2dPanner::factory }, - { string (""), 0 } + { string (""), 0, 0 } }; XMLNode& @@ -1348,30 +996,23 @@ Panner::state (bool full) XMLNode* root = new XMLNode (X_("Panner")); char buf[32]; - for (iterator p = begin(); p != end(); ++p) { - root->add_child_nocopy ((*p)->state (full)); - } - root->add_property (X_("linked"), (_linked ? "yes" : "no")); - snprintf (buf, sizeof (buf), "%d", _link_direction); - root->add_property (X_("link_direction"), buf); + root->add_property (X_("link_direction"), enum_2_string (_link_direction)); root->add_property (X_("bypassed"), (bypassed() ? "yes" : "no")); /* add each output */ for (vector::iterator o = outputs.begin(); o != outputs.end(); ++o) { XMLNode* onode = new XMLNode (X_("Output")); - snprintf (buf, sizeof (buf), "%f", (*o).x); + snprintf (buf, sizeof (buf), "%.12g", (*o).x); onode->add_property (X_("x"), buf); - snprintf (buf, sizeof (buf), "%f", (*o).y); + snprintf (buf, sizeof (buf), "%.12g", (*o).y); onode->add_property (X_("y"), buf); root->add_child_nocopy (*onode); } - if (full) { - if (save () == 0) { - root->add_property (X_("automation"), Glib::path_get_basename (automation_path)); - } + for (vector::const_iterator i = begin(); i != end(); ++i) { + root->add_child_nocopy ((*i)->state (full)); } return *root; @@ -1400,8 +1041,8 @@ Panner::set_state (const XMLNode& node) } if ((prop = node.property (X_("link_direction"))) != 0) { - sscanf (prop->value().c_str(), "%d", &i); - set_link_direction ((LinkDirection) (i)); + LinkDirection ld; /* here to provide type information */ + set_link_direction (LinkDirection (string_2_enum (prop->value(), ld))); } nlist = node.children(); @@ -1412,10 +1053,10 @@ Panner::set_state (const XMLNode& node) float x, y; prop = (*niter)->property (X_("x")); - sscanf (prop->value().c_str(), "%f", &x); + sscanf (prop->value().c_str(), "%g", &x); prop = (*niter)->property (X_("y")); - sscanf (prop->value().c_str(), "%f", &y); + sscanf (prop->value().c_str(), "%g", &y); outputs.push_back (Output (x, y)); } @@ -1462,7 +1103,7 @@ Panner::set_state (const XMLNode& node) } } - /* don't try to load automation if it wasn't marked as existing */ + /* don't try to do old-school automation loading if it wasn't marked as existing */ if ((prop = node.property (X_("automation")))) { @@ -1481,7 +1122,7 @@ bool Panner::touching () const { for (vector::const_iterator i = begin(); i != end(); ++i) { - if ((*i)->automation().touching ()) { + if ((*i)->pan_control()->list()->touching ()) { return true; } } @@ -1489,14 +1130,6 @@ Panner::touching () const return false; } -void -Panner::reset_midi_control (MIDI::Port* port, bool on) -{ - for (vector::const_iterator i = begin(); i != end(); ++i) { - (*i)->reset_midi_control (port, on); - } -} - void Panner::set_position (float xpos, StreamPanner& orig) { @@ -1641,40 +1274,210 @@ Panner::set_position (float xpos, float ypos, float zpos, StreamPanner& orig) } void -Panner::send_all_midi_feedback () +Panner::distribute_no_automation (BufferSet& inbufs, BufferSet& outbufs, nframes_t nframes, nframes_t offset, gain_t gain_coeff) { - if (_session.get_midi_feedback()) { - float xpos; - - // do feedback for all panners - for (vector::iterator i = begin(); i != end(); ++i) { - (*i)->get_effective_position (xpos); + if (outbufs.count().n_audio() == 0) { + // Don't want to lose audio... + assert(inbufs.count().n_audio() == 0); + return; + } + + // We shouldn't be called in the first place... + assert(!bypassed()); + assert(!empty()); + + + if (outbufs.count().n_audio() == 1) { + + AudioBuffer& dst = outbufs.get_audio(0); + + if (gain_coeff == 0.0f) { + + /* only one output, and gain was zero, so make it silent */ + + dst.silence(offset); + + } else if (gain_coeff == 1.0f){ + + /* mix all buffers into the output */ + + // copy the first + dst.read_from(inbufs.get_audio(0), nframes, offset); + + // accumulate starting with the second + BufferSet::audio_iterator i = inbufs.audio_begin(); + for (++i; i != inbufs.audio_end(); ++i) { + dst.accumulate_from(*i, nframes, offset); + } + + } else { + + /* mix all buffers into the output, scaling them all by the gain */ + + // copy the first + dst.read_from(inbufs.get_audio(0), nframes, offset); + + // accumulate (with gain) starting with the second + BufferSet::audio_iterator i = inbufs.audio_begin(); + for (++i; i != inbufs.audio_end(); ++i) { + dst.accumulate_with_gain_from(*i, nframes, offset, gain_coeff); + } - (*i)->midi_control().send_feedback (xpos); } - + + return; + } + + /* the terrible silence ... */ + for (BufferSet::audio_iterator i = outbufs.audio_begin(); i != outbufs.audio_end(); ++i) { + i->silence(nframes, offset); + } + + BufferSet::audio_iterator i = inbufs.audio_begin(); + + for (iterator pan = begin(); pan != end() && i != inbufs.audio_end(); ++pan, ++i) { + (*pan)->distribute (*i, outbufs, gain_coeff, nframes); } } -MIDI::byte* -Panner::write_midi_feedback (MIDI::byte* buf, int32_t& bufsize) +void +Panner::distribute (BufferSet& inbufs, BufferSet& outbufs, nframes_t start_frame, nframes_t end_frame, nframes_t nframes, nframes_t offset) +{ + if (outbufs.count().n_audio() == 0) { + // Failing to deliver audio we were asked to deliver is a bug + assert(inbufs.count().n_audio() == 0); + return; + } + + // We shouldn't be called in the first place... + assert(!bypassed()); + assert(!empty()); + + // If we shouldn't play automation defer to distribute_no_automation + if ( !( automation_state() & Play || + ((automation_state() & Touch) && !touching()) ) ) { + + // Speed quietning + gain_t gain_coeff = 1.0; + if (fabsf(_session.transport_speed()) > 1.5f) { + gain_coeff = speed_quietning; + } + + distribute_no_automation(inbufs, outbufs, nframes, offset, gain_coeff); + return; + } + + // Otherwise.. let the automation flow, baby + + if (outbufs.count().n_audio() == 1) { + + AudioBuffer& dst = outbufs.get_audio(0); + + // FIXME: apply gain automation? + + // copy the first + dst.read_from(inbufs.get_audio(0), nframes, offset); + + // accumulate starting with the second + BufferSet::audio_iterator i = inbufs.audio_begin(); + for (++i; i != inbufs.audio_end(); ++i) { + dst.accumulate_from(*i, nframes, offset); + } + + return; + } + + // More than 1 output, we should have 1 panner for each input + assert(size() == inbufs.count().n_audio()); + + /* the terrible silence ... */ + for (BufferSet::audio_iterator i = outbufs.audio_begin(); i != outbufs.audio_end(); ++i) { + i->silence(nframes, offset); + } + + BufferSet::audio_iterator i = inbufs.audio_begin(); + for (iterator pan = begin(); pan != end(); ++pan, ++i) { + (*pan)->distribute_automated (*i, outbufs, start_frame, end_frame, nframes, _session.pan_automation_buffer()); + } +} + +/* old school automation handling */ + +void +Panner::set_name (string str) { - AutoState astate = automation_state (); + automation_path = _session.automation_dir(); + automation_path += _session.snap_name(); + automation_path += "-pan-"; + automation_path += legalize_for_path (str); + automation_path += ".automation"; +} - if (_session.get_midi_feedback() && - (astate == Play || (astate == Touch && !touching()))) { - - float xpos; - - // do feedback for all panners - for (vector::iterator i = begin(); i != end(); ++i) { - (*i)->get_effective_position (xpos); +int +Panner::load () +{ + char line[128]; + uint32_t linecnt = 0; + float version; + iterator sp; + LocaleGuard lg (X_("POSIX")); + + if (automation_path.length() == 0) { + return 0; + } + + if (access (automation_path.c_str(), F_OK)) { + return 0; + } + + ifstream in (automation_path.c_str()); + + if (!in) { + error << string_compose (_("cannot open pan automation file %1 (%2)"), + automation_path, strerror (errno)) + << endmsg; + return -1; + } + + sp = begin(); + + while (in.getline (line, sizeof(line), '\n')) { + + if (++linecnt == 1) { + if (memcmp (line, X_("version"), 7) == 0) { + if (sscanf (line, "version %f", &version) != 1) { + error << string_compose(_("badly formed version number in pan automation event file \"%1\""), automation_path) << endmsg; + return -1; + } + } else { + error << string_compose(_("no version information in pan automation event file \"%1\" (first line = %2)"), + automation_path, line) << endmsg; + return -1; + } + + continue; + } + + if (strlen (line) == 0 || line[0] == '#') { + continue; + } + + if (strcmp (line, "begin") == 0) { + + if (sp == end()) { + error << string_compose (_("too many panner states found in pan automation file %1"), + automation_path) + << endmsg; + return -1; + } + + if ((*sp)->load (in, automation_path, linecnt)) { + return -1; + } - buf = (*i)->midi_control().write_feedback (buf, bufsize, xpos); + ++sp; } - } - return buf; + return 0; } -