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