692938514181edf328aa5a873570726ad6f1046d
[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
36 SndFileSource::SndFileSource (const XMLNode& node)
37         : AudioFileSource (node)
38 {
39         init (_name);
40
41         if (open()) {
42                 throw failed_constructor ();
43         }
44
45         if (_build_peakfiles) {
46                 if (initialize_peakfile (false, _path)) {
47                         sf_close (sf);
48                         sf = 0;
49                         throw failed_constructor ();
50                 }
51         }
52
53         AudioSourceCreated (this); /* EMIT SIGNAL */
54 }
55
56 SndFileSource::SndFileSource (string idstr, Flag flags)
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 (_build_peakfiles) {
66                 if (initialize_peakfile (false, _path)) {
67                         sf_close (sf);
68                         sf = 0;
69                         throw failed_constructor ();
70                 }
71         }
72
73
74         AudioSourceCreated (this); /* EMIT SIGNAL */
75 }
76
77 SndFileSource::SndFileSource (string idstr, SampleFormat sfmt, HeaderFormat hf, jack_nframes_t rate, Flag flags)
78         : AudioFileSource(idstr, flags, sfmt, hf)
79 {
80         int fmt = 0;
81
82         init (idstr);
83
84         cerr << "creating " << idstr << " hf = " << hf << endl;
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                 
160                 /* XXX do something about this field */
161                 
162                 snprintf (_broadcast_info->umid, sizeof (_broadcast_info->umid), "%s", "fnord");
163                 
164                 /* coding history is added by libsndfile */
165
166                 if (sf_command (sf, SFC_SET_BROADCAST_INFO, _broadcast_info, sizeof (_broadcast_info)) != SF_TRUE) {
167                         char errbuf[256];
168                         sf_error_str (0, errbuf, sizeof (errbuf) - 1);
169                         error << string_compose (_("cannot set broadcast info for audio file %1 (%2); dropping broadcast info for this file"), _path, errbuf) << endmsg;
170                         _flags = Flag (_flags & ~Broadcast);
171                         delete _broadcast_info;
172                         _broadcast_info = 0;
173                 }
174         }
175         
176         if (_build_peakfiles) {
177                 if (initialize_peakfile (false, _path)) {
178                         sf_close (sf);
179                         sf = 0;
180                         throw failed_constructor ();
181                 }
182         }
183
184         /* since SndFileSource's constructed with this constructor can be writable, make sure we update if the header info changes */
185
186         if (writable()) {
187                 HeaderPositionOffsetChanged.connect (mem_fun (*this, &AudioFileSource::handle_header_position_change));
188         }
189
190         if (_build_peakfiles) {
191                 if (initialize_peakfile (false, _path)) {
192                         sf_close (sf);
193                         sf = 0;
194                         throw failed_constructor ();
195                 }
196         }
197
198         AudioSourceCreated (this); /* EMIT SIGNAL */
199 }
200
201 void 
202 SndFileSource::init (const string& idstr)
203 {
204         string::size_type pos;
205         string file;
206
207         interleave_buf = 0;
208         interleave_bufsize = 0;
209         sf = 0;
210         _broadcast_info = 0;
211
212         if ((pos = idstr.find_last_of (':')) == string::npos) {
213                 channel = 0;
214                 _name = Glib::path_get_basename (idstr);
215         } else {
216                 channel = atoi (idstr.substr (pos+1).c_str());
217                 _name = Glib::path_get_basename (idstr.substr (0, pos));
218         }
219
220         /* although libsndfile says we don't need to set this,
221            valgrind and source code shows us that we do.
222         */
223
224         memset (&_info, 0, sizeof(_info));
225 }
226
227 int
228 SndFileSource::open ()
229 {
230         if ((sf = sf_open (_path.c_str(), (writable() ? SFM_RDWR : SFM_READ), &_info)) == 0) {
231                 char errbuf[256];
232                 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
233                 error << string_compose(_("SndFileSource: cannot open file \"%1\" for %2 (%3)"), 
234                                         _path, (writable() ? "read+write" : "reading"), errbuf) << endmsg;
235                 return -1;
236         }
237
238         if (channel >= _info.channels) {
239                 error << string_compose(_("SndFileSource: file only contains %1 channels; %2 is invalid as a channel number"), _info.channels, channel) << endmsg;
240                 sf_close (sf);
241                 sf = 0;
242                 return -1;
243         }
244
245         _length = _info.frames;
246
247         if (writable()) {
248                 sf_command (sf, SFC_SET_UPDATE_HEADER_AUTO, 0, SF_FALSE);
249         }
250
251         return 0;
252 }
253
254 void
255 SndFileSource::close ()
256 {
257         if (sf) {
258                 sf_close (sf);
259                 sf = 0;
260         }
261 }
262
263 SndFileSource::~SndFileSource ()
264 {
265         GoingAway (this); /* EMIT SIGNAL */
266
267         close ();
268
269         if (interleave_buf) {
270                 delete [] interleave_buf;
271         }
272
273         if (_broadcast_info) {
274                 delete [] _broadcast_info;
275         }
276 }
277
278 float
279 SndFileSource::sample_rate () const 
280 {
281         return _info.samplerate;
282 }
283
284 jack_nframes_t
285 SndFileSource::read_unlocked (Sample *dst, jack_nframes_t start, jack_nframes_t cnt, char * workbuf) const
286 {
287         int32_t nread;
288         float *ptr;
289         uint32_t real_cnt;
290         jack_nframes_t file_cnt;
291
292         if (start > _length) {
293
294                 /* read starts beyond end of data, just memset to zero */
295                 
296                 file_cnt = 0;
297
298         } else if (start + cnt > _length) {
299                 
300                 /* read ends beyond end of data, read some, memset the rest */
301                 
302                 file_cnt = _length - start;
303
304         } else {
305                 
306                 /* read is entirely within data */
307
308                 file_cnt = cnt;
309         }
310         
311         if (file_cnt) {
312
313                 if (sf_seek (sf, (off_t) start, SEEK_SET) < 0) {
314                         char errbuf[256];
315                         sf_error_str (0, errbuf, sizeof (errbuf) - 1);
316                         error << string_compose(_("SndFileSource: could not seek to frame %1 within %2 (%3)"), start, _name.substr (1), errbuf) << endmsg;
317                         return 0;
318                 }
319                 
320                 if (_info.channels == 1) {
321                         jack_nframes_t ret = sf_read_float (sf, dst, file_cnt);
322                         _read_data_count = cnt * sizeof(float);
323                         return ret;
324                 }
325         }
326
327         if (file_cnt != cnt) {
328                 jack_nframes_t delta = cnt - file_cnt;
329                 memset (dst+file_cnt, 0, sizeof (Sample) * delta);
330         }
331
332         real_cnt = cnt * _info.channels;
333
334         if (interleave_bufsize < real_cnt) {
335                 
336                 if (interleave_buf) {
337                         delete [] interleave_buf;
338                 }
339                 interleave_bufsize = real_cnt;
340                 interleave_buf = new float[interleave_bufsize];
341         }
342         
343         nread = sf_read_float (sf, interleave_buf, real_cnt);
344         ptr = interleave_buf + channel;
345         nread /= _info.channels;
346         
347         /* stride through the interleaved data */
348         
349         for (int32_t n = 0; n < nread; ++n) {
350                 dst[n] = *ptr;
351                 ptr += _info.channels;
352         }
353
354         _read_data_count = cnt * sizeof(float);
355                 
356         return nread;
357 }
358
359 jack_nframes_t 
360 SndFileSource::write_unlocked (Sample *data, jack_nframes_t cnt, char * workbuf)
361 {
362         if (!writable()) {
363                 return 0;
364         }
365
366         if (_info.channels != 1) {
367                 fatal << string_compose (_("programming error: %1 %2"), X_("SndFileSource::write called on non-mono file"), _path) << endmsg;
368                 /*NOTREACHED*/
369                 return 0;
370         }
371         
372         jack_nframes_t oldlen;
373         int32_t frame_pos = _length;
374         
375         if (write_float (data, frame_pos, cnt) != cnt) {
376                 return 0;
377         }
378
379         oldlen = _length;
380         update_length (oldlen, cnt);
381
382         if (_build_peakfiles) {
383                 PeakBuildRecord *pbr = 0;
384                 
385                 if (pending_peak_builds.size()) {
386                                 pbr = pending_peak_builds.back();
387                         }
388                         
389                         if (pbr && pbr->frame + pbr->cnt == oldlen) {
390                                 
391                                 /* the last PBR extended to the start of the current write,
392                                    so just extend it again.
393                                 */
394
395                                 pbr->cnt += cnt;
396                         } else {
397                                 pending_peak_builds.push_back (new PeakBuildRecord (oldlen, cnt));
398                         }
399                         
400                         _peaks_built = false;
401         }
402         
403         
404         if (_build_peakfiles) {
405                 queue_for_peaks (*this);
406         }
407
408         _write_data_count = cnt;
409         
410         return cnt;
411 }
412
413 int
414 SndFileSource::update_header (jack_nframes_t when, struct tm& now, time_t tnow)
415 {       
416         /* allow derived classes to override how this is done */
417
418         set_timeline_position (when);
419
420         if (_flags & Broadcast) {
421                 /* this will flush the header implicitly */
422                 return setup_broadcast_info (when, now, tnow);
423         } else {
424                 return flush_header ();
425         }
426 }
427
428 int
429 SndFileSource::flush_header ()
430 {
431         return (sf_command (sf, SFC_UPDATE_HEADER_NOW, 0, 0) != SF_TRUE);
432 }
433
434 int
435 SndFileSource::setup_broadcast_info (jack_nframes_t when, struct tm& now, time_t tnow)
436 {
437         /* random code is 9 digits */
438         
439         int random_code = random() % 999999999;
440         
441         snprintf (_broadcast_info->originator_reference, sizeof (_broadcast_info->originator_reference), "%2s%3s%12s%02d%02d%02d%9d",
442                   bwf_country_code,
443                   bwf_organization_code,
444                   bwf_serial_number,
445                   now.tm_hour,
446                   now.tm_min,
447                   now.tm_sec,
448                   random_code);
449         
450         snprintf (_broadcast_info->origination_date, sizeof (_broadcast_info->origination_date), "%4d-%02d-%02d",
451                   1900 + now.tm_year,
452                   now.tm_mon,
453                   now.tm_mday);
454         
455         snprintf (_broadcast_info->origination_time, sizeof (_broadcast_info->origination_time), "%02d-%02d-%02d",
456                   now.tm_hour,
457                   now.tm_min,
458                   now.tm_sec);
459
460         /* now update header position taking header offset into account */
461         
462         set_header_timeline_position ();
463
464         /* note that libsndfile flushes the header to disk when resetting the broadcast info */
465
466         if (sf_command (sf, SFC_SET_BROADCAST_INFO, _broadcast_info, sizeof (*_broadcast_info)) != SF_TRUE) {
467                 error << string_compose (_("cannot set broadcast info for audio file %1; Dropping broadcast info for this file"), _path) << endmsg;
468                 _flags = Flag (_flags & ~Broadcast);
469                 delete _broadcast_info;
470                 _broadcast_info = 0;
471                 return -1;
472         }
473
474         return 0;
475 }
476
477 void
478 SndFileSource::set_header_timeline_position ()
479 {
480         uint64_t pos;
481
482         _broadcast_info->time_reference_high = 0;
483
484         if (header_position_negative) {
485
486                 if (ULONG_LONG_MAX - header_position_offset < timeline_position) {
487                         pos = ULONG_LONG_MAX; // impossible
488                 } else {
489                         pos = timeline_position + header_position_offset;
490                 }
491
492         } else {
493
494                 if (timeline_position < header_position_offset) {
495                         pos = 0;
496                 } else {
497                         pos = timeline_position - header_position_offset;
498                 }
499         }
500
501         _broadcast_info->time_reference_high = (pos >> 32);
502         _broadcast_info->time_reference_low = (pos & 0xffffffff);
503 }
504
505 jack_nframes_t
506 SndFileSource::write_float (Sample* data, jack_nframes_t frame_pos, jack_nframes_t cnt)
507 {
508         if (sf_seek (sf, frame_pos, SEEK_SET) != frame_pos) {
509                 error << string_compose (_("%1: cannot seek to %2"), _path, frame_pos) << endmsg;
510                 return 0;
511         }
512         
513         if (sf_writef_float (sf, data, cnt) != (ssize_t) cnt) {
514                 return 0;
515         }
516         
517         return cnt;
518 }