initial volley of work for AudioPlaylistSource, the basic prototype for sources-that...
[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
28 #include "pbd/error.h"
29 #include "pbd/convert.h"
30 #include "pbd/enumwriter.h"
31
32 #include "ardour/audioplaylist.h"
33 #include "ardour/audio_playlist_source.h"
34 #include "ardour/audioregion.h"
35 #include "ardour/debug.h"
36 #include "ardour/session.h"
37 #include "ardour/session_directory.h"
38 #include "ardour/session_playlists.h"
39 #include "ardour/source_factory.h"
40
41 #include "i18n.h"
42
43 using namespace std;
44 using namespace ARDOUR;
45 using namespace PBD;
46
47 AudioPlaylistSource::AudioPlaylistSource (Session& s, const std::string& name, boost::shared_ptr<AudioPlaylist> p, 
48                                           uint32_t chn, frameoffset_t begin, framecnt_t len, bool copy, Source::Flag flags)
49         : Source (s, DataType::AUDIO, name)
50         , AudioSource (s, name)
51         , _playlist_channel (chn)
52 {
53         /* PlaylistSources are never writable, renameable, removable or destructive */
54         _flags = Flag (_flags & ~(Writable|CanRename|Removable|RemovableIfEmpty|RemoveAtDestroy|Destructive));
55
56         if (!copy) {
57                 _playlist = p;
58                 _playlist_offset = begin;
59                 _playlist_length = len;
60         } else {
61                 _playlist.reset (new AudioPlaylist (p, begin, len, "XXXNAMEXXX", true));
62                 _playlist_offset = 0;
63                 _playlist_length = len;
64         }
65
66         _peak_path = tempnam (_session.session_directory().peak_path().to_string().c_str(), "apspk");
67 }
68
69 AudioPlaylistSource::AudioPlaylistSource (Session& s, const XMLNode& node)
70         : Source (s, DataType::AUDIO, "toBeRenamed")
71         , AudioSource (s, node)
72 {
73         /* PlaylistSources are never writable, renameable, removable or destructive */
74         _flags = Flag (_flags & ~(Writable|CanRename|Removable|RemovableIfEmpty|RemoveAtDestroy|Destructive));
75         
76         set_state (node, 3000);
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         snprintf (buf, sizeof (buf), "%" PRIi64, _playlist_offset);
89         node.add_property ("offset", buf);
90         snprintf (buf, sizeof (buf), "%" PRIu64, _playlist_length);
91         node.add_property ("length", buf);
92         snprintf (buf, sizeof (buf), "%" PRIu32, _playlist_channel);
93         node.add_property ("channel", buf);
94         node.add_property ("name", name());
95         node.add_property ("peak-path", _peak_path);
96
97         return node;
98 }
99
100 int
101 AudioPlaylistSource::set_state (const XMLNode& node, int /* version */) 
102 {
103         /* get playlist ID */
104
105         const XMLProperty *prop = node.property (X_("playlist"));
106
107         if (!prop) {
108                 throw failed_constructor ();
109         }
110
111         PBD::ID id (prop->value());
112
113         /* get playlist */
114
115         boost::shared_ptr<Playlist> p = _session.playlists->by_id (id);
116
117         if (!p) {
118                 throw failed_constructor ();
119         }
120
121         /* other properties */
122
123         if ((prop = node.property (X_("name"))) == 0) {
124                 throw failed_constructor ();
125         }
126         
127         set_name (prop->value());
128
129         if ((prop = node.property (X_("offset"))) == 0) {
130                 throw failed_constructor ();
131         }
132         sscanf (prop->value().c_str(), "%" PRIi64, &_playlist_offset);
133
134         if ((prop = node.property (X_("length"))) == 0) {
135                 throw failed_constructor ();
136         }
137
138         sscanf (prop->value().c_str(), "%" PRIu64, &_playlist_length);
139
140         if ((prop = node.property (X_("channel"))) == 0) {
141                 throw failed_constructor ();
142         }
143
144         sscanf (prop->value().c_str(), "%" PRIu32, &_playlist_channel);
145
146         if ((prop = node.property (X_("peak-path"))) == 0) {
147                 throw failed_constructor ();
148         }
149
150         _peak_path = prop->value ();
151
152         return 0;
153 }
154
155 framecnt_t 
156 AudioPlaylistSource::read_unlocked (Sample* dst, framepos_t start, framecnt_t cnt) const
157 {
158         framecnt_t to_read;
159         framecnt_t to_zero;
160         pair<framepos_t,framepos_t> extent = _playlist->get_extent();
161
162         /* we must be careful not to read beyond the end of our "section" of
163          * the playlist, because otherwise we may read data that exists, but
164          * is not supposed be part of our data.
165          */
166
167         if (cnt > _playlist_length - start) {
168                 to_read = _playlist_length - start;
169                 to_zero = cnt - to_read;
170         } else {
171                 to_read = cnt;
172                 to_zero = 0;
173         }
174
175         _playlist->read (dst, 0, 0, start+_playlist_offset, to_read, _playlist_channel);
176         
177         if (to_zero) {
178                 memset (dst+to_read, 0, sizeof (Sample) * to_zero);
179         }
180
181         return cnt;
182 }
183
184 framecnt_t 
185 AudioPlaylistSource::write_unlocked (Sample *src, framecnt_t cnt) 
186 {
187         fatal << string_compose (_("programming error: %1"), "AudioPlaylistSource::write() called - should be impossible") << endmsg;
188         /*NOTREACHED*/
189         return 0;
190 }
191
192 bool
193 AudioPlaylistSource::empty () const
194 {
195         return !_playlist || _playlist->empty();
196 }
197
198 uint32_t
199 AudioPlaylistSource::n_channels () const
200 {
201         /* use just the first region to decide */
202
203         if (empty()) {
204                 return 1;
205         }
206
207         boost::shared_ptr<Region> r = _playlist->region_list().front ();
208         boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
209
210         return ar->audio_source()->n_channels ();
211 }
212
213 float
214 AudioPlaylistSource::sample_rate () const
215 {
216         /* use just the first region to decide */
217
218         if (empty()) {
219                 _session.frame_rate ();
220         }
221
222         boost::shared_ptr<Region> r = _playlist->region_list().front ();
223         boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
224
225         return ar->audio_source()->sample_rate ();
226 }
227
228 int
229 AudioPlaylistSource::setup_peakfile ()
230 {
231         /* the peak data is setup once and once only 
232          */
233
234         if (!Glib::file_test (_peak_path, Glib::FILE_TEST_EXISTS)) {
235                 /* the 2nd argument here will be passed
236                    in to ::peak_path, and is irrelevant
237                    since our peak file path is fixed and
238                    not dependent on anything.
239                 */
240                 
241                 return initialize_peakfile (true, string());
242         } 
243         return 0;
244 }
245
246 string
247 AudioPlaylistSource::peak_path (string /*audio_path*/)
248 {
249         return _peak_path;
250 }