CSL  5.2
SoundFileJ.cpp
Go to the documentation of this file.
1 //
2 // SoundFile.cpp -- sound file class using JUCE
3 //
4 // See the copyright notice and acknowledgment of authors in the file COPYRIGHT
5 //
6 
7 #include "SoundFileJ.h"
8 
9 using namespace csl;
10 
11 //
12 // JSoundFile implementation
13 //
14 
15 JSoundFile::JSoundFile(string tpath, int tstart, int tstop)
16  : Abst_SoundFile(tpath, tstart, tstop),
17  mAFReader(0),
18  mAFWriter(0),
19  mIOFile(0),
20  mOutStream(0) {
21  try {
22  openForRead(); // read and cache whole file
23  setToEnd();
24  } catch (CException & e) {
25  logMsg(kLogError, "File open exception caught: %s", e.mMessage.c_str());
26  return;
27  }
28 }
29 
30 //JSoundFile::JSoundFile(string folder, string tpath, int tstart, int tstop)
31 // : Abst_SoundFile(folder, tpath, tstart, tstop),
32 // mAFReader(0),
33 // mAFWriter(0),
34 // mIOFile(0),
35 // mOutStream(0) {
36 // try {
37 // openForRead(); // read and cache whole file
38 // setToEnd();
39 // } catch (CException & e) {
40 // logMsg(kLogError, "File open exception caught: %s", e.mMessage.c_str());
41 // return;
42 // }
43 //}
44 
46  : Abst_SoundFile(otherSndFile),
47  mAFReader(otherSndFile.mAFReader),
48  mAFWriter(otherSndFile.mAFWriter),
49  mIOFile(otherSndFile.mIOFile),
50  mOutStream(otherSndFile.mOutStream) { }
51 
53  if (mAFReader)
54  delete mAFReader;
55  if (mAFWriter)
56  delete mAFWriter;
57 }
58 
59 // Accessors
60 
61 unsigned JSoundFile::duration() const {
62  return mAFReader ? (unsigned) mAFReader->lengthInSamples : 0;
63 }
64 
66  String fmt = mAFReader->getFormatName();
67  if (fmt[0] == 'A')
68  return kSoundFileFormatAIFF;
69  else if (fmt[0] == 'W')
70  return kSoundFileFormatWAV;
71  return kSoundFileFormatOther;
72 }
73 
74 // set up the receiver's variables based on the file
75 
77  mIsValid = ((mAFReader != NULL) || (mAFWriter != NULL));
78  if ( ! mIsValid) {
79 // logMsg(kLogError, "Cannot open sound file \"%s\"", mPath.c_str());
80  return;
81  }
82  if (mAFReader) {
83  mFrameRate = (unsigned) mAFReader->sampleRate;
84  mNumChannels = (unsigned) mAFReader->numChannels;
85  mNumFrames = (unsigned) mAFReader->lengthInSamples;
86  mBytesPerSample = (unsigned) mAFReader->bitsPerSample / 8;
87  } else if (mAFWriter) {
88  mFrameRate = (unsigned) mAFWriter->getSampleRate();
89  mNumChannels = (unsigned) mAFWriter->getNumChannels();
90  mNumFrames = 0;
91  mBytesPerSample = (unsigned) mAFWriter->getBitsPerSample() / 8;
92  }
93  if (mStart > 0)
95  else
96  mStart = 0;
97  if ((mStop < 0) && mAFReader)
98  mStop = mAFReader->lengthInSamples;
99 }
100 
101 void JSoundFile::openForRead(bool load) throw (CException) {
102  mMode = kSoundFileRead;
103  String fname(mPath.c_str());
104  mIOFile = new File(fname); // create a JUCE file object
105  if ( ! mIOFile->exists()) {
106  logMsg(kLogError, "Cannot find sound file \"%s\"", mPath.c_str());
107  return;
108  }
109  AudioFormatManager formatManager; // get a format manager
110  formatManager.registerBasicFormats(); // set it up with the basic types (wav and aiff).
111  mAFReader = formatManager.createReaderFor(*mIOFile);
112 
113  this->initFromSndfile();
114  if ( ! mIsValid) {
115  logMsg(kLogError, "Cannot open sound file \"%s\"", mPath.c_str());
116  return;
117  }
118  if (mNumFrames <= CGestalt::maxSndFileFrames()) { // read file if size < global max
119 // logMsg("Read sound file \"%s\" %d frames %g sec %d channels",
120 // mPath.c_str(), duration(), durationInSecs(), channels());
121  unsigned numFrames = mAFReader->lengthInSamples;
122 
123  this->readBufferFromFile(numFrames); // read entire file
124 
125  mCurrentFrame = mStart;
126  }
127 }
128 
129 void JSoundFile::openForWrite(SoundFileFormat tformat, unsigned tchannels, unsigned rate, unsigned bitDepth)
130  throw (CException) {
131  mMode = kSoundFileWrite;
132  String fname(mPath.c_str());
133  mIOFile = new File(fname); // create a JUCE file object
134  mOutStream = mIOFile->createOutputStream();
135  StringPairArray metaDict;
136  AiffAudioFormat afmt;
137  WavAudioFormat wfmt;
138 
139  switch (tformat) {
141  mAFWriter = afmt.createWriterFor(mOutStream, (double) rate, tchannels, bitDepth, metaDict, 0);
142  break;
143  case kSoundFileFormatWAV:
144  mAFWriter = wfmt.createWriterFor(mOutStream, (double) rate, tchannels, bitDepth, metaDict, 0);
145  break;
146  default:
147  logMsg (kLogError, "Unsupported sound file format");
148  throw IOError("Unsupported sound file format");
149  }
150  initFromSndfile();
151 }
152 
154  freeBuffer();
155 }
156 
157 // read some samples from the file into the temp buffer
158 
159 void JSoundFile::readBufferFromFile(unsigned numFrames) {
160  SampleBuffer sampleBufferPtr;
161  unsigned currentFrame = mCurrentFrame;
162  unsigned myChannels = mNumChannels;
163 
164  this->checkBuffer(numFrames); // check my buffer, allocate if necessary
165  // if we are at the end of the file and not looping
166  if ((currentFrame >= (unsigned) mStop) && !mIsLooping) {
167  for (unsigned i = 0; i < mNumChannels; i++) {
168  sampleBufferPtr = mWavetable.monoBuffer(i);
169  memset(sampleBufferPtr, 0, numFrames * sizeof(sample));
170  }
171  return;
172  }
173  // JUCE read fcn
174 // logMsg ("Sound file read %d", numFrames);
175 // if (mAFReader->read(mWavetable.mBuffers, myChannels, mCurrentFrame, numFrames, false)) {
176  if (1) {
177  currentFrame += numFrames;
178  } else {
179  logMsg (kLogError, "Sound file read error");
180  throw IOError("Sound file read error");
181  }
182  // if we are past the end of the file...
183  if (currentFrame > (unsigned) mStop) {
184  unsigned numFramesRemaining = currentFrame - mStop;
185  unsigned numFramesRead = numFrames - numFramesRemaining;
186 // SampleBuffer tempBufferPtr = sampleBufferPtr + (numFramesRead * myChannels);
187  if (mIsLooping) { // loop back to start of file
188  while (numFramesRead < numFrames) {
189  currentFrame = seekTo(0, kPositionStart);
190  // call JUCE read function
191 // TODO LMS commented out mAFReader->read(mWavetable.mBuffers, myChannels, mCurrentFrame, numFrames, false);
192  currentFrame += numFramesRead;
193  }
194  } else {
195  unsigned bytesToClear = numFramesRemaining * sizeof(sample);
196  for (unsigned i = 0; i < mNumChannels; i++) {
197  sampleBufferPtr = mWavetable.monoBuffer(i);
198  memset(sampleBufferPtr, 0, bytesToClear);
199  }
200  }
201  }
202 }
203 
204 //void JSoundFile::convertFormat(unsigned num, unsigned start) {
205 // SampleBuffer sampleBufferPtr;
206 // int * intBufferPtr;
207 // sample normFactor = 1.0 / (float) (32768 << 16);
208 //
209 // for (unsigned channelIndex = 0; channelIndex < mNumChannels; channelIndex++) {
210 /// sampleBufferPtr = mWavetable.monoBuffer(channelIndex) + start;
211 // intBufferPtr = mConvBuffer[channelIndex];
212 // for (unsigned j = 0; j < num; j++) {
213 // *sampleBufferPtr++ = ((float) *intBufferPtr++) * normFactor;
214 // }
215 // }
216 //}
217 
218 // Seek
219 
220 unsigned JSoundFile::seekTo(int position, SeekPosition whence) throw(CException) {
221  int whenceInt;
222  if (this->isCached()) {
223  mCurrentFrame = position;
224  return mCurrentFrame;
225  }
226  switch (whence) {
227  case kPositionStart:
228  whenceInt = position;
229  break;
230  case kPositionCurrent:
231  whenceInt = mCurrentFrame - position;
232  break;
233  case kPositionEnd:
234  whenceInt = duration() - position;
235  break;
236  default:
237  whenceInt = SEEK_CUR;
238  logMsg("Error: Invalid position seek flag. Used kPositionCurrent.");
239  break;
240  }
241  if (mOutStream)
242  mOutStream->setPosition(whenceInt);
243  return mCurrentFrame;
244 }
245 
246 // write a CSL buffer to the interleaved output file
247 
248 void JSoundFile::writeBuffer(Buffer &inputBuffer) throw(CException) {
249  unsigned numFrames = inputBuffer.mNumFrames;
250  // JUCE write fcn
251 // if (mAFWriter->write(inputBuffer.mBuffers, numFrames)) {
252  if (1) {
253  mCurrentFrame += numFrames;
254  } else {
255  logMsg (kLogError, "Sound file write error");
256  throw IOError("Sound file write error");
257  }
258 }
259