preparations for a working speaker editing dialog
[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         cerr << "Added speaker " << id << " at " << position.azi << " /= " << position.ele << endl;
93
94         _speakers.push_back (Speaker (id, position));
95         update ();
96
97         dump_speakers (cerr);
98         Changed ();
99
100         return id;
101 }        
102
103 void
104 Speakers::remove_speaker (int id)
105 {
106         for (vector<Speaker>::iterator i = _speakers.begin(); i != _speakers.end(); ) {
107                 if ((*i).id == id) {
108                         i = _speakers.erase (i);
109                         update ();
110                         break;
111                 } 
112         }
113 }
114
115 void
116 Speakers::move_speaker (int id, const AngularVector& new_position)
117 {
118         for (vector<Speaker>::iterator i = _speakers.begin(); i != _speakers.end(); ++i) {
119                 if ((*i).id == id) {
120                         (*i).move (new_position);
121                         update ();
122                         break;
123                 }
124         }
125 }
126
127 void
128 Speakers::setup_default_speakers (uint32_t n)
129 {
130         /* default assignment of speaker position for n speakers */
131
132         assert (n>0);
133
134         switch (n) {
135         case 1:
136                 add_speaker (AngularVector (0.0, 0.0));
137                 break;
138
139         case 2:
140                 add_speaker (AngularVector (0.0, 0.0));
141                 add_speaker (AngularVector (180.0, 0,0));
142                 break;
143
144         case 3:
145                 /* top, bottom kind-of-left & bottom kind-of-right */
146                 add_speaker (AngularVector (90.0, 0.0));
147                 add_speaker (AngularVector (215.0, 0,0));
148                 add_speaker (AngularVector (335.0, 0,0));
149                 break;
150         case 4:
151                 /* clockwise from top left */
152                 add_speaker (AngularVector (135.0, 0.0));
153                 add_speaker (AngularVector (45.0, 0.0));
154                 add_speaker (AngularVector (335.0, 0.0));
155                 add_speaker (AngularVector (215.0, 0.0));
156                 break;
157
158         default: 
159         {
160                 double degree_step = 360.0 / n;
161                 double deg;
162                 uint32_t i;
163
164                 /* even number of speakers? make sure the top two are either side of "top".
165                    otherwise, just start at the "top" (90.0 degrees) and rotate around
166                 */
167
168                 if (n % 2) {
169                         deg = 90.0 - degree_step;
170                 } else {
171                         deg = 90.0;
172                 }
173                 for (i = 0; i < n; ++i, deg += degree_step) {
174                         add_speaker (AngularVector (deg, 0.0));
175                 }
176         }
177         }
178 }
179         
180 XMLNode&
181 Speakers::get_state ()
182 {
183         XMLNode* node = new XMLNode (X_("Speakers"));
184         char buf[32];
185         LocaleGuard lg (X_("POSIX"));
186
187         for (vector<Speaker>::const_iterator i = _speakers.begin(); i != _speakers.end(); ++i) {
188                 XMLNode* speaker = new XMLNode (X_("Speaker"));
189
190                 snprintf (buf, sizeof (buf), "%.12g", (*i).angles().azi);
191                 speaker->add_property (X_("azimuth"), buf);
192                 snprintf (buf, sizeof (buf), "%.12g", (*i).angles().ele);
193                 speaker->add_property (X_("elevation"), buf);
194                 snprintf (buf, sizeof (buf), "%.12g", (*i).angles().length);
195                 speaker->add_property (X_("distance"), buf);
196
197                 node->add_child_nocopy (*speaker);
198         }
199         
200         return *node;
201 }
202
203 int
204 Speakers::set_state (const XMLNode& node, int /*version*/)
205 {
206         XMLNodeConstIterator i;
207         const XMLProperty* prop;
208         double a, e, d;
209         LocaleGuard lg (X_("POSIX"));
210         int n = 0;
211
212         _speakers.clear ();
213
214         for (i = node.children().begin(); i != node.children().end(); ++i, ++n) {
215                 if ((*i)->name() == X_("Speaker")) {
216                         if ((prop = (*i)->property (X_("azimuth"))) == 0) {
217                                 warning << _("Speaker information is missing azimuth - speaker ignored") << endmsg;
218                                 continue;
219                         }
220                         a = atof (prop->value());
221
222                         if ((prop = (*i)->property (X_("elevation"))) == 0) {
223                                 warning << _("Speaker information is missing elevation - speaker ignored") << endmsg;
224                                 continue;
225                         }
226                         e = atof (prop->value());
227                                             
228                         if ((prop = (*i)->property (X_("distance"))) == 0) {
229                                 warning << _("Speaker information is missing distance - speaker ignored") << endmsg;
230                                 continue;
231                         }
232                         d = atof (prop->value());
233
234                         add_speaker (AngularVector (a, e, d));
235                 }
236         }
237
238         update ();
239         
240         return 0;
241 }