Merged with trunk (painfully)
[ardour.git] / libs / ardour / sndfilesource.cc
1 /*
2     Copyright (C) 2006 Paul Davis 
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18     $Id$
19 */
20
21 #include <ardour/sndfilesource.h>
22
23 #include "i18n.h"
24
25 using namespace ARDOUR;
26
27 SndFileSource::SndFileSource (const XMLNode& node)
28         : ExternalSource (node)
29 {
30         init (_name, true);
31         SourceCreated (this); /* EMIT SIGNAL */
32 }
33
34 SndFileSource::SndFileSource (const string& idstr, bool build_peak)
35         : ExternalSource(idstr, build_peak)
36 {
37         init (idstr, build_peak);
38
39         if (build_peak) {
40                  SourceCreated (this); /* EMIT SIGNAL */
41         }
42 }
43
44 void 
45 SndFileSource::init (const string& idstr, bool build_peak)
46 {
47         string::size_type pos;
48         string file;
49
50         tmpbuf = 0;
51         tmpbufsize = 0;
52         sf = 0;
53
54         _name = idstr;
55
56         if ((pos = idstr.find_last_of (':')) == string::npos) {
57                 channel = 0;
58                 file = idstr;
59         } else {
60                 channel = atoi (idstr.substr (pos+1).c_str());
61                 file = idstr.substr (0, pos);
62         }
63
64         /* although libsndfile says we don't need to set this,
65            valgrind and source code shows us that we do.
66         */
67
68         memset (&_info, 0, sizeof(_info));
69
70         /* note that we temporarily truncated _id at the colon */
71         
72         if ((sf = sf_open (file.c_str(), SFM_READ, &_info)) == 0) {
73                 char errbuf[256];
74                 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
75                 error << string_compose(_("SndFileSource: cannot open file \"%1\" (%2)"), file, errbuf) << endmsg;
76                 throw failed_constructor();
77         }
78
79         if (channel >= _info.channels) {
80                 error << string_compose(_("SndFileSource: file only contains %1 channels; %2 is invalid as a channel number"), _info.channels, channel) << endmsg;
81                 sf_close (sf);
82                 sf = 0;
83                 throw failed_constructor();
84         }
85
86         _length = _info.frames;
87         _path = file;
88
89         if (build_peak) {
90                 if (initialize_peakfile (false, _path)) {
91                         sf_close (sf);
92                         sf = 0;
93                         throw failed_constructor ();
94                 }
95         }
96 }
97
98 SndFileSource::~SndFileSource ()
99
100 {
101         GoingAway (this); /* EMIT SIGNAL */
102
103         if (sf) {
104                 sf_close (sf);
105         }
106
107         if (tmpbuf) {
108                 delete [] tmpbuf;
109         }
110 }
111
112 float
113 SndFileSource::sample_rate () const 
114 {
115         return _info.samplerate;
116 }
117
118 jack_nframes_t
119 SndFileSource::read (Sample *dst, jack_nframes_t start, jack_nframes_t cnt, char * workbuf) const
120 {
121         int32_t nread;
122         float *ptr;
123         uint32_t real_cnt;
124
125         if (sf_seek (sf, (off_t) start, SEEK_SET) < 0) {
126                 char errbuf[256];
127                 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
128                 error << string_compose(_("SndFileSource: could not seek to frame %1 within %2 (%3)"), start, _name.substr (1), errbuf) << endmsg;
129                 return 0;
130         }
131
132         if (_info.channels == 1) {
133                 jack_nframes_t ret = sf_read_float (sf, dst, cnt);
134                 _read_data_count = cnt * sizeof(float);
135                 return ret;
136         }
137
138         real_cnt = cnt * _info.channels;
139
140         {
141                 Glib::Mutex::Lock lm (_tmpbuf_lock);
142                 
143                 if (tmpbufsize < real_cnt) {
144                         
145                         if (tmpbuf) {
146                                 delete [] tmpbuf;
147                         }
148                         tmpbufsize = real_cnt;
149                         tmpbuf = new float[tmpbufsize];
150                 }
151                 
152                 nread = sf_read_float (sf, tmpbuf, real_cnt);
153                 ptr = tmpbuf + channel;
154                 nread /= _info.channels;
155                 
156                 /* stride through the interleaved data */
157                 
158                 for (int32_t n = 0; n < nread; ++n) {
159                         dst[n] = *ptr;
160                         ptr += _info.channels;
161                 }
162         }
163
164         _read_data_count = cnt * sizeof(float);
165                 
166         return nread;
167 }
168