CSL  5.2
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 
56  : Abst_SoundFile(otherSndFile),
57  mURL(0) { }
58 
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 
72  return kSoundFileFormatAIFF; // ??
73 }
74 
75 // set up the receiver's variables based on the file
76 
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)
85  else
86  mStart = 0;
87  if (mStop < 0)
88  mStop = mNumFrames;
89 }
90 
91 // CASoundFile::openForRead()
92 
94  OSStatus err = noErr;
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 
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 
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 
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