2 Copyright (C) 2000 Paul Davis
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.
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.
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.
23 #include <cstdio> /* for sprintf */
27 #include <sigc++/bind.h>
29 #include <pbd/stl_delete.h>
30 #include <pbd/xml++.h>
31 #include <pbd/enumwriter.h>
33 #include <ardour/location.h>
34 #include <ardour/session.h>
35 #include <ardour/audiofilesource.h>
42 using namespace ARDOUR;
46 Location::Location (const Location& other)
47 : _name (other._name),
48 _start (other._start),
52 /* start and end flags can never be copied, because there can only ever be one of each */
54 _flags = Flags (_flags & ~IsStart);
55 _flags = Flags (_flags & ~IsEnd);
58 Location::Location (const XMLNode& node)
60 if (set_state (node)) {
61 throw failed_constructor ();
66 Location::operator= (const Location& other)
73 _start = other._start;
75 _flags = other._flags;
77 /* "changed" not emitted on purpose */
83 Location::set_start (nframes_t s)
91 start_changed(this); /* EMIT SIGNAL */
95 Session::StartTimeChanged (); /* EMIT SIGNAL */
96 AudioFileSource::set_header_position_offset ( s );
100 Session::EndTimeChanged (); /* EMIT SIGNAL */
106 if (((is_auto_punch() || is_auto_loop()) && s >= _end) || s > _end) {
112 start_changed(this); /* EMIT SIGNAL */
119 Location::set_end (nframes_t e)
125 end_changed(this); /* EMIT SIGNAL */
130 if (((is_auto_punch() || is_auto_loop()) && e <= _start) || e < _start) {
136 end_changed(this); /* EMIT SIGNAL */
142 Location::set (nframes_t start, nframes_t end)
144 if (is_mark() && start != end) {
146 } else if (((is_auto_punch() || is_auto_loop()) && start >= end) || (start > end)) {
150 if (_start != start) {
152 start_changed(this); /* EMIT SIGNAL */
157 end_changed(this); /* EMIT SIGNAL */
163 Location::set_hidden (bool yn, void *src)
165 if (set_flag_internal (yn, IsHidden)) {
166 FlagsChanged (this, src); /* EMIT SIGNAL */
171 Location::set_cd (bool yn, void *src)
173 if (set_flag_internal (yn, IsCDMarker)) {
174 FlagsChanged (this, src); /* EMIT SIGNAL */
179 Location::set_is_end (bool yn, void *src)
181 if (set_flag_internal (yn, IsEnd)) {
182 FlagsChanged (this, src); /* EMIT SIGNAL */
187 Location::set_is_start (bool yn, void *src)
189 if (set_flag_internal (yn, IsStart)) {
190 FlagsChanged (this, src); /* EMIT SIGNAL */
195 Location::set_auto_punch (bool yn, void *src)
197 if (is_mark() || _start == _end) {
201 if (set_flag_internal (yn, IsAutoPunch)) {
202 FlagsChanged (this, src); /* EMIT SIGNAL */
207 Location::set_auto_loop (bool yn, void *src)
209 if (is_mark() || _start == _end) {
213 if (set_flag_internal (yn, IsAutoLoop)) {
214 FlagsChanged (this, src); /* EMIT SIGNAL */
219 Location::set_flag_internal (bool yn, Flags flag)
222 if (!(_flags & flag)) {
223 _flags = Flags (_flags | flag);
228 _flags = Flags (_flags & ~flag);
236 Location::set_mark (bool yn)
238 /* This function is private, and so does not emit signals */
240 if (_start != _end) {
244 set_flag_internal (yn, IsMark);
249 Location::cd_info_node(const string & name, const string & value)
251 XMLNode* root = new XMLNode("CD-Info");
253 root->add_property("name", name);
254 root->add_property("value", value);
261 Location::get_state (void)
263 XMLNode *node = new XMLNode ("Location");
266 typedef map<string, string>::const_iterator CI;
268 for(CI m = cd_info.begin(); m != cd_info.end(); ++m){
269 node->add_child_nocopy(cd_info_node(m->first, m->second));
272 id().print (buf, sizeof (buf));
273 node->add_property("id", buf);
274 node->add_property ("name", name());
275 snprintf (buf, sizeof (buf), "%u", start());
276 node->add_property ("start", buf);
277 snprintf (buf, sizeof (buf), "%u", end());
278 node->add_property ("end", buf);
279 node->add_property ("flags", enum_2_string (_flags));
285 Location::set_state (const XMLNode& node)
287 const XMLProperty *prop;
289 XMLNodeList cd_list = node.children();
290 XMLNodeConstIterator cd_iter;
296 if (node.name() != "Location") {
297 error << _("incorrect XML node passed to Location::set_state") << endmsg;
301 if ((prop = node.property ("id")) == 0) {
302 warning << _("XML node for Location has no ID information") << endmsg;
304 _id = prop->value ();
307 if ((prop = node.property ("name")) == 0) {
308 error << _("XML node for Location has no name information") << endmsg;
312 set_name (prop->value());
314 if ((prop = node.property ("start")) == 0) {
315 error << _("XML node for Location has no start information") << endmsg;
319 /* can't use set_start() here, because _end
320 may make the value of _start illegal.
323 _start = atoi (prop->value().c_str());
325 if ((prop = node.property ("end")) == 0) {
326 error << _("XML node for Location has no end information") << endmsg;
330 _end = atoi (prop->value().c_str());
332 if ((prop = node.property ("flags")) == 0) {
333 error << _("XML node for Location has no flags information") << endmsg;
337 _flags = Flags (string_2_enum (prop->value(), _flags));
339 for (cd_iter = cd_list.begin(); cd_iter != cd_list.end(); ++cd_iter) {
343 if (cd_node->name() != "CD-Info") {
347 if ((prop = cd_node->property ("name")) != 0) {
348 cd_name = prop->value();
350 throw failed_constructor ();
353 if ((prop = cd_node->property ("value")) != 0) {
354 cd_value = prop->value();
356 throw failed_constructor ();
360 cd_info[cd_name] = cd_value;
364 changed(this); /* EMIT SIGNAL */
369 /*---------------------------------------------------------------------- */
371 Locations::Locations ()
374 current_location = 0;
377 Locations::~Locations ()
379 for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
380 LocationList::iterator tmp = i;
388 Locations::set_current (Location *loc, bool want_lock)
394 Glib::Mutex::Lock lm (lock);
395 ret = set_current_unlocked (loc);
397 ret = set_current_unlocked (loc);
401 current_changed (current_location); /* EMIT SIGNAL */
407 Locations::next_available_name(string& result,string base)
409 LocationList::iterator i;
415 bool available[SUFFIX_MAX+1];
418 for (int k=1; k<SUFFIX_MAX; k++) {
422 for (i = locations.begin(); i != locations.end(); ++i) {
424 temp = location->name();
425 if (l && !temp.find(base,0)) {
426 suffix = atoi(temp.substr(l,3));
427 if (suffix) available[suffix] = false;
430 for (int k=1; k<=SUFFIX_MAX; k++) {
432 snprintf (buf, 31, "%d", k);
441 Locations::set_current_unlocked (Location *loc)
443 if (find (locations.begin(), locations.end(), loc) == locations.end()) {
444 error << _("Locations: attempt to use unknown location as selected location") << endmsg;
448 current_location = loc;
456 Glib::Mutex::Lock lm (lock);
458 for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
460 LocationList::iterator tmp = i;
463 if (!(*i)->is_end() && !(*i)->is_start()) {
470 current_location = 0;
473 changed (); /* EMIT SIGNAL */
474 current_changed (0); /* EMIT SIGNAL */
478 Locations::clear_markers ()
481 Glib::Mutex::Lock lm (lock);
482 LocationList::iterator tmp;
484 for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
488 if ((*i)->is_mark() && !(*i)->is_end() && !(*i)->is_start()) {
496 changed (); /* EMIT SIGNAL */
500 Locations::clear_ranges ()
503 Glib::Mutex::Lock lm (lock);
504 LocationList::iterator tmp;
506 for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
511 if (!(*i)->is_mark()) {
519 current_location = 0;
522 changed (); /* EMIT SIGNAL */
523 current_changed (0); /* EMIT SIGNAL */
527 Locations::add (Location *loc, bool make_current)
530 Glib::Mutex::Lock lm (lock);
531 locations.push_back (loc);
534 current_location = loc;
538 added (loc); /* EMIT SIGNAL */
541 current_changed (current_location); /* EMIT SIGNAL */
546 Locations::remove (Location *loc)
549 bool was_removed = false;
550 bool was_current = false;
551 LocationList::iterator i;
553 if (loc->is_end() || loc->is_start()) {
558 Glib::Mutex::Lock lm (lock);
560 for (i = locations.begin(); i != locations.end(); ++i) {
564 if (current_location == loc) {
565 current_location = 0;
575 removed (loc); /* EMIT SIGNAL */
578 current_changed (0); /* EMIT SIGNAL */
581 changed (); /* EMIT_SIGNAL */
586 Locations::location_changed (Location* loc)
588 changed (); /* EMIT SIGNAL */
592 Locations::get_state ()
594 XMLNode *node = new XMLNode ("Locations");
595 LocationList::iterator iter;
596 Glib::Mutex::Lock lm (lock);
598 for (iter = locations.begin(); iter != locations.end(); ++iter) {
599 node->add_child_nocopy ((*iter)->get_state ());
606 Locations::set_state (const XMLNode& node)
609 XMLNodeConstIterator niter;
611 if (node.name() != "Locations") {
612 error << _("incorrect XML mode passed to Locations::set_state") << endmsg;
616 nlist = node.children();
619 current_location = 0;
622 Glib::Mutex::Lock lm (lock);
624 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
628 Location *loc = new Location (**niter);
629 locations.push_back (loc);
632 catch (failed_constructor& err) {
633 error << _("could not load location from session file - ignored") << endmsg;
637 if (locations.size()) {
639 current_location = locations.front();
641 current_location = 0;
645 changed (); /* EMIT SIGNAL */
650 struct LocationStartEarlierComparison
652 bool operator() (Location *a, Location *b) {
653 return a->start() < b->start();
657 struct LocationStartLaterComparison
659 bool operator() (Location *a, Location *b) {
660 return a->start() > b->start();
665 Locations::first_location_before (nframes_t frame)
670 Glib::Mutex::Lock lm (lock);
674 LocationStartLaterComparison cmp;
677 /* locs is now sorted latest..earliest */
679 for (LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
680 if (!(*i)->is_hidden() && (*i)->start() < frame) {
689 Locations::first_location_after (nframes_t frame)
694 Glib::Mutex::Lock lm (lock);
698 LocationStartEarlierComparison cmp;
701 /* locs is now sorted earliest..latest */
703 for (LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
704 if (!(*i)->is_hidden() && (*i)->start() > frame) {
713 Locations::first_mark_before (nframes_t frame)
718 Glib::Mutex::Lock lm (lock);
722 LocationStartLaterComparison cmp;
725 /* locs is now sorted latest..earliest */
727 for (LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
728 if (!(*i)->is_hidden()) {
729 if ((*i)->is_mark()) {
730 /* MARK: start == end */
731 if ((*i)->start() < frame) {
732 return (*i)->start();
735 /* RANGE: start != end, compare start and end */
736 if ((*i)->end() < frame) {
739 if ((*i)->start () < frame) {
740 return (*i)->start();
750 Locations::first_mark_after (nframes_t frame)
755 Glib::Mutex::Lock lm (lock);
759 LocationStartEarlierComparison cmp;
762 /* locs is now sorted earliest..latest */
764 for (LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
765 if (!(*i)->is_hidden()) {
766 if ((*i)->is_mark()) {
767 /* MARK, start == end so just compare start */
768 if ((*i)->start() > frame) {
769 return (*i)->start();
772 /* RANGE, start != end, compare start and end */
773 if ((*i)->start() > frame ) {
774 return (*i)->start ();
776 if ((*i)->end() > frame) {
787 Locations::end_location () const
789 for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
790 if ((*i)->is_end()) {
791 return const_cast<Location*> (*i);
798 Locations::start_location () const
800 for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
801 if ((*i)->is_start()) {
802 return const_cast<Location*> (*i);
809 Locations::auto_loop_location () const
811 for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
812 if ((*i)->is_auto_loop()) {
813 return const_cast<Location*> (*i);
820 Locations::auto_punch_location () const
822 for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
823 if ((*i)->is_auto_punch()) {
824 return const_cast<Location*> (*i);
831 Locations::num_range_markers () const
834 Glib::Mutex::Lock lm (lock);
835 for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
836 if ((*i)->is_range_marker()) {
844 Locations::get_location_by_id(PBD::ID id)
846 LocationList::iterator it;
847 for (it = locations.begin(); it != locations.end(); it++)
848 if (id == (*it)->id())