faed2409e54c744eb6860e8ab9d85b5c8168cfeb
[ardour.git] / libs / ardour / sndfilesource.cc
1 /*
2     Copyright (C) 2006 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     $Id$
19 */
20
21 #include <cerrno>
22 #include <climits>
23
24 #include <pwd.h>
25 #include <sys/utsname.h>
26
27 #include <glibmm/miscutils.h>
28
29 #include <ardour/sndfilesource.h>
30
31 #include "i18n.h"
32
33 using namespace std;
34 using namespace ARDOUR;
35 using namespace PBD;
36
37 SndFileSource::SndFileSource (const XMLNode& node)
38         : AudioFileSource (node)
39 {
40         init (_name);
41
42         if (open()) {
43                 throw failed_constructor ();
44         }
45
46         if (_build_peakfiles) {
47                 if (initialize_peakfile (false, _path)) {
48                         sf_close (sf);
49                         sf = 0;
50                         throw failed_constructor ();
51                 }
52         }
53 }
54
55 SndFileSource::SndFileSource (string idstr, Flag flags)
56                                         /* files created this way are never writable or removable */
57         : AudioFileSource (idstr, Flag (flags & ~(Writable|Removable|RemovableIfEmpty|RemoveAtDestroy)))
58 {
59         init (idstr);
60
61         if (open()) {
62                 throw failed_constructor ();
63         }
64
65         if (!(_flags & NoPeakFile) && _build_peakfiles) {
66                 if (initialize_peakfile (false, _path)) {
67                         sf_close (sf);
68                         sf = 0;
69                         throw failed_constructor ();
70                 }
71         }
72 }
73
74 SndFileSource::SndFileSource (string idstr, SampleFormat sfmt, HeaderFormat hf, jack_nframes_t rate, Flag flags)
75         : AudioFileSource(idstr, flags, sfmt, hf)
76 {
77         int fmt = 0;
78
79         init (idstr);
80
81         switch (hf) {
82         case CAF:
83                 fmt = SF_FORMAT_CAF;
84                 _flags = Flag (_flags & ~Broadcast);
85                 break;
86
87         case AIFF:
88                 fmt = SF_FORMAT_AIFF;
89                 _flags = Flag (_flags & ~Broadcast);
90                 break;
91
92         case BWF:
93                 fmt = SF_FORMAT_WAV;
94                 _flags = Flag (_flags | Broadcast);
95                 break;
96
97         case WAVE:
98                 fmt = SF_FORMAT_WAV;
99                 _flags = Flag (_flags & ~Broadcast);
100                 break;
101
102         case WAVE64:
103                 fmt = SF_FORMAT_W64;
104                 _flags = Flag (_flags & ~Broadcast);
105                 break;
106
107         default:
108                 fatal << string_compose (_("programming error: %1"), X_("unsupported audio header format requested")) << endmsg;
109                 /*NOTREACHED*/
110                 break;
111
112         }
113
114         switch (sfmt) {
115         case FormatFloat:
116                 fmt |= SF_FORMAT_FLOAT;
117                 break;
118
119         case FormatInt24:
120                 fmt |= SF_FORMAT_PCM_24;
121                 break;
122         }
123         
124         _info.channels = 1;
125         _info.samplerate = rate;
126         _info.format = fmt;
127
128         if (open()) {
129                 throw failed_constructor();
130         }
131
132         if (writable() && (_flags & Broadcast)) {
133
134                 _broadcast_info = new SF_BROADCAST_INFO;
135                 memset (_broadcast_info, 0, sizeof (*_broadcast_info));
136                 
137                 snprintf (_broadcast_info->description, sizeof (_broadcast_info->description), "BWF %s", _name.c_str());
138                 
139                 struct utsname utsinfo;
140
141                 if (uname (&utsinfo)) {
142                         error << string_compose(_("FileSource: cannot get host information for BWF header (%1)"), strerror(errno)) << endmsg;
143                         return;
144                 }
145                 
146                 snprintf (_broadcast_info->originator, sizeof (_broadcast_info->originator), "ardour:%s:%s:%s:%s:%s)", 
147                           Glib::get_real_name().c_str(),
148                           utsinfo.nodename,
149                           utsinfo.sysname,
150                           utsinfo.release,
151                           utsinfo.version);
152                 
153                 _broadcast_info->version = 1;  
154                 _broadcast_info->time_reference_low = 0;  
155                 _broadcast_info->time_reference_high = 0;  
156                 
157                 /* XXX do something about this field */
158                 
159                 snprintf (_broadcast_info->umid, sizeof (_broadcast_info->umid), "%s", "fnord");
160                 
161                 /* coding history is added by libsndfile */
162
163                 if (sf_command (sf, SFC_SET_BROADCAST_INFO, _broadcast_info, sizeof (_broadcast_info)) != SF_TRUE) {
164                         char errbuf[256];
165                         sf_error_str (0, errbuf, sizeof (errbuf) - 1);
166                         error << string_compose (_("cannot set broadcast info for audio file %1 (%2); dropping broadcast info for this file"), _path, errbuf) << endmsg;
167                         _flags = Flag (_flags & ~Broadcast);
168                         delete _broadcast_info;
169                         _broadcast_info = 0;
170                 }
171                 
172         }
173         
174         if (!(_flags & NoPeakFile) && _build_peakfiles) {
175                 if (initialize_peakfile (true, _path)) {
176                         sf_close (sf);
177                         sf = 0;
178                         throw failed_constructor ();
179                 }
180         }
181 }
182
183 void 
184 SndFileSource::init (const string& idstr)
185 {
186         string::size_type pos;
187         string file;
188
189         interleave_buf = 0;
190         interleave_bufsize = 0;
191         sf = 0;
192         _broadcast_info = 0;
193
194         if ((pos = idstr.find_last_of (':')) == string::npos) {
195                 channel = 0;
196                 _name = Glib::path_get_basename (idstr);
197         } else {
198                 channel = atoi (idstr.substr (pos+1).c_str());
199                 _name = Glib::path_get_basename (idstr.substr (0, pos));
200         }
201
202         /* although libsndfile says we don't need to set this,
203            valgrind and source code shows us that we do.
204         */
205
206         memset (&_info, 0, sizeof(_info));
207 }
208
209 int
210 SndFileSource::open ()
211 {
212         if ((sf = sf_open (_path.c_str(), (writable() ? SFM_RDWR : SFM_READ), &_info)) == 0) {
213                 char errbuf[256];
214                 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
215                 error << string_compose(_("SndFileSource: cannot open file \"%1\" for %2 (%3)"), 
216                                         _path, (writable() ? "read+write" : "reading"), errbuf) << endmsg;
217                 return -1;
218         }
219
220         if (channel >= _info.channels) {
221                 error << string_compose(_("SndFileSource: file only contains %1 channels; %2 is invalid as a channel number"), _info.channels, channel) << endmsg;
222                 sf_close (sf);
223                 sf = 0;
224                 return -1;
225         }
226
227         _length = _info.frames;
228
229
230         _broadcast_info = new SF_BROADCAST_INFO;
231         memset (_broadcast_info, 0, sizeof (*_broadcast_info));
232         
233         /* lookup broadcast info */
234         
235         if (sf_command (sf, SFC_GET_BROADCAST_INFO, _broadcast_info, sizeof (*_broadcast_info)) != SF_TRUE) {
236
237                 /* if the file has data but no broadcast info, then clearly, there is no broadcast info */
238
239                 if (_length) {
240                         delete _broadcast_info;
241                         _broadcast_info = 0;
242                         _flags = Flag (_flags & ~Broadcast);
243                 }
244
245                 set_timeline_position (header_position_offset);
246
247         } else {
248         
249                 /* XXX 64 bit alert: when JACK switches to a 64 bit frame count, this needs to use the high bits
250                    of the time reference.
251                 */
252
253                 set_timeline_position ( _broadcast_info->time_reference_low );
254         }
255
256         if (writable()) {
257                 sf_command (sf, SFC_SET_UPDATE_HEADER_AUTO, 0, SF_FALSE);
258         }
259
260         return 0;
261 }
262
263 SndFileSource::~SndFileSource ()
264 {
265         GoingAway (); /* EMIT SIGNAL */
266
267         if (sf) {
268                 sf_close (sf);
269                 sf = 0;
270         }
271
272         if (interleave_buf) {
273                 delete [] interleave_buf;
274         }
275
276         if (_broadcast_info) {
277                 delete _broadcast_info;
278         }
279 }
280
281 float
282 SndFileSource::sample_rate () const 
283 {
284         return _info.samplerate;
285 }
286
287 jack_nframes_t
288 SndFileSource::read_unlocked (Sample *dst, jack_nframes_t start, jack_nframes_t cnt) const
289 {
290         int32_t nread;
291         float *ptr;
292         uint32_t real_cnt;
293         jack_nframes_t file_cnt;
294
295         //destructive (tape) tracks need to offset reads and writes by the timeline position
296         if (_flags && ARDOUR::Destructive == ARDOUR::Destructive) {
297                 start -= timeline_position;
298         }
299
300         if (start > _length) {
301
302                 /* read starts beyond end of data, just memset to zero */
303                 
304                 file_cnt = 0;
305
306         } else if (start + cnt > _length) {
307                 
308                 /* read ends beyond end of data, read some, memset the rest */
309                 
310                 file_cnt = _length - start;
311
312         } else {
313                 
314                 /* read is entirely within data */
315
316                 file_cnt = cnt;
317         }
318         
319         if (file_cnt) {
320
321                 if (sf_seek (sf, (sf_count_t) start, SEEK_SET|SFM_READ) != (sf_count_t) start) {
322                         char errbuf[256];
323                         sf_error_str (0, errbuf, sizeof (errbuf) - 1);
324                         error << string_compose(_("SndFileSource: could not seek to frame %1 within %2 (%3)"), start, _name.substr (1), errbuf) << endmsg;
325                         return 0;
326                 }
327                 
328                 if (_info.channels == 1) {
329                         jack_nframes_t ret = sf_read_float (sf, dst, file_cnt);
330                         _read_data_count = cnt * sizeof(float);
331                         return ret;
332                 }
333         }
334
335         if (file_cnt != cnt) {
336                 jack_nframes_t delta = cnt - file_cnt;
337                 memset (dst+file_cnt, 0, sizeof (Sample) * delta);
338         }
339
340         real_cnt = cnt * _info.channels;
341
342         if (interleave_bufsize < real_cnt) {
343                 
344                 if (interleave_buf) {
345                         delete [] interleave_buf;
346                 }
347                 interleave_bufsize = real_cnt;
348                 interleave_buf = new float[interleave_bufsize];
349         }
350         
351         nread = sf_read_float (sf, interleave_buf, real_cnt);
352         ptr = interleave_buf + channel;
353         nread /= _info.channels;
354         
355         /* stride through the interleaved data */
356         
357         for (int32_t n = 0; n < nread; ++n) {
358                 dst[n] = *ptr;
359                 ptr += _info.channels;
360         }
361
362         _read_data_count = cnt * sizeof(float);
363                 
364         return nread;
365 }
366
367 jack_nframes_t 
368 SndFileSource::write_unlocked (Sample *data, jack_nframes_t cnt)
369 {
370         if (!writable()) {
371                 return 0;
372         }
373
374         if (_info.channels != 1) {
375                 fatal << string_compose (_("programming error: %1 %2"), X_("SndFileSource::write called on non-mono file"), _path) << endmsg;
376                 /*NOTREACHED*/
377                 return 0;
378         }
379         
380         jack_nframes_t oldlen;
381         int32_t frame_pos = _length;
382         
383         if (write_float (data, frame_pos, cnt) != cnt) {
384                 return 0;
385         }
386
387         oldlen = _length;
388         update_length (oldlen, cnt);
389
390         if (_build_peakfiles) {
391                 PeakBuildRecord *pbr = 0;
392                 
393                 if (pending_peak_builds.size()) {
394                                 pbr = pending_peak_builds.back();
395                         }
396                         
397                         if (pbr && pbr->frame + pbr->cnt == oldlen) {
398                                 
399                                 /* the last PBR extended to the start of the current write,
400                                    so just extend it again.
401                                 */
402
403                                 pbr->cnt += cnt;
404                         } else {
405                                 pending_peak_builds.push_back (new PeakBuildRecord (oldlen, cnt));
406                         }
407                         
408                         _peaks_built = false;
409         }
410         
411         
412         if (_build_peakfiles) {
413                 queue_for_peaks (this);
414         }
415
416         _write_data_count = cnt;
417         
418         return cnt;
419 }
420
421 int
422 SndFileSource::update_header (jack_nframes_t when, struct tm& now, time_t tnow)
423 {       
424         set_timeline_position (when);
425
426         if (_flags & Broadcast) {
427                 if (setup_broadcast_info (when, now, tnow)) {
428                         return -1;
429                 }
430         } 
431
432         return flush_header ();
433 }
434
435 int
436 SndFileSource::flush_header ()
437 {
438         if (!writable() || (sf == 0)) {
439                 return -1;
440         }
441
442         return (sf_command (sf, SFC_UPDATE_HEADER_NOW, 0, 0) != SF_TRUE);
443 }
444
445 int
446 SndFileSource::setup_broadcast_info (jack_nframes_t when, struct tm& now, time_t tnow)
447 {
448         if (!writable()) {
449                 return -1;
450         }
451
452         if (!(_flags & Broadcast)) {
453                 return 0;
454         }
455
456         /* random code is 9 digits */
457         
458         int random_code = random() % 999999999;
459         
460         snprintf (_broadcast_info->originator_reference, sizeof (_broadcast_info->originator_reference), "%2s%3s%12s%02d%02d%02d%9d",
461                   bwf_country_code,
462                   bwf_organization_code,
463                   bwf_serial_number,
464                   now.tm_hour,
465                   now.tm_min,
466                   now.tm_sec,
467                   random_code);
468         
469         snprintf (_broadcast_info->origination_date, sizeof (_broadcast_info->origination_date), "%4d-%02d-%02d",
470                   1900 + now.tm_year,
471                   now.tm_mon,
472                   now.tm_mday);
473         
474         snprintf (_broadcast_info->origination_time, sizeof (_broadcast_info->origination_time), "%02d:%02d:%02d",
475                   now.tm_hour,
476                   now.tm_min,
477                   now.tm_sec);
478
479         /* now update header position taking header offset into account */
480         
481         set_header_timeline_position ();
482
483         if (sf_command (sf, SFC_SET_BROADCAST_INFO, _broadcast_info, sizeof (*_broadcast_info)) != SF_TRUE) {
484                 error << string_compose (_("cannot set broadcast info for audio file %1; Dropping broadcast info for this file"), _path) << endmsg;
485                 _flags = Flag (_flags & ~Broadcast);
486                 delete _broadcast_info;
487                 _broadcast_info = 0;
488                 return -1;
489         }
490
491         return 0;
492 }
493
494 void
495 SndFileSource::set_header_timeline_position ()
496 {
497         if (!(_flags & Broadcast)) {
498                 return;
499         }
500
501        _broadcast_info->time_reference_high = (timeline_position >> 32);
502        _broadcast_info->time_reference_low = (timeline_position & 0xffffffff);
503
504         if (sf_command (sf, SFC_SET_BROADCAST_INFO, _broadcast_info, sizeof (*_broadcast_info)) != SF_TRUE) {
505                 error << string_compose (_("cannot set broadcast info for audio file %1; Dropping broadcast info for this file"), _path) << endmsg;
506                 _flags = Flag (_flags & ~Broadcast);
507                 delete _broadcast_info;
508                 _broadcast_info = 0;
509         }
510 }
511
512 jack_nframes_t
513 SndFileSource::write_float (Sample* data, jack_nframes_t frame_pos, jack_nframes_t cnt)
514 {
515         if (sf_seek (sf, frame_pos, SEEK_SET|SFM_WRITE) != frame_pos) {
516                 error << string_compose (_("%1: cannot seek to %2"), _path, frame_pos) << endmsg;
517                 return 0;
518         }
519         
520         if (sf_writef_float (sf, data, cnt) != (ssize_t) cnt) {
521                 return 0;
522         }
523         
524         return cnt;
525 }
526
527 jack_nframes_t
528 SndFileSource::natural_position() const
529 {
530         return timeline_position;
531 }