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