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 */
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"
35 #include "ardour/tempo.h"
42 using namespace ARDOUR
;
45 Location::Location (Session
& s
)
46 : SessionHandleRef (s
)
51 , _position_lock_style (AudioTime
)
57 Location::Location (Session
& s
, framepos_t sample_start
, framepos_t sample_end
, const std::string
&name
, Flags bits
)
58 : SessionHandleRef (s
)
60 , _start (sample_start
)
64 , _position_lock_style (AudioTime
)
66 recompute_bbt_from_frames ();
72 Location::Location (const Location
& other
)
73 : SessionHandleRef (other
._session
)
74 , StatefulDestructible()
76 , _start (other
._start
)
77 , _bbt_start (other
._bbt_start
)
79 , _bbt_end (other
._bbt_end
)
80 , _flags (other
._flags
)
81 , _position_lock_style (other
._position_lock_style
)
83 /* copy is not locked even if original was */
91 Location::Location (Session
& s
, const XMLNode
& node
)
92 : SessionHandleRef (s
)
93 , _position_lock_style (AudioTime
)
95 /* Note: _position_lock_style is initialised above in case set_state doesn't set it
96 (for 2.X session file compatibility).
99 if (set_state (node
, Stateful::loading_state_version
)) {
100 throw failed_constructor ();
103 assert (_start
>= 0);
108 Location::operator= (const Location
& other
)
110 if (this == &other
) {
115 _start
= other
._start
;
116 _bbt_start
= other
._bbt_start
;
118 _bbt_end
= other
._bbt_end
;
119 _flags
= other
._flags
;
120 _position_lock_style
= other
._position_lock_style
;
122 /* copy is not locked even if original was */
126 /* "changed" not emitted on purpose */
128 assert (_start
>= 0);
134 /** Set start position.
135 * @param s New start.
136 * @param force true to force setting, even if the given new start is after the current end.
137 * @param allow_bbt_recompute True to recompute BBT start time from the new given start time.
140 Location::set_start (framepos_t s
, bool force
, bool allow_bbt_recompute
)
147 if (((is_auto_punch() || is_auto_loop()) && s
>= _end
) || (!is_mark() && s
> _end
)) {
156 if (allow_bbt_recompute
) {
157 recompute_bbt_from_frames ();
160 start_changed (this); /* EMIT SIGNAL */
161 end_changed (this); /* EMIT SIGNAL */
164 assert (_start
>= 0);
172 framepos_t
const old
= _start
;
175 if (allow_bbt_recompute
) {
176 recompute_bbt_from_frames ();
178 start_changed (this); /* EMIT SIGNAL */
179 if (is_session_range ()) {
180 Session::StartTimeChanged (old
); /* EMIT SIGNAL */
181 AudioFileSource::set_header_position_offset (s
);
185 assert (_start
>= 0);
190 /** Set end position.
192 * @param force true to force setting, even if the given new start is after the current end.
193 * @param allow_bbt_recompute True to recompute BBT end time from the new given end time.
196 Location::set_end (framepos_t e
, bool force
, bool allow_bbt_recompute
)
203 if (((is_auto_punch() || is_auto_loop()) && e
<= _start
) || e
< _start
) {
212 if (allow_bbt_recompute
) {
213 recompute_bbt_from_frames ();
215 start_changed (this); /* EMIT SIGNAL */
216 end_changed (this); /* EMIT SIGNAL */
219 assert (_start
>= 0);
226 framepos_t
const old
= _end
;
229 if (allow_bbt_recompute
) {
230 recompute_bbt_from_frames ();
232 end_changed(this); /* EMIT SIGNAL */
234 if (is_session_range()) {
235 Session::EndTimeChanged (old
); /* EMIT SIGNAL */
245 Location::set (framepos_t start
, framepos_t end
, bool allow_bbt_recompute
)
248 if (((is_auto_punch() || is_auto_loop()) && start
>= end
) || (!is_mark() && start
> end
)) {
252 /* now we know these values are ok, so force-set them */
253 int const s
= set_start (start
, true, allow_bbt_recompute
);
254 int const e
= set_end (end
, true, allow_bbt_recompute
);
256 return (s
== 0 && e
== 0) ? 0 : -1;
260 Location::move_to (framepos_t pos
)
268 _end
= _start
+ length();
269 recompute_bbt_from_frames ();
271 changed (this); /* EMIT SIGNAL */
274 assert (_start
>= 0);
281 Location::set_hidden (bool yn
, void *src
)
283 if (set_flag_internal (yn
, IsHidden
)) {
284 FlagsChanged (this, src
); /* EMIT SIGNAL */
289 Location::set_cd (bool yn
, void *src
)
291 // XXX this really needs to be session start
292 // but its not available here - leave to GUI
295 error
<< _("You cannot put a CD marker at this position") << endmsg
;
299 if (set_flag_internal (yn
, IsCDMarker
)) {
300 FlagsChanged (this, src
); /* EMIT SIGNAL */
305 Location::set_is_range_marker (bool yn
, void *src
)
307 if (set_flag_internal (yn
, IsRangeMarker
)) {
308 FlagsChanged (this, src
); /* EMIT SIGNAL */
313 Location::set_auto_punch (bool yn
, void *src
)
315 if (is_mark() || _start
== _end
) {
319 if (set_flag_internal (yn
, IsAutoPunch
)) {
320 FlagsChanged (this, src
); /* EMIT SIGNAL */
325 Location::set_auto_loop (bool yn
, void *src
)
327 if (is_mark() || _start
== _end
) {
331 if (set_flag_internal (yn
, IsAutoLoop
)) {
332 FlagsChanged (this, src
); /* EMIT SIGNAL */
337 Location::set_flag_internal (bool yn
, Flags flag
)
340 if (!(_flags
& flag
)) {
341 _flags
= Flags (_flags
| flag
);
346 _flags
= Flags (_flags
& ~flag
);
354 Location::set_mark (bool yn
)
356 /* This function is private, and so does not emit signals */
358 if (_start
!= _end
) {
362 set_flag_internal (yn
, IsMark
);
367 Location::cd_info_node(const string
& name
, const string
& value
)
369 XMLNode
* root
= new XMLNode("CD-Info");
371 root
->add_property("name", name
);
372 root
->add_property("value", value
);
379 Location::get_state ()
381 XMLNode
*node
= new XMLNode ("Location");
384 typedef map
<string
, string
>::const_iterator CI
;
386 for(CI m
= cd_info
.begin(); m
!= cd_info
.end(); ++m
){
387 node
->add_child_nocopy(cd_info_node(m
->first
, m
->second
));
390 id().print (buf
, sizeof (buf
));
391 node
->add_property("id", buf
);
392 node
->add_property ("name", name());
393 snprintf (buf
, sizeof (buf
), "%" PRId64
, start());
394 node
->add_property ("start", buf
);
395 snprintf (buf
, sizeof (buf
), "%" PRId64
, end());
396 node
->add_property ("end", buf
);
397 node
->add_property ("flags", enum_2_string (_flags
));
398 node
->add_property ("locked", (_locked
? "yes" : "no"));
399 node
->add_property ("position-lock-style", enum_2_string (_position_lock_style
));
405 Location::set_state (const XMLNode
& node
, int /*version*/)
407 const XMLProperty
*prop
;
409 XMLNodeList cd_list
= node
.children();
410 XMLNodeConstIterator cd_iter
;
416 if (node
.name() != "Location") {
417 error
<< _("incorrect XML node passed to Location::set_state") << endmsg
;
421 if ((prop
= node
.property ("id")) == 0) {
422 warning
<< _("XML node for Location has no ID information") << endmsg
;
424 _id
= prop
->value ();
427 if ((prop
= node
.property ("name")) == 0) {
428 error
<< _("XML node for Location has no name information") << endmsg
;
432 set_name (prop
->value());
434 if ((prop
= node
.property ("start")) == 0) {
435 error
<< _("XML node for Location has no start information") << endmsg
;
439 /* can't use set_start() here, because _end
440 may make the value of _start illegal.
443 sscanf (prop
->value().c_str(), "%" PRId64
, &_start
);
445 if ((prop
= node
.property ("end")) == 0) {
446 error
<< _("XML node for Location has no end information") << endmsg
;
450 sscanf (prop
->value().c_str(), "%" PRId64
, &_end
);
452 if ((prop
= node
.property ("flags")) == 0) {
453 error
<< _("XML node for Location has no flags information") << endmsg
;
457 _flags
= Flags (string_2_enum (prop
->value(), _flags
));
459 if ((prop
= node
.property ("locked")) != 0) {
460 _locked
= string_is_affirmative (prop
->value());
465 for (cd_iter
= cd_list
.begin(); cd_iter
!= cd_list
.end(); ++cd_iter
) {
469 if (cd_node
->name() != "CD-Info") {
473 if ((prop
= cd_node
->property ("name")) != 0) {
474 cd_name
= prop
->value();
476 throw failed_constructor ();
479 if ((prop
= cd_node
->property ("value")) != 0) {
480 cd_value
= prop
->value();
482 throw failed_constructor ();
486 cd_info
[cd_name
] = cd_value
;
489 if ((prop
= node
.property ("position-lock-style")) != 0) {
490 _position_lock_style
= PositionLockStyle (string_2_enum (prop
->value(), _position_lock_style
));
493 recompute_bbt_from_frames ();
495 changed (this); /* EMIT SIGNAL */
497 assert (_start
>= 0);
504 Location::set_position_lock_style (PositionLockStyle ps
)
506 if (_position_lock_style
== ps
) {
510 _position_lock_style
= ps
;
512 recompute_bbt_from_frames ();
514 PositionLockStyleChanged (this); /* EMIT SIGNAL */
518 Location::recompute_bbt_from_frames ()
520 if (_position_lock_style
!= MusicTime
) {
524 _session
.tempo_map().bbt_time (_start
, _bbt_start
);
525 _session
.tempo_map().bbt_time (_end
, _bbt_end
);
529 Location::recompute_frames_from_bbt ()
531 if (_position_lock_style
!= MusicTime
) {
535 TempoMap
& map (_session
.tempo_map());
536 set (map
.frame_time (_bbt_start
), map
.frame_time (_bbt_end
), false);
553 /*---------------------------------------------------------------------- */
555 Locations::Locations (Session
& s
)
556 : SessionHandleRef (s
)
558 current_location
= 0;
561 Locations::~Locations ()
563 for (LocationList::iterator i
= locations
.begin(); i
!= locations
.end(); ) {
564 LocationList::iterator tmp
= i
;
572 Locations::set_current (Location
*loc
, bool want_lock
)
577 Glib::Mutex::Lock
lm (lock
);
578 ret
= set_current_unlocked (loc
);
580 ret
= set_current_unlocked (loc
);
584 current_changed (current_location
); /* EMIT SIGNAL */
590 Locations::next_available_name(string
& result
,string base
)
592 LocationList::iterator i
;
598 bool available
[SUFFIX_MAX
+1];
601 for (int k
=1; k
<SUFFIX_MAX
; k
++) {
605 for (i
= locations
.begin(); i
!= locations
.end(); ++i
) {
607 temp
= location
->name();
608 if (l
&& !temp
.find(base
,0)) {
609 suffix
= atoi(temp
.substr(l
,3).c_str());
610 if (suffix
) available
[suffix
] = false;
613 for (int k
=1; k
<=SUFFIX_MAX
; k
++) {
615 snprintf (buf
, 31, "%d", k
);
624 Locations::set_current_unlocked (Location
*loc
)
626 if (find (locations
.begin(), locations
.end(), loc
) == locations
.end()) {
627 error
<< _("Locations: attempt to use unknown location as selected location") << endmsg
;
631 current_location
= loc
;
639 Glib::Mutex::Lock
lm (lock
);
641 for (LocationList::iterator i
= locations
.begin(); i
!= locations
.end(); ) {
643 LocationList::iterator tmp
= i
;
646 if (!(*i
)->is_session_range()) {
653 current_location
= 0;
656 changed (OTHER
); /* EMIT SIGNAL */
657 current_changed (0); /* EMIT SIGNAL */
661 Locations::clear_markers ()
664 Glib::Mutex::Lock
lm (lock
);
665 LocationList::iterator tmp
;
667 for (LocationList::iterator i
= locations
.begin(); i
!= locations
.end(); ) {
671 if ((*i
)->is_mark() && !(*i
)->is_session_range()) {
679 changed (OTHER
); /* EMIT SIGNAL */
683 Locations::clear_ranges ()
686 Glib::Mutex::Lock
lm (lock
);
687 LocationList::iterator tmp
;
689 for (LocationList::iterator i
= locations
.begin(); i
!= locations
.end(); ) {
694 if (!(*i
)->is_mark()) {
702 current_location
= 0;
705 changed (OTHER
); /* EMIT SIGNAL */
706 current_changed (0); /* EMIT SIGNAL */
710 Locations::add (Location
*loc
, bool make_current
)
715 Glib::Mutex::Lock
lm (lock
);
716 locations
.push_back (loc
);
719 current_location
= loc
;
723 added (loc
); /* EMIT SIGNAL */
726 current_changed (current_location
); /* EMIT SIGNAL */
729 if (loc
->is_session_range()) {
730 Session::StartTimeChanged (0);
731 Session::EndTimeChanged (1);
736 Locations::remove (Location
*loc
)
738 bool was_removed
= false;
739 bool was_current
= false;
740 LocationList::iterator i
;
742 if (loc
->is_session_range()) {
747 Glib::Mutex::Lock
lm (lock
);
749 for (i
= locations
.begin(); i
!= locations
.end(); ++i
) {
753 if (current_location
== loc
) {
754 current_location
= 0;
764 removed (loc
); /* EMIT SIGNAL */
767 current_changed (0); /* EMIT SIGNAL */
770 changed (REMOVAL
); /* EMIT_SIGNAL */
775 Locations::location_changed (Location
* /*loc*/)
777 changed (OTHER
); /* EMIT SIGNAL */
781 Locations::get_state ()
783 XMLNode
*node
= new XMLNode ("Locations");
784 LocationList::iterator iter
;
785 Glib::Mutex::Lock
lm (lock
);
787 for (iter
= locations
.begin(); iter
!= locations
.end(); ++iter
) {
788 node
->add_child_nocopy ((*iter
)->get_state ());
795 Locations::set_state (const XMLNode
& node
, int version
)
797 if (node
.name() != "Locations") {
798 error
<< _("incorrect XML mode passed to Locations::set_state") << endmsg
;
802 XMLNodeList nlist
= node
.children();
804 /* build up a new locations list in here */
805 LocationList new_locations
;
807 current_location
= 0;
809 Location
* session_range_location
= 0;
810 if (version
< 3000) {
811 session_range_location
= new Location (_session
, 0, 0, _("session"), Location::IsSessionRange
);
812 new_locations
.push_back (session_range_location
);
816 Glib::Mutex::Lock
lm (lock
);
818 XMLNodeConstIterator niter
;
819 for (niter
= nlist
.begin(); niter
!= nlist
.end(); ++niter
) {
823 XMLProperty
const * prop_id
= (*niter
)->property ("id");
825 PBD::ID
id (prop_id
->value ());
827 LocationList::const_iterator i
= locations
.begin();
828 while (i
!= locations
.end () && (*i
)->id() != id
) {
833 if (i
!= locations
.end()) {
834 /* we can re-use an old Location object */
836 loc
->set_state (**niter
, version
);
838 loc
= new Location (_session
, **niter
);
843 if (version
< 3000) {
844 /* look for old-style IsStart / IsEnd properties in this location;
845 if they are present, update the session_range_location accordingly
847 XMLProperty
const * prop
= (*niter
)->property ("flags");
849 string v
= prop
->value ();
851 string::size_type
const c
= v
.find_first_of (',');
852 string
const s
= v
.substr (0, c
);
853 if (s
== X_("IsStart")) {
854 session_range_location
->set_start (loc
->start(), true);
856 } else if (s
== X_("IsEnd")) {
857 session_range_location
->set_end (loc
->start(), true);
861 if (c
== string::npos
) {
865 v
= v
.substr (c
+ 1);
871 new_locations
.push_back (loc
);
875 catch (failed_constructor
& err
) {
876 error
<< _("could not load location from session file - ignored") << endmsg
;
880 locations
= new_locations
;
882 if (locations
.size()) {
883 current_location
= locations
.front();
885 current_location
= 0;
889 changed (OTHER
); /* EMIT SIGNAL */
894 struct LocationStartEarlierComparison
896 bool operator() (Location
*a
, Location
*b
) {
897 return a
->start() < b
->start();
901 struct LocationStartLaterComparison
903 bool operator() (Location
*a
, Location
*b
) {
904 return a
->start() > b
->start();
909 Locations::first_location_before (framepos_t frame
, bool include_special_ranges
)
914 Glib::Mutex::Lock
lm (lock
);
918 LocationStartLaterComparison cmp
;
921 /* locs is now sorted latest..earliest */
923 for (LocationList::iterator i
= locs
.begin(); i
!= locs
.end(); ++i
) {
924 if (!include_special_ranges
&& ((*i
)->is_auto_loop() || (*i
)->is_auto_punch())) {
927 if (!(*i
)->is_hidden() && (*i
)->start() < frame
) {
936 Locations::first_location_after (framepos_t frame
, bool include_special_ranges
)
941 Glib::Mutex::Lock
lm (lock
);
945 LocationStartEarlierComparison cmp
;
948 /* locs is now sorted earliest..latest */
950 for (LocationList::iterator i
= locs
.begin(); i
!= locs
.end(); ++i
) {
951 if (!include_special_ranges
&& ((*i
)->is_auto_loop() || (*i
)->is_auto_punch())) {
954 if (!(*i
)->is_hidden() && (*i
)->start() > frame
) {
962 /** Look for the `marks' (either locations which are marks, or start/end points of range markers) either
963 * side of a frame. Note that if frame is exactly on a `mark', that mark will not be considered for returning
965 * @param frame Frame to look for.
966 * @param before Filled in with the position of the last `mark' before `frame' (or max_framepos if none exists)
967 * @param after Filled in with the position of the next `mark' after `frame' (or max_framepos if none exists)
970 Locations::marks_either_side (framepos_t
const frame
, framepos_t
& before
, framepos_t
& after
) const
972 before
= after
= max_framepos
;
977 Glib::Mutex::Lock
lm (lock
);
981 /* Get a list of positions; don't store any that are exactly on our requested position */
983 std::list
<framepos_t
> positions
;
985 for (LocationList::const_iterator i
= locs
.begin(); i
!= locs
.end(); ++i
) {
986 if (((*i
)->is_auto_loop() || (*i
)->is_auto_punch())) {
990 if (!(*i
)->is_hidden()) {
991 if ((*i
)->is_mark ()) {
992 if ((*i
)->start() != frame
) {
993 positions
.push_back ((*i
)->start ());
996 if ((*i
)->start() != frame
) {
997 positions
.push_back ((*i
)->start ());
999 if ((*i
)->end() != frame
) {
1000 positions
.push_back ((*i
)->end ());
1006 if (positions
.empty ()) {
1012 std::list
<framepos_t
>::iterator i
= positions
.begin ();
1013 while (i
!= positions
.end () && *i
< frame
) {
1017 if (i
== positions
.end ()) {
1018 /* run out of marks */
1019 before
= positions
.back ();
1025 if (i
== positions
.begin ()) {
1035 Locations::session_range_location () const
1037 for (LocationList::const_iterator i
= locations
.begin(); i
!= locations
.end(); ++i
) {
1038 if ((*i
)->is_session_range()) {
1039 return const_cast<Location
*> (*i
);
1046 Locations::auto_loop_location () const
1048 for (LocationList::const_iterator i
= locations
.begin(); i
!= locations
.end(); ++i
) {
1049 if ((*i
)->is_auto_loop()) {
1050 return const_cast<Location
*> (*i
);
1057 Locations::auto_punch_location () const
1059 for (LocationList::const_iterator i
= locations
.begin(); i
!= locations
.end(); ++i
) {
1060 if ((*i
)->is_auto_punch()) {
1061 return const_cast<Location
*> (*i
);
1068 Locations::num_range_markers () const
1071 Glib::Mutex::Lock
lm (lock
);
1072 for (LocationList::const_iterator i
= locations
.begin(); i
!= locations
.end(); ++i
) {
1073 if ((*i
)->is_range_marker()) {
1081 Locations::get_location_by_id(PBD::ID id
)
1083 LocationList::iterator it
;
1084 for (it
= locations
.begin(); it
!= locations
.end(); ++it
)
1085 if (id
== (*it
)->id())
1092 Locations::find_all_between (framepos_t start
, framepos_t end
, LocationList
& ll
, Location::Flags flags
)
1094 Glib::Mutex::Lock
lm (lock
);
1096 for (LocationList::const_iterator i
= locations
.begin(); i
!= locations
.end(); ++i
) {
1097 if ((flags
== 0 || (*i
)->matches (flags
)) &&
1098 ((*i
)->start() >= start
&& (*i
)->end() < end
)) {