CSL  6.0
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, bool doRead)
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  if ( ! doRead)
50  return;
51  try {
52  openForRead(doRead); // read and cache whole file
53  setToEnd();
54  } catch (CException & e) {
55 // logMsg(kLogError, "File open exception caught: %s", e.mMessage.c_str());
56  return;
57  }
58 }
59 
61  : Abst_SoundFile(otherSndFile),
62  mURL(0) { }
63 
64 CASoundFile::CASoundFile(CFURLRef path, bool doRead)
65  : Abst_SoundFile(""),
66  mURL(path) {
67  char theName[CSL_NAME_LEN];
68  CFStringGetCString (CFURLGetString(path), theName, CSL_NAME_LEN, kCFStringEncodingMacRoman);
69  mPath = string(theName);
70  if ( ! doRead)
71  return;
72  try {
73  openForRead(doRead); // read and cache whole file
74  setToEnd();
75  } catch (CException & e) {
76 // logMsg(kLogError, "File open exception caught: %s", e.mMessage.c_str());
77  return;
78  }
79 }
80 
81 CASoundFile::~CASoundFile() { /* no-op */ }
82 
83 // Accessors
84 
86  return kSoundFileFormatAIFF; // ??
87 }
88 
89 // set up the receiver's variables based on the file
90 
92  mIsValid = (mFrameRate != 0.0);
93  if ( ! mIsValid) {
94 // logMsg(kLogError, "Cannot open sound file \"%s\"", mPath.c_str());
95  return;
96  }
97  if (mStart > 0)
99  else
100  mStart = 0;
101  if (mStop < 0)
102  mStop = mNumFrames;
103 }
104 
105 // CASoundFile::openForRead()
106 
107 void CASoundFile::openForRead(bool load) throw (CException) {
108  OSStatus err = noErr;
109  mMode = kSoundFileRead;
110  UInt32 propertySize, fileSize;
111 
112  if (mURL == 0) // create URL is opened with a file name
113  mURL = CFURLCreateFromFileSystemRepresentation(NULL,
114  (const UInt8*) mPath.c_str(), mPath.size(), false);
115  // open audio file
116  AudioFileOpenURL(mURL, 0x01, kAudioFileCAFType, &mSoundID);
117 
118  err = AudioFileGetPropertyInfo(mSoundID, // AudioFileID inAudioFile,
119  kAudioFilePropertyAudioDataPacketCount, // AudioFilePropertyID inPropertyID,
120  &propertySize, // UInt32 *outDataSize,
121  NULL); // UInt32 *isWritable
122  if (err) {
123  printf("\nCASoundFile::openForRead: AudioFileGetPropertyInfo Error = %x\nFile: \"%s\"\n\n",
124  (int) err, mPath.c_str());
125  return;
126  }
127  err = AudioFileGetProperty(mSoundID, kAudioFilePropertyAudioDataPacketCount, &propertySize, &fileSize);
128  if (err) {
129  printf("\nCASoundFile::openForRead: AudioFileGetProperty Error = %x\nFile: \"%s\"\n\n",
130  (int) err, mPath.c_str());
131  return;
132  }
133  AudioStreamBasicDescription fileFormat;
134  propertySize = sizeof(AudioStreamBasicDescription);
135  err = AudioFileGetProperty(mSoundID, kAudioFilePropertyDataFormat, &propertySize, &fileFormat);
136  if (err) {
137  printf("\nCASoundFile::openForRead: AudioFileGetProperty2 Error = %x\nFile: \"%s\"\n\n",
138  (int) err, mPath.c_str());
139  return;
140  }
141 // logMsg("Format = 0x%x", fileFormat.mFormatID);
142 // logMsg("Flags = 0x%x", fileFormat.mFormatFlags);
143  mFrameRate = (unsigned) fileFormat.mSampleRate;
144  mNumChannels = (unsigned) fileFormat.mChannelsPerFrame;
145  mNumFrames = (unsigned) fileSize;
146  mBigEndian = fileFormat.mFormatFlags & kAudioFormatFlagIsBigEndian;
147  mBytesPerSample = (unsigned) fileFormat.mBitsPerChannel / 8;
148  if (mBytesPerSample == 0) {
149  logMsg(kLogError, "Sound file \"%s\" has mBitsPerChannel = 0\n", mPath.c_str());
150  mBytesPerSample = 2;
151  }
152 
153  this->initFromSndfile();
154  if ( ! mIsValid) {
155  logMsg(kLogError, "Cannot open sound file \"%s\"\n", mPath.c_str());
156  return;
157  }
158 
159  if (load && (mNumFrames <= CGestalt::maxSndFileFrames())) { // read file if size < global max
160 // logMsg("Open/read sound file \"%s\" %d frames %g sec %d channels",
161 // mPath.c_str(), duration(), durationInSecs(), channels());
162  this->readBufferFromFile(mNumFrames); //////// read entire file ////////
163  mCurrentFrame = mStart;
164  }
165 }
166 
167 void CASoundFile::openForWrite(SoundFileFormat tformat, unsigned tchannels,
168  unsigned rate, unsigned bitDepth) throw (CException) {
169  logMsg("Error: CASoundFile::openForWrite unsupported");
170  // no-op
171 }
172 
174  freeBuffer();
175 }
176 
177 // read some samples from the file into the temp buffer
178 
179 void CASoundFile::readBufferFromFile(unsigned numFrames) {
180  OSStatus err = noErr;
181  SampleBuffer sampleBufferPtr;
182  unsigned currentFrame = mCurrentFrame;
183  unsigned myChannels = mNumChannels;
184  UInt32 numBytes, fileSize;
185 
186  this->checkBuffer(numFrames); // check my buffer, allocate if necessary
187  // if we are at the end of the file and not looping
188  if ((currentFrame >= (unsigned) mStop) && !mIsLooping) {
189  for (unsigned i = 0; i < mNumChannels; i++) {
190  sampleBufferPtr = mWavetable.buffer(i);
191  memset(sampleBufferPtr, 0, numFrames * sizeof(sample));
192  }
193  return;
194  }
195  fileSize = numFrames;
196  numBytes = numFrames * myChannels * mBytesPerSample;
197  unsigned char * soundBuffer = (unsigned char *) malloc(numBytes); // create a temp buffer to read into
198 
199 // OSStatus AudioFileReadPackets (
200 // AudioFileID inAudioFile,
201 // Boolean inUseCache,
202 // UInt32 *outNumBytes,
203 // AudioStreamPacketDescription *outPacketDescriptions,
204 // SInt64 inStartingPacket,
205 // UInt32 *ioNumPackets,
206 // void *outBuffer
207 // );
208  // read the samples
209  err = AudioFileReadPackets(mSoundID, false, &numBytes, NULL, currentFrame, &fileSize, soundBuffer);
210  if (err) {
211  printf("\nCASoundFile::openForRead: AudioFileReadPackets Error = %x\nFile: \"%s\"\n\n",
212  (int) err, mPath.c_str());
213  return;
214  }
215  if (fileSize != numFrames) {
216  printf("\nCASoundFile::openForRead: read too few samples, %d instead of %d\n",
217  (int) fileSize, numFrames);
218  }
219  // now process the short buffer just read in
220  unsigned short * tBfr = (unsigned short *) soundBuffer;
221  unsigned char * cBfr = soundBuffer;
222  unsigned short uVal;
223  int iVal;
224  short sVal;
225 // printf("\nCASoundFile::readBufferFromFile: %d ch %d bps\n\n", myChannels, mBytesPerSample);
226  if (myChannels == 1) { // copy mono data
227  sample * sampPtr = mWavetable.buffer(0); // this code is lifted from JUCE (AiffAudioFormat.cpp)
228  if (mBytesPerSample == 2) { // 16-bit samples
229  sample scale = 1.0f / 32867.0f;
230  for (unsigned j = 0; j < fileSize; j++) { // copy/scale sample data
231  uVal = *tBfr++;
232  CONVERT_16_BIT(uVal, sVal, mBigEndian);
233  *sampPtr++ = (sample) sVal * scale;
234  }
235  } else if (mBytesPerSample == 3) { // 24-bit samples
236  sample scale = (sample) (1.0 / 8388608.0);
237  for (unsigned j = 0; j < fileSize; j++) { // copy/scale sample data
238  CONVERT_24_BIT(iVal, cBfr, mBigEndian);
239  *sampPtr++ = (sample) iVal * scale;
240  cBfr += 3;
241  }
242  }
243  } else if (myChannels == 2) { // multichannel files: copy/scale, then deinterleave
244  sample * sampBuf = (sample *) malloc(numFrames * myChannels * sizeof(sample));
245  sample * sampPtr = sampBuf;
246  if (mBytesPerSample == 2) { // 16-bit samples
247  sample scale = 1.0f / 32867.0f; // copy/scale sample data
248  for (unsigned j = 0; j < (fileSize * myChannels); j++) {
249  uVal = *tBfr++;
250  CONVERT_16_BIT(uVal, sVal, mBigEndian);
251  *sampPtr++ = (sample) sVal * scale;
252  }
253  } else if (mBytesPerSample == 3) { // 24-bit samples
254  sample scale = (sample) (1.0 / 8388608.0);
255  for (unsigned j = 0; j < (fileSize * myChannels); j++) { // copy/scale sample data
256  CONVERT_24_BIT(iVal, cBfr, mBigEndian);
257  *sampPtr++ = (sample) iVal * scale;
258  cBfr += 3;
259  }
260  }
261  Interleaver interl; // multichannel deinterleaver
262  interl.deinterleave(mWavetable, sampBuf, fileSize, myChannels);
263  free(sampBuf);
264  } // else? ToDo: handle other # channels?
265  free(soundBuffer); // free the temp buffer
266  currentFrame += numFrames;
267 
268  // if we are past the end of the file...
269  if (currentFrame > (unsigned) mStop) {
270  unsigned numFramesRemaining = currentFrame - mStop;
271  unsigned numFramesRead = numFrames - numFramesRemaining;
272 // SampleBuffer tempBufferPtr = sampleBufferPtr + (numFramesRead * myChannels);
273  if (mIsLooping) { // loop back to start of file
274  while (numFramesRead < numFrames) {
275  currentFrame = seekTo(0, kPositionStart);
276  // ToDo: handle loop here // call JUCE read function
277 // mAFReader->read(mWavetable.mBuffers, myChannels, mCurrentFrame, numFrames, false);
278  currentFrame += numFramesRead;
279  }
280  } else {
281  unsigned bytesToClear = numFramesRemaining * sizeof(sample);
282  for (unsigned i = 0; i < mNumChannels; i++) {
283  sampleBufferPtr = mWavetable.buffer(i);
284  memset(sampleBufferPtr, 0, bytesToClear);
285  }
286  }
287  }
288  return;
289 }
290 
291 // Seek
292 
293 unsigned CASoundFile::seekTo(int position, SeekPosition whence) throw(CException) {
294  if (this->isCached())
295  return mCurrentFrame;
296  switch (whence) {
297  case kPositionStart:
298  mCurrentFrame = position;
299  break;
300  case kPositionCurrent:
301  mCurrentFrame = mCurrentFrame - position;
302  break;
303  case kPositionEnd:
304  mCurrentFrame = duration() - position;
305  break;
306  default:
307  mCurrentFrame = SEEK_CUR;
308  logMsg("Error: Invalid position seek flag. Used kPositionCurrent.");
309  break;
310  }
311  return mCurrentFrame;
312 }
313 
314 // write a CSL buffer to the interleaved output file
315 
316 void CASoundFile::writeBuffer(Buffer &inputBuffer) throw(CException) {
317  logMsg("Error: CASoundFile::writeBuffer unsupported");
318  // no-op
319 }
320 
321 // log snd file props
322 
324  const char * nam = path().c_str();
325  if (strlen(nam) > 50)
326  logMsg("SndFile \"%s\"\n\t\t%d Hz, %d ch, %5.3f sec %s",
327  nam, frameRate(), channels(), durationInSecs(),
328  (mBigEndian ? "BigE" : "LittleE"));
329  else
330  logMsg("SndFile \"%s\" - %d Hz, %d ch, %5.3f sec %s",
331  nam, frameRate(), channels(), durationInSecs(),
332  (mBigEndian ? "BigE" : "LittleE"));
333 }
334 
sample * SampleBuffer
1-channel buffer data type, vector of (sample)
Definition: CSL_Types.h:194
signed short int16
Definition: SoundFileCA.cpp:13
void logMsg(const char *format,...)
These are the public logging messages.
Definition: CGestalt.cpp:292
SeekPosition
Enumeration for seek flags.
Definition: CSL_Core.h:560
#define kSoundFileFormatAIFF
Definition: SoundFile.h:60
unsigned mNumFrames
sample frames
Definition: SoundFile.h:189
AudioFileID mSoundID
the CS audio file ID
Definition: SoundFileCA.h:50
unsigned frameRate()
Definition: CSL_Core.h:249
static unsigned mFrameRate
default sample rate (tested up to 96000)
Definition: CGestalt.cpp:32
AdditiveInstrument.h – Sum-of-sines synthesis instrument class.
Definition: Accessor.h:17
void writeBuffer(Buffer &inputBuffer)
write a buffer of data into the file
Interleaver handles copying interleaved sample buffers (like sound files and inter-process sockets) t...
Definition: CSL_Core.h:672
static unsigned maxSndFileFrames()
the max num frames that can be cached
Definition: CGestalt.cpp:56
void openForRead(bool load=true)
signed int int32
Definition: SoundFileCA.cpp:15
void initFromSndfile()
read SF header
Definition: SoundFileCA.cpp:91
#define kSoundFileRead
Sound file constants.
Definition: SoundFile.h:54
unsigned mFrameRate
trigger ignored here
Definition: CSL_Core.h:288
int SoundFileFormat
Definition: SoundFile.h:65
virtual void setToEnd()
set to end position
Definition: SoundFile.cpp:334
virtual SampleBuffer buffer(unsigned bufNum)
convenience accessors for sample buffers
Definition: CSL_Core.cpp:66
signed char int8
Definition: SoundFileCA.cpp:11
string path()
file name
Definition: SoundFile.h:158
CASoundFile(CFURLRef path, bool load=true)
Definition: SoundFileCA.cpp:64
CFURLRef mURL
the full URL of the file
Definition: SoundFileCA.h:49
void deinterleave(Buffer &output, SampleBuffer samples, unsigned numFrames, unsigned numChannels)
De-interleave = copy from interleaved SampleBuffer to CSL Buffer object.
Definition: CSL_Core.cpp:1337
unsigned short uint16
Definition: SoundFileCA.cpp:14
bool mIsValid
is my file valid?
Definition: SoundFile.h:185
bool mBigEndian
flag for endian-ness of sound file (varies byformat)
Definition: SoundFileCA.h:51
#define CSL_NAME_LEN
default string length
Definition: CSL_Types.h:121
unsigned mBytesPerSample
the # of bytes per sample
Definition: SoundFile.h:190
unsigned mCurrentFrame
where I currently am in the buffer
Definition: CSL_Core.h:580
float sample
(could be changed to int, or double)
Definition: CSL_Types.h:191
unsigned mNumChannels
my "expected" number of output channels
Definition: CSL_Core.h:292
virtual void freeBuffer()
free the file cache
Definition: SoundFile.cpp:165
void readBufferFromFile(unsigned numFrames)
read a buffer from the file (possibly all of it)
bool mIsLooping
am i looping start-stop?
Definition: SoundFile.h:186
#define CONVERT_16_BIT(unsV, sinV, bigE)
Definition: SoundFileCA.cpp:18
int mStop
starting/ending frames (or -1 if not used)
Definition: SoundFile.h:187
SoundFileFormat format()
get format
Definition: SoundFileCA.cpp:85
unsigned seekTo(int position, SeekPosition whence)
seek to some position relative to "whence"
Buffer mWavetable
the stored wave form
Definition: Oscillator.h:84
string mPath
file name
Definition: SoundFile.h:182
unsigned int uint32
Definition: SoundFileCA.cpp:16
void close()
close file seek to some position
Buffer – the multi-channel sample buffer class (passed around between generators and IO guys)...
Definition: CSL_Core.h:106
void dump()
log snd file props < open file and get stats; read it if "load"
void checkBuffer(unsigned numFrames)
allocate buffer lazily
Definition: SoundFile.cpp:172
Here's the abstract sound file reader/writer class, a sample player UGen. The concrete subclasses rep...
Definition: SoundFile.h:101
unsigned char uint8
Definition: SoundFileCA.cpp:12
unsigned channels() const
accessors
Definition: SoundFile.cpp:157
float durationInSecs()
number of frames in the sound file
Definition: SoundFile.cpp:161
#define CONVERT_24_BIT(res, ptr, bigE)
Definition: SoundFileCA.cpp:25
void openForWrite(SoundFileFormat format=kSoundFileFormatAIFF, unsigned channels=1, unsigned rate=44100, unsigned bitDepth=16)
Open a file for write. Default values are some common format.
Base class of CSL exceptions (written upper-case). Has a string message.
CoreAudio sound file.
Definition: SoundFileCA.h:20