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