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