Proper use of AudioBufferList.
[ardour.git] / libs / ardour / coreaudio_source.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 <pbd/error.h>
21
22 #include <ardour/coreaudio_source.h>
23
24 #include "i18n.h"
25
26 using namespace ARDOUR;
27
28 CoreAudioSource::CoreAudioSource (const XMLNode& node)
29         : ExternalSource (node)
30 {
31         init (_name, true);
32         SourceCreated (this); /* EMIT SIGNAL */
33 }
34
35 CoreAudioSource::CoreAudioSource (const string& idstr, bool build_peak)
36         : ExternalSource(idstr, build_peak)
37 {
38         init (idstr, build_peak);
39
40         if (build_peak) {
41                  SourceCreated (this); /* EMIT SIGNAL */
42         }
43 }
44
45 void 
46 CoreAudioSource::init (const string& idstr, bool build_peak)
47 {
48         string::size_type pos;
49         string file;
50
51         tmpbuf = 0;
52         tmpbufsize = 0;
53         af = 0;
54         OSStatus err = noErr;
55
56         _name = idstr;
57
58         if ((pos = idstr.find_last_of (':')) == string::npos) {
59                 channel = 0;
60                 file = idstr;
61         } else {
62                 channel = atoi (idstr.substr (pos+1).c_str());
63                 file = idstr.substr (0, pos);
64         }
65
66         /* note that we temporarily truncated _id at the colon */
67         FSRef ref;
68         err = FSPathMakeRef ((UInt8*)file.c_str(), &ref, 0);
69         if (err != noErr) {
70                 error << err << endmsg;
71                 throw failed_constructor();
72         }
73
74         err = ExtAudioFileOpen (&ref, &af);
75         if (err != noErr) {
76                 error << err << endmsg;
77                 ExtAudioFileDispose (af);
78                 throw failed_constructor();
79         }
80
81         AudioStreamBasicDescription absd;
82         memset(&absd, 0, sizeof(absd));
83         size_t absd_size = sizeof(absd);
84         err = ExtAudioFileGetProperty(af,
85                         kExtAudioFileProperty_FileDataFormat, &absd_size, &absd);
86         if (err != noErr) {
87                 error << err << endmsg;
88                 ExtAudioFileDispose (af);
89                 throw failed_constructor();
90         }
91         n_channels = absd.mChannelsPerFrame;
92
93         if (channel >= n_channels) {
94                 error << string_compose(_("CoreAudioSource: file only contains %1 channels; %2 is invalid as a channel number"), n_channels, channel) << endmsg;
95                 error << err << endmsg;
96                 ExtAudioFileDispose (af);
97                 throw failed_constructor();
98         }
99
100         int64_t ca_frames;
101         size_t prop_size = sizeof(ca_frames);
102
103         err = ExtAudioFileGetProperty(af, kExtAudioFileProperty_FileLengthFrames, &prop_size, &ca_frames);
104         if (err != noErr) {
105                 error << err << endmsg;
106                 ExtAudioFileDispose (af);
107                 throw failed_constructor();
108         }
109         _length = ca_frames;
110
111         _path = file;
112
113         if (build_peak) {
114                 if (initialize_peakfile (false, file)) {
115                         error << "initialize peakfile failed" << endmsg;
116                         ExtAudioFileDispose (af);
117                         throw failed_constructor ();
118                 }
119         }
120 }
121
122 CoreAudioSource::~CoreAudioSource ()
123 {
124         GoingAway (this); /* EMIT SIGNAL */
125
126         if (af) {
127                 ExtAudioFileDispose (af);
128         }
129
130         if (tmpbuf) {
131                 delete [] tmpbuf;
132         }
133 }
134
135 jack_nframes_t
136 CoreAudioSource::read (Sample *dst, jack_nframes_t start, jack_nframes_t cnt, char * workbuf) const
137 {
138         OSStatus err = noErr;
139
140         err = ExtAudioFileSeek(af, start);
141         if (err != noErr) {
142                 error << string_compose(_("CoreAudioSource: could not seek to frame %1 within %2 (%3)"), start, _name.substr (1), err) << endmsg;
143                 return 0;
144         }
145
146         AudioBufferList abl;
147         abl.mNumberBuffers = 1;
148         abl.mBuffers[0].mNumberChannels = n_channels;
149         abl.mBuffers[0].mDataByteSize = cnt;
150         abl.mBuffers[0].mData = dst;
151
152         if (n_channels == 1) {
153                 err = ExtAudioFileRead(af, (UInt32*) &cnt, &abl);
154                 _read_data_count = cnt * sizeof(float);
155                 return cnt;
156         }
157
158         uint32_t real_cnt = cnt * n_channels;
159
160         {
161                 LockMonitor lm (_tmpbuf_lock, __LINE__, __FILE__);
162                 
163                 if (tmpbufsize < real_cnt) {
164                         
165                         if (tmpbuf) {
166                                 delete [] tmpbuf;
167                         }
168                         tmpbufsize = real_cnt;
169                         tmpbuf = new float[tmpbufsize];
170                 }
171
172                 abl.mBuffers[0].mDataByteSize = real_cnt;
173                 abl.mBuffers[0].mData = tmpbuf;
174                 
175                 err = ExtAudioFileRead(af, (UInt32*) &real_cnt, &abl);
176                 float *ptr = tmpbuf + channel;
177                 real_cnt /= n_channels;
178                 
179                 /* stride through the interleaved data */
180                 
181                 for (uint32_t n = 0; n < real_cnt; ++n) {
182                         dst[n] = *ptr;
183                         ptr += n_channels;
184                 }
185         }
186
187         _read_data_count = cnt * sizeof(float);
188                 
189         return real_cnt;
190 }
191