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.
22 #include <cstdio> /* for sprintf */
26 #include <sigc++/bind.h>
28 #include "pbd/stl_delete.h"
29 #include "pbd/xml++.h"
30 #include "pbd/enumwriter.h"
32 #include "ardour/location.h"
33 #include "ardour/session.h"
34 #include "ardour/audiofilesource.h"
41 using namespace ARDOUR;
45 Location::Location (const Location& other)
46 : StatefulDestructible(),
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);
57 /* copy is not locked even if original was */
62 Location::Location (const XMLNode& node)
64 if (set_state (node)) {
65 throw failed_constructor ();
70 Location::operator= (const Location& other)
77 _start = other._start;
79 _flags = other._flags;
81 /* copy is not locked even if original was */
85 /* "changed" not emitted on purpose */
91 Location::set_start (nframes64_t s)
103 start_changed(this); /* EMIT SIGNAL */
104 end_changed(this); /* EMIT SIGNAL */
108 Session::StartTimeChanged (); /* EMIT SIGNAL */
109 AudioFileSource::set_header_position_offset ( s );
113 Session::EndTimeChanged (); /* EMIT SIGNAL */
119 if (((is_auto_punch() || is_auto_loop()) && s >= _end) || s > _end) {
125 start_changed(this); /* EMIT SIGNAL */
132 Location::set_end (nframes64_t e)
142 start_changed(this); /* EMIT SIGNAL */
143 end_changed(this); /* EMIT SIGNAL */
146 Session::StartTimeChanged (); /* EMIT SIGNAL */
150 Session::EndTimeChanged (); /* EMIT SIGNAL */
157 if (((is_auto_punch() || is_auto_loop()) && e <= _start) || e < _start) {
163 end_changed(this); /* EMIT SIGNAL */
169 Location::set (nframes64_t start, nframes64_t end)
175 if (is_mark() && start != end) {
177 } else if (((is_auto_punch() || is_auto_loop()) && start >= end) || (start > end)) {
181 if (_start != start) {
183 start_changed(this); /* EMIT SIGNAL */
188 end_changed(this); /* EMIT SIGNAL */
194 Location::move_to (nframes64_t pos)
202 _end = _start + length();
204 changed (this); /* EMIT SIGNAL */
211 Location::set_hidden (bool yn, void *src)
213 if (set_flag_internal (yn, IsHidden)) {
214 FlagsChanged (this, src); /* EMIT SIGNAL */
219 Location::set_cd (bool yn, void *src)
221 // XXX this really needs to be session start
222 // but its not available here - leave to GUI
225 error << _("You cannot put a CD marker at this position") << endmsg;
229 if (set_flag_internal (yn, IsCDMarker)) {
230 FlagsChanged (this, src); /* EMIT SIGNAL */
235 Location::set_is_end (bool yn, void *src)
237 if (set_flag_internal (yn, IsEnd)) {
238 FlagsChanged (this, src); /* EMIT SIGNAL */
243 Location::set_is_start (bool yn, void *src)
245 if (set_flag_internal (yn, IsStart)) {
246 FlagsChanged (this, src); /* EMIT SIGNAL */
251 Location::set_is_range_marker (bool yn, void *src)
253 if (set_flag_internal (yn, IsRangeMarker)) {
254 FlagsChanged (this, src); /* EMIT SIGNAL */
259 Location::set_auto_punch (bool yn, void *src)
261 if (is_mark() || _start == _end) {
265 if (set_flag_internal (yn, IsAutoPunch)) {
266 FlagsChanged (this, src); /* EMIT SIGNAL */
271 Location::set_auto_loop (bool yn, void *src)
273 if (is_mark() || _start == _end) {
277 if (set_flag_internal (yn, IsAutoLoop)) {
278 FlagsChanged (this, src); /* EMIT SIGNAL */
283 Location::set_flag_internal (bool yn, Flags flag)
286 if (!(_flags & flag)) {
287 _flags = Flags (_flags | flag);
292 _flags = Flags (_flags & ~flag);
300 Location::set_mark (bool yn)
302 /* This function is private, and so does not emit signals */
304 if (_start != _end) {
308 set_flag_internal (yn, IsMark);
313 Location::cd_info_node(const string & name, const string & value)
315 XMLNode* root = new XMLNode("CD-Info");
317 root->add_property("name", name);
318 root->add_property("value", value);
325 Location::get_state (void)
327 XMLNode *node = new XMLNode ("Location");
330 typedef map<string, string>::const_iterator CI;
332 for(CI m = cd_info.begin(); m != cd_info.end(); ++m){
333 node->add_child_nocopy(cd_info_node(m->first, m->second));
336 id().print (buf, sizeof (buf));
337 node->add_property("id", buf);
338 node->add_property ("name", name());
339 snprintf (buf, sizeof (buf), "%" PRId64, start());
340 node->add_property ("start", buf);
341 snprintf (buf, sizeof (buf), "%" PRId64, end());
342 node->add_property ("end", buf);
343 node->add_property ("flags", enum_2_string (_flags));
344 node->add_property ("locked", (_locked ? "yes" : "no"));
350 Location::set_state (const XMLNode& node)
352 const XMLProperty *prop;
354 XMLNodeList cd_list = node.children();
355 XMLNodeConstIterator cd_iter;
361 if (node.name() != "Location") {
362 error << _("incorrect XML node passed to Location::set_state") << endmsg;
366 if ((prop = node.property ("id")) == 0) {
367 warning << _("XML node for Location has no ID information") << endmsg;
369 _id = prop->value ();
372 if ((prop = node.property ("name")) == 0) {
373 error << _("XML node for Location has no name information") << endmsg;
377 set_name (prop->value());
379 if ((prop = node.property ("start")) == 0) {
380 error << _("XML node for Location has no start information") << endmsg;
384 /* can't use set_start() here, because _end
385 may make the value of _start illegal.
388 sscanf (prop->value().c_str(), "%" PRId64, &_start);
390 if ((prop = node.property ("end")) == 0) {
391 error << _("XML node for Location has no end information") << endmsg;
395 sscanf (prop->value().c_str(), "%" PRId64, &_end);
397 if ((prop = node.property ("flags")) == 0) {
398 error << _("XML node for Location has no flags information") << endmsg;
402 _flags = Flags (string_2_enum (prop->value(), _flags));
404 if ((prop = node.property ("locked")) != 0) {
405 _locked = string_is_affirmative (prop->value());
410 for (cd_iter = cd_list.begin(); cd_iter != cd_list.end(); ++cd_iter) {
414 if (cd_node->name() != "CD-Info") {
418 if ((prop = cd_node->property ("name")) != 0) {
419 cd_name = prop->value();
421 throw failed_constructor ();
424 if ((prop = cd_node->property ("value")) != 0) {
425 cd_value = prop->value();
427 throw failed_constructor ();
431 cd_info[cd_name] = cd_value;
435 changed(this); /* EMIT SIGNAL */
440 /*---------------------------------------------------------------------- */
442 Locations::Locations ()
445 current_location = 0;
448 Locations::~Locations ()
450 for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
451 LocationList::iterator tmp = i;
459 Locations::set_current (Location *loc, bool want_lock)
465 Glib::Mutex::Lock lm (lock);
466 ret = set_current_unlocked (loc);
468 ret = set_current_unlocked (loc);
472 current_changed (current_location); /* EMIT SIGNAL */
478 Locations::next_available_name(string& result,string base)
480 LocationList::iterator i;
486 bool available[SUFFIX_MAX+1];
489 for (int k=1; k<SUFFIX_MAX; k++) {
493 for (i = locations.begin(); i != locations.end(); ++i) {
495 temp = location->name();
496 if (l && !temp.find(base,0)) {
497 suffix = atoi(temp.substr(l,3).c_str());
498 if (suffix) available[suffix] = false;
501 for (int k=1; k<=SUFFIX_MAX; k++) {
503 snprintf (buf, 31, "%d", k);
512 Locations::set_current_unlocked (Location *loc)
514 if (find (locations.begin(), locations.end(), loc) == locations.end()) {
515 error << _("Locations: attempt to use unknown location as selected location") << endmsg;
519 current_location = loc;
527 Glib::Mutex::Lock lm (lock);
529 for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
531 LocationList::iterator tmp = i;
534 if (!(*i)->is_end() && !(*i)->is_start()) {
541 current_location = 0;
544 changed (); /* EMIT SIGNAL */
545 current_changed (0); /* EMIT SIGNAL */
549 Locations::clear_markers ()
552 Glib::Mutex::Lock lm (lock);
553 LocationList::iterator tmp;
555 for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
559 if ((*i)->is_mark() && !(*i)->is_end() && !(*i)->is_start()) {
567 changed (); /* EMIT SIGNAL */
571 Locations::clear_ranges ()
574 Glib::Mutex::Lock lm (lock);
575 LocationList::iterator tmp;
577 for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
582 if (!(*i)->is_mark()) {
590 current_location = 0;
593 changed (); /* EMIT SIGNAL */
594 current_changed (0); /* EMIT SIGNAL */
598 Locations::add (Location *loc, bool make_current)
601 Glib::Mutex::Lock lm (lock);
602 locations.push_back (loc);
605 current_location = loc;
609 added (loc); /* EMIT SIGNAL */
612 current_changed (current_location); /* EMIT SIGNAL */
617 Locations::remove (Location *loc)
620 bool was_removed = false;
621 bool was_current = false;
622 LocationList::iterator i;
624 if (loc->is_end() || loc->is_start()) {
629 Glib::Mutex::Lock lm (lock);
631 for (i = locations.begin(); i != locations.end(); ++i) {
635 if (current_location == loc) {
636 current_location = 0;
646 removed (loc); /* EMIT SIGNAL */
649 current_changed (0); /* EMIT SIGNAL */
652 changed (); /* EMIT_SIGNAL */
657 Locations::location_changed (Location* /*loc*/)
659 changed (); /* EMIT SIGNAL */
663 Locations::get_state ()
665 XMLNode *node = new XMLNode ("Locations");
666 LocationList::iterator iter;
667 Glib::Mutex::Lock lm (lock);
669 for (iter = locations.begin(); iter != locations.end(); ++iter) {
670 node->add_child_nocopy ((*iter)->get_state ());
677 Locations::set_state (const XMLNode& node)
680 XMLNodeConstIterator niter;
682 if (node.name() != "Locations") {
683 error << _("incorrect XML mode passed to Locations::set_state") << endmsg;
687 nlist = node.children();
690 current_location = 0;
693 Glib::Mutex::Lock lm (lock);
695 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
699 Location *loc = new Location (**niter);
700 locations.push_back (loc);
703 catch (failed_constructor& err) {
704 error << _("could not load location from session file - ignored") << endmsg;
708 if (locations.size()) {
710 current_location = locations.front();
712 current_location = 0;
716 changed (); /* EMIT SIGNAL */
721 struct LocationStartEarlierComparison
723 bool operator() (Location *a, Location *b) {
724 return a->start() < b->start();
728 struct LocationStartLaterComparison
730 bool operator() (Location *a, Location *b) {
731 return a->start() > b->start();
736 Locations::first_location_before (nframes64_t frame, bool include_special_ranges)
741 Glib::Mutex::Lock lm (lock);
745 LocationStartLaterComparison cmp;
748 /* locs is now sorted latest..earliest */
750 for (LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
751 if (!include_special_ranges && ((*i)->is_auto_loop() || (*i)->is_auto_punch())) {
754 if (!(*i)->is_hidden() && (*i)->start() < frame) {
763 Locations::first_location_after (nframes64_t frame, bool include_special_ranges)
768 Glib::Mutex::Lock lm (lock);
772 LocationStartEarlierComparison cmp;
775 /* locs is now sorted earliest..latest */
777 for (LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
778 if (!include_special_ranges && ((*i)->is_auto_loop() || (*i)->is_auto_punch())) {
781 if (!(*i)->is_hidden() && (*i)->start() > frame) {
790 Locations::first_mark_before (nframes64_t frame, bool include_special_ranges)
795 Glib::Mutex::Lock lm (lock);
799 LocationStartLaterComparison cmp;
802 /* locs is now sorted latest..earliest */
804 for (LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
805 if (!include_special_ranges && ((*i)->is_auto_loop() || (*i)->is_auto_punch())) {
808 if (!(*i)->is_hidden()) {
809 if ((*i)->is_mark()) {
810 /* MARK: start == end */
811 if ((*i)->start() < frame) {
812 return (*i)->start();
815 /* RANGE: start != end, compare start and end */
816 if ((*i)->end() < frame) {
819 if ((*i)->start () < frame) {
820 return (*i)->start();
830 Locations::first_mark_after (nframes64_t frame, bool include_special_ranges)
835 Glib::Mutex::Lock lm (lock);
839 LocationStartEarlierComparison cmp;
842 /* locs is now sorted earliest..latest */
844 for (LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
845 if (!include_special_ranges && ((*i)->is_auto_loop() || (*i)->is_auto_punch())) {
848 if (!(*i)->is_hidden()) {
849 if ((*i)->is_mark()) {
850 /* MARK, start == end so just compare start */
851 if ((*i)->start() > frame) {
852 return (*i)->start();
855 /* RANGE, start != end, compare start and end */
856 if ((*i)->start() > frame ) {
857 return (*i)->start ();
859 if ((*i)->end() > frame) {
870 Locations::end_location () const
872 for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
873 if ((*i)->is_end()) {
874 return const_cast<Location*> (*i);
881 Locations::start_location () const
883 for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
884 if ((*i)->is_start()) {
885 return const_cast<Location*> (*i);
892 Locations::auto_loop_location () const
894 for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
895 if ((*i)->is_auto_loop()) {
896 return const_cast<Location*> (*i);
903 Locations::auto_punch_location () const
905 for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
906 if ((*i)->is_auto_punch()) {
907 return const_cast<Location*> (*i);
914 Locations::num_range_markers () const
917 Glib::Mutex::Lock lm (lock);
918 for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
919 if ((*i)->is_range_marker()) {
927 Locations::get_location_by_id(PBD::ID id)
929 LocationList::iterator it;
930 for (it = locations.begin(); it != locations.end(); it++)
931 if (id == (*it)->id())
938 Locations::find_all_between (nframes64_t start, nframes64_t end, LocationList& ll, Location::Flags flags)
940 Glib::Mutex::Lock lm (lock);
942 for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
943 if ((flags == 0 || (*i)->matches (flags)) &&
944 ((*i)->start() >= start && (*i)->end() < end)) {