Fix broken whitespace. I'd apologize for the compile times if it was my fault :D
[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
77 AudioPlaylistSource::~AudioPlaylistSource ()
78 {
79 }
80
81 XMLNode&
82 AudioPlaylistSource::get_state ()
83 {
84         XMLNode& node (AudioSource::get_state ());
85         char buf[64];
86
87         /* merge PlaylistSource state */
88
89         PlaylistSource::add_state (node);
90
91         snprintf (buf, sizeof (buf), "%" PRIu32, _playlist_channel);
92         node.add_property ("channel", buf);
93
94         return node;
95 }
96
97 int
98 AudioPlaylistSource::set_state (const XMLNode& node, int version)
99 {
100         return set_state (node, version, true);
101 }
102
103 int
104 AudioPlaylistSource::set_state (const XMLNode& node, int version, bool with_descendants)
105 {
106         if (with_descendants) {
107                 if (Source::set_state (node, version) ||
108                     PlaylistSource::set_state (node, version) ||
109                     AudioSource::set_state (node, version)) {
110                         return -1;
111                 }
112         }
113
114         const XMLProperty* prop;
115         pair<framepos_t,framepos_t> extent = _playlist->get_extent();
116         AudioSource::_length = extent.second - extent.first;
117
118         if ((prop = node.property (X_("channel"))) == 0) {
119                 throw failed_constructor ();
120         }
121
122         sscanf (prop->value().c_str(), "%" PRIu32, &_playlist_channel);
123
124         ensure_buffers_for_level (_level, _session.frame_rate());
125
126         return 0;
127 }
128
129 framecnt_t
130 AudioPlaylistSource::read_unlocked (Sample* dst, framepos_t start, framecnt_t cnt) const
131 {
132         boost::shared_ptr<Sample> sbuf;
133         boost::shared_ptr<gain_t> gbuf;
134         framecnt_t to_read;
135         framecnt_t to_zero;
136         pair<framepos_t,framepos_t> extent = _playlist->get_extent();
137
138         /* we must be careful not to read beyond the end of our "section" of
139          * the playlist, because otherwise we may read data that exists, but
140          * is not supposed be part of our data.
141          */
142
143         if (cnt > _playlist_length - start) {
144                 to_read = _playlist_length - start;
145                 to_zero = cnt - to_read;
146         } else {
147                 to_read = cnt;
148                 to_zero = 0;
149         }
150
151         {
152                 /* Don't need to hold the lock for the actual read, and
153                    actually, we cannot, but we do want to interlock
154                    with any changes to the list of buffers caused
155                    by creating new nested playlists/sources
156                 */
157                 Glib::Mutex::Lock lm (_level_buffer_lock);
158                 sbuf = _mixdown_buffers[_level-1];
159                 gbuf = _gain_buffers[_level-1];
160         }
161
162         boost::dynamic_pointer_cast<AudioPlaylist>(_playlist)->read (dst, sbuf.get(), gbuf.get(), start+_playlist_offset, to_read, _playlist_channel);
163
164         if (to_zero) {
165                 memset (dst+to_read, 0, sizeof (Sample) * to_zero);
166         }
167
168         return cnt;
169 }
170
171 framecnt_t
172 AudioPlaylistSource::write_unlocked (Sample *src, framecnt_t cnt)
173 {
174         fatal << string_compose (_("programming error: %1"), "AudioPlaylistSource::write() called - should be impossible") << endmsg;
175         /*NOTREACHED*/
176         return 0;
177 }
178
179 bool
180 AudioPlaylistSource::empty () const
181 {
182         return !_playlist || _playlist->empty();
183 }
184
185 uint32_t
186 AudioPlaylistSource::n_channels () const
187 {
188         /* use just the first region to decide */
189
190         if (empty()) {
191                 return 1;
192         }
193
194         boost::shared_ptr<Region> r = _playlist->region_list().front ();
195         boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
196
197         return ar->audio_source()->n_channels ();
198 }
199
200 float
201 AudioPlaylistSource::sample_rate () const
202 {
203         /* use just the first region to decide */
204
205         if (empty()) {
206                 _session.frame_rate ();
207         }
208
209         boost::shared_ptr<Region> r = _playlist->region_list().front ();
210         boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
211
212         return ar->audio_source()->sample_rate ();
213 }
214
215 int
216 AudioPlaylistSource::setup_peakfile ()
217 {
218         _peak_path = Glib::build_filename (_session.session_directory().peak_path().to_string(), name() + ARDOUR::peakfile_suffix);
219         return initialize_peakfile (false, string());
220 }
221
222 string
223 AudioPlaylistSource::peak_path (string /*audio_path_IGNORED*/)
224 {
225         return _peak_path;
226 }
227
228