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