*** NEW CODING POLICY ***
[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 */
19
20 #include <cstring>
21 #include <cerrno>
22 #include <climits>
23 #include <cstdarg>
24
25 #include <pwd.h>
26 #include <sys/utsname.h>
27 #include <sys/stat.h>
28
29 #include <glibmm/miscutils.h>
30
31 #include "ardour/sndfilesource.h"
32 #include "ardour/sndfile_helpers.h"
33 #include "ardour/utils.h"
34 #include "ardour/version.h"
35
36 #include "i18n.h"
37
38 using namespace std;
39 using namespace ARDOUR;
40 using namespace PBD;
41 using Glib::ustring;
42
43 gain_t* SndFileSource::out_coefficient = 0;
44 gain_t* SndFileSource::in_coefficient = 0;
45 nframes_t SndFileSource::xfade_frames = 64;
46 const Source::Flag SndFileSource::default_writable_flags = Source::Flag (
47                 Source::Writable |
48                 Source::Removable |
49                 Source::RemovableIfEmpty |
50                 Source::CanRename );
51
52 SndFileSource::SndFileSource (Session& s, const XMLNode& node)
53         : Source(s, node)
54         , AudioFileSource (s, node)
55 {
56         init_sndfile ();
57
58         if (open()) {
59                 throw failed_constructor ();
60         }
61 }
62
63 /** Files created this way are never writable or removable */
64 SndFileSource::SndFileSource (Session& s, const ustring& path, bool embedded, int chn, Flag flags)
65         : Source(s, DataType::AUDIO, path, flags)
66         , AudioFileSource (s, path, embedded,
67                         Flag (flags & ~(Writable|Removable|RemovableIfEmpty|RemoveAtDestroy)))
68 {
69         _channel = chn;
70
71         init_sndfile ();
72
73         if (open()) {
74                 throw failed_constructor ();
75         }
76 }
77
78 /** This constructor is used to construct new files, not open existing ones. */
79 SndFileSource::SndFileSource (Session& s, const ustring& path, bool embedded,
80                 SampleFormat sfmt, HeaderFormat hf, nframes_t rate, Flag flags)
81         : Source(s, DataType::AUDIO, path, flags)
82         , AudioFileSource (s, path, embedded, flags, sfmt, hf)
83 {
84         int fmt = 0;
85
86         init_sndfile ();
87
88         _file_is_new = true;
89
90         switch (hf) {
91         case CAF:
92                 fmt = SF_FORMAT_CAF;
93                 _flags = Flag (_flags & ~Broadcast);
94                 break;
95
96         case AIFF:
97                 fmt = SF_FORMAT_AIFF;
98                 _flags = Flag (_flags & ~Broadcast);
99                 break;
100
101         case BWF:
102                 fmt = SF_FORMAT_WAV;
103                 _flags = Flag (_flags | Broadcast);
104                 break;
105
106         case WAVE:
107                 fmt = SF_FORMAT_WAV;
108                 _flags = Flag (_flags & ~Broadcast);
109                 break;
110
111         case WAVE64:
112                 fmt = SF_FORMAT_W64;
113                 _flags = Flag (_flags & ~Broadcast);
114                 break;
115
116         default:
117                 fatal << string_compose (_("programming error: %1"), X_("unsupported audio header format requested")) << endmsg;
118                 /*NOTREACHED*/
119                 break;
120
121         }
122
123         switch (sfmt) {
124         case FormatFloat:
125                 fmt |= SF_FORMAT_FLOAT;
126                 break;
127
128         case FormatInt24:
129                 fmt |= SF_FORMAT_PCM_24;
130                 break;
131
132         case FormatInt16:
133                 fmt |= SF_FORMAT_PCM_16;
134                 break;
135         }
136         
137         _info.channels = 1;
138         _info.samplerate = rate;
139         _info.format = fmt;
140
141         if (open()) {
142                 throw failed_constructor();
143         }
144
145         if (writable() && (_flags & Broadcast)) {
146
147                 if (!_broadcast_info) {
148                         _broadcast_info = new BroadcastInfo;
149                 }
150
151                 _broadcast_info->set_from_session (s, header_position_offset);
152                 _broadcast_info->set_description (string_compose ("BWF %1", _name));
153
154                 if (!_broadcast_info->write_to_file (sf)) {
155                         error << string_compose (_("cannot set broadcast info for audio file %1 (%2); dropping broadcast info for this file"),
156                                                    _path, _broadcast_info->get_error())
157                               << endmsg;
158                         _flags = Flag (_flags & ~Broadcast);
159                         delete _broadcast_info;
160                         _broadcast_info = 0;
161                 }
162         }
163 }
164
165 void 
166 SndFileSource::init_sndfile ()
167 {
168         ustring file;
169
170         // lets try to keep the object initalizations here at the top
171         xfade_buf = 0;
172         sf = 0;
173         _broadcast_info = 0;
174
175         if (is_embedded()) {
176                 _name = _path;
177         } else {
178                 _name = Glib::path_get_basename (_path);
179         }
180
181         /* although libsndfile says we don't need to set this,
182            valgrind and source code shows us that we do.
183         */
184
185         memset (&_info, 0, sizeof(_info));
186
187         _capture_start = false;
188         _capture_end = false;
189         file_pos = 0;
190
191         if (destructive()) {    
192                 xfade_buf = new Sample[xfade_frames];
193                 _timeline_position = header_position_offset;
194         }
195
196         AudioFileSource::HeaderPositionOffsetChanged.connect (
197                         mem_fun (*this, &SndFileSource::handle_header_position_change));
198 }
199
200 int
201 SndFileSource::open ()
202 {
203         if ((sf = sf_open (_path.c_str(), (writable() ? SFM_RDWR : SFM_READ), &_info)) == 0) {
204                 char errbuf[256];
205                 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
206 #ifndef HAVE_COREAUDIO
207                 /* if we have CoreAudio, we will be falling back to that if libsndfile fails,
208                    so we don't want to see this message.
209                 */
210
211                 error << string_compose(_("SndFileSource: cannot open file \"%1\" for %2 (%3)"), 
212                                         _path, (writable() ? "read+write" : "reading"), errbuf) << endmsg;
213 #endif
214                 return -1;
215         }
216
217         if (_channel >= _info.channels) {
218 #ifndef HAVE_COREAUDIO
219                 error << string_compose(_("SndFileSource: file only contains %1 channels; %2 is invalid as a channel number"), _info.channels, _channel) << endmsg;
220 #endif
221                 sf_close (sf);
222                 sf = 0;
223                 return -1;
224         }
225
226         _length = _info.frames;
227
228         if (!_broadcast_info) {
229                 _broadcast_info = new BroadcastInfo;
230         }
231         
232         bool bwf_info_exists = _broadcast_info->load_from_file (sf);
233
234         set_timeline_position (bwf_info_exists ? _broadcast_info->get_time_reference() : header_position_offset);
235
236         if (_length != 0 && !bwf_info_exists) {
237                 delete _broadcast_info;
238                 _broadcast_info = 0;
239                 _flags = Flag (_flags & ~Broadcast);
240         }
241
242         if (writable()) {
243                 sf_command (sf, SFC_SET_UPDATE_HEADER_AUTO, 0, SF_FALSE);
244         }
245
246         return 0;
247 }
248
249 SndFileSource::~SndFileSource ()
250 {
251         GoingAway (); /* EMIT SIGNAL */
252
253         if (sf) {
254                 sf_close (sf);
255                 sf = 0;
256
257                 /* stupid libsndfile updated the headers on close,
258                    so touch the peakfile if it exists and has data
259                    to make sure its time is as new as the audio
260                    file.
261                 */
262
263                 touch_peakfile ();
264         }
265
266         delete _broadcast_info;
267         delete [] xfade_buf;
268 }
269
270 float
271 SndFileSource::sample_rate () const 
272 {
273         return _info.samplerate;
274 }
275
276 nframes_t
277 SndFileSource::read_unlocked (Sample *dst, sframes_t start, nframes_t cnt) const
278 {
279         int32_t nread;
280         float *ptr;
281         uint32_t real_cnt;
282         nframes_t file_cnt;
283
284         if (start > _length) {
285
286                 /* read starts beyond end of data, just memset to zero */
287                 
288                 file_cnt = 0;
289
290         } else if (start + cnt > _length) {
291                 
292                 /* read ends beyond end of data, read some, memset the rest */
293                 
294                 file_cnt = _length - start;
295
296         } else {
297                 
298                 /* read is entirely within data */
299
300                 file_cnt = cnt;
301         }
302         
303         if (file_cnt != cnt) {
304                 nframes_t delta = cnt - file_cnt;
305                 memset (dst+file_cnt, 0, sizeof (Sample) * delta);
306         }
307
308         if (file_cnt) {
309
310                 if (sf_seek (sf, (sf_count_t) start, SEEK_SET|SFM_READ) != (sf_count_t) start) {
311                         char errbuf[256];
312                         sf_error_str (0, errbuf, sizeof (errbuf) - 1);
313                         error << string_compose(_("SndFileSource: could not seek to frame %1 within %2 (%3)"), start, _name.substr (1), errbuf) << endmsg;
314                         return 0;
315                 }
316                 
317                 if (_info.channels == 1) {
318                         nframes_t ret = sf_read_float (sf, dst, file_cnt);
319                         _read_data_count = ret * sizeof(float);
320                         if (ret != file_cnt) {
321                                 char errbuf[256];
322                                 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
323                                 cerr << string_compose(_("SndFileSource: @ %1 could not read %2 within %3 (%4) (len = %5)"), start, file_cnt, _name.substr (1), errbuf, _length) << endl;
324                         }
325                         return ret;
326                 }
327         }
328
329         real_cnt = cnt * _info.channels;
330
331         Sample* interleave_buf = get_interleave_buffer (real_cnt);
332         
333         nread = sf_read_float (sf, interleave_buf, real_cnt);
334         ptr = interleave_buf + _channel;
335         nread /= _info.channels;
336         
337         /* stride through the interleaved data */
338         
339         for (int32_t n = 0; n < nread; ++n) {
340                 dst[n] = *ptr;
341                 ptr += _info.channels;
342         }
343
344         _read_data_count = cnt * sizeof(float);
345                 
346         return nread;
347 }
348
349 nframes_t 
350 SndFileSource::write_unlocked (Sample *data, nframes_t cnt)
351 {
352         if (destructive()) {
353                 return destructive_write_unlocked (data, cnt);
354         } else {
355                 return nondestructive_write_unlocked (data, cnt);
356         }
357 }
358
359 nframes_t 
360 SndFileSource::nondestructive_write_unlocked (Sample *data, nframes_t cnt)
361 {
362         if (!writable()) {
363                 warning << string_compose (_("attempt to write a non-writable audio file source (%1)"), _path) << endmsg;
364                 return 0;
365         }
366
367         if (_info.channels != 1) {
368                 fatal << string_compose (_("programming error: %1 %2"), X_("SndFileSource::write called on non-mono file"), _path) << endmsg;
369                 /*NOTREACHED*/
370                 return 0;
371         }
372         
373         nframes_t oldlen;
374         int32_t frame_pos = _length;
375
376         if (write_float (data, frame_pos, cnt) != cnt) {
377                 return 0;
378         }
379
380         oldlen = _length;
381         update_length (oldlen, cnt);
382
383         if (_build_peakfiles) {
384                 compute_and_write_peaks (data, frame_pos, cnt, false, true);
385         }
386
387         _write_data_count = cnt;
388
389         return cnt;
390 }
391
392 nframes_t
393 SndFileSource::destructive_write_unlocked (Sample* data, nframes_t cnt)
394 {
395         nframes_t old_file_pos;
396
397         if (!writable()) {
398                 warning << string_compose (_("attempt to write a non-writable audio file source (%1)"), _path) << endmsg;
399                 return 0;
400         }
401
402         if (_capture_start && _capture_end) {
403
404                 /* start and end of capture both occur within the data we are writing,
405                    so do both crossfades.
406                 */
407
408                 _capture_start = false;
409                 _capture_end = false;
410                 
411                 /* move to the correct location place */
412                 file_pos = capture_start_frame - _timeline_position;
413                 
414                 // split cnt in half
415                 nframes_t subcnt = cnt / 2;
416                 nframes_t ofilepos = file_pos;
417                 
418                 // fade in
419                 if (crossfade (data, subcnt, 1) != subcnt) {
420                         return 0;
421                 }
422                 
423                 file_pos += subcnt;
424                 Sample * tmpdata = data + subcnt;
425                 
426                 // fade out
427                 subcnt = cnt - subcnt;
428                 if (crossfade (tmpdata, subcnt, 0) != subcnt) {
429                         return 0;
430                 }
431                 
432                 file_pos = ofilepos; // adjusted below
433
434         } else if (_capture_start) {
435
436                 /* start of capture both occur within the data we are writing,
437                    so do the fade in
438                 */
439
440                 _capture_start = false;
441                 _capture_end = false;
442                 
443                 /* move to the correct location place */
444                 file_pos = capture_start_frame - _timeline_position;
445
446                 if (crossfade (data, cnt, 1) != cnt) {
447                         return 0;
448                 }
449                 
450         } else if (_capture_end) {
451
452                 /* end of capture both occur within the data we are writing,
453                    so do the fade out
454                 */
455
456                 _capture_start = false;
457                 _capture_end = false;
458                 
459                 if (crossfade (data, cnt, 0) != cnt) {
460                         return 0;
461                 }
462
463         } else {
464
465                 /* in the middle of recording */
466
467                 if (write_float (data, file_pos, cnt) != cnt) {
468                         return 0;
469                 }
470         }
471
472         old_file_pos = file_pos;
473         update_length (file_pos, cnt);
474
475         if (_build_peakfiles) {
476                 compute_and_write_peaks (data, file_pos, cnt, false, true);
477         }
478
479         file_pos += cnt;
480         
481         return cnt;
482 }
483
484 int
485 SndFileSource::update_header (sframes_t when, struct tm& now, time_t tnow)
486 {       
487         set_timeline_position (when);
488
489         if (_flags & Broadcast) {
490                 if (setup_broadcast_info (when, now, tnow)) {
491                         return -1;
492                 }
493         } 
494
495         return flush_header ();
496 }
497
498 int
499 SndFileSource::flush_header ()
500 {
501         if (!writable() || (sf == 0)) {
502                 warning << string_compose (_("attempt to flush a non-writable audio file source (%1)"), _path) << endmsg;
503                 return -1;
504         }
505         return (sf_command (sf, SFC_UPDATE_HEADER_NOW, 0, 0) != SF_TRUE);
506 }
507
508 int
509 SndFileSource::setup_broadcast_info (sframes_t when, struct tm& now, time_t tnow)
510 {
511         if (!writable()) {
512                 warning << string_compose (_("attempt to store broadcast info in a non-writable audio file source (%1)"), _path) << endmsg;
513                 return -1;
514         }
515
516         if (!(_flags & Broadcast)) {
517                 return 0;
518         }
519
520         _broadcast_info->set_originator_ref ();
521         _broadcast_info->set_origination_time (&now);
522         
523         /* now update header position taking header offset into account */
524         
525         set_header_timeline_position ();
526
527         if (!_broadcast_info->write_to_file (sf)) {
528                 error << string_compose (_("cannot set broadcast info for audio file %1 (%2); dropping broadcast info for this file"),
529                                            _path, _broadcast_info->get_error())
530                       << endmsg;
531                 _flags = Flag (_flags & ~Broadcast);
532                 delete _broadcast_info;
533                 _broadcast_info = 0;
534         }
535
536         return 0;
537 }
538
539 void
540 SndFileSource::set_header_timeline_position ()
541 {
542         if (!(_flags & Broadcast)) {
543                 return;
544         }
545
546         _broadcast_info->set_time_reference (_timeline_position);
547
548         if (!_broadcast_info->write_to_file (sf)) {
549                 error << string_compose (_("cannot set broadcast info for audio file %1 (%2); dropping broadcast info for this file"),
550                                            _path, _broadcast_info->get_error())
551                       << endmsg;
552                 _flags = Flag (_flags & ~Broadcast);
553                 delete _broadcast_info;
554                 _broadcast_info = 0;
555         }
556 }
557
558 nframes_t
559 SndFileSource::write_float (Sample* data, sframes_t frame_pos, nframes_t cnt)
560 {
561         if (sf_seek (sf, frame_pos, SEEK_SET|SFM_WRITE) < 0) {
562                 char errbuf[256];
563                 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
564                 error << string_compose (_("%1: cannot seek to %2 (libsndfile error: %3"), _path, frame_pos, errbuf) << endmsg;
565                 return 0;
566         }
567         
568         if (sf_writef_float (sf, data, cnt) != (ssize_t) cnt) {
569                 return 0;
570         }
571         
572         return cnt;
573 }
574
575 sframes_t
576 SndFileSource::natural_position() const
577 {
578         return _timeline_position;
579 }
580
581 bool
582 SndFileSource::set_destructive (bool yn)
583 {
584         if (yn) {
585                 _flags = Flag (_flags | Destructive);
586                 if (!xfade_buf) {
587                         xfade_buf = new Sample[xfade_frames];
588                 }
589                 clear_capture_marks ();
590                 _timeline_position = header_position_offset;
591         } else {
592                 _flags = Flag (_flags & ~Destructive);
593                 _timeline_position = 0;
594                 /* leave xfade buf alone in case we need it again later */
595         }
596
597         return true;
598 }
599
600 void
601 SndFileSource::clear_capture_marks ()
602 {
603         _capture_start = false;
604         _capture_end = false;
605 }       
606
607 void
608 SndFileSource::mark_capture_start (sframes_t pos)
609 {
610         if (destructive()) {
611                 if (pos < _timeline_position) {
612                         _capture_start = false;
613                 } else {
614                         _capture_start = true;
615                         capture_start_frame = pos;
616                 }
617         }
618 }
619
620 void
621 SndFileSource::mark_capture_end()
622 {
623         if (destructive()) {
624                 _capture_end = true;
625         }
626 }
627
628 nframes_t
629 SndFileSource::crossfade (Sample* data, nframes_t cnt, int fade_in)
630 {
631         nframes_t xfade = min (xfade_frames, cnt);
632         nframes_t nofade = cnt - xfade;
633         Sample* fade_data = 0;
634         nframes_t fade_position = 0; // in frames
635         ssize_t retval;
636         nframes_t file_cnt;
637
638         if (fade_in) {
639                 fade_position = file_pos;
640                 fade_data = data;
641         } else {
642                 fade_position = file_pos + nofade;
643                 fade_data = data + nofade;
644         }
645
646         if (fade_position > _length) {
647                 
648                 /* read starts beyond end of data, just memset to zero */
649                 
650                 file_cnt = 0;
651
652         } else if (fade_position + xfade > _length) {
653                 
654                 /* read ends beyond end of data, read some, memset the rest */
655                 
656                 file_cnt = _length - fade_position;
657
658         } else {
659                 
660                 /* read is entirely within data */
661
662                 file_cnt = xfade;
663         }
664
665         if (file_cnt) {
666                 
667                 if ((retval = read_unlocked (xfade_buf, fade_position, file_cnt)) != (ssize_t) file_cnt) {
668                         if (retval >= 0 && errno == EAGAIN) {
669                                 /* XXX - can we really trust that errno is meaningful here?  yes POSIX, i'm talking to you.
670                                  * short or no data there */
671                                 memset (xfade_buf, 0, xfade * sizeof(Sample));
672                         } else {
673                                 error << string_compose(_("SndFileSource: \"%1\" bad read retval: %2 of %5 (%3: %4)"), _path, retval, errno, strerror (errno), xfade) << endmsg;
674                                 return 0;
675                         }
676                 }
677         } 
678
679         if (file_cnt != xfade) {
680                 nframes_t delta = xfade - file_cnt;
681                 memset (xfade_buf+file_cnt, 0, sizeof (Sample) * delta);
682         }
683         
684         if (nofade && !fade_in) {
685                 if (write_float (data, file_pos, nofade) != nofade) {
686                         error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
687                         return 0;
688                 }
689         }
690
691         if (xfade == xfade_frames) {
692
693                 nframes_t n;
694
695                 /* use the standard xfade curve */
696                 
697                 if (fade_in) {
698
699                         /* fade new material in */
700                         
701                         for (n = 0; n < xfade; ++n) {
702                                 xfade_buf[n] = (xfade_buf[n] * out_coefficient[n]) + (fade_data[n] * in_coefficient[n]);
703                         }
704
705                 } else {
706
707
708                         /* fade new material out */
709                         
710                         for (n = 0; n < xfade; ++n) {
711                                 xfade_buf[n] = (xfade_buf[n] * in_coefficient[n]) + (fade_data[n] * out_coefficient[n]);
712                         }
713                 }
714
715         } else if (xfade < xfade_frames) {
716
717                 gain_t in[xfade];
718                 gain_t out[xfade];
719
720                 /* short xfade, compute custom curve */
721
722                 compute_equal_power_fades (xfade, in, out);
723
724                 for (nframes_t n = 0; n < xfade; ++n) {
725                         xfade_buf[n] = (xfade_buf[n] * out[n]) + (fade_data[n] * in[n]);                
726                 }
727
728         } else if (xfade) {
729
730                 /* long xfade length, has to be computed across several calls */
731
732         }
733
734         if (xfade) {
735                 if (write_float (xfade_buf, fade_position, xfade) != xfade) {
736                         error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
737                         return 0;
738                 }
739         }
740         
741         if (fade_in && nofade) {
742                 if (write_float (data + xfade, file_pos + xfade, nofade) != nofade) {
743                         error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
744                         return 0;
745                 }
746         }
747
748         return cnt;
749 }
750
751 sframes_t
752 SndFileSource::last_capture_start_frame () const
753 {
754         if (destructive()) {
755                 return capture_start_frame;
756         } else {
757                 return 0;
758         }
759 }
760
761 void
762 SndFileSource::handle_header_position_change ()
763 {
764         if (destructive()) {
765                 if ( _length != 0 ) {
766                         error << string_compose(_("Filesource: start time is already set for existing file (%1): Cannot change start time."), _path ) << endmsg;
767                         //in the future, pop up a dialog here that allows user to regenerate file with new start offset
768                 } else if (writable()) {
769                         _timeline_position = header_position_offset;
770                         set_header_timeline_position ();  //this will get flushed if/when the file is recorded to
771                 }
772         }
773 }
774
775 void
776 SndFileSource::setup_standard_crossfades (nframes_t rate)
777 {
778         /* This static method is assumed to have been called by the Session
779            before any DFS's are created.
780         */
781
782         xfade_frames = (nframes_t) floor ((Config->get_destructive_xfade_msecs () / 1000.0) * rate);
783
784         delete [] out_coefficient;
785         delete [] in_coefficient;
786
787         out_coefficient = new gain_t[xfade_frames];
788         in_coefficient = new gain_t[xfade_frames];
789
790         compute_equal_power_fades (xfade_frames, in_coefficient, out_coefficient);
791 }
792
793 void
794 SndFileSource::set_timeline_position (int64_t pos)
795 {
796         // destructive track timeline postion does not change
797         // except at instantion or when header_position_offset 
798         // (session start) changes
799
800         if (!destructive()) {
801                 AudioFileSource::set_timeline_position (pos);
802         } 
803 }
804
805 int
806 SndFileSource::get_soundfile_info (const ustring& path, SoundFileInfo& info, string& error_msg)
807 {
808         SNDFILE *sf;
809         SF_INFO sf_info;
810         BroadcastInfo binfo;
811
812         sf_info.format = 0; // libsndfile says to clear this before sf_open().
813
814         if ((sf = sf_open ((char*) path.c_str(), SFM_READ, &sf_info)) == 0) { 
815                 char errbuf[256];
816                 error_msg = sf_error_str (0, errbuf, sizeof (errbuf) - 1);
817                 return false;
818         }
819
820         info.samplerate  = sf_info.samplerate;
821         info.channels    = sf_info.channels;
822         info.length      = sf_info.frames;
823         info.format_name = string_compose("Format: %1, %2",
824                                            sndfile_major_format(sf_info.format),
825                                            sndfile_minor_format(sf_info.format));
826
827         info.timecode = binfo.load_from_file (sf) ? binfo.get_time_reference() : 0;
828
829         sf_close (sf);
830
831         return true;
832 }
833
834 bool
835 SndFileSource::one_of_several_channels () const
836 {
837         return _info.channels > 1;
838 }
839