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