liveMedia/WAVAudioFileSource.cpp

Go to the documentation of this file.
00001 /**********
00002 This library is free software; you can redistribute it and/or modify it under
00003 the terms of the GNU Lesser General Public License as published by the
00004 Free Software Foundation; either version 2.1 of the License, or (at your
00005 option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
00006 
00007 This library is distributed in the hope that it will be useful, but WITHOUT
00008 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
00009 FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
00010 more details.
00011 
00012 You should have received a copy of the GNU Lesser General Public License
00013 along with this library; if not, write to the Free Software Foundation, Inc.,
00014 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00015 **********/
00016 // "liveMedia"
00017 // Copyright (c) 1996-2008 Live Networks, Inc.  All rights reserved.
00018 // A WAV audio file source
00019 // Implementation
00020 
00021 #include "WAVAudioFileSource.hh"
00022 #include "InputFile.hh"
00023 #include "GroupsockHelper.hh"
00024 
00026 
00027 WAVAudioFileSource*
00028 WAVAudioFileSource::createNew(UsageEnvironment& env, char const* fileName) {
00029   do {
00030     FILE* fid = OpenInputFile(env, fileName);
00031     if (fid == NULL) break;
00032 
00033     WAVAudioFileSource* newSource = new WAVAudioFileSource(env, fid);
00034     if (newSource != NULL && newSource->bitsPerSample() == 0) {
00035       // The WAV file header was apparently invalid.
00036       Medium::close(newSource);
00037       break;
00038     }
00039 
00040     newSource->fFileSize = (unsigned)GetFileSize(fileName, fid);
00041 
00042     return newSource;
00043   } while (0);
00044 
00045   return NULL;
00046 }
00047 
00048 unsigned WAVAudioFileSource::numPCMBytes() const {
00049   if (fFileSize < fWAVHeaderSize) return 0;
00050   return fFileSize - fWAVHeaderSize;
00051 }
00052 
00053 void WAVAudioFileSource::setScaleFactor(int scale) {
00054   fScaleFactor = scale;
00055 
00056   if (fScaleFactor < 0 && ftell(fFid) > 0) {
00057     // Because we're reading backwards, seek back one sample, to ensure that
00058     // (i)  we start reading the last sample before the start point, and
00059     // (ii) we don't hit end-of-file on the first read.
00060     int const bytesPerSample = (fNumChannels*fBitsPerSample)/8;
00061     fseek(fFid, -bytesPerSample, SEEK_CUR);
00062   }
00063 }
00064 
00065 void WAVAudioFileSource::seekToPCMByte(unsigned byteNumber) {
00066   byteNumber += fWAVHeaderSize;
00067   if (byteNumber > fFileSize) byteNumber = fFileSize;
00068 
00069   fseek(fFid, byteNumber, SEEK_SET);
00070 }
00071 
00072 unsigned char WAVAudioFileSource::getAudioFormat() {
00073   return fAudioFormat;
00074 }
00075 
00076 
00077 #define nextc fgetc(fid)
00078 
00079 static Boolean get4Bytes(FILE* fid, unsigned& result) { // little-endian
00080   int c0, c1, c2, c3;
00081   if ((c0 = nextc) == EOF || (c1 = nextc) == EOF ||
00082       (c2 = nextc) == EOF || (c3 = nextc) == EOF) return False;
00083   result = (c3<<24)|(c2<<16)|(c1<<8)|c0;
00084   return True;
00085 }
00086 
00087 static Boolean get2Bytes(FILE* fid, unsigned short& result) {//little-endian
00088   int c0, c1;
00089   if ((c0 = nextc) == EOF || (c1 = nextc) == EOF) return False;
00090   result = (c1<<8)|c0;
00091   return True;
00092 }
00093 
00094 static Boolean skipBytes(FILE* fid, int num) {
00095   while (num-- > 0) {
00096     if (nextc == EOF) return False;
00097   }
00098   return True;
00099 }
00100 
00101 WAVAudioFileSource::WAVAudioFileSource(UsageEnvironment& env, FILE* fid)
00102   : AudioInputDevice(env, 0, 0, 0, 0)/* set the real parameters later */,
00103     fFid(fid), fLastPlayTime(0), fWAVHeaderSize(0), fFileSize(0), fScaleFactor(1),
00104     fAudioFormat(WA_UNKNOWN) {
00105   // Check the WAV file header for validity.
00106   // Note: The following web pages contain info about the WAV format:
00107   // http://www.technology.niagarac.on.ca/courses/comp630/WavFileFormat.html
00108   // http://ccrma-www.stanford.edu/CCRMA/Courses/422/projects/WaveFormat/
00109   // http://www.ringthis.com/dev/wave_format.htm
00110   // http://www.lightlink.com/tjweber/StripWav/Canon.html
00111   // http://www.borg.com/~jglatt/tech/wave.htm
00112   // http://www.wotsit.org/download.asp?f=wavecomp
00113 
00114   Boolean success = False; // until we learn otherwise
00115   do {
00116     // RIFF Chunk:
00117     if (nextc != 'R' || nextc != 'I' || nextc != 'F' || nextc != 'F') break;
00118     if (!skipBytes(fid, 4)) break;
00119     if (nextc != 'W' || nextc != 'A' || nextc != 'V' || nextc != 'E') break;
00120 
00121     // FORMAT Chunk:
00122     if (nextc != 'f' || nextc != 'm' || nextc != 't' || nextc != ' ') break;
00123     unsigned formatLength;
00124     if (!get4Bytes(fid, formatLength)) break;
00125     unsigned short audioFormat;
00126     if (!get2Bytes(fid, audioFormat)) break;
00127 
00128     fAudioFormat = (unsigned char)audioFormat;
00129     if (fAudioFormat != WA_PCM && fAudioFormat != WA_PCMA && fAudioFormat != WA_PCMU) { 
00130       // not PCM/PCMU/PCMA - we can't handle this
00131       env.setResultMsg("Audio format is not PCM/PCMU/PCMA");
00132       break;
00133     }
00134     unsigned short numChannels;
00135     if (!get2Bytes(fid, numChannels)) break;
00136     fNumChannels = (unsigned char)numChannels;
00137     if (fNumChannels < 1 || fNumChannels > 2) { // invalid # channels
00138       char errMsg[100];
00139       sprintf(errMsg, "Bad # channels: %d", fNumChannels);
00140       env.setResultMsg(errMsg);
00141       break;
00142     }
00143     if (!get4Bytes(fid, fSamplingFrequency)) break;
00144     if (fSamplingFrequency == 0) {
00145       env.setResultMsg("Bad sampling frequency: 0");
00146       break;
00147     }
00148     if (!skipBytes(fid, 6)) break;
00149     unsigned short bitsPerSample;
00150     if (!get2Bytes(fid, bitsPerSample)) break;
00151     fBitsPerSample = (unsigned char)bitsPerSample;
00152     if (fBitsPerSample == 0) {
00153       env.setResultMsg("Bad bits-per-sample: 0");
00154       break;
00155     }
00156     if (!skipBytes(fid, formatLength - 16)) break;
00157 
00158     // FACT chunk (optional):
00159     int c = nextc;
00160     if (c == 'f') {
00161       if (nextc != 'a' || nextc != 'c' || nextc != 't') break;
00162       unsigned factLength;
00163       if (!get4Bytes(fid, factLength)) break;
00164       if (!skipBytes(fid, factLength)) break;
00165       c = nextc;
00166     }
00167 
00168     // DATA Chunk:
00169     if (c != 'd' || nextc != 'a' || nextc != 't' || nextc != 'a') break;
00170     if (!skipBytes(fid, 4)) break;
00171 
00172     // The header is good; the remaining data are the sample bytes.
00173     fWAVHeaderSize = ftell(fid);
00174     success = True;
00175   } while (0);
00176   
00177   if (!success) {
00178     env.setResultMsg("Bad WAV file format");
00179     // Set "fBitsPerSample" to zero, to indicate failure:
00180     fBitsPerSample = 0;
00181     return;
00182   }
00183 
00184   fPlayTimePerSample = 1e6/(double)fSamplingFrequency;
00185 
00186   // Although PCM is a sample-based format, we group samples into
00187   // 'frames' for efficient delivery to clients.  Set up our preferred
00188   // frame size to be close to 20 ms, if possible, but always no greater
00189   // than 1400 bytes (to ensure that it will fit in a single RTP packet)
00190   unsigned maxSamplesPerFrame = (1400*8)/(fNumChannels*fBitsPerSample);
00191   unsigned desiredSamplesPerFrame = (unsigned)(0.02*fSamplingFrequency);
00192   unsigned samplesPerFrame = desiredSamplesPerFrame < maxSamplesPerFrame
00193     ? desiredSamplesPerFrame : maxSamplesPerFrame;
00194   fPreferredFrameSize = (samplesPerFrame*fNumChannels*fBitsPerSample)/8;
00195 }
00196 
00197 WAVAudioFileSource::~WAVAudioFileSource() {
00198   CloseInputFile(fFid);
00199 }
00200 
00201 // Note: We should change the following to use asynchronous file reading, #####
00202 // as we now do with ByteStreamFileSource. #####
00203 void WAVAudioFileSource::doGetNextFrame() {
00204   if (feof(fFid) || ferror(fFid)) {
00205     handleClosure(this);
00206     return;
00207   }
00208 
00209   // Try to read as many bytes as will fit in the buffer provided
00210   // (or "fPreferredFrameSize" if less)
00211   if (fPreferredFrameSize < fMaxSize) {
00212     fMaxSize = fPreferredFrameSize;
00213   }
00214   unsigned const bytesPerSample = (fNumChannels*fBitsPerSample)/8;
00215   unsigned bytesToRead = fMaxSize - fMaxSize%bytesPerSample;
00216   if (fScaleFactor == 1) {
00217     // Common case - read samples in bulk:
00218     fFrameSize = fread(fTo, 1, bytesToRead, fFid);
00219   } else {
00220     // We read every 'fScaleFactor'th sample:
00221     fFrameSize = 0; 
00222     while (bytesToRead > 0) {
00223       size_t bytesRead = fread(fTo, 1, bytesPerSample, fFid);
00224       if (bytesRead <= 0) break;
00225       fTo += bytesRead;
00226       fFrameSize += bytesRead;
00227       bytesToRead -= bytesRead;
00228 
00229       // Seek to the appropriate place for the next sample:
00230       fseek(fFid, (fScaleFactor-1)*bytesPerSample, SEEK_CUR);
00231     }
00232   }
00233 
00234   // Set the 'presentation time' and 'duration' of this frame:
00235   if (fPresentationTime.tv_sec == 0 && fPresentationTime.tv_usec == 0) {
00236     // This is the first frame, so use the current time:
00237     gettimeofday(&fPresentationTime, NULL);
00238   } else {
00239     // Increment by the play time of the previous data:
00240     unsigned uSeconds   = fPresentationTime.tv_usec + fLastPlayTime;
00241     fPresentationTime.tv_sec += uSeconds/1000000;
00242     fPresentationTime.tv_usec = uSeconds%1000000;
00243   }
00244 
00245   // Remember the play time of this data:
00246   fDurationInMicroseconds = fLastPlayTime
00247     = (unsigned)((fPlayTimePerSample*fFrameSize)/bytesPerSample);
00248 
00249   // Switch to another task, and inform the reader that he has data:
00250 #if defined(__WIN32__) || defined(_WIN32)
00251   // HACK: One of our applications that uses this source uses an
00252   // implementation of scheduleDelayedTask() that performs very badly
00253   // (chewing up lots of CPU time, apparently polling) on Windows.
00254   // Until this is fixed, we just call our "afterGetting()" function
00255   // directly.  This avoids infinite recursion, as long as our sink
00256   // is discontinuous, which is the case for the RTP sink that
00257   // this application uses. #####
00258   afterGetting(this);
00259 #else
00260   nextTask() = envir().taskScheduler().scheduleDelayedTask(0,
00261                         (TaskFunc*)FramedSource::afterGetting, this);
00262 #endif
00263 }
00264 
00265 Boolean WAVAudioFileSource::setInputPort(int /*portIndex*/) {
00266   return True;
00267 }
00268 
00269 double WAVAudioFileSource::getAverageLevel() const {
00270   return 0.0;//##### fix this later
00271 }

Generated on Tue Jul 22 06:39:07 2008 for live by  doxygen 1.5.2