📄 wavaudiofilesource.cpp
字号:
// WAVAudioFileSource.cpp: implementation of the CWAVAudioFileSource class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "WAVAudioFileSource.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
WAVAudioFileSource*
WAVAudioFileSource::createNew(char const* fileName,BOOL ConvertToULaw) {
do {
FILE* fid = fopen(fileName, "rb");
if (fid == NULL) break;
WAVAudioFileSource* newSource = new WAVAudioFileSource(fid,ConvertToULaw);
if (newSource != NULL && newSource->bitsPerSample() == 0) {
// The WAV file header was apparently invalid.
delete newSource; newSource = NULL;
}
newSource->fFileSize = CUtility::GetFileSize(fileName, fid);
return newSource;
} while (0);
return NULL;
}
unsigned WAVAudioFileSource::numPCMBytes() const {
if (fFileSize < fWAVHeaderSize) return 0;
return fFileSize - fWAVHeaderSize;
}
void WAVAudioFileSource::setScaleFactor(int scale) {
fScaleFactor = scale;
if (fScaleFactor < 0 && ftell(fFid) > 0) {
// Because we're reading backwards, seek back one sample, to ensure that
// (i) we start reading the last sample before the start point, and
// (ii) we don't hit end-of-file on the first read.
int const bytesPerSample = (fNumChannels*fBitsPerSample)/8;
fseek(fFid, -bytesPerSample, SEEK_CUR);
}
}
void WAVAudioFileSource::seekToPCMByte(unsigned byteNumber) {
byteNumber += fWAVHeaderSize;
if (byteNumber > fFileSize) byteNumber = fFileSize;
fseek(fFid, byteNumber, SEEK_SET);
}
#define nextc fgetc(fid)
#define ucEOF ((unsigned char)EOF)
static Boolean get4Bytes(FILE* fid, unsigned& result) { // little-endian
unsigned char c0, c1, c2, c3;
if ((c0 = nextc) == ucEOF || (c1 = nextc) == ucEOF ||
(c2 = nextc) == ucEOF || (c3 = nextc) == ucEOF) return False;
result = (c3<<24)|(c2<<16)|(c1<<8)|c0;
return True;
}
static Boolean get2Bytes(FILE* fid, unsigned short& result) {//little-endian
unsigned char c0, c1;
if ((c0 = nextc) == ucEOF || (c1 = nextc) == ucEOF) return False;
result = (c1<<8)|c0;
return True;
}
static Boolean skipBytes(FILE* fid, int num) {
while (num-- > 0) {
if (nextc == ucEOF) return False;
}
return True;
}
WAVAudioFileSource::WAVAudioFileSource(FILE* fid,BOOL ConvertToULaw)
:fFid(fid), fLastPlayTime(0), fWAVHeaderSize(0), fFileSize(0),
fScaleFactor(1) ,fConvertToULaw(ConvertToULaw)
{
// Check the WAV file header for validity.
// Note: The following web pages contain info about the WAV format:
// http://www.technology.niagarac.on.ca/courses/comp630/WavFileFormat.html
// http://ccrma-www.stanford.edu/CCRMA/Courses/422/projects/WaveFormat/
// http://www.ringthis.com/dev/wave_format.htm
// http://www.lightlink.com/tjweber/StripWav/Canon.html
// http://www.borg.com/~jglatt/tech/wave.htm
// http://www.wotsit.org/download.asp?f=wavecomp
Boolean success = False; // until we learn otherwise
do {
// RIFF Chunk:
if (nextc != 'R' || nextc != 'I' || nextc != 'F' || nextc != 'F') break;
if (!skipBytes(fid, 4)) break;
if (nextc != 'W' || nextc != 'A' || nextc != 'V' || nextc != 'E') break;
// FORMAT Chunk:
if (nextc != 'f' || nextc != 'm' || nextc != 't' || nextc != ' ') break;
unsigned formatLength;
if (!get4Bytes(fid, formatLength)) break;
unsigned short audioFormat;
if (!get2Bytes(fid, audioFormat)) break;
if (audioFormat != 1) { // not PCM - we can't handle this
CUtility::setResultMsg("Audio format is not PCM");
break;
}
unsigned short numChannels;
if (!get2Bytes(fid, numChannels)) break;
fNumChannels = (unsigned char)numChannels;
if (fNumChannels < 1 || fNumChannels > 2) { // invalid # channels
char errMsg[100];
sprintf(errMsg, "Bad # channels: %d", fNumChannels);
CUtility::setResultMsg(errMsg);
break;
}
if (!get4Bytes(fid, fSamplingFrequency)) break;
if (fSamplingFrequency == 0) {
CUtility::setResultMsg("Bad sampling frequency: 0");
break;
}
if (!skipBytes(fid, 6)) break;
unsigned short bitsPerSample;
if (!get2Bytes(fid, bitsPerSample)) break;
fBitsPerSample = (unsigned char)bitsPerSample;
if (fBitsPerSample == 0) {
CUtility::setResultMsg("Bad bits-per-sample: 0");
break;
}
if (!skipBytes(fid, formatLength - 16)) break;
// FACT chunk (optional):
unsigned char c = nextc;
if (c == 'f') {
if (nextc != 'a' || nextc != 'c' || nextc != 't') break;
unsigned factLength;
if (!get4Bytes(fid, factLength)) break;
if (!skipBytes(fid, factLength)) break;
c = nextc;
}
// DATA Chunk:
if (c != 'd' || nextc != 'a' || nextc != 't' || nextc != 'a') break;
if (!skipBytes(fid, 4)) break;
// The header is good; the remaining data are the sample bytes.
fWAVHeaderSize = ftell(fid);
success = True;
} while (0);
if (!success) {
CUtility::setResultMsg("Bad WAV file format");
// Set "fBitsPerSample" to zero, to indicate failure:
fBitsPerSample = 0;
return;
}
fPlayTimePerSample = 1e6/(double)fSamplingFrequency;
// Although PCM is a sample-based format, we group samples into
// 'frames' for efficient delivery to clients. Set up our preferred
// frame size to be close to 20 ms, if possible, but always no greater
// than 1400 bytes (to ensure that it will fit in a single RTP packet)
unsigned maxSamplesPerFrame = (1400*8)/(fNumChannels*fBitsPerSample);
unsigned desiredSamplesPerFrame = (unsigned)(0.02*fSamplingFrequency);
unsigned samplesPerFrame = desiredSamplesPerFrame < maxSamplesPerFrame
? desiredSamplesPerFrame : maxSamplesPerFrame;
fPreferredFrameSize = (samplesPerFrame*fNumChannels*fBitsPerSample)/8;
}
WAVAudioFileSource::~WAVAudioFileSource() {
if(fFid)
fclose(fFid);
}
void WAVAudioFileSource::doGetNextFrame() {
if (feof(fFid) || ferror(fFid)) {
return;
}
// Try to read as many bytes as will fit in the buffer provided
// (or "fPreferredFrameSize" if less)
if (fPreferredFrameSize < fMaxSize) {
fMaxSize = fPreferredFrameSize;
}
unsigned const bytesPerSample = (fNumChannels*fBitsPerSample)/8;
unsigned bytesToRead = fMaxSize - fMaxSize%bytesPerSample;
if (fScaleFactor == 1) {
// Common case - read samples in bulk:
fFrameSize = fread(fTo, 1, bytesToRead, fFid);
} else {
// We read every 'fScaleFactor'th sample:
fFrameSize = 0;
while (bytesToRead > 0) {
size_t bytesRead = fread(fTo, 1, bytesPerSample, fFid);
if (bytesRead <= 0) break;
fTo += bytesRead;
fFrameSize += bytesRead;
bytesToRead -= bytesRead;
// Seek to the appropriate place for the next sample:
fseek(fFid, (fScaleFactor-1)*bytesPerSample, SEEK_CUR);
}
}
// Set the 'presentation time' and 'duration' of this frame:
if (fPresentationTime.tv_sec == 0 && fPresentationTime.tv_usec == 0) {
// This is the first frame, so use the current time:
CUtility::gettimeofday(&fPresentationTime, NULL);
} else {
// Increment by the play time of the previous data:
unsigned uSeconds = fPresentationTime.tv_usec + fLastPlayTime;
fPresentationTime.tv_sec += uSeconds/1000000;
fPresentationTime.tv_usec = uSeconds%1000000;
}
// Remember the play time of this data:
fDurationInMicroseconds = fLastPlayTime
= (unsigned)((fPlayTimePerSample*fFrameSize)/bytesPerSample);
// HACK: One of our applications that uses this source uses an
// implementation of scheduleDelayedTask() that performs very badly
// (chewing up lots of CPU time, apparently polling) on Windows.
// Until this is fixed, we just call our "afterGetting()" function
// directly. This avoids infinite recursion, as long as our sink
// is discontinuous, which is the case for the RTP sink that
// this application uses. #####
//Convert data byte order
if (fBitsPerSample == 16)
{
if (fConvertToULaw)
{
}
else
{
EndianSwap16();
}
}
afterGetting(this);
}
void WAVAudioFileSource::EndianSwap16()
{
/*
// Add in any filter necessary to transform the data prior to streaming:
if (fBitsPerSample == 16) {
// Note that samples in the WAV audio file are in little-endian order.
if (fConvertToULaw) {
// Add a filter that converts from raw 16-bit PCM audio
// to 8-bit u-law audio:
resultSource
= uLawFromPCMAudioSource::createNew(envir(), wavSource, 1);//little-endian
bitsPerSecond /= 2;
} else {
// Add a filter that converts from little-endian to network (big-endian) order:
resultSource = EndianSwap16::createNew(envir(), wavSource);
}
} else { // fBitsPerSample == 8
// Don't do any transformation; send the 8-bit PCM data 'as is':
resultSource = wavSource;
}*/
// Swap the byte order of the 16-bit values that we have just read (in place):
unsigned numValues = fFrameSize/2;
short* value = (short*)fTo;
for (unsigned i = 0; i < numValues; ++i) {
short const orig = value[i];
value[i] = ((orig&0xFF)<<8) | ((orig&0xFF00)>>8);
}
// Complete delivery to the client:
fFrameSize = numValues*2;
}
unsigned char WAVAudioFileSource::GetPayloadFormatCode()
{
char* mimeType;
unsigned char payloadFormatCode;
if (fBitsPerSample == 16) {
if (fConvertToULaw) {
mimeType = "PCMU";
if (fSamplingFrequency == 8000 && fNumChannels == 1) {
payloadFormatCode = 0; // a static RTP payload type
} else {
payloadFormatCode = 0;
}
} else {
mimeType = "L16";
if (fSamplingFrequency == 44100 && fNumChannels == 2) {
payloadFormatCode = 10; // a static RTP payload type
} else if (fSamplingFrequency == 44100 && fNumChannels == 1) {
payloadFormatCode = 11; // a static RTP payload type
} else {
payloadFormatCode = 0;
}
}
} else { // fBitsPerSample == 8
mimeType = "L8";
payloadFormatCode = 0;
}
return payloadFormatCode;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -