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 */
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
;
45 Location::Location (const Location
& other
)
46 : StatefulDestructible(),
48 _start (other
._start
),
52 /* copy is not locked even if original was */
57 Location::Location (const XMLNode
& node
)
59 if (set_state (node
, Stateful::loading_state_version
)) {
60 throw failed_constructor ();
65 Location::operator= (const Location
& other
)
72 _start
= other
._start
;
74 _flags
= other
._flags
;
76 /* copy is not locked even if original was */
80 /* "changed" not emitted on purpose */
85 /** Set start position.
87 * @param force true to force setting, even if the given new start is after the current end.
90 Location::set_start (nframes64_t s
, bool force
)
97 if (((is_auto_punch() || is_auto_loop()) && s
>= _end
) || (!is_mark() && s
> _end
)) {
106 start_changed (this); /* EMIT SIGNAL */
107 end_changed (this); /* EMIT SIGNAL */
114 start_changed (this); /* EMIT SIGNAL */
115 if (is_session_range ()) {
116 Session::StartTimeChanged (); /* EMIT SIGNAL */
117 AudioFileSource::set_header_position_offset (s
);
124 /** Set end position.
126 * @param force true to force setting, even if the given new start is after the current end.
129 Location::set_end (nframes64_t e
, bool force
)
136 if (((is_auto_punch() || is_auto_loop()) && e
<= _start
) || e
< _start
) {
145 start_changed (this); /* EMIT SIGNAL */
146 end_changed (this); /* EMIT SIGNAL */
153 end_changed(this); /* EMIT SIGNAL */
155 if (is_session_range()) {
156 Session::EndTimeChanged (); /* EMIT SIGNAL */
164 Location::set (nframes64_t start
, nframes64_t end
)
167 if (((is_auto_punch() || is_auto_loop()) && start
>= end
) || (!is_mark() && start
> end
)) {
171 /* now we know these values are ok, so force-set them */
172 int const s
= set_start (start
, true);
173 int const e
= set_end (end
, true);
175 return (s
== 0 && e
== 0) ? 0 : -1;
179 Location::move_to (nframes64_t pos
)
187 _end
= _start
+ length();
189 changed (this); /* EMIT SIGNAL */
196 Location::set_hidden (bool yn
, void *src
)
198 if (set_flag_internal (yn
, IsHidden
)) {
199 FlagsChanged (this, src
); /* EMIT SIGNAL */
204 Location::set_cd (bool yn
, void *src
)
206 // XXX this really needs to be session start
207 // but its not available here - leave to GUI
210 error
<< _("You cannot put a CD marker at this position") << endmsg
;
214 if (set_flag_internal (yn
, IsCDMarker
)) {
215 FlagsChanged (this, src
); /* EMIT SIGNAL */
220 Location::set_is_range_marker (bool yn
, void *src
)
222 if (set_flag_internal (yn
, IsRangeMarker
)) {
223 FlagsChanged (this, src
); /* EMIT SIGNAL */
228 Location::set_auto_punch (bool yn
, void *src
)
230 if (is_mark() || _start
== _end
) {
234 if (set_flag_internal (yn
, IsAutoPunch
)) {
235 FlagsChanged (this, src
); /* EMIT SIGNAL */
240 Location::set_auto_loop (bool yn
, void *src
)
242 if (is_mark() || _start
== _end
) {
246 if (set_flag_internal (yn
, IsAutoLoop
)) {
247 FlagsChanged (this, src
); /* EMIT SIGNAL */
252 Location::set_flag_internal (bool yn
, Flags flag
)
255 if (!(_flags
& flag
)) {
256 _flags
= Flags (_flags
| flag
);
261 _flags
= Flags (_flags
& ~flag
);
269 Location::set_mark (bool yn
)
271 /* This function is private, and so does not emit signals */
273 if (_start
!= _end
) {
277 set_flag_internal (yn
, IsMark
);
282 Location::cd_info_node(const string
& name
, const string
& value
)
284 XMLNode
* root
= new XMLNode("CD-Info");
286 root
->add_property("name", name
);
287 root
->add_property("value", value
);
294 Location::get_state (void)
296 XMLNode
*node
= new XMLNode ("Location");
299 typedef map
<string
, string
>::const_iterator CI
;
301 for(CI m
= cd_info
.begin(); m
!= cd_info
.end(); ++m
){
302 node
->add_child_nocopy(cd_info_node(m
->first
, m
->second
));
305 id().print (buf
, sizeof (buf
));
306 node
->add_property("id", buf
);
307 node
->add_property ("name", name());
308 snprintf (buf
, sizeof (buf
), "%" PRId64
, start());
309 node
->add_property ("start", buf
);
310 snprintf (buf
, sizeof (buf
), "%" PRId64
, end());
311 node
->add_property ("end", buf
);
312 node
->add_property ("flags", enum_2_string (_flags
));
313 node
->add_property ("locked", (_locked
? "yes" : "no"));
319 Location::set_state (const XMLNode
& node
, int /*version*/)
321 const XMLProperty
*prop
;
323 XMLNodeList cd_list
= node
.children();
324 XMLNodeConstIterator cd_iter
;
330 if (node
.name() != "Location") {
331 error
<< _("incorrect XML node passed to Location::set_state") << endmsg
;
335 if ((prop
= node
.property ("id")) == 0) {
336 warning
<< _("XML node for Location has no ID information") << endmsg
;
338 _id
= prop
->value ();
341 if ((prop
= node
.property ("name")) == 0) {
342 error
<< _("XML node for Location has no name information") << endmsg
;
346 set_name (prop
->value());
348 if ((prop
= node
.property ("start")) == 0) {
349 error
<< _("XML node for Location has no start information") << endmsg
;
353 /* can't use set_start() here, because _end
354 may make the value of _start illegal.
357 sscanf (prop
->value().c_str(), "%" PRId64
, &_start
);
359 if ((prop
= node
.property ("end")) == 0) {
360 error
<< _("XML node for Location has no end information") << endmsg
;
364 sscanf (prop
->value().c_str(), "%" PRId64
, &_end
);
366 if ((prop
= node
.property ("flags")) == 0) {
367 error
<< _("XML node for Location has no flags information") << endmsg
;
371 _flags
= Flags (string_2_enum (prop
->value(), _flags
));
373 if ((prop
= node
.property ("locked")) != 0) {
374 _locked
= string_is_affirmative (prop
->value());
379 for (cd_iter
= cd_list
.begin(); cd_iter
!= cd_list
.end(); ++cd_iter
) {
383 if (cd_node
->name() != "CD-Info") {
387 if ((prop
= cd_node
->property ("name")) != 0) {
388 cd_name
= prop
->value();
390 throw failed_constructor ();
393 if ((prop
= cd_node
->property ("value")) != 0) {
394 cd_value
= prop
->value();
396 throw failed_constructor ();
400 cd_info
[cd_name
] = cd_value
;
404 changed(this); /* EMIT SIGNAL */
409 /*---------------------------------------------------------------------- */
411 Locations::Locations ()
414 current_location
= 0;
417 Locations::~Locations ()
419 for (LocationList::iterator i
= locations
.begin(); i
!= locations
.end(); ) {
420 LocationList::iterator tmp
= i
;
428 Locations::set_current (Location
*loc
, bool want_lock
)
434 Glib::Mutex::Lock
lm (lock
);
435 ret
= set_current_unlocked (loc
);
437 ret
= set_current_unlocked (loc
);
441 current_changed (current_location
); /* EMIT SIGNAL */
447 Locations::next_available_name(string
& result
,string base
)
449 LocationList::iterator i
;
455 bool available
[SUFFIX_MAX
+1];
458 for (int k
=1; k
<SUFFIX_MAX
; k
++) {
462 for (i
= locations
.begin(); i
!= locations
.end(); ++i
) {
464 temp
= location
->name();
465 if (l
&& !temp
.find(base
,0)) {
466 suffix
= atoi(temp
.substr(l
,3).c_str());
467 if (suffix
) available
[suffix
] = false;
470 for (int k
=1; k
<=SUFFIX_MAX
; k
++) {
472 snprintf (buf
, 31, "%d", k
);
481 Locations::set_current_unlocked (Location
*loc
)
483 if (find (locations
.begin(), locations
.end(), loc
) == locations
.end()) {
484 error
<< _("Locations: attempt to use unknown location as selected location") << endmsg
;
488 current_location
= loc
;
496 Glib::Mutex::Lock
lm (lock
);
498 for (LocationList::iterator i
= locations
.begin(); i
!= locations
.end(); ) {
500 LocationList::iterator tmp
= i
;
503 if (!(*i
)->is_session_range()) {
510 current_location
= 0;
513 changed (OTHER
); /* EMIT SIGNAL */
514 current_changed (0); /* EMIT SIGNAL */
518 Locations::clear_markers ()
521 Glib::Mutex::Lock
lm (lock
);
522 LocationList::iterator tmp
;
524 for (LocationList::iterator i
= locations
.begin(); i
!= locations
.end(); ) {
528 if ((*i
)->is_mark() && !(*i
)->is_session_range()) {
536 changed (OTHER
); /* EMIT SIGNAL */
540 Locations::clear_ranges ()
543 Glib::Mutex::Lock
lm (lock
);
544 LocationList::iterator tmp
;
546 for (LocationList::iterator i
= locations
.begin(); i
!= locations
.end(); ) {
551 if (!(*i
)->is_mark()) {
559 current_location
= 0;
562 changed (OTHER
); /* EMIT SIGNAL */
563 current_changed (0); /* EMIT SIGNAL */
567 Locations::add (Location
*loc
, bool make_current
)
572 Glib::Mutex::Lock
lm (lock
);
573 locations
.push_back (loc
);
576 current_location
= loc
;
580 added (loc
); /* EMIT SIGNAL */
583 current_changed (current_location
); /* EMIT SIGNAL */
588 Locations::remove (Location
*loc
)
590 bool was_removed
= false;
591 bool was_current
= false;
592 LocationList::iterator i
;
594 if (loc
->is_session_range()) {
599 Glib::Mutex::Lock
lm (lock
);
601 for (i
= locations
.begin(); i
!= locations
.end(); ++i
) {
605 if (current_location
== loc
) {
606 current_location
= 0;
616 removed (loc
); /* EMIT SIGNAL */
619 current_changed (0); /* EMIT SIGNAL */
622 changed (REMOVAL
); /* EMIT_SIGNAL */
627 Locations::location_changed (Location
* /*loc*/)
629 changed (OTHER
); /* EMIT SIGNAL */
633 Locations::get_state ()
635 XMLNode
*node
= new XMLNode ("Locations");
636 LocationList::iterator iter
;
637 Glib::Mutex::Lock
lm (lock
);
639 for (iter
= locations
.begin(); iter
!= locations
.end(); ++iter
) {
640 node
->add_child_nocopy ((*iter
)->get_state ());
647 Locations::set_state (const XMLNode
& node
, int version
)
649 if (node
.name() != "Locations") {
650 error
<< _("incorrect XML mode passed to Locations::set_state") << endmsg
;
654 XMLNodeList nlist
= node
.children();
657 current_location
= 0;
659 Location
* session_range_location
= 0;
660 if (version
< 3000) {
661 session_range_location
= new Location (0, 0, _("session"), Location::IsSessionRange
);
662 locations
.push_back (session_range_location
);
666 Glib::Mutex::Lock
lm (lock
);
668 XMLNodeConstIterator niter
;
669 for (niter
= nlist
.begin(); niter
!= nlist
.end(); ++niter
) {
673 Location
*loc
= new Location (**niter
);
677 if (version
< 3000) {
678 /* look for old-style IsStart / IsEnd properties in this location;
679 if they are present, update the session_range_location accordingly
681 XMLProperty
const * prop
= (*niter
)->property ("flags");
683 string v
= prop
->value ();
685 string::size_type
const c
= v
.find_first_of (',');
686 string
const s
= v
.substr (0, c
);
687 if (s
== X_("IsStart")) {
688 session_range_location
->set_start (loc
->start());
690 } else if (s
== X_("IsEnd")) {
691 session_range_location
->set_end (loc
->start());
695 if (c
== string::npos
) {
699 v
= v
.substr (c
+ 1);
705 locations
.push_back (loc
);
709 catch (failed_constructor
& err
) {
710 error
<< _("could not load location from session file - ignored") << endmsg
;
714 if (locations
.size()) {
715 current_location
= locations
.front();
717 current_location
= 0;
721 changed (OTHER
); /* EMIT SIGNAL */
726 struct LocationStartEarlierComparison
728 bool operator() (Location
*a
, Location
*b
) {
729 return a
->start() < b
->start();
733 struct LocationStartLaterComparison
735 bool operator() (Location
*a
, Location
*b
) {
736 return a
->start() > b
->start();
741 Locations::first_location_before (nframes64_t frame
, bool include_special_ranges
)
746 Glib::Mutex::Lock
lm (lock
);
750 LocationStartLaterComparison cmp
;
753 /* locs is now sorted latest..earliest */
755 for (LocationList::iterator i
= locs
.begin(); i
!= locs
.end(); ++i
) {
756 if (!include_special_ranges
&& ((*i
)->is_auto_loop() || (*i
)->is_auto_punch())) {
759 if (!(*i
)->is_hidden() && (*i
)->start() < frame
) {
768 Locations::first_location_after (nframes64_t frame
, bool include_special_ranges
)
773 Glib::Mutex::Lock
lm (lock
);
777 LocationStartEarlierComparison cmp
;
780 /* locs is now sorted earliest..latest */
782 for (LocationList::iterator i
= locs
.begin(); i
!= locs
.end(); ++i
) {
783 if (!include_special_ranges
&& ((*i
)->is_auto_loop() || (*i
)->is_auto_punch())) {
786 if (!(*i
)->is_hidden() && (*i
)->start() > frame
) {
794 /** Look for the `marks' (either locations which are marks, or start/end points of range markers) either
796 * @param frame Frame to look for.
797 * @param before Filled in with the position of the last `mark' before `frame' (or max_frames if none exists)
798 * @param after Filled in with the position of the last `mark' after `frame' (or max_frames if none exists)
801 Locations::marks_either_side (nframes64_t
const frame
, nframes64_t
& before
, nframes64_t
& after
) const
803 before
= after
= max_frames
;
808 Glib::Mutex::Lock
lm (lock
);
812 std::list
<nframes64_t
> positions
;
814 for (LocationList::const_iterator i
= locs
.begin(); i
!= locs
.end(); ++i
) {
815 if (((*i
)->is_auto_loop() || (*i
)->is_auto_punch())) {
819 if (!(*i
)->is_hidden()) {
820 if ((*i
)->is_mark ()) {
821 positions
.push_back ((*i
)->start ());
823 positions
.push_back ((*i
)->start ());
824 positions
.push_back ((*i
)->end ());
829 if (positions
.empty ()) {
835 std::list
<nframes64_t
>::iterator i
= positions
.begin ();
836 while (i
!= positions
.end () && *i
< frame
) {
840 if (i
== positions
.end ()) {
841 /* run out of marks */
842 before
= positions
.back ();
848 if (i
== positions
.begin ()) {
858 Locations::session_range_location () const
860 for (LocationList::const_iterator i
= locations
.begin(); i
!= locations
.end(); ++i
) {
861 if ((*i
)->is_session_range()) {
862 return const_cast<Location
*> (*i
);
869 Locations::auto_loop_location () const
871 for (LocationList::const_iterator i
= locations
.begin(); i
!= locations
.end(); ++i
) {
872 if ((*i
)->is_auto_loop()) {
873 return const_cast<Location
*> (*i
);
880 Locations::auto_punch_location () const
882 for (LocationList::const_iterator i
= locations
.begin(); i
!= locations
.end(); ++i
) {
883 if ((*i
)->is_auto_punch()) {
884 return const_cast<Location
*> (*i
);
891 Locations::num_range_markers () const
894 Glib::Mutex::Lock
lm (lock
);
895 for (LocationList::const_iterator i
= locations
.begin(); i
!= locations
.end(); ++i
) {
896 if ((*i
)->is_range_marker()) {
904 Locations::get_location_by_id(PBD::ID id
)
906 LocationList::iterator it
;
907 for (it
= locations
.begin(); it
!= locations
.end(); ++it
)
908 if (id
== (*it
)->id())
915 Locations::find_all_between (nframes64_t start
, nframes64_t end
, LocationList
& ll
, Location::Flags flags
)
917 Glib::Mutex::Lock
lm (lock
);
919 for (LocationList::const_iterator i
= locations
.begin(); i
!= locations
.end(); ++i
) {
920 if ((flags
== 0 || (*i
)->matches (flags
)) &&
921 ((*i
)->start() >= start
&& (*i
)->end() < end
)) {