somewhat working VBAP panning (no interpolation, and some outputs don't get output)
[ardour.git] / libs / ardour / speakers.cc
1 /*
2     Copyright (C) 2010 Paul Davis
3
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.
8
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.
13
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.
17 */
18
19 #include "pbd/error.h"
20 #include "pbd/convert.h"
21 #include "pbd/locale_guard.h"
22
23 #include "ardour/speaker.h"
24 #include "ardour/speakers.h"
25
26 #include "i18n.h"
27
28 using namespace ARDOUR;
29 using namespace PBD;
30 using namespace std;
31
32 Speaker::Speaker (int i, const AngularVector& position)
33         : id (i)
34 {
35         move (position);
36 }
37
38 void
39 Speaker::move (const AngularVector& new_position)
40 {
41         _angles = new_position;
42         _angles.cartesian (_coords);
43 }
44
45 Speakers::Speakers ()
46 {
47 }
48
49 Speakers::Speakers (const Speakers& s)
50 {
51         _speakers = s._speakers;
52 }
53
54 Speakers::~Speakers ()
55 {
56 }
57
58 Speakers&
59 Speakers::operator= (const Speakers& s)
60 {
61         if (&s != this) {
62                 _speakers = s._speakers;
63         }
64         return *this;
65 }
66
67 void
68 Speakers::dump_speakers (ostream& o)
69 {
70         for (vector<Speaker>::iterator i = _speakers.begin(); i != _speakers.end(); ++i) {
71                 o << "Speaker " << (*i).id << " @ " 
72                   << (*i).coords().x << ", " << (*i).coords().y << ", " << (*i).coords().z
73                   << " azimuth " << (*i).angles().azi
74                   << " elevation " << (*i).angles().ele
75                   << " distance " << (*i).angles().length
76                   << endl;
77         }
78 }
79
80 void
81 Speakers::clear_speakers ()
82 {
83         _speakers.clear ();
84         update ();
85 }
86
87 int 
88 Speakers::add_speaker (const AngularVector& position)
89 {
90         int id = _speakers.size();
91
92         _speakers.push_back (Speaker (id, position));
93         update ();
94
95         Changed ();
96
97         return id;
98 }        
99
100 void
101 Speakers::remove_speaker (int id)
102 {
103         for (vector<Speaker>::iterator i = _speakers.begin(); i != _speakers.end(); ) {
104                 if ((*i).id == id) {
105                         i = _speakers.erase (i);
106                         update ();
107                         break;
108                 } 
109         }
110 }
111
112 void
113 Speakers::move_speaker (int id, const AngularVector& new_position)
114 {
115         for (vector<Speaker>::iterator i = _speakers.begin(); i != _speakers.end(); ++i) {
116                 if ((*i).id == id) {
117                         (*i).move (new_position);
118                         update ();
119                         break;
120                 }
121         }
122 }
123
124 void
125 Speakers::setup_default_speakers (uint32_t n)
126 {
127         /* default assignment of speaker position for n speakers */
128
129         assert (n>0);
130
131         switch (n) {
132         case 1:
133                 add_speaker (AngularVector (0.0, 0.0));
134                 break;
135
136         case 2:
137                 add_speaker (AngularVector (0.0, 0.0));
138                 add_speaker (AngularVector (180.0, 0,0));
139                 break;
140
141         case 3:
142                 /* top, bottom kind-of-left & bottom kind-of-right */
143                 add_speaker (AngularVector (90.0, 0.0));
144                 add_speaker (AngularVector (215.0, 0,0));
145                 add_speaker (AngularVector (335.0, 0,0));
146                 break;
147         case 4:
148                 /* clockwise from top left */
149                 add_speaker (AngularVector (135.0, 0.0));
150                 add_speaker (AngularVector (45.0, 0.0));
151                 add_speaker (AngularVector (335.0, 0.0));
152                 add_speaker (AngularVector (215.0, 0.0));
153                 break;
154
155         default: 
156         {
157                 double degree_step = 360.0 / n;
158                 double deg;
159                 uint32_t i;
160
161                 /* even number of speakers? make sure the top two are either side of "top".
162                    otherwise, just start at the "top" (90.0 degrees) and rotate around
163                 */
164
165                 if (n % 2) {
166                         deg = 90.0 - degree_step;
167                 } else {
168                         deg = 90.0;
169                 }
170                 for (i = 0; i < n; ++i, deg += degree_step) {
171                         add_speaker (AngularVector (deg, 0.0));
172                 }
173         }
174         }
175 }
176         
177 XMLNode&
178 Speakers::get_state ()
179 {
180         XMLNode* node = new XMLNode (X_("Speakers"));
181         char buf[32];
182         LocaleGuard lg (X_("POSIX"));
183
184         for (vector<Speaker>::const_iterator i = _speakers.begin(); i != _speakers.end(); ++i) {
185                 XMLNode* speaker = new XMLNode (X_("Speaker"));
186
187                 snprintf (buf, sizeof (buf), "%.12g", (*i).angles().azi);
188                 speaker->add_property (X_("azimuth"), buf);
189                 snprintf (buf, sizeof (buf), "%.12g", (*i).angles().ele);
190                 speaker->add_property (X_("elevation"), buf);
191                 snprintf (buf, sizeof (buf), "%.12g", (*i).angles().length);
192                 speaker->add_property (X_("distance"), buf);
193
194                 node->add_child_nocopy (*speaker);
195         }
196         
197         return *node;
198 }
199
200 int
201 Speakers::set_state (const XMLNode& node, int /*version*/)
202 {
203         XMLNodeConstIterator i;
204         const XMLProperty* prop;
205         double a, e, d;
206         LocaleGuard lg (X_("POSIX"));
207         int n = 0;
208
209         _speakers.clear ();
210
211         for (i = node.children().begin(); i != node.children().end(); ++i, ++n) {
212                 if ((*i)->name() == X_("Speaker")) {
213                         if ((prop = (*i)->property (X_("azimuth"))) == 0) {
214                                 warning << _("Speaker information is missing azimuth - speaker ignored") << endmsg;
215                                 continue;
216                         }
217                         a = atof (prop->value());
218
219                         if ((prop = (*i)->property (X_("elevation"))) == 0) {
220                                 warning << _("Speaker information is missing elevation - speaker ignored") << endmsg;
221                                 continue;
222                         }
223                         e = atof (prop->value());
224                                             
225                         if ((prop = (*i)->property (X_("distance"))) == 0) {
226                                 warning << _("Speaker information is missing distance - speaker ignored") << endmsg;
227                                 continue;
228                         }
229                         d = atof (prop->value());
230
231                         add_speaker (AngularVector (a, e, d));
232                 }
233         }
234
235         update ();
236         
237         return 0;
238 }