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