second half of "bomb out if playlist construction from XML fails"
[ardour.git] / libs / ardour / audio_playlist_source.cc
1 /*
2     Copyright (C) 2011 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 #ifdef WAF_BUILD
20 #include "libardour-config.h"
21 #endif
22
23 #include <vector>
24 #include <cstdio>
25
26 #include <glibmm/fileutils.h>
27 #include <glibmm/miscutils.h>
28
29 #include "pbd/error.h"
30 #include "pbd/convert.h"
31 #include "pbd/enumwriter.h"
32
33 #include "ardour/audioplaylist.h"
34 #include "ardour/audio_playlist_source.h"
35 #include "ardour/audioregion.h"
36 #include "ardour/debug.h"
37 #include "ardour/filename_extensions.h"
38 #include "ardour/session.h"
39 #include "ardour/session_directory.h"
40 #include "ardour/session_playlists.h"
41 #include "ardour/source_factory.h"
42
43 #include "i18n.h"
44
45 using namespace std;
46 using namespace ARDOUR;
47 using namespace PBD;
48
49 AudioPlaylistSource::AudioPlaylistSource (Session& s, const ID& orig, const std::string& name, boost::shared_ptr<AudioPlaylist> p,
50                                           uint32_t chn, frameoffset_t begin, framecnt_t len, Source::Flag flags)
51         : Source (s, DataType::AUDIO, name)
52         , PlaylistSource (s, orig, name, p, DataType::AUDIO, begin, len, flags)
53         , AudioSource (s, name)
54         , _playlist_channel (chn)
55 {
56         AudioSource::_length = len;
57         ensure_buffers_for_level (_level, _session.frame_rate());
58 }
59
60 AudioPlaylistSource::AudioPlaylistSource (Session& s, const XMLNode& node)
61         : Source (s, node)
62         , PlaylistSource (s, node)
63         , AudioSource (s, node)
64 {
65         /* PlaylistSources are never writable, renameable, removable or destructive */
66         _flags = Flag (_flags & ~(Writable|CanRename|Removable|RemovableIfEmpty|RemoveAtDestroy|Destructive));
67
68         /* ancestors have already called ::set_state() in their XML-based
69            constructors.
70         */
71
72         if (set_state (node, Stateful::loading_state_version, false)) {
73                 throw failed_constructor ();
74         }
75
76         AudioSource::_length = _playlist_length;
77 }
78
79 AudioPlaylistSource::~AudioPlaylistSource ()
80 {
81 }
82
83 XMLNode&
84 AudioPlaylistSource::get_state ()
85 {
86         XMLNode& node (AudioSource::get_state ());
87         char buf[64];
88
89         /* merge PlaylistSource state */
90
91         PlaylistSource::add_state (node);
92
93         snprintf (buf, sizeof (buf), "%" PRIu32, _playlist_channel);
94         node.add_property ("channel", buf);
95
96         return node;
97 }
98
99 int
100 AudioPlaylistSource::set_state (const XMLNode& node, int version)
101 {
102         return set_state (node, version, true);
103 }
104
105 int
106 AudioPlaylistSource::set_state (const XMLNode& node, int version, bool with_descendants)
107 {
108         if (with_descendants) {
109                 if (Source::set_state (node, version) ||
110                     PlaylistSource::set_state (node, version) ||
111                     AudioSource::set_state (node, version)) {
112                         return -1;
113                 }
114         }
115
116         const XMLProperty* prop;
117         pair<framepos_t,framepos_t> extent = _playlist->get_extent();
118
119         cerr << "APS " << id() << " playlist ID " << _playlist->id() << " has " << _playlist->n_regions() << " playlist extents = " << extent.first << " .. " << extent.second << endl;
120
121         AudioSource::_length = extent.second - extent.first;
122
123         if ((prop = node.property (X_("channel"))) == 0) {
124                 throw failed_constructor ();
125         }
126
127         sscanf (prop->value().c_str(), "%" PRIu32, &_playlist_channel);
128
129         ensure_buffers_for_level (_level, _session.frame_rate());
130
131         return 0;
132 }
133
134 framecnt_t
135 AudioPlaylistSource::read_unlocked (Sample* dst, framepos_t start, framecnt_t cnt) const
136 {
137         boost::shared_ptr<Sample> sbuf;
138         boost::shared_ptr<gain_t> gbuf;
139         framecnt_t to_read;
140         framecnt_t to_zero;
141         pair<framepos_t,framepos_t> extent = _playlist->get_extent();
142
143         /* we must be careful not to read beyond the end of our "section" of
144          * the playlist, because otherwise we may read data that exists, but
145          * is not supposed be part of our data.
146          */
147
148         if (cnt > _playlist_length - start) {
149                 to_read = _playlist_length - start;
150                 to_zero = cnt - to_read;
151         } else {
152                 to_read = cnt;
153                 to_zero = 0;
154         }
155
156         {
157                 /* Don't need to hold the lock for the actual read, and
158                    actually, we cannot, but we do want to interlock
159                    with any changes to the list of buffers caused
160                    by creating new nested playlists/sources
161                 */
162                 Glib::Mutex::Lock lm (_level_buffer_lock);
163                 sbuf = _mixdown_buffers[_level-1];
164                 gbuf = _gain_buffers[_level-1];
165         }
166
167         boost::dynamic_pointer_cast<AudioPlaylist>(_playlist)->read (dst, sbuf.get(), gbuf.get(), start+_playlist_offset, to_read, _playlist_channel);
168
169         if (to_zero) {
170                 memset (dst+to_read, 0, sizeof (Sample) * to_zero);
171         }
172
173         return cnt;
174 }
175
176 framecnt_t
177 AudioPlaylistSource::write_unlocked (Sample *src, framecnt_t cnt)
178 {
179         fatal << string_compose (_("programming error: %1"), "AudioPlaylistSource::write() called - should be impossible") << endmsg;
180         /*NOTREACHED*/
181         return 0;
182 }
183
184 bool
185 AudioPlaylistSource::empty () const
186 {
187         return !_playlist || _playlist->empty();
188 }
189
190 uint32_t
191 AudioPlaylistSource::n_channels () const
192 {
193         /* use just the first region to decide */
194
195         if (empty()) {
196                 return 1;
197         }
198
199         boost::shared_ptr<Region> r = _playlist->region_list().front ();
200         boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
201
202         return ar->audio_source()->n_channels ();
203 }
204
205 float
206 AudioPlaylistSource::sample_rate () const
207 {
208         /* use just the first region to decide */
209
210         if (empty()) {
211                 _session.frame_rate ();
212         }
213
214         boost::shared_ptr<Region> r = _playlist->region_list().front ();
215         boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
216
217         return ar->audio_source()->sample_rate ();
218 }
219
220 int
221 AudioPlaylistSource::setup_peakfile ()
222 {
223         _peak_path = Glib::build_filename (_session.session_directory().peak_path().to_string(), name() + ARDOUR::peakfile_suffix);
224         return initialize_peakfile (false, string());
225 }
226
227 string
228 AudioPlaylistSource::peak_path (string /*audio_path_IGNORED*/)
229 {
230         return _peak_path;
231 }
232
233