CSL  6.0
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 // Factory method
16 
17 JSoundFile * JSoundFile::openSndfile(string tpath, int tstart, int tstop, bool doRead) {
18  return new JSoundFile(tpath, tstart, tstop);
19 }
20 
21 //JSoundFile * JSoundFile::openSndfile(string path, juce::MemoryInputStream * strm) {
22 // return new JSoundFile(path, strm);
23 //}
24 
25 
26 //JSoundFile * JSoundFile::openSndfile(float maxDurInSecs, string tpath) {
27 // return new JSoundFile(maxDurInSecs, tpath);
28 //}
29 
30 JSoundFile::JSoundFile(string tpath, int tstart, int tstop)
31  : Abst_SoundFile(tpath, tstart, tstop),
32  mAFReader(0),
33  mAFWriter(0),
34  mIOFile(0) {
35  try {
36  openForRead(); // read and cache whole file
37  setToEnd();
38  } catch (CException & e) {
39  logMsg(kLogError, "File open exception caught: %s", e.mMessage.c_str());
40  return;
41  }
42 }
43 
44 //JSoundFile::JSoundFile(string tpath, juce::MemoryInputStream * strm)
45 // : Abst_SoundFile(tpath, -1, -1),
46 // mAFReader(0),
47 // mAFWriter(0),
48 // mIOFile(0) {
49 // try {
50 // openForRead(strm); // read and cache whole file
51 // setToEnd();
52 // } catch (CException & e) {
53 // logMsg(kLogError, "File open exception caught: %s", e.mMessage.c_str());
54 // return;
55 // }
56 //}
57 
58 //JSoundFile::JSoundFile(string folder, string tpath, int tstart, int tstop)
59 // : Abst_SoundFile(folder, tpath, tstart, tstop),
60 // mAFReader(0),
61 // mAFWriter(0),
62 // mIOFile(0),
63 // mOutStream(0) {
64 // try {
65 // openForRead(); // read and cache whole file
66 // setToEnd();
67 // } catch (CException & e) {
68 // logMsg(kLogError, "File open exception caught: %s", e.mMessage.c_str());
69 // return;
70 // }
71 //}
72 
74  : Abst_SoundFile(otherSndFile),
75  mAFReader(otherSndFile.mAFReader),
76  mAFWriter(otherSndFile.mAFWriter),
77  mIOFile(otherSndFile.mIOFile) { }
78 
79 // d'tor cleans up poinger members
80 
82  if (mAFReader)
83  delete mAFReader;
84  if (mAFWriter)
85  delete mAFWriter;
86 // if (mOutStream)
87 // delete mOutStream;
88  if (mIOFile)
89  delete mIOFile;
90 }
91 
92 // Accessors
93 
94 unsigned JSoundFile::duration() const {
95  return mAFReader ? (unsigned) mAFReader->lengthInSamples : 0;
96 }
97 
99  juce::String fmt = mAFReader->getFormatName();
100  if (fmt[0] == 'A')
101  return kSoundFileFormatAIFF;
102  else if (fmt[0] == 'W')
103  return kSoundFileFormatWAV;
104  return kSoundFileFormatOther;
105 }
106 
107 // set up the receiver's variables based on the file
108 
110  mIsValid = ((mAFReader != NULL) || (mAFWriter != NULL));
111  if ( ! mIsValid) {
112 // logMsg(kLogError, "Cannot open sound file \"%s\"", mPath.c_str());
113  return;
114  }
115  if (mAFReader) {
116  mFrameRate = (unsigned) mAFReader->sampleRate;
117  mNumChannels = (unsigned) mAFReader->numChannels;
118  mNumFrames = (unsigned) mAFReader->lengthInSamples;
119  mBytesPerSample = (unsigned) mAFReader->bitsPerSample / 8;
120  } else if (mAFWriter) {
121  mFrameRate = (unsigned) mAFWriter->getSampleRate();
122  mNumChannels = (unsigned) mAFWriter->getNumChannels();
123  mNumFrames = 0;
124  mBytesPerSample = (unsigned) mAFWriter->getBitsPerSample() / 8;
125  }
126  if (mStart > 0)
128  else
129  mStart = 0;
130  if ((mStop < 0) && mAFReader)
131  mStop = (int) mAFReader->lengthInSamples;
132 }
133 
134 void JSoundFile::openForRead(bool load) throw (CException) {
135  mMode = kSoundFileRead;
136  juce::String fname(mPath.c_str());
137  mIOFile = new juce::File(fname); // create a JUCE file object
138  if ( ! mIOFile->exists()) {
139 // logMsg(kLogError, "Cannot find sound file \"%s\"", mPath.c_str());
140  return;
141  }
142  juce::AudioFormatManager formatManager; // get a format manager
143  formatManager.registerBasicFormats(); // set it up with the basic types (wav and aiff).
144  mAFReader = formatManager.createReaderFor(*mIOFile);
145 
146  this->initFromSndfile();
147  if ( ! mIsValid) {
148  logMsg(kLogError, "Cannot open sound file \"%s\"", mPath.c_str());
149  return;
150  }
151  if (mNumFrames <= CGestalt::maxSndFileFrames()) { // read file if size < global max
152 // logMsg("Read sound file \"%s\" %d frames %g sec %d channels",
153 // mPath.c_str(), duration(), durationInSecs(), channels());
154  unsigned numFrames = (unsigned) mAFReader->lengthInSamples;
155 
156  this->readBufferFromFile(numFrames); // read entire file
157 
158  mCurrentFrame = mStart;
159  }
160 }
161 
162 // openForRead given a MemoryInputStream
163 
164 //void JSoundFile::openForRead(juce::MemoryInputStream * memStrm) throw (CException) {
165 // mMode = kSoundFileRead;
166 //// String fname(mPath.c_str());
167 //// mIOFile = new File(fname); // create a JUCE file object
168 //// if ( ! mIOFile->exists()) {
169 ////// logMsg(kLogError, "Cannot find sound file \"%s\"", mPath.c_str());
170 //// return;
171 //// }
172 // juce::AudioFormatManager formatManager; // get a format manager
173 // formatManager.registerBasicFormats(); // set it up with the basic types (wav and aiff).
174 // unique_ptr<juce::MemoryInputStream> mems2 = std::move(memStrm->get());
175 // mAFReader = formatManager.createReaderFor(mems2);
176 //
177 // this->initFromSndfile();
178 // if ( ! mIsValid) {
179 // logMsg(kLogError, "Cannot open sound file \"%s\"", mPath.c_str());
180 // return;
181 // }
182 // if (mNumFrames <= CGestalt::maxSndFileFrames()) { // read file if size < global max
183 //// logMsg("Read sound file \"%s\" %d frames %g sec %d channels",
184 //// mPath.c_str(), duration(), durationInSecs(), channels());
185 // unsigned numFrames = mAFReader->lengthInSamples;
186 //
187 // this->readBufferFromFile(numFrames); // read entire file
188 //
189 // mCurrentFrame = mStart;
190 // }
191 //}
192 
193 void JSoundFile::openForWrite(SoundFileFormat tformat, unsigned tchannels, unsigned rate, unsigned bitDepth)
194  throw (CException) {
195  mMode = kSoundFileWrite;
196  juce::String fname(mPath.c_str());
197  mIOFile = new juce::File(fname); // create a JUCE file object
198  mOutStream = mIOFile->createOutputStream().get();
199  juce::StringPairArray metaDict;
200  juce::AiffAudioFormat afmt;
201  juce::WavAudioFormat wfmt;
202 // createWriterFor (OutputStream* out, double sampleRate, unsigned int numberOfChannels,
203 // int bitsPerSample, const StringPairArray& metadataValues, int /*qualityOptionIndex*/)
204  switch (tformat) {
206  mAFWriter = afmt.createWriterFor(mOutStream, (double) rate, tchannels, bitDepth, metaDict, 0);
207  break;
208  case kSoundFileFormatWAV:
209  mAFWriter = wfmt.createWriterFor(mOutStream, (double) rate, tchannels, bitDepth, metaDict, 0);
210  break;
211  default:
212  logMsg (kLogError, "Unsupported sound file format");
213  throw IOError("Unsupported sound file format");
214  }
215  initFromSndfile();
216 }
217 
219  freeBuffer();
220  if (mMode == kSoundFileWrite)
221  mOutStream->flush();
222 }
223 
224 // read some samples from the file into the temp buffer
225 
226 void JSoundFile::readBufferFromFile(unsigned numFrames) {
227  SampleBuffer sampleBufferPtr;
228  unsigned currentFrame = mCurrentFrame;
229  unsigned myChannels = mNumChannels;
230 
231  this->checkBuffer(numFrames); // check my buffer, allocate if necessary
232  // if we are at the end of the file and not looping
233  if ((currentFrame >= (unsigned) mStop) && !mIsLooping) {
234  for (unsigned i = 0; i < mNumChannels; i++) {
235  sampleBufferPtr = mWavetable.buffer(i);
236  memset(sampleBufferPtr, 0, numFrames * sizeof(sample));
237  }
238  return;
239  }
240  // JUCE read fcn
241 // logMsg ("JSound file read %d", numFrames);
242  // create a temp AudioSampleBuffer for the wavetable
243  juce::AudioSampleBuffer asBuffer(mWavetable.buffers(), myChannels, numFrames);
244  mAFReader->read(&asBuffer, mCurrentFrame, numFrames, 0, true, true);
245  currentFrame += numFrames;
246 // } else {
247 // logMsg (kLogError, "Sound file read error");
248 // throw IOError("Sound file read error");
249 // }
250  // if we are past the end of the file...
251  if (currentFrame > (unsigned) mStop) {
252  unsigned numFramesRemaining = currentFrame - mStop;
253  unsigned numFramesRead = numFrames - numFramesRemaining;
254 // SampleBuffer tempBufferPtr = sampleBufferPtr + (numFramesRead * myChannels);
255  if (mIsLooping) { // loop back to start of file
256  while (numFramesRead < numFrames) {
257  currentFrame = seekTo(0, kPositionStart);
258  // call JUCE read function
259 // mAFReader->read(mWavetable.buffers(), myChannels, mCurrentFrame, numFrames, false);
260  mAFReader->read(&asBuffer, mCurrentFrame, numFrames, 0, true, true);
261  currentFrame += numFramesRead;
262  }
263  } else {
264  unsigned bytesToClear = numFramesRemaining * sizeof(sample);
265  for (unsigned i = 0; i < mNumChannels; i++) {
266  sampleBufferPtr = mWavetable.buffer(i);
267  memset(sampleBufferPtr, 0, bytesToClear);
268  }
269  }
270  }
271 }
272 
273 //void JSoundFile::convertFormat(unsigned num, unsigned start) {
274 // SampleBuffer sampleBufferPtr;
275 // int * intBufferPtr;
276 // sample normFactor = 1.0 / (float) (32768 << 16);
277 //
278 // for (unsigned channelIndex = 0; channelIndex < mNumChannels; channelIndex++) {
279 /// sampleBufferPtr = mWavetable.buffer(channelIndex) + start;
280 // intBufferPtr = mConvBuffer[channelIndex];
281 // for (unsigned j = 0; j < num; j++) {
282 // *sampleBufferPtr++ = ((float) *intBufferPtr++) * normFactor;
283 // }
284 // }
285 //}
286 
287 // Seek
288 
289 unsigned JSoundFile::seekTo(int position, SeekPosition whence) throw(CException) {
290  int whenceInt;
291  if (this->isCached()) {
292  mCurrentFrame = position;
293  return mCurrentFrame;
294  }
295  switch (whence) {
296  case kPositionStart:
297  whenceInt = position;
298  break;
299  case kPositionCurrent:
300  whenceInt = mCurrentFrame - position;
301  break;
302  case kPositionEnd:
303  whenceInt = duration() - position;
304  break;
305  default:
306  whenceInt = SEEK_CUR;
307  logMsg("Error: Invalid position seek flag. Used kPositionCurrent.");
308  break;
309  }
310  if (mOutStream)
311  mOutStream->setPosition(whenceInt);
312  return mCurrentFrame;
313 }
314 
315 // write a CSL buffer to the interleaved output file
316 
317 void JSoundFile::writeBuffer(Buffer &inputBuffer) throw(CException) {
318  unsigned numFrames = inputBuffer.mNumFrames;
319  unsigned numChans = inputBuffer.mNumChannels;
320  // use an AudioSampleBuffer
321  juce::AudioSampleBuffer asb(numChans, numFrames);
322  asb.setDataToReferTo (inputBuffer.buffers(),numChans, numFrames);
323  mAFWriter->writeFromAudioSampleBuffer (asb, 0, numFrames);
324 // if (mAFWriter->writeFromFloatArrays((const float **)inputBuffer.buffers(), 1, numFrames)) {
325 // mCurrentFrame += numFrames;
326 // } else {
327 // logMsg (kLogError, "Sound file write error");
328 // throw IOError("Sound file write error");
329 // }
330 }
331 
332 // write a CSL buffer to the interleaved output file
333 
334 void JSoundFile::writeBuffer(Buffer &inputBuffer, unsigned fromFrame, unsigned toFrame) throw(CException) {
335  unsigned numFrames = inputBuffer.mNumFrames;
336  // JUCE write fcn
337 // writeFromFloatArrays (const float **channels, int numChannels, int numSamples)
338  SampleBufferArray inp = inputBuffer.buffers();
339  for (unsigned i = 0; i < mNumChannels; i++)
340  inp[i] += fromFrame;
341 // if (mAFWriter->writeFromFloatArrays((const float **)inp, 1, (toFrame - fromFrame))) {
342 // mCurrentFrame += numFrames;
343 // } else {
344 // logMsg (kLogError, "Sound file write error");
345  throw IOError("Sound file write error");
346 // }
347  for (unsigned i = 0; i < mNumChannels; i++)
348  inp[i] -= fromFrame;
349 }
350 
void writeBuffer(Buffer &inputBuffer)
write a buffer of data into the file
Definition: SoundFileJ.cpp:317
sample * SampleBuffer
1-channel buffer data type, vector of (sample)
Definition: CSL_Types.h:194
juce::AudioFormatReader * mAFReader
my reader
Definition: SoundFileJ.h:51
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
JSoundFile(string path, int start=-1, int stop=-1)
C'tors.
Definition: SoundFileJ.cpp:30
AdditiveInstrument.h – Sum-of-sines synthesis instrument class.
Definition: Accessor.h:17
SoundFileMode mMode
r/w mode
Definition: SoundFile.h:183
static unsigned maxSndFileFrames()
the max num frames that can be cached
Definition: CGestalt.cpp:56
#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
unsigned duration() const
number of frames in the sound file
Definition: SoundFileJ.cpp:94
juce::File * mIOFile
my JUCE file
Definition: SoundFileJ.h:53
void readBufferFromFile(unsigned numFrames)
read a buffer from the file (possibly all of it)
Definition: SoundFileJ.cpp:226
void openForRead(bool load=true)
Definition: SoundFileJ.cpp:134
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.
Definition: SoundFileJ.cpp:193
bool mIsValid
is my file valid?
Definition: SoundFile.h:185
JUCE sound file.
Definition: SoundFileJ.h:19
unsigned mBytesPerSample
the # of bytes per sample
Definition: SoundFile.h:190
#define kSoundFileFormatOther
Definition: SoundFile.h:64
void initFromSndfile()
read SF header
Definition: SoundFileJ.cpp:109
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
#define kSoundFileWrite
Definition: SoundFile.h:55
#define kSoundFileFormatWAV
Definition: SoundFile.h:59
virtual void freeBuffer()
free the file cache
Definition: SoundFile.cpp:165
bool mIsLooping
am i looping start-stop?
Definition: SoundFile.h:186
int mStop
starting/ending frames (or -1 if not used)
Definition: SoundFile.h:187
juce::AudioFormatWriter * mAFWriter
and my writer
Definition: SoundFileJ.h:52
Buffer mWavetable
the stored wave form
Definition: Oscillator.h:84
IO Error.
void close()
close file seek to some position
Definition: SoundFileJ.cpp:218
juce::FileOutputStream * mOutStream
Definition: SoundFileJ.h:54
Buffer – the multi-channel sample buffer class (passed around between generators and IO guys)...
Definition: CSL_Core.h:106
void checkBuffer(unsigned numFrames)
allocate buffer lazily
Definition: SoundFile.cpp:172
SoundFileFormat format()
get format open file and get stats
Definition: SoundFileJ.cpp:98
Here's the abstract sound file reader/writer class, a sample player UGen. The concrete subclasses rep...
Definition: SoundFile.h:101
virtual SampleBuffer * buffers()
Definition: CSL_Core.h:155
static JSoundFile * openSndfile(string path, int start=-1, int stop=-1, bool doRead=true)
Factory methods.
Definition: SoundFileJ.cpp:17
Base class of CSL exceptions (written upper-case). Has a string message.
SampleBuffer * SampleBufferArray
Multi-channel buffer data type.
Definition: CSL_Types.h:196
unsigned seekTo(int position, SeekPosition whence)
sampleBufferPtr = mWavetable.buffer(channelIndex) + start;
Definition: SoundFileJ.cpp:289