CSL 5.2

- Main Page
- Related Pages
- Modules
- Namespaces
- Classes
- Files

- File List
- File Members

SoundFileCA.cpp
Go to the documentation of this file.
1 //
2 // SoundFileCA.cpp -- sound file class using CoreAudio
3 //
4 // See the copyright notice and acknowledgment of authors in the file COPYRIGHT
5 //
6
7 #include "SoundFileCA.h"
8
9 // JUCE types
10
11 typedef signed char int8;
12 typedef unsigned char uint8;
13 typedef signed short int16;
14 typedef unsigned short uint16;
15 typedef signed int int32;
16 typedef unsigned int uint32;
17
18 #define CONVERT_16_BIT(unsV, sinV, bigE) \
19 if (bigE) \
20 unsV = ((unsV << 8) | (unsV >> 8)); \
21 sinV = unsV & 0x7fff; \
22 if (unsV & 0x8000) \
23 sinV += 0x8000
24
25 #define CONVERT_24_BIT(res, ptr, bigE) \
26 if (bigE) \
27 res = (((int) ptr[2]) << 16) \
28 | (((uint32) (uint8) ptr[1]) << 8) \
29 | ((uint32) (uint8) ptr[0]); \
30 else \
31 res = (((int) ptr[0]) << 16) \
32 | (((uint32) (uint8) ptr[1]) << 8) \
33 | ((uint32) (uint8) ptr[2])
34
35 using namespace csl;
36
37 //
38 // CASoundFile implementation
39 //
40
41 CASoundFile::CASoundFile(string tpath, int tstart, int tstop)
42 : Abst_SoundFile(tpath, tstart, tstop),
43 mURL(0) {
44
45 if (tpath.size() > 0) // set up the file URL
46 mURL = CFURLCreateWithFileSystemPath (NULL,
47 (CFStringCreateWithCString (NULL, tpath.c_str(), kCFStringEncodingMacRoman)),
48 kCFURLPOSIXPathStyle, false);
49 }
50
51 CASoundFile::CASoundFile(string folder, string tpath, int start, int stop)
52 : Abst_SoundFile(folder, tpath, start, stop),
53 mURL(0) { }
54
55 CASoundFile::CASoundFile(CASoundFile & otherSndFile)
56 : Abst_SoundFile(otherSndFile),
57 mURL(0) { }
58
59 CASoundFile::CASoundFile(CFURLRef path)
60 : Abst_SoundFile(""),
61 mURL(path) {
62 char theName[CSL_NAME_LEN];
63 CFStringGetCString (CFURLGetString(path), theName, CSL_NAME_LEN, kCFStringEncodingMacRoman);
64 mPath = string(theName);
65 }
66
67 CASoundFile::~CASoundFile() { /* no-op */ }
68
69 // Accessors
70
71 SoundFileFormat CASoundFile::format() {
72 return kSoundFileFormatAIFF; // ??
73 }
74
75 // set up the receiver's variables based on the file
76
77 void CASoundFile::initFromSndfile() {
78 mIsValid = (mFrameRate != 0.0);
79 if ( ! mIsValid) {
80 // logMsg(kLogError, "Cannot open sound file \"%s\"", mPath.c_str());
81 return;
82 }
83 if (mStart > 0)
84 seekTo(mStart, kPositionStart);
85 else
86 mStart = 0;
87 if (mStop < 0)
88 mStop = mNumFrames;
89 }
90
91 // CASoundFile::openForRead()
92
93 void CASoundFile::openForRead() throw (CException) {
94 OSStatus err = noErr;
95 mMode = kSoundFileRead;
96 UInt32 propertySize, fileSize;
97
98 if (mURL == 0) // create URL is opened with a file name
99 mURL = CFURLCreateFromFileSystemRepresentation(NULL,
100 (const UInt8*) mPath.c_str(), mPath.size(), false);
101 // open audio file
102 AudioFileOpenURL(mURL, 0x01, kAudioFileCAFType, &mSoundID);
103
104 err = AudioFileGetPropertyInfo(mSoundID, // AudioFileID inAudioFile,
105 kAudioFilePropertyAudioDataPacketCount, // AudioFilePropertyID inPropertyID,
106 &propertySize, // UInt32 *outDataSize,
107 NULL); // UInt32 *isWritable
108 if (err) {
109 printf("\nCASoundFile::openForRead: AudioFileGetPropertyInfo Error = %x\nFile: \"%s\"\n\n",
110 (int) err, mPath.c_str());
111 return;
112 }
113 err = AudioFileGetProperty(mSoundID, kAudioFilePropertyAudioDataPacketCount, &propertySize, &fileSize);
114 if (err) {
115 printf("\nCASoundFile::openForRead: AudioFileGetProperty Error = %x\nFile: \"%s\"\n\n",
116 (int) err, mPath.c_str());
117 return;
118 }
119 AudioStreamBasicDescription fileFormat;
120 propertySize = sizeof(AudioStreamBasicDescription);
121 err = AudioFileGetProperty(mSoundID, kAudioFilePropertyDataFormat, &propertySize, &fileFormat);
122 if (err) {
123 printf("\nCASoundFile::openForRead: AudioFileGetProperty2 Error = %x\nFile: \"%s\"\n\n",
124 (int) err, mPath.c_str());
125 return;
126 }
127 // logMsg("Format = 0x%x", fileFormat.mFormatID);
128 // logMsg("Flags = 0x%x", fileFormat.mFormatFlags);
129 mFrameRate = (unsigned) fileFormat.mSampleRate;
130 mNumChannels = (unsigned) fileFormat.mChannelsPerFrame;
131 mNumFrames = (unsigned) fileSize;
132 mBigEndian = fileFormat.mFormatFlags & kAudioFormatFlagIsBigEndian;
133 mBytesPerSample = (unsigned) fileFormat.mBitsPerChannel / 8;
134 if (mBytesPerSample == 0) {
135 logMsg(kLogError, "Sound file \"%s\" has mBitsPerChannel = 0\n", mPath.c_str());
136 mBytesPerSample = 2;
137 }
138
139 this->initFromSndfile();
140 if ( ! mIsValid) {
141 logMsg(kLogError, "Cannot open sound file \"%s\"\n", mPath.c_str());
142 return;
143 }
144 if (mNumFrames <= CGestalt::maxSndFileFrames()) { // read file if size < global max
145 // logMsg("Open/read sound file \"%s\" %d frames %g sec %d channels",
146 // mPath.c_str(), duration(), durationInSecs(), channels());
147
148 this->readBufferFromFile(mNumFrames); // read entire file
149
150 mCurrentFrame = mStart;
151 }
152 }
153
154 void CASoundFile::openForWrite(SoundFileFormat tformat, unsigned tchannels,
155 unsigned rate, unsigned bitDepth) throw (CException) {
156 logMsg("Error: CASoundFile::openForWrite unsupported");
157 // no-op
158 }
159
160 void CASoundFile::close() {
161 freeBuffer();
162 }
163
164 // read some samples from the file into the temp buffer
165
166 void CASoundFile::readBufferFromFile(unsigned numFrames) {
167 OSStatus err = noErr;
168 SampleBuffer sampleBufferPtr;
169 unsigned currentFrame = mCurrentFrame;
170 unsigned myChannels = mNumChannels;
171 UInt32 numBytes, fileSize;
172
173 this->checkBuffer(numFrames); // check my buffer, allocate if necessary
174 // if we are at the end of the file and not looping
175 if ((currentFrame >= (unsigned) mStop) && !mIsLooping) {
176 for (unsigned i = 0; i < mNumChannels; i++) {
177 sampleBufferPtr = mWavetable.monoBuffer(i);
178 memset(sampleBufferPtr, 0, numFrames * sizeof(sample));
179 }
180 return;
181 }
182 fileSize = numFrames;
183 numBytes = numFrames * myChannels * mBytesPerSample;
184 unsigned char * soundBuffer = (unsigned char *) malloc(numBytes); // create a temp buffer to read into
185
186 // OSStatus AudioFileReadPackets (
187 // AudioFileID inAudioFile,
188 // Boolean inUseCache,
189 // UInt32 *outNumBytes,
190 // AudioStreamPacketDescription *outPacketDescriptions,
191 // SInt64 inStartingPacket,
192 // UInt32 *ioNumPackets,
193 // void *outBuffer
194 // );
195 // read the samples
196 err = AudioFileReadPackets(mSoundID, false, &numBytes, NULL, currentFrame, &fileSize, soundBuffer);
197 if (err) {
198 printf("\nCASoundFile::openForRead: AudioFileReadPackets Error = %x\nFile: \"%s\"\n\n",
199 (int) err, mPath.c_str());
200 return;
201 }
202 if (fileSize != numFrames) {
203 printf("\nCASoundFile::openForRead: read too few samples, %d instead of %d\n",
204 (int) fileSize, numFrames);
205 }
206 // now process the short buffer just read in
207 unsigned short * tBfr = (unsigned short *) soundBuffer;
208 unsigned char * cBfr = soundBuffer;
209 unsigned short uVal;
210 int iVal;
211 short sVal;
212 // printf("\nCASoundFile::readBufferFromFile: %d ch %d bps\n\n", myChannels, mBytesPerSample);
213 if (myChannels == 1) { // copy mono data
214 sample * sampPtr = mWavetable.buffer(0]; // this code is lifted from JUCE (AiffAudioFormat.cpp)
215 if (mBytesPerSample == 2) { // 16-bit samples
216 sample scale = 1.0f / 32867.0f;
217 for (unsigned j = 0; j < fileSize; j++) { // copy/scale sample data
218 uVal = *tBfr++;
219 CONVERT_16_BIT(uVal, sVal, mBigEndian);
220 *sampPtr++ = (sample) sVal * scale;
221 }
222 } else if (mBytesPerSample == 3) { // 24-bit samples
223 sample scale = (sample) (1.0 / 8388608.0);
224 for (unsigned j = 0; j < fileSize; j++) { // copy/scale sample data
225 CONVERT_24_BIT(iVal, cBfr, mBigEndian);
226 *sampPtr++ = (sample) iVal * scale;
227 cBfr += 3;
228 }
229 }
230 } else if (myChannels == 2) { // multichannel files: copy/scale, then deinterleave
231 sample * sampBuf = (sample *) malloc(numFrames * myChannels * sizeof(sample));
232 sample * sampPtr = sampBuf;
233 if (mBytesPerSample == 2) { // 16-bit samples
234 sample scale = 1.0f / 32867.0f; // copy/scale sample data
235 for (unsigned j = 0; j < (fileSize * myChannels); j++) {
236 uVal = *tBfr++;
237 CONVERT_16_BIT(uVal, sVal, mBigEndian);
238 *sampPtr++ = (sample) sVal * scale;
239 }
240 } else if (mBytesPerSample == 3) { // 24-bit samples
241 sample scale = (sample) (1.0 / 8388608.0);
242 for (unsigned j = 0; j < (fileSize * myChannels); j++) { // copy/scale sample data
243 CONVERT_24_BIT(iVal, cBfr, mBigEndian);
244 *sampPtr++ = (sample) iVal * scale;
245 cBfr += 3;
246 }
247 }
248 Interleaver interl; // multichannel deinterleaver
249 interl.deinterleave(mWavetable, sampBuf, fileSize, myChannels);
250 free(sampBuf);
251 } // else? ToDo: handle other # channels?
252 free(soundBuffer); // free the temp buffer
253 currentFrame += numFrames;
254
255 // if we are past the end of the file...
256 if (currentFrame > (unsigned) mStop) {
257 unsigned numFramesRemaining = currentFrame - mStop;
258 unsigned numFramesRead = numFrames - numFramesRemaining;
259 // SampleBuffer tempBufferPtr = sampleBufferPtr + (numFramesRead * myChannels);
260 if (mIsLooping) { // loop back to start of file
261 while (numFramesRead < numFrames) {
262 currentFrame = seekTo(0, kPositionStart);
263 // ToDo: handle loop here // call JUCE read function
264 // mAFReader->read(mWavetable.mBuffers, myChannels, mCurrentFrame, numFrames, false);
265 currentFrame += numFramesRead;
266 }
267 } else {
268 unsigned bytesToClear = numFramesRemaining * sizeof(sample);
269 for (unsigned i = 0; i < mNumChannels; i++) {
270 sampleBufferPtr = mWavetable.monoBuffer(i);
271 memset(sampleBufferPtr, 0, bytesToClear);
272 }
273 }
274 }
275 return;
276 }
277
278 // Seek
279
280 unsigned CASoundFile::seekTo(int position, SeekPosition whence) throw(CException) {
281 if (this->isCached())
282 return mCurrentFrame;
283 switch (whence) {
284 case kPositionStart:
285 mCurrentFrame = position;
286 break;
287 case kPositionCurrent:
288 mCurrentFrame = mCurrentFrame - position;
289 break;
290 case kPositionEnd:
291 mCurrentFrame = duration() - position;
292 break;
293 default:
294 mCurrentFrame = SEEK_CUR;
295 logMsg("Error: Invalid position seek flag. Used kPositionCurrent.");
296 break;
297 }
298 return mCurrentFrame;
299 }
300
301 // write a CSL buffer to the interleaved output file
302
303 void CASoundFile::writeBuffer(Buffer &inputBuffer) throw(CException) {
304 logMsg("Error: CASoundFile::writeBuffer unsupported");
305 // no-op
306 }
307
308 // log snd file props
309
310 void CASoundFile::dump() {
311 const char * nam = path().c_str();
312 if (strlen(nam) > 50)
313 logMsg("SndFile \"%s\"\n\t\t%d Hz, %d ch, %5.3f sec %s",
314 nam, frameRate(), channels(), durationInSecs(),
315 (mBigEndian ? "BigE" : "LittleE"));
316 else
317 logMsg("SndFile \"%s\" - %d Hz, %d ch, %5.3f sec %s",
318 nam, frameRate(), channels(), durationInSecs(),
319 (mBigEndian ? "BigE" : "LittleE"));
320 }
321