CSL  5.2
SoundFileL.cpp
Go to the documentation of this file.
1 //
2 // SoundFileL.cpp -- sound file class using libsndfile
3 // See the copyright notice and acknowledgment of authors in the file COPYRIGHT
4 //
5 
6 #include "SoundFileL.h"
7 #include "math.h"
8 #include "SoundFileMP3.h"
9 
10 using namespace csl;
11 
12 
13 /// Sound file name extensions we recognize - ToDo: should this be in a config file?
14 
15 const char * gSndFileExts[] = { "mp3", "wav", "aiff", "aif", "mp4", "m4p", "m4b", "m4a", "aac", "flac", 0 };
16 
17 ///< Answer whether the given name looks like a snd file
18 
19 bool LSoundFile::isSndfileName(const char * path) {
20  const char * dot = strrchr(path, '.'); // find file name ext
21  if ( ! dot)
22  return false;
23  dot++;
24  if ( ! dot)
25  return false;
26  for (unsigned i = 0; gSndFileExts[i]; i++) {
27  if(strcasestr(dot, gSndFileExts[i]))
28  return true;
29  }
30  return false;
31 }
32 
33 ///< Answer the snd file type
34 
36  const char * lastDot = strrchr(path, '.'); // find the last dot in the name
37  if ( ! lastDot)
38  return kSoundFileFormatOther;
39  lastDot++;
40  if ( ! lastDot)
41  return kSoundFileFormatOther;
42  if (strcasestr(lastDot, "wav")) // guess the file type from the file name
43  return kSoundFileFormatWAV;
44  if (strcasestr(lastDot, "mp3"))
45  return kSoundFileFormatMP3;
46  if (strcasestr(lastDot, "aif"))
47  return kSoundFileFormatAIFF;
48  if (strcasestr(lastDot, "aiff"))
49  return kSoundFileFormatAIFF;
50  if (strcasestr(lastDot, "m4a"))
51  return kSoundFileFormatMP4;
52  if (strcasestr(lastDot, "mp4"))
53  return kSoundFileFormatMP4;
54  if (strcasestr(lastDot, "m4b"))
55  return kSoundFileFormatMP4;
56  if (strcasestr(lastDot, "aac"))
57  return kSoundFileFormatAAC;
58  if (strcasestr(lastDot, "flac"))
59  return kSoundFileFormatFLAC;
60  if (strcasestr(lastDot, "caf"))
61  return kSoundFileFormatCAF;
62  if (strcasestr(lastDot, "ogg"))
63  return kSoundFileFormatOGG;
64  if (strcasestr(lastDot, "shn"))
65  return kSoundFileFormatSHN;
66  if (strcasestr(lastDot, "snd"))
67  return kSoundFileFormatSND;
68  return kSoundFileFormatOther;
69 }
70 
71 // Answer the MIME type based on the file name
72 
73 const char * LSoundFile::mimeType(const char * path) {
75  switch (fmt) {
76  case kSoundFileFormatAIFF: return "audio/x-aiff";
77  case kSoundFileFormatMP3: return "audio/mpeg";
78  case kSoundFileFormatMP4: return "audio/mp4a-latm";
79  case kSoundFileFormatWAV: return "audio/x-wav";
80  default: return "audio/basic";
81  }
82 }
83 
84 // Factory method
85 
86 LSoundFile * LSoundFile::openSndfile(string tpath, int tstart, int tstop, bool doRead) {
87  const char * fname = tpath.c_str();
88 #ifdef USE_libMAD
89  if (strcasestr(fname, ".mp3")) { // if an MP3
90  return new MP3File(tpath, tstart, tstop);
91  } // if an MP4/AAC
92 #endif
93 #ifdef USE_libFAAD
94  if ((strcasestr(fname, ".mp4")) || (strcasestr(fname, ".m4a"))
95  || (strcasestr(fname, ".m4p")) || (strcasestr(fname, ".aac"))) {
96  return new MP4File(tpath, tstart, tstop);
97  }
98 #endif
99  return new LSoundFile(tpath, tstart, tstop, doRead); // else try LibSndfile
100 }
101 
102 LSoundFile * LSoundFile::openSndfile(float maxDurInSecs, string tpath) {
103  const char * fname = tpath.c_str();
104 #ifdef USE_libMAD
105  if (strcasestr(fname, ".mp3")) { // if an MP3
106  return new MP3File(maxDurInSecs, tpath);
107  } // if an MP4/AAC
108 #endif
109 #ifdef USE_libFAAD
110  if ((strcasestr(fname, ".mp4")) || (strcasestr(fname, ".m4a"))
111  || (strcasestr(fname, ".m4p")) || (strcasestr(fname, ".aac"))) {
112  return new MP4File(maxDurInSecs, tpath);
113  }
114 #endif
115  return new LSoundFile(maxDurInSecs, tpath); // else try LibSndfile
116 }
117 
118 
119 // LSoundFile Constructors
120 
121 LSoundFile::LSoundFile(string tpath, int tstart, int tstop, bool doRead, float maxDurInSecs)
122  : Abst_SoundFile(tpath, tstart, tstop),
123  mSFInfo(new SF_INFO),
124  mSndfile(NULL),
125  mMaxDurInSecs (maxDurInSecs) {
126  mSFInfo->format = 0;
127  mNumFrames = 0;
128 #ifdef CSL_USE_SRConv
129  mSRateConv = NULL;
130 #endif
131  if ( ! doRead)
132  return;
133  try {
134  openForRead(); // read and cache whole file
135  setToEnd();
136  } catch (CException & e) {
137 // logMsg(kLogError, "File open exception caught: %s", e.mMessage.c_str());
138  return;
139  }
140 }
141 
142 // this version sets maxSize and always reads
143 
144 LSoundFile::LSoundFile(float maxDurInSecs, string tpath)
145  : Abst_SoundFile(tpath, -1, -1),
146  mSFInfo(new SF_INFO),
147  mSndfile(NULL),
148  mMaxDurInSecs (maxDurInSecs) {
149  mSFInfo->format = 0;
150  mNumFrames = 0;
151 #ifdef CSL_USE_SRConv
152  mSRateConv = NULL;
153 #endif
154  try {
155  openForRead(); // read and cache whole file
156  setToEnd();
157  } catch (CException & e) {
158  }
159 }
160 
161 // Copy constructor -- shares sample buffer
162 
164  : Abst_SoundFile(otherSndFile),
165  mSFInfo(otherSndFile.sfInfo()),
166  mSndfile(otherSndFile.sndFile()) {
167  if ( ! otherSndFile.isCached())
168  logMsg(kLogError, "Cannot copy uncached sound file \"%s\"", mPath.c_str());
169 // logMsg("Open sound file \"%s\"", mPath.c_str());
170 #ifdef CSL_USE_SRConv
171  mSRateConv = NULL;
172 #endif
173 }
174 
175 // Clean up data allocated
176 
178  if (mSndfile)
179  sf_close(mSndfile);
180  freeBuffer();
181  delete mSFInfo;
182 #ifdef CSL_USE_SRConv
183  if (mSRateConv)
184  src_delete (mSRateConv);
185 #endif
186 }
187 
188 //
189 // set up the receiver's variables based on the file
190 //
191 
193  mIsValid = (mSndfile != NULL);
194  if ( ! mIsValid) {
195 // logMsg(kLogError, "Cannot open sound file \"%s\"", mPath.c_str());
196  return;
197  }
198  mFrameRate = (unsigned) mSFInfo->samplerate;
199  mNumChannels = (unsigned) mSFInfo->channels;
200  mNumFrames = (unsigned) mSFInfo->frames;
201  if (mMaxDurInSecs > 0.0 && (mNumFrames > (unsigned) (mMaxDurInSecs * mFrameRate))) // truncate if too long
203  mBase = 0;
204  switch (mSFInfo->format & 0x0f) {
205  case 1: mBytesPerSample = 1; break; // Signed 8 bit
206  case 2: mBytesPerSample = 2; break; // Signed 16 bit
207  case 3: mBytesPerSample = 3; break; // Signed 24 bit
208  case 4: mBytesPerSample = 4; break; // Signed 32 bit
209  case 5: mBytesPerSample = 1; break; // unsigned 8 bit
210  case 6: mBytesPerSample = 4; break; // 32 bit float
211  case 7: mBytesPerSample = 8; break; // 64 bit float
212  }
213  if (mStart > 0) seekTo(mStart);
214  else mStart = 0;
215  if (mStop < 0) mStop = mNumFrames;
216 
217 #ifdef CSL_USE_SRConv
218  if (mMode == kSoundFileRead) { // sample rate convertor (SRC) state struct
219  mSRateConv = src_new (CSL_SRC_MODE, mNumChannels, & mSRCReturn);
220  if (mSRateConv == NULL)
221  logMsg (kLogError, "SampleRateConvertor creation error : %s\n", src_strerror (mSRCReturn));
222  }
223 #endif
224 }
225 
226 //void LSoundFile::checkBuffer(unsigned numFrames) {
227 // Abst_SoundFile::checkBuffer(numFrames);
228 //#ifdef CSL_USE_SRConv
229 // if ( ! mSRConvBuffer.mAreBuffersAllocated) { // if no SRC buffer allocated
230 // mSRConvBuffer.setSize(1, CGestalt::maxBufferFrames() * mNumChannels);
231 // mSRConvBuffer.allocateBuffers();
232 // }
233 //#endif
234 //}
235 
236 ///< answer if file has all of its samples in RAM
237 
239  return (mWavetable.mNumFrames == mNumFrames);
240 }
241 
242 ///< answer if file has X samples in RAM
243 
244 bool LSoundFile::isCached(unsigned samps) {
245  return (mWavetable.mNumFrames > (mCurrentFrame + samps));
246 }
247 
249  switch (mSFInfo->format) {
250  case SF_FORMAT_WAV:
251  return kSoundFileFormatWAV; break;
252  case SF_FORMAT_AIFF:
253  return kSoundFileFormatAIFF; break;
254  case SF_FORMAT_AU:
255  return kSoundFileFormatSND; break;
256  case SF_FORMAT_RAW:
257  return kSoundFileFormatRaw; break;
258  default:
259  logMsg("Error: Couldn't get the file format.");
260  }
261  return kSoundFileFormatRaw;
262 }
263 
264 // ~~~~ Open functions ~~~~
265 
266 void LSoundFile::openForRead(bool load) throw (CException) {
267  if (mMode == kSoundFileRead)
268  return;
269  mMode = kSoundFileRead;
270  mSndfile = sf_open(mPath.c_str(), SFM_READ, mSFInfo); // libsndfile open call
271  if (mSndfile != NULL) { // check result
272  this->initFromSndfile();
273  if (load && (mNumFrames <= CGestalt::maxSndFileFrames())) { // read file if size < global max
274 // logMsg("Open/read sound file \"%s\" %d frames %g sec %d channels",
275 // mPath.c_str(), duration(), durationInSecs(), channels());
276  this->readBufferFromFile(mNumFrames); // read entire file, de-interleave
277  }
278 // mCurrentFrame = mStart;
279 // this->setWaveform(mSampleBuffer); // set up oscillator pointers
280 // mWavetable.mDidIAllocateBuffers = true;
281 // if (this->isCached())
282 // logMsg("\t\tSound file cached");
283  }
284  if (mIsValid)
285  this->readTags();
286 }
287 
288 void LSoundFile::openForWrite(SoundFileFormat tformat, unsigned tchannels,
289  unsigned rate, unsigned bitDepth) throw (CException) {
290  if (mMode == kSoundFileWrite)
291  return;
292  mMode = kSoundFileWrite;
293  mSFInfo->samplerate = rate;
294  mSFInfo->channels = tchannels;
295  switch (tformat) {
296  case kSoundFileFormatWAV:
297  mSFInfo->format = SF_FORMAT_WAV; break;
299  mSFInfo->format = SF_FORMAT_AIFF; break;
300  case kSoundFileFormatSND:
301  mSFInfo->format = SF_FORMAT_AU; break;
302  case kSoundFileFormatRaw:
303  default:
304  mSFInfo->format = SF_FORMAT_RAW; break;
305  }
306  if (bitDepth == 16)
307  mSFInfo->format = mSFInfo->format | SF_FORMAT_PCM_16;
308  else if (bitDepth == 32)
309  mSFInfo->format = mSFInfo->format | SF_FORMAT_FLOAT;
310  else
311  logMsg("Invalid bitDepth for sound file %s. Use either 16 or 32 bits.", mPath.c_str());
312 
313  if ( ! sf_format_check(mSFInfo)) {
314  logMsg(kLogError, "Invalid format for sound file %s", mPath.c_str());
315  mIsValid = false;
316  return;
317  }
318  mSndfile = sf_open(mPath.c_str(), SFM_WRITE, mSFInfo);
319  initFromSndfile();
320 }
321 
324  mSndfile = sf_open(mPath.c_str(), SFM_RDWR, mSFInfo);
325  initFromSndfile();
326 }
327 
329  sf_close(mSndfile);
330  mSndfile = NULL;
331  freeBuffer();
332 }
333 
334 // file seek via sf_seek call
335 
336 unsigned LSoundFile::seekTo(int position, SeekPosition whence) throw(CException) {
337  int whenceInt;
338  if (this->isCached())
339  return mCurrentFrame;
340  switch (whence) {
341  case kPositionStart: whenceInt = SEEK_SET; break;
342  case kPositionCurrent: whenceInt = SEEK_CUR; break;
343  case kPositionEnd: whenceInt = SEEK_END; break;
344  default:
345  whenceInt = SEEK_CUR;
346  logMsg("Error: Invalid position seek flag. Used kPositionCurrent.");
347  break;
348  } // sf_seek call
349  mCurrentFrame = sf_seek(mSndfile, position, whenceInt);
350  mBase = mCurrentFrame;
351  return mCurrentFrame;
352 }
353 
354 ///////////////// next_buffer -- the work is done here //////////
355 
356 void LSoundFile::nextBuffer(Buffer &outputBuffer) throw(CException) {
357  unsigned numFrames = outputBuffer.mNumFrames;
358  unsigned currentFrame = mCurrentFrame;
359  DECLARE_SCALABLE_CONTROLS; // declare the scale/offset buffers and values
360  bool atEnd = false;
361 
362  if (currentFrame >= (unsigned) mStop) { // if done
363  outputBuffer.zeroBuffers();
364  return;
365  }
366  if (currentFrame + numFrames >= (unsigned) mStop) { // if final window
367  numFrames = mStop - currentFrame;
368  outputBuffer.zeroBuffers();
369  atEnd = true;
370  }
371  if ( ! this->isCached(numFrames)) { // if not playing from cache buffer
372  if (! atEnd)
373  this->readBufferFromFile(numFrames); // read from file
374  }
375  LOAD_SCALABLE_CONTROLS; // load the scaleC and offsetC from the constant or dynamic value
376  if (mRate == 1.0) { // if playing at normal rate
377  if (IS_UNSCALED) { // and there's no scale/offset
378  unsigned numBytes = numFrames * sizeof(sample); // buffer copy loop
379  for (unsigned i = 0; i < outputBuffer.mNumChannels; i++) {
380  SampleBuffer dataOutPtr = mWavetable.monoBuffer(csl_min(i, (mNumChannels - 1))) + currentFrame;
381  memcpy(outputBuffer.monoBuffer(i), dataOutPtr, numBytes);
382  }
383  } else { // do full scaled/offset copy
384  sample samp;
385  for (unsigned i = 0; i < outputBuffer.mNumChannels; i++) {
386  SampleBuffer buffer = outputBuffer.monoBuffer(i); // get pointer to the selected output channel
387  SampleBuffer dPtr = mWavetable.monoBuffer(csl_min(i, (mNumChannels - 1))) + currentFrame;
388 
389  for (unsigned i = 0; i < numFrames; i++) { // sample loop
390  samp = (*dPtr++ * scaleValue) + offsetValue; // get and scale the file sample
391  *buffer++ = samp;
392  UPDATE_SCALABLE_CONTROLS; // update the dynamic scale/offset
393  }
394  scalePort->resetPtr();
395  offsetPort->resetPtr();
396  }
397  }
398  currentFrame += numFrames; // incrememnt buf ptr
399  } else { // if mRate != 1.0,
400 
401 #ifndef CSL_USE_SRConv // normal case: use wavetable interpolation
402  for (unsigned i = 0; i < mNumChannels; i++)
403  WavetableOscillator::nextBuffer(outputBuffer, i);
404  currentFrame += numFrames; // incrememnt buf ptr
405 
406 #else // else use Sample rate convertor
407  int answer; // if we're doing SRC transposition
408  if (this->isCached()) // if playing from cache buffer
409  mSRateData.data_in = mWavetable.monoBuffer(0) + (currentFrame * mNumChannels);
410  else
411  mSRateData.data_in = mWavetable.monoBuffer(0);
412  mSRateData.data_out = mSRateData.data_in; // store for later
413  mSRateData.input_frames = numFrames;
414  mSRateData.output_frames = numFrames;
415  mSRateData.data_out = mSRConvBuffer.monoBuffer(0);
416  mSRateData.end_of_input = 0;
417  mSRateData.src_ratio = mRate;
418  if (answer = src_set_ratio (mSRateConv, mRate)) // set the transposition ratio
419  logMsg (kLogError, "SampleRateConvertor configuration error : %s\n",
420  src_strerror (answer));
421 
422  if (answer = src_process (mSRateConv, & mSRateData)) // run SampleRateConvertor
423  logMsg (kLogError, "SampleRateConvertor runtime error : %s\n",
424  src_strerror (answer));
425 
426  mSRateData.data_out = mSRConvBuffer.monoBuffer(0); // reset out ptr
427  currentFrame += mSRateData.input_frames_used; // input frames actually read
428  numFrames = mSRateData.output_frames_gen; // output frames actually written
429 #endif
430  }
431  if ((currentFrame >= (unsigned) mStop) && mIsLooping) // if we are past the end of the file...
432  currentFrame = 0; // this will click, have to call nextBuffer() recursively here
433  mCurrentFrame = currentFrame; // store back to member
434  return;
435 }
436 
437 // write a CSL buffer to the interleaved output file
438 
439 void LSoundFile::writeBuffer(Buffer &inputBuffer) throw(CException) {
440  unsigned numFrames = inputBuffer.mNumFrames;
441  if (mSFInfo->channels > 1) { // interleave stereo-to-mono
442  mWavetable.setSize(1, mNumChannels * numFrames);
443  mWavetable.allocateBuffers();
444  // call interleaver
445  mInterleaver.interleave(inputBuffer, mWavetable.monoBuffer(0),
446  numFrames, mSFInfo->channels);
447  sf_writef_float(mSndfile, mWavetable.monoBuffer(0), numFrames); // libsndfile write
448  } else { // mono file write
449  sf_writef_float(mSndfile, inputBuffer.monoBuffer(0), numFrames); // libsndfile write
450  }
451  mCurrentFrame += numFrames;
452  return;
453 }
454 
455 // readBufferFromFile - read some samples from the file into the temp buffer
456 
457 void LSoundFile::readBufferFromFile(unsigned numFrames) {
458  sf_count_t numFramesRead;
459  unsigned currentFrame = mCurrentFrame;
460  unsigned myChannels = mSFInfo->channels;
461 
462  this->checkBuffer(numFrames); // check my buffer, allocate if necessary
463  SampleBuffer sampleBufferPtr;
464  if ((myChannels > 1)) { // read into temp buffer to multichannel files, then de-interleave
465  SAFE_MALLOC(sampleBufferPtr, sample, (myChannels * numFrames));
466  } else {
467  sampleBufferPtr = mWavetable.monoBuffer(0);
468  }
469  // if we are at the end of the file and not looping
470  if ((currentFrame >= (unsigned) mStop) && !mIsLooping) {
471  memset(sampleBufferPtr, 0, numFrames * myChannels * sizeof(sample));
472  return;
473  }
474  mBase = sf_seek(mSndfile, 0, SEEK_CUR); // get position
475  // libsndfile read function
476  numFramesRead = sf_readf_float(mSndfile, sampleBufferPtr, numFrames);
477  if ((unsigned) numFramesRead != numFrames)
478  logMsg(kLogError, "LSoundFile::readBufferFromFile sf_readf_float requested %d got %d",
479  numFrames, numFramesRead);
480 // mWavetable.mNumFrames = numFrames;
481  mCurrentFrame = 0;
482  // if we are past the end of the file...
483  if ((mBase + numFrames) > (unsigned) mStop) {
484  unsigned numFramesRemaining = numFrames - numFramesRead;
485  SampleBuffer tempBufferPtr = sampleBufferPtr + (numFramesRead * myChannels);
486  if (mIsLooping) { // loop back to start of file
487  while ((unsigned) numFramesRead < numFrames) {
488  currentFrame = sf_seek(mSndfile, 0, SEEK_SET); // libsndfile seek/read function
489  numFramesRead += sf_readf_float(mSndfile, tempBufferPtr, numFramesRemaining);
490  currentFrame += numFramesRead;
491  }
492  } else {
493  unsigned bytesToClear = numFramesRemaining * myChannels * sizeof(sample);
494  memset(tempBufferPtr, 0, bytesToClear);
495  }
496  }
497  if (myChannels > 1) { // auto-de-interleave if multichannel
498  mInterleaver.deinterleave(mWavetable, sampleBufferPtr, numFrames, mNumChannels);
499  SAFE_FREE(sampleBufferPtr);
500  }
502 }
503 
504 #ifdef CSL_USE_SRConv // sample-rate conversion
505 
506 #define BUFFER_LEN 4096 /*-(1<<16)-*/
507 
508 // fcn prototypes below
509 
510 static sf_count_t sample_rate_convert (SNDFILE *infile, SNDFILE *outfile,
511  int converter, double src_ratio, int channels, double * gain);
512 
513 static double apply_gain (float * data, long frames, int channels, double max, double gain);
514 
515 #endif // SR_Conv
516 
517 //-------------------------------------------------------------------------------
518 
519 // SF_Virtual structure
520 /*
521 typedef sf_count_t (*sf_vio_get_filelen) (void *user_data) ;
522 typedef sf_count_t (*sf_vio_seek) (sf_count_t offset, int whence, void *user_data) ;
523 typedef sf_count_t (*sf_vio_read) (void *ptr, sf_count_t count, void *user_data) ;
524 typedef sf_count_t (*sf_vio_write) (const void *ptr, sf_count_t count, void *user_data) ;
525 typedef sf_count_t (*sf_vio_tell) (void *user_data) ;
526 
527 struct SF_VIRTUAL_IO
528 { sf_vio_get_filelen get_filelen ;
529  sf_vio_seek seek ;
530  sf_vio_read read ;
531  sf_vio_write write ;
532  sf_vio_tell tell ;
533 } ;
534 */
535 
536 // Call-back functions for SF_VIRTUAL_IO struct
537 
538 //sf_vio_get_filelen lsf_getFileLen(void * vBuffer) {
539 // Buffer * buf = (Buffer *) vfile
540 //}
541 //
542 //sf_vio_seek lsf_seek(sf_count_t offset, int whence, void * vBuffer) {
543 //
544 //}
545 //
546 //sf_vio_tell lsf_tell(void * vBuffer) {
547 //
548 //}
549 //
550 //
551 //sf_vio_read lsf_read(void *ptr, sf_count_t count, void * vBuffer) {
552 //
553 //}
554 //
555 //
556 //sf_vio_write lsf_write(const void *ptr, sf_count_t count, void * vBuffer) {
557 //
558 //}
559 
560 
561 
562 // OLD WAY
563 
564 //void LSoundFile::convertRate(char * mTempPath, int fromRate, int toRate) {
565 // SNDFILE *infile, *outfile;
566 // SF_INFO sfinfo;
567 // sf_count_t count;
568 // char tempName[CSL_NAME_LEN];
569 //
570 // double src_ratio = -1.0, gain = 1.0;
571 // int converter;
572 // converter = SRC_SINC_BEST_QUALITY;
573 // logMsg("Convert-rate of \"%s\" from %d to %d", mTempPath, fromRate, toRate);
574 // // create temp name and rename file
575 // sprintf(tempName, "%s.TEMP", mTempPath);
576 // if (rename(mTempPath, tempName) != 0) {
577 // logMsg(kLogError, "Convert-rate cannot rename \"%s\" to \"%s\"", mTempPath, tempName);
578 // perror("SoundFile::convertRate");
579 // return;
580 // }
581 // infile = sf_open(tempName, SFM_READ, &sfinfo);
582 // src_ratio = (double) toRate / (double) fromRate;
583 // sfinfo.samplerate = toRate;
584 // if (src_is_valid_ratio (src_ratio) == 0) {
585 // logMsg (kLogError, "Error: Sample rate change out of valid range.");
586 // sf_close (infile);
587 // return;
588 // }
589 // outfile = sf_open (mTempPath, SFM_WRITE, &sfinfo);
590 // if (outfile == NULL) {
591 // logMsg (kLogError, "Error: Cannot open output file.");
592 // sf_close (infile);
593 // return;
594 // }
595 // sf_command (outfile, SFC_SET_UPDATE_HEADER_AUTO, NULL, SF_TRUE);
596 // sf_command (outfile, SFC_SET_CLIPPING, NULL, SF_TRUE);
597 // do {
598 // count = sample_rate_convert(infile, outfile, converter, src_ratio, sfinfo.channels, &gain);
599 // } while (count < 0);
600 //
601 // logMsg("Convert-rate: output frames %ld", (long) count);
602 // sf_close(infile);
603 // sf_close(outfile);
604 // if (remove(tempName) != 0)
605 // logMsg(kLogError, "Convert-rate cannot remove temp file \"%s\"\n", tempName);
606 //}
607 
608 /////////////////////////
609 // These 2 functions are taken from libSampleRate
610 //
611 
612 //static sf_count_t sample_rate_convert (SNDFILE *infile, SNDFILE *outfile, int converter,
613 // double src_ratio, int channels, double * gain) {
614 // static float input [BUFFER_LEN];
615 // static float output [BUFFER_LEN];
616 // SRC_STATE * src_state;
617 // SRC_DATA src_data;
618 // int error;
619 // double max = 0.0;
620 // sf_count_t output_count = 0;
621 // sf_seek (infile, 0, SEEK_SET);
622 // sf_seek (outfile, 0, SEEK_SET);
623 //
624 // /* Initialize the sample rate converter. */
625 // if ((src_state = src_new (converter, channels, &error)) == NULL) {
626 // logMsg ("Error : src_new() failed : %s.", src_strerror (error));
627 // exit (1);
628 // };
629 //
630 // src_data.end_of_input = 0; /* Set this later. */
631 //
632 // /* Start with zero to force load in while loop. */
633 // src_data.input_frames = 0;
634 // src_data.data_in = input;
635 // src_data.src_ratio = src_ratio;
636 // src_data.data_out = output;
637 // src_data.output_frames = BUFFER_LEN /channels;
638 //
639 // while (1) {
640 // /* If the input buffer is empty, refill it. */
641 // if (src_data.input_frames == 0) {
642 // src_data.input_frames = sf_readf_float (infile, input, BUFFER_LEN / channels);
643 // src_data.data_in = input;
644 //
645 // /* The last read will not be a full buffer, so snd_of_input. */
646 // if (src_data.input_frames < BUFFER_LEN / channels)
647 // src_data.end_of_input = SF_TRUE;
648 // }
649 //
650 // if ((error = src_process (src_state, &src_data))) {
651 // logMsg ("SRC Error : %s", src_strerror (error));
652 // return (0);
653 // }
654 //
655 // /* Terminate if done. */
656 // if (src_data.end_of_input && src_data.output_frames_gen == 0)
657 // break;
658 //
659 // max = apply_gain (src_data.data_out, src_data.output_frames_gen, channels, max, *gain);
660 //
661 // /* Write output. */
662 // sf_writef_float (outfile, output, src_data.output_frames_gen);
663 // output_count += src_data.output_frames_gen;
664 //
665 // src_data.data_in += src_data.input_frames_used * channels;
666 // src_data.input_frames -= src_data.input_frames_used;
667 // }
668 //
669 // src_state = src_delete (src_state);
670 //
671 // if (max > 1.0) { *gain = 1.0 / max;
672 // logMsg ("Output has clipped. Restarting conversion to prevent clipping.");
673 // output_count = 0;
674 // sf_command (outfile, SFC_FILE_TRUNCATE, &output_count, sizeof (output_count));
675 // return -1;
676 // }
677 //
678 // return output_count;
679 //} /* sample_rate_convert */
680 //
681 //static double apply_gain (float * data, long frames, int channels, double max, double gain) {
682 // for (long k = 0; k < frames * channels; k++) {
683 // double val = (double) data [k];
684 // val *= gain;
685 // data[k] = val;
686 // val = fabs (val);
687 // if (val > max)
688 // max = val;
689 // }
690 // return max;
691 //} /* apply_gain */