+
+void
+Panner::distribute_no_automation (BufferSet& inbufs, BufferSet& outbufs, nframes_t nframes, gain_t gain_coeff)
+{
+ 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 (nframes);
+
+ } else if (gain_coeff == 1.0f){
+
+ /* mix all buffers into the output */
+
+ // copy the first
+ dst.read_from(inbufs.get_audio(0), nframes);
+
+ // accumulate starting with the second
+ if (inbufs.count().n_audio() > 0) {
+ BufferSet::audio_iterator i = inbufs.audio_begin();
+ for (++i; i != inbufs.audio_end(); ++i) {
+ dst.merge_from(*i, nframes);
+ }
+ }
+
+ } else {
+
+ /* mix all buffers into the output, scaling them all by the gain */
+
+ // copy the first
+ dst.read_from(inbufs.get_audio(0), nframes);
+
+ // accumulate (with gain) starting with the second
+ if (inbufs.count().n_audio() > 0) {
+ BufferSet::audio_iterator i = inbufs.audio_begin();
+ for (++i; i != inbufs.audio_end(); ++i) {
+ dst.accumulate_with_gain_from(*i, nframes, gain_coeff);
+ }
+ }
+
+ }
+
+ return;
+ }
+
+ /* the terrible silence ... */
+ for (BufferSet::audio_iterator i = outbufs.audio_begin(); i != outbufs.audio_end(); ++i) {
+ i->silence(nframes);
+ }
+
+ BufferSet::audio_iterator i = inbufs.audio_begin();
+
+ for (vector<StreamPanner*>::iterator pan = _streampanners.begin(); pan != _streampanners.end() && i != inbufs.audio_end(); ++pan, ++i) {
+ (*pan)->distribute (*i, outbufs, gain_coeff, nframes);
+ }
+}
+
+void
+Panner::run (BufferSet& inbufs, BufferSet& outbufs, framepos_t start_frame, framepos_t end_frame, nframes_t nframes)
+{
+ 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 && Config->get_quieten_at_speed ()) {
+ gain_coeff = speed_quietning;
+ }
+
+ distribute_no_automation (inbufs, outbufs, nframes, 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);
+
+ // accumulate starting with the second
+ BufferSet::audio_iterator i = inbufs.audio_begin();
+ for (++i; i != inbufs.audio_end(); ++i) {
+ dst.merge_from(*i, nframes);
+ }
+
+ return;
+ }
+
+ // More than 1 output, we should have 1 panner for each input
+ //assert(_streampanners.size() == inbufs.count().n_audio());
+
+ /* the terrible silence ... */
+ for (BufferSet::audio_iterator i = outbufs.audio_begin(); i != outbufs.audio_end(); ++i) {
+ i->silence(nframes);
+ }
+
+ BufferSet::audio_iterator i = inbufs.audio_begin();
+ for (vector<StreamPanner*>::iterator pan = _streampanners.begin(); pan != _streampanners.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)
+{
+ automation_path = Glib::build_filename(_session.automation_dir(),
+ _session.snap_name() + "-pan-" + legalize_for_path (str) + ".automation");
+}
+*/
+
+int
+Panner::load ()
+{
+ char line[128];
+ uint32_t linecnt = 0;
+ float version;
+ vector<StreamPanner*>::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 = _streampanners.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 == _streampanners.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;
+}
+
+void
+Panner::set_mono (bool yn)
+{
+ if (yn != _mono) {
+ _mono = yn;
+ StateChanged ();
+ }
+
+ for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
+ (*i)->set_mono (yn);
+ }
+}
+
+string
+Panner::value_as_string (double v)
+{
+ if (Panner::equivalent (v, 0.5)) {
+ return _("C");
+ } else if (equivalent (v, 0)) {
+ return _("L");
+ } else if (equivalent (v, 1)) {
+ return _("R");
+ } else if (v < 0.5) {
+ stringstream s;
+ s << fixed << setprecision (0) << _("L") << ((0.5 - v) * 200) << "%";
+ return s.str();
+ } else {
+ stringstream s;
+ s << fixed << setprecision (0) << _("R") << ((v -0.5) * 200) << "%";
+ return s.str ();
+ }
+
+ return "";
+}
+
+void
+Panner::setup_speakers (uint32_t nouts)
+{
+ switch (nouts) {
+ case 3:
+ outputs.push_back (Output (0.5, 0));
+ outputs.push_back (Output (0, 1.0));
+ outputs.push_back (Output (1.0, 1.0));
+ break;
+ case 4:
+ outputs.push_back (Output (0, 0));
+ outputs.push_back (Output (1.0, 0));
+ outputs.push_back (Output (1.0, 1.0));
+ outputs.push_back (Output (0, 1.0));
+ break;
+
+ case 5: //square+offcenter center
+ outputs.push_back (Output (0, 0));
+ outputs.push_back (Output (1.0, 0));
+ outputs.push_back (Output (1.0, 1.0));
+ outputs.push_back (Output (0, 1.0));
+ outputs.push_back (Output (0.5, 0.75));
+ break;
+
+ default:
+ /* XXX horrible placement. FIXME */
+ for (uint32_t n = 0; n < nouts; ++n) {
+ outputs.push_back (Output (0.1 * n, 0.1 * n));
+ }
+ }
+
+ VBAPSpeakers& speakers (_session.get_speakers());
+
+ speakers.clear_speakers ();
+
+ for (vector<Output>::iterator o = outputs.begin(); o != outputs.end(); ++o) {
+ double azimuth;
+ double elevation;
+
+ cart_to_azi_ele ((*o).x + 1.0, (*o).y + 1.0, (*o).z, azimuth, elevation);
+ speakers.add_speaker (azimuth, elevation);
+ }
+}