cmAudioFile.h/c : Added cmAudioFileSetSRate(), cmAudioFileSine() and chnaged cmAudioFileTest() to take a variable argument list.

Added the ability to write WAV files.
cmAudioFileCreate() now creates a WAV or AIF header based on the file name extension.
This commit is contained in:
Kevin Larke 2015-04-10 11:58:10 -07:00
parent 69bf9815e3
commit 072831b32a
2 changed files with 366 additions and 141 deletions

View File

@ -12,20 +12,19 @@
// #define _24to32_aif( p ) ((int)( ((p[0]>127?255:0) << 24) + (((int)p[0]) << 16) + (((int)p[1]) <<8) + p[2])) // no-swap equivalent // #define _24to32_aif( p ) ((int)( ((p[0]>127?255:0) << 24) + (((int)p[0]) << 16) + (((int)p[1]) <<8) + p[2])) // no-swap equivalent
// See note in:_cmAudioFileReadAiffHeader() // See note in:_cmAudioFileReadFileHdr()
// Note that this code byte swaps as it converts - this is to counter the byte swap that occurs in cmAudioFileReadInt(). // Note that this code byte swaps as it converts - this is to counter the byte swap that occurs in cmAudioFileReadInt().
#define _24to32_aif( p ) ((int)( ((p[0]>127?255:0) << 0) + (((int)p[2]) << 24) + (((int)p[1]) <<16) + (((int)p[0]) << 8))) #define _24to32_aif( p ) ((int)( ((p[0]>127?255:0) << 0) + (((int)p[2]) << 24) + (((int)p[1]) <<16) + (((int)p[0]) << 8)))
#define _24to32_wav( p ) ((int)( ((p[2]>127?255:0) << 24) + (((int)p[2]) << 16) + (((int)p[1]) <<8) + p[0])) #define _24to32_wav( p ) ((int)( ((p[2]>127?255:0) << 24) + (((int)p[2]) << 16) + (((int)p[1]) <<8) + p[0]))
#define _cmAfSwap16(v) cmSwap16(v)
#define _cmAfSwap32(v) cmSwap32(v)
#ifdef cmBIG_ENDIAN #ifdef cmBIG_ENDIAN
#define _cmAfSwap16(v) (v)
#define _cmAfSwap32(v) (v)
#define _cmAifSwapFl (0) #define _cmAifSwapFl (0)
#define _cmWavSwapFl (1) #define _cmWavSwapFl (1)
#else #else
#define _cmAfSwap16(v) cmSwap16(v)
#define _cmAfSwap32(v) cmSwap32(v)
#define _cmAifSwapFl (1) #define _cmAifSwapFl (1)
#define _cmWavSwapFl (0) #define _cmWavSwapFl (0)
#endif #endif
@ -61,7 +60,7 @@ typedef struct
cmAudioFileMarker_t* markArray; cmAudioFileMarker_t* markArray;
unsigned flags; unsigned flags;
cmChar_t* fn; cmChar_t* fn;
} cmAudioFileGuts; } cmAf_t;
cmAudioErrRecd _cmAudioFileErrArray[] = cmAudioErrRecd _cmAudioFileErrArray[] =
{ {
@ -81,9 +80,9 @@ cmAudioErrRecd _cmAudioFileErrArray[] =
cmAudioFileH_t cmNullAudioFileH = { NULL }; cmAudioFileH_t cmNullAudioFileH = { NULL };
cmAudioFileGuts* _cmAudioFileGutsPtr( cmAudioFileH_t h ) cmAf_t* _cmAudioFileHandleToPtr( cmAudioFileH_t h )
{ {
cmAudioFileGuts* p = (cmAudioFileGuts*)h.h; cmAf_t* p = (cmAf_t*)h.h;
if( p == NULL ) if( p == NULL )
assert(p!=NULL); assert(p!=NULL);
@ -91,7 +90,7 @@ cmAudioFileGuts* _cmAudioFileGutsPtr( cmAudioFileH_t h )
return p; return p;
} }
cmRC_t _cmAudioFileError( cmAudioFileGuts* p, cmRC_t rc ) cmRC_t _cmAudioFileError( cmAf_t* p, cmRC_t rc )
{ {
if( rc > kUnknownErrAfRC ) if( rc > kUnknownErrAfRC )
rc = kUnknownErrAfRC; rc = kUnknownErrAfRC;
@ -100,11 +99,11 @@ cmRC_t _cmAudioFileError( cmAudioFileGuts* p, cmRC_t rc )
return rc; return rc;
} }
cmAudioFileGuts* _cmAudioFileValidate( cmAudioFileH_t h, cmRC_t* rcPtr, bool writeFl ) cmAf_t* _cmAudioFileValidate( cmAudioFileH_t h, cmRC_t* rcPtr, bool writeFl )
{ {
*rcPtr = kOkAfRC; *rcPtr = kOkAfRC;
cmAudioFileGuts* p = _cmAudioFileGutsPtr(h); cmAf_t* p = _cmAudioFileHandleToPtr(h);
if( p == NULL ) if( p == NULL )
*rcPtr = kInvalidHandleAfRC; *rcPtr = kInvalidHandleAfRC;
@ -116,22 +115,22 @@ cmAudioFileGuts* _cmAudioFileValidate( cmAudioFileH_t h, cmRC_t* rcPtr, bool wri
return *rcPtr == kOkAfRC ? p : NULL; return *rcPtr == kOkAfRC ? p : NULL;
} }
cmAudioFileGuts* _cmAudioFileReadGutsPtr( cmAudioFileH_t h, cmRC_t* rcPtr ) cmAf_t* _cmAudioFileReadGutsPtr( cmAudioFileH_t h, cmRC_t* rcPtr )
{ return _cmAudioFileValidate( h, rcPtr, false ); } { return _cmAudioFileValidate( h, rcPtr, false ); }
cmAudioFileGuts* _cmAudioFileWriteGutsPtr( cmAudioFileH_t h, cmRC_t* rcPtr ) cmAf_t* _cmAudioFileWriteGutsPtr( cmAudioFileH_t h, cmRC_t* rcPtr )
{ return _cmAudioFileValidate( h, rcPtr, true ); } { return _cmAudioFileValidate( h, rcPtr, true ); }
cmRC_t _cmAudioFileSeek( cmAudioFileGuts* p, long byteOffset, int origin ) cmRC_t _cmAudioFileSeek( cmAf_t* p, long byteOffset, int origin )
{ {
if( fseek(p->fp,byteOffset,origin) != 0 ) if( fseek(p->fp,byteOffset,origin) != 0 )
return _cmAudioFileError(p,kSeekFailAfRC); return _cmAudioFileError(p,kSeekFailAfRC);
return kOkAfRC; return kOkAfRC;
} }
cmRC_t _cmAudioFileRead( cmAudioFileGuts* p, void* eleBuf, unsigned bytesPerEle, unsigned eleCnt ) cmRC_t _cmAudioFileRead( cmAf_t* p, void* eleBuf, unsigned bytesPerEle, unsigned eleCnt )
{ {
if( fread(eleBuf,bytesPerEle,eleCnt,p->fp) != eleCnt ) if( fread(eleBuf,bytesPerEle,eleCnt,p->fp) != eleCnt )
return _cmAudioFileError(p,kReadFailAfRC); return _cmAudioFileError(p,kReadFailAfRC);
@ -139,14 +138,13 @@ cmRC_t _cmAudioFileRead( cmAudioFileGuts* p, void* eleBuf, unsigned bytesPerEle,
return kOkAfRC; return kOkAfRC;
} }
cmRC_t _cmAudioFileReadUInt32( cmAudioFileGuts* p, cmUInt32_t* valuePtr ) cmRC_t _cmAudioFileReadUInt32( cmAf_t* p, cmUInt32_t* valuePtr )
{ {
cmRC_t rc; cmRC_t rc;
if(( rc = _cmAudioFileRead(p, valuePtr, sizeof(*valuePtr), 1 )) != kOkAfRC ) if(( rc = _cmAudioFileRead(p, valuePtr, sizeof(*valuePtr), 1 )) != kOkAfRC )
return rc; return rc;
if( cmIsFlag(p->info.flags,kSwapAfFl) ) if( cmIsFlag(p->info.flags,kSwapAfFl) )
*valuePtr = _cmAfSwap32(*valuePtr); *valuePtr = _cmAfSwap32(*valuePtr);
@ -154,7 +152,7 @@ cmRC_t _cmAudioFileReadUInt32( cmAudioFileGuts* p, cmUInt32_t* valuePtr )
} }
cmRC_t _cmAudioFileReadUInt16( cmAudioFileGuts* p, cmUInt16_t* valuePtr ) cmRC_t _cmAudioFileReadUInt16( cmAf_t* p, cmUInt16_t* valuePtr )
{ {
cmRC_t rc; cmRC_t rc;
@ -167,7 +165,7 @@ cmRC_t _cmAudioFileReadUInt16( cmAudioFileGuts* p, cmUInt16_t* valuePtr )
return rc; return rc;
} }
cmRC_t _cmAudioFileReadPascalString( cmAudioFileGuts* p, char s[kAudioFileLabelCharCnt] ) cmRC_t _cmAudioFileReadPascalString( cmAf_t* p, char s[kAudioFileLabelCharCnt] )
{ {
cmRC_t rc; cmRC_t rc;
unsigned char n; unsigned char n;
@ -186,7 +184,7 @@ cmRC_t _cmAudioFileReadPascalString( cmAudioFileGuts* p, char s[kAudioFileLabelC
return rc; return rc;
} }
cmRC_t _cmAudioFileReadString( cmAudioFileGuts* p, char* s, unsigned sn ) cmRC_t _cmAudioFileReadString( cmAf_t* p, char* s, unsigned sn )
{ {
cmRC_t rc; cmRC_t rc;
if((rc = _cmAudioFileRead(p,s,sn,1)) != kOkAfRC ) if((rc = _cmAudioFileRead(p,s,sn,1)) != kOkAfRC )
@ -195,7 +193,7 @@ cmRC_t _cmAudioFileReadString( cmAudioFileGuts* p, char* s, unsigned sn )
return kOkAfRC; return kOkAfRC;
} }
cmRC_t _cmAudioFileReadX80( cmAudioFileGuts* p, double* x80Ptr ) cmRC_t _cmAudioFileReadX80( cmAf_t* p, double* x80Ptr )
{ {
unsigned char s[10]; unsigned char s[10];
cmRC_t rc = kOkAfRC; cmRC_t rc = kOkAfRC;
@ -207,7 +205,7 @@ cmRC_t _cmAudioFileReadX80( cmAudioFileGuts* p, double* x80Ptr )
return kOkAfRC; return kOkAfRC;
} }
cmRC_t _cmAudioFileReadChunkHdr( cmAudioFileGuts* p, cmUInt32_t* chkIdPtr, unsigned* chkByteCntPtr ) cmRC_t _cmAudioFileReadChunkHdr( cmAf_t* p, cmUInt32_t* chkIdPtr, unsigned* chkByteCntPtr )
{ {
cmRC_t rc = kOkAfRC; cmRC_t rc = kOkAfRC;
@ -226,7 +224,7 @@ cmRC_t _cmAudioFileReadChunkHdr( cmAudioFileGuts* p, cmUInt32_t* chkIdPtr, unsig
return rc; return rc;
} }
cmRC_t _cmAudioFileReadAiffHeader( cmAudioFileGuts* p, unsigned constFormId, unsigned constAifId, bool swapFl ) cmRC_t _cmAudioFileReadFileHdr( cmAf_t* p, unsigned constFormId, unsigned constAifId, bool swapFl )
{ {
cmRC_t rc = kOkAfRC; cmRC_t rc = kOkAfRC;
cmUInt32_t formId = 0; cmUInt32_t formId = 0;
@ -280,7 +278,7 @@ cmRC_t _cmAudioFileReadAiffHeader( cmAudioFileGuts* p, unsigned constFormId, uns
return rc; return rc;
} }
cmRC_t _cmAudioFileReadCommChunk( cmAudioFileGuts* p ) cmRC_t _cmAudioFileReadCommChunk( cmAf_t* p )
{ {
cmRC_t rc = kOkAfRC; cmRC_t rc = kOkAfRC;
cmUInt16_t ui16; cmUInt16_t ui16;
@ -330,7 +328,7 @@ cmRC_t _cmAudioFileReadCommChunk( cmAudioFileGuts* p )
return rc; return rc;
} }
cmRC_t _cmAudioFileReadSsndChunk( cmAudioFileGuts* p ) cmRC_t _cmAudioFileReadSsndChunk( cmAf_t* p )
{ {
cmRC_t rc = kOkAfRC; cmRC_t rc = kOkAfRC;
@ -350,7 +348,7 @@ cmRC_t _cmAudioFileReadSsndChunk( cmAudioFileGuts* p )
return rc; return rc;
} }
cmRC_t _cmAudioFileReadMarkerChunk( cmAudioFileGuts* p ) cmRC_t _cmAudioFileReadMarkerChunk( cmAf_t* p )
{ {
cmRC_t rc = kOkAfRC; cmRC_t rc = kOkAfRC;
@ -388,7 +386,7 @@ cmRC_t _cmAudioFileReadMarkerChunk( cmAudioFileGuts* p )
return rc; return rc;
} }
cmRC_t _cmAudioFileReadFmtChunk( cmAudioFileGuts* p ) cmRC_t _cmAudioFileReadFmtChunk( cmAf_t* p )
{ {
cmRC_t rc = kOkAfRC; cmRC_t rc = kOkAfRC;
unsigned short fmtId, chCnt, blockAlign, bits; unsigned short fmtId, chCnt, blockAlign, bits;
@ -424,7 +422,7 @@ cmRC_t _cmAudioFileReadFmtChunk( cmAudioFileGuts* p )
return rc; return rc;
} }
cmRC_t _cmAudioFileReadDatcmhunk( cmAudioFileGuts* p, unsigned chkByteCnt ) cmRC_t _cmAudioFileReadDatcmhunk( cmAf_t* p, unsigned chkByteCnt )
{ {
// if the 'fmt' chunk was read before the 'data' chunk then info.chCnt is non-zero // if the 'fmt' chunk was read before the 'data' chunk then info.chCnt is non-zero
if( p->info.chCnt != 0 ) if( p->info.chCnt != 0 )
@ -437,7 +435,7 @@ cmRC_t _cmAudioFileReadDatcmhunk( cmAudioFileGuts* p, unsigned chkByteCnt )
return kOkAfRC; return kOkAfRC;
} }
cmRC_t _cmAudioFileReadBextChunk( cmAudioFileGuts* p) cmRC_t _cmAudioFileReadBextChunk( cmAf_t* p)
{ {
cmRC_t rc = kOkAfRC; cmRC_t rc = kOkAfRC;
@ -472,8 +470,8 @@ cmAudioFileH_t cmAudioFileNewOpen( const cmChar_t* fn, cmAudioFileInfo_t* afInfo
cmAudioFileH_t h; cmAudioFileH_t h;
cmRC_t rc = kOkAfRC; cmRC_t rc = kOkAfRC;
h.h = cmMemAllocZ( cmAudioFileGuts, 1 ); h.h = cmMemAllocZ( cmAf_t, 1 );
cmErrSetup(&((cmAudioFileGuts*)h.h)->err,rpt,"Audio File"); cmErrSetup(&((cmAf_t*)h.h)->err,rpt,"Audio File");
if( fn != NULL ) if( fn != NULL )
if((rc = cmAudioFileOpen(h,fn,afInfoPtr)) != kOkAfRC ) if((rc = cmAudioFileOpen(h,fn,afInfoPtr)) != kOkAfRC )
@ -496,8 +494,8 @@ cmAudioFileH_t cmAudioFileNewCreate( const cmChar_t* fn, double srate, unsigned
cmAudioFileH_t h; cmAudioFileH_t h;
cmRC_t rc = kOkAfRC; cmRC_t rc = kOkAfRC;
h.h = cmMemAllocZ(cmAudioFileGuts,1); h.h = cmMemAllocZ(cmAf_t,1);
cmErrSetup(&((cmAudioFileGuts*)h.h)->err,rpt,"Audio File"); cmErrSetup(&((cmAf_t*)h.h)->err,rpt,"Audio File");
if( fn != NULL ) if( fn != NULL )
if((rc = cmAudioFileCreate(h,fn,srate,bits,chCnt)) != kOkAfRC ) if((rc = cmAudioFileCreate(h,fn,srate,bits,chCnt)) != kOkAfRC )
@ -515,23 +513,15 @@ cmAudioFileH_t cmAudioFileNewCreate( const cmChar_t* fn, double srate, unsigned
return h; return h;
} }
cmRC_t cmAudioFileOpen( cmAudioFileH_t h, const cmChar_t* fn, cmAudioFileInfo_t* infoPtr ) cmRC_t _cmAudioFileOpen( cmAf_t* p, const cmChar_t* fn, const char* fileModeStr )
{ {
cmRC_t rc = kOkAfRC; cmRC_t rc = kOkAfRC;
cmAudioFileGuts* p = _cmAudioFileGutsPtr(h);
// verify the file is closed before opening
if( cmAudioFileIsOpen(h) )
if((rc = cmAudioFileClose(&h)) != kOkAfRC )
return rc;
// zero the info record // zero the info record
memset(&p->info,0,sizeof(p->info)); memset(&p->info,0,sizeof(p->info));
// open the file // open the file
if((p->fp = fopen(fn,"rb")) == NULL ) if((p->fp = fopen(fn,fileModeStr)) == NULL )
{ {
p->fn = (cmChar_t*)fn; // set the file name so that the error msg can use it p->fn = (cmChar_t*)fn; // set the file name so that the error msg can use it
rc = _cmAudioFileError(p,kOpenFailAfRC); rc = _cmAudioFileError(p,kOpenFailAfRC);
@ -540,8 +530,8 @@ cmRC_t cmAudioFileOpen( cmAudioFileH_t h, const cmChar_t* fn, cmAudioFileIn
} }
// read the file header // read the file header
if((rc = _cmAudioFileReadAiffHeader(p,kAiffFileId,kAiffChkId,_cmAifSwapFl)) != kOkAfRC ) if((rc = _cmAudioFileReadFileHdr(p,kAiffFileId,kAiffChkId,_cmAifSwapFl)) != kOkAfRC )
if((rc = _cmAudioFileReadAiffHeader(p,kWavFileId,kWavChkId,_cmWavSwapFl)) != kOkAfRC ) if((rc = _cmAudioFileReadFileHdr(p,kWavFileId,kWavChkId,_cmWavSwapFl)) != kOkAfRC )
goto errLabel; goto errLabel;
// seek past the file header // seek past the file header
@ -603,6 +593,32 @@ cmRC_t cmAudioFileOpen( cmAudioFileH_t h, const cmChar_t* fn, cmAudioFileIn
goto errLabel; goto errLabel;
} }
errLabel:
if( rc!=kOkAfRC && p->fp != NULL )
{
fclose(p->fp);
p->fp = NULL;
}
return rc;
}
cmRC_t cmAudioFileOpen( cmAudioFileH_t h, const cmChar_t* fn, cmAudioFileInfo_t* infoPtr )
{
cmRC_t rc = kOkAfRC;
cmAf_t* p = _cmAudioFileHandleToPtr(h);
// verify the file is closed before opening
if( cmAudioFileIsOpen(h) )
if((rc = cmAudioFileClose(&h)) != kOkAfRC )
return rc;
// read the file header
if((rc = _cmAudioFileOpen(p, fn, "rb" )) != kOkAfRC )
goto errLabel;
// seek to the first sample offset // seek to the first sample offset
if((rc = _cmAudioFileSeek(p,p->smpByteOffs,SEEK_SET)) != kOkAfRC ) if((rc = _cmAudioFileSeek(p,p->smpByteOffs,SEEK_SET)) != kOkAfRC )
goto errLabel; goto errLabel;
@ -618,10 +634,9 @@ cmRC_t cmAudioFileOpen( cmAudioFileH_t h, const cmChar_t* fn, cmAudioFileIn
errLabel: errLabel:
cmAudioFileClose(&h); cmAudioFileClose(&h);
return rc; return rc;
} }
cmRC_t _cmAudioFileWriteBytes( cmAudioFileGuts* p, const void* b, unsigned bn ) cmRC_t _cmAudioFileWriteBytes( cmAf_t* p, const void* b, unsigned bn )
{ {
cmRC_t rc = kOkAfRC; cmRC_t rc = kOkAfRC;
if( fwrite( b, bn, 1, p->fp ) != 1 ) if( fwrite( b, bn, 1, p->fp ) != 1 )
@ -630,23 +645,27 @@ cmRC_t _cmAudioFileWriteBytes( cmAudioFileGuts* p, const void* b, unsigned bn
return rc; return rc;
} }
cmRC_t _cmAudioFileWriteId( cmAudioFileGuts* p, const char* s ) cmRC_t _cmAudioFileWriteId( cmAf_t* p, const char* s )
{ return _cmAudioFileWriteBytes( p, s, strlen(s)) ; } { return _cmAudioFileWriteBytes( p, s, strlen(s)) ; }
cmRC_t _cmAudioFileWriteUInt32( cmAudioFileGuts* p, unsigned v ) cmRC_t _cmAudioFileWriteUInt32( cmAf_t* p, unsigned v )
{ {
if( cmIsFlag(p->info.flags,kSwapAfFl) )
v = _cmAfSwap32(v); v = _cmAfSwap32(v);
return _cmAudioFileWriteBytes( p, &v, sizeof(v)) ; return _cmAudioFileWriteBytes( p, &v, sizeof(v)) ;
} }
cmRC_t _cmAudioFileWriteUInt16( cmAudioFileGuts* p, unsigned short v ) cmRC_t _cmAudioFileWriteUInt16( cmAf_t* p, unsigned short v )
{ {
if( cmIsFlag(p->info.flags,kSwapAfFl) )
v = _cmAfSwap16(v); v = _cmAfSwap16(v);
return _cmAudioFileWriteBytes( p, &v, sizeof(v)) ; return _cmAudioFileWriteBytes( p, &v, sizeof(v)) ;
} }
cmRC_t _cmAudioFileWriteHdr( cmAudioFileGuts* p ) cmRC_t _cmAudioFileWriteAiffHdr( cmAf_t* p )
{ {
cmRC_t rc = kOkAfRC; cmRC_t rc = kOkAfRC;
unsigned char srateX80[10]; unsigned char srateX80[10];
@ -685,10 +704,54 @@ cmRC_t _cmAudioFileWriteHdr( cmAudioFileGuts* p )
return rc; return rc;
} }
cmRC_t _cmAudioFileWriteWavHdr( cmAf_t* p )
{
cmRC_t rc = kOkAfRC;
short chCnt = p->info.chCnt;
unsigned frmCnt = p->info.frameCnt;
short bits = p->info.bits;
unsigned srate = p->info.srate;
short fmtId = 1;
unsigned bytesPerSmp = bits/8;
unsigned hdrByteCnt = 36;
unsigned fmtByteCnt = 16;
unsigned blockAlignCnt = chCnt * bytesPerSmp;
unsigned sampleCnt = chCnt * frmCnt;
unsigned dataByteCnt = sampleCnt * bytesPerSmp;
if(( rc = _cmAudioFileSeek( p, 0, SEEK_SET )) != kOkAfRC )
return rc;
if((rc = _cmAudioFileWriteId( p, "RIFF")) != kOkAfRC ) goto errLabel;
if((rc = _cmAudioFileWriteUInt32( p, hdrByteCnt + dataByteCnt)) != kOkAfRC ) goto errLabel;
if((rc = _cmAudioFileWriteId( p, "WAVE")) != kOkAfRC ) goto errLabel;
if((rc = _cmAudioFileWriteId( p, "fmt ")) != kOkAfRC ) goto errLabel;
if((rc = _cmAudioFileWriteUInt32( p, fmtByteCnt)) != kOkAfRC ) goto errLabel;
if((rc = _cmAudioFileWriteUInt16( p, fmtId)) != kOkAfRC ) goto errLabel;
if((rc = _cmAudioFileWriteUInt16( p, chCnt)) != kOkAfRC ) goto errLabel;
if((rc = _cmAudioFileWriteUInt32( p, srate)) != kOkAfRC ) goto errLabel;
if((rc = _cmAudioFileWriteUInt32( p, srate * blockAlignCnt)) != kOkAfRC ) goto errLabel;
if((rc = _cmAudioFileWriteUInt16( p, blockAlignCnt)) != kOkAfRC ) goto errLabel;
if((rc = _cmAudioFileWriteUInt16( p, bits)) != kOkAfRC ) goto errLabel;
if((rc = _cmAudioFileWriteId( p, "data")) != kOkAfRC ) goto errLabel;
if((rc = _cmAudioFileWriteUInt32( p, dataByteCnt)) != kOkAfRC ) goto errLabel;
errLabel:
return rc;
}
cmRC_t _cmAudioFileWriteHdr( cmAf_t* p )
{
if( cmIsFlag(p->info.flags,kWavAfFl) )
return _cmAudioFileWriteWavHdr(p);
return _cmAudioFileWriteAiffHdr(p);
}
cmRC_t cmAudioFileCreate( cmAudioFileH_t h, const cmChar_t* fn, double srate, unsigned bits, unsigned chCnt ) cmRC_t cmAudioFileCreate( cmAudioFileH_t h, const cmChar_t* fn, double srate, unsigned bits, unsigned chCnt )
{ {
cmRC_t rc = kOkAfRC; cmRC_t rc = kOkAfRC;
cmAudioFileGuts* p = _cmAudioFileGutsPtr(h); cmAf_t* p = _cmAudioFileHandleToPtr(h);
cmFileSysPathPart_t* pp = NULL; cmFileSysPathPart_t* pp = NULL;
// verify the file is closed before opening // verify the file is closed before opening
@ -696,7 +759,8 @@ cmRC_t cmAudioFileCreate( cmAudioFileH_t h, const cmChar_t* fn, double srat
if((rc = cmAudioFileClose(&h)) != kOkAfRC ) if((rc = cmAudioFileClose(&h)) != kOkAfRC )
return rc; return rc;
// all audio files are written as AIF's - if the file name is given some other extension then issue a warning // all audio files are written as AIF's or WAV's -
// if the file name is given some other extension then issue a warning and write an AIF.
if( fn!=NULL && strlen(fn) && ((pp = cmFsPathParts(fn)) != NULL) ) if( fn!=NULL && strlen(fn) && ((pp = cmFsPathParts(fn)) != NULL) )
{ {
unsigned i; unsigned i;
@ -712,13 +776,25 @@ cmRC_t cmAudioFileCreate( cmAudioFileH_t h, const cmChar_t* fn, double srat
ext[i] = toupper(ext[i]); ext[i] = toupper(ext[i]);
} }
// determine the file type to write
if( cmIsFlag(p->info.flags,kWavAfFl) || strcmp(ext,"WAV")==0 )
{
p->info.flags = cmSetFlag(p->info.flags,kWavAfFl);
p->info.flags = cmClrFlag(p->info.flags,kAiffAfFl);
}
else
{
if( pp->extStr==NULL || (strcmp(ext,"AIF") && strcmp(ext,"AIFF")) ) if( pp->extStr==NULL || (strcmp(ext,"AIF") && strcmp(ext,"AIFF")) )
cmRptPrintf(p->err.rpt,"The AIF audio file '%s' is being written with a file extension other than 'AIF' or 'AIFF'.",cmStringNullGuard(fn)); cmRptPrintf(p->err.rpt,"The AIF audio file '%s' is being written with a file extension other than 'AIF' or 'AIFF'.",cmStringNullGuard(fn));
p->info.flags = cmClrFlag(p->info.flags,kWavAfFl);
p->info.flags = cmSetFlag(p->info.flags,kAiffAfFl);
}
cmFsFreePathParts(pp); cmFsFreePathParts(pp);
} }
// open the file // open the file for writing
if((p->fp = fopen(fn,"wb")) == NULL ) if((p->fp = fopen(fn,"wb")) == NULL )
{ {
p->fn = (cmChar_t*)fn; // set the file name so that the error msg can use it p->fn = (cmChar_t*)fn; // set the file name so that the error msg can use it
@ -734,9 +810,16 @@ cmRC_t cmAudioFileCreate( cmAudioFileH_t h, const cmChar_t* fn, double srat
p->info.frameCnt = 0; p->info.frameCnt = 0;
p->flags = kWriteAudioGutsFl; p->flags = kWriteAudioGutsFl;
// set the swap flags
bool swapFl = cmIsFlag(p->info.flags,kWavAfFl) ? _cmWavSwapFl : _cmAifSwapFl;
p->info.flags = cmEnaFlag(p->info.flags,kSwapAfFl, swapFl);
p->info.flags = cmEnaFlag(p->info.flags,kSwapSamplesAfFl,swapFl);
strcpy(p->fn,fn); strcpy(p->fn,fn);
if((rc = _cmAudioFileWriteHdr( p )) != kOkAfRC ) if((rc = _cmAudioFileWriteHdr(p)) != kOkAfRC )
goto errLabel; goto errLabel;
return rc; return rc;
@ -751,7 +834,7 @@ cmRC_t cmAudioFileClose( cmAudioFileH_t* h )
{ {
assert( h != NULL); assert( h != NULL);
cmAudioFileGuts* p = _cmAudioFileGutsPtr(*h); cmAf_t* p = _cmAudioFileHandleToPtr(*h);
cmRC_t rc = kOkAfRC; cmRC_t rc = kOkAfRC;
if( p->fp == NULL ) if( p->fp == NULL )
@ -785,7 +868,7 @@ cmRC_t cmAudioFileDelete( cmAudioFileH_t* h)
if( h->h == NULL ) if( h->h == NULL )
return kOkAfRC; return kOkAfRC;
cmAudioFileGuts* p = _cmAudioFileGutsPtr(*h); cmAf_t* p = _cmAudioFileHandleToPtr(*h);
if( p->fp != NULL ) if( p->fp != NULL )
rc = cmAudioFileClose(h); rc = cmAudioFileClose(h);
@ -805,28 +888,28 @@ bool cmAudioFileIsOpen( cmAudioFileH_t h )
if( !cmAudioFileIsValid(h) ) if( !cmAudioFileIsValid(h) )
return false; return false;
return _cmAudioFileGutsPtr(h)->fp != NULL; return _cmAudioFileHandleToPtr(h)->fp != NULL;
} }
bool cmAudioFileIsEOF( cmAudioFileH_t h ) bool cmAudioFileIsEOF( cmAudioFileH_t h )
{ {
cmRC_t rc = kOkAfRC; cmRC_t rc = kOkAfRC;
cmAudioFileGuts* p = _cmAudioFileReadGutsPtr(h,&rc); cmAf_t* p = _cmAudioFileReadGutsPtr(h,&rc);
return (rc != kOkAfRC) || (p==NULL) || (p->curFrmIdx >= p->info.frameCnt) || (p->fp==NULL) || feof(p->fp) ? true : false; return (rc != kOkAfRC) || (p==NULL) || (p->curFrmIdx >= p->info.frameCnt) || (p->fp==NULL) || feof(p->fp) ? true : false;
} }
unsigned cmAudioFileTell( cmAudioFileH_t h ) unsigned cmAudioFileTell( cmAudioFileH_t h )
{ {
cmRC_t rc = kOkAfRC; cmRC_t rc = kOkAfRC;
cmAudioFileGuts* p = _cmAudioFileReadGutsPtr(h,&rc); cmAf_t* p = _cmAudioFileReadGutsPtr(h,&rc);
return (rc==kOkAfRC && p!=NULL) ? p->curFrmIdx : cmInvalidIdx; return (rc==kOkAfRC && p!=NULL) ? p->curFrmIdx : cmInvalidIdx;
} }
cmRC_t cmAudioFileSeek( cmAudioFileH_t h, unsigned frmIdx ) cmRC_t cmAudioFileSeek( cmAudioFileH_t h, unsigned frmIdx )
{ {
cmRC_t rc = kOkAfRC; cmRC_t rc = kOkAfRC;
cmAudioFileGuts* p = _cmAudioFileReadGutsPtr(h,&rc); cmAf_t* p = _cmAudioFileReadGutsPtr(h,&rc);
if( rc != kOkAfRC ) if( rc != kOkAfRC )
return rc; return rc;
@ -840,12 +923,10 @@ cmRC_t cmAudioFileSeek( cmAudioFileH_t h, unsigned frmIdx )
} }
cmRC_t _cmAudioFileReadInt( cmAudioFileH_t h, unsigned totalFrmCnt, unsigned chIdx, unsigned chCnt, int* buf[], unsigned* actualFrmCntPtr, bool sumFl ) cmRC_t _cmAudioFileReadInt( cmAudioFileH_t h, unsigned totalFrmCnt, unsigned chIdx, unsigned chCnt, int* buf[], unsigned* actualFrmCntPtr, bool sumFl )
{ {
cmRC_t rc = kOkAfRC; cmRC_t rc = kOkAfRC;
cmAudioFileGuts* p = _cmAudioFileReadGutsPtr(h,&rc); cmAf_t* p = _cmAudioFileReadGutsPtr(h,&rc);
if( rc != kOkAfRC ) if( rc != kOkAfRC )
return rc; return rc;
@ -1013,7 +1094,7 @@ cmRC_t _cmAudioFileReadInt( cmAudioFileH_t h, unsigned totalFrmCnt, unsigned chI
cmRC_t _cmAudioFileReadRealSamples( cmAudioFileH_t h, unsigned totalFrmCnt, unsigned chIdx, unsigned chCnt, float** fbuf, double** dbuf, unsigned* actualFrmCntPtr, bool sumFl ) cmRC_t _cmAudioFileReadRealSamples( cmAudioFileH_t h, unsigned totalFrmCnt, unsigned chIdx, unsigned chCnt, float** fbuf, double** dbuf, unsigned* actualFrmCntPtr, bool sumFl )
{ {
cmRC_t rc = kOkAfRC; cmRC_t rc = kOkAfRC;
cmAudioFileGuts* p = _cmAudioFileReadGutsPtr(h,&rc); cmAf_t* p = _cmAudioFileReadGutsPtr(h,&rc);
if( rc != kOkAfRC ) if( rc != kOkAfRC )
return rc; return rc;
@ -1246,7 +1327,7 @@ cmRC_t cmAudioFileGetSumDouble( const char* fn, unsigned begFrmIdx, unsigned
cmRC_t cmAudioFileWriteInt( cmAudioFileH_t h, unsigned frmCnt, unsigned chCnt, int** srcPtrPtr ) cmRC_t cmAudioFileWriteInt( cmAudioFileH_t h, unsigned frmCnt, unsigned chCnt, int** srcPtrPtr )
{ {
cmRC_t rc = kOkAfRC; cmRC_t rc = kOkAfRC;
cmAudioFileGuts* p = _cmAudioFileWriteGutsPtr(h,&rc ); cmAf_t* p = _cmAudioFileWriteGutsPtr(h,&rc );
if( rc != kOkAfRC ) if( rc != kOkAfRC )
return rc; return rc;
@ -1262,21 +1343,27 @@ cmRC_t cmAudioFileWriteInt( cmAudioFileH_t h, unsigned frmCnt, unsigned ch
{ {
unsigned n = cmMin( frmCnt-wrFrmCnt, bufFrmCnt ); unsigned n = cmMin( frmCnt-wrFrmCnt, bufFrmCnt );
// interleave each channel into buf[]
for(ci=0; ci<chCnt; ++ci) for(ci=0; ci<chCnt; ++ci)
{ {
// get the begin and end source pointers
const int* sbp = srcPtrPtr[ci] + wrFrmCnt; const int* sbp = srcPtrPtr[ci] + wrFrmCnt;
const int* sep = sbp + n; const int* sep = sbp + n;
switch( p->info.bits ) // 8 bit samples can't be byte swapped
{ if( p->info.bits == 8 )
case 8:
{ {
char* dbp = buf + ci; char* dbp = buf + ci;
for(; sbp < sep; dbp+=chCnt ) for(; sbp < sep; dbp+=chCnt )
*dbp = (char)*sbp++; *dbp = (char)*sbp++;
} }
break; else
{
// if the samples do need to be byte swapped
if( cmIsFlag(p->info.flags,kSwapSamplesAfFl) )
{
switch( p->info.bits )
{
case 16: case 16:
{ {
short* dbp = (short*)buf; short* dbp = (short*)buf;
@ -1310,8 +1397,50 @@ cmRC_t cmAudioFileWriteInt( cmAudioFileH_t h, unsigned frmCnt, unsigned ch
default: default:
{ assert(0);} { assert(0);}
} }
}
}
else // interleave without byte swapping
{
switch( p->info.bits )
{
case 16:
{
short* dbp = (short*)buf;
for(dbp+=ci; sbp < sep; dbp+=chCnt, ++sbp )
*dbp = (short)*sbp;
}
break;
case 24:
{
unsigned char* dbp = (unsigned char*)buf;
for( dbp+=(ci*3); sbp < sep; dbp+=(3*chCnt), ++sbp)
{
unsigned char* s = (unsigned char*)sbp;
dbp[0] = s[0];
dbp[1] = s[1];
dbp[2] = s[2];
}
}
break;
case 32:
{
int* dbp = (int*)buf;
for(dbp+=ci; sbp < sep; dbp+=chCnt, ++sbp )
*dbp = *sbp;
}
break;
default:
{ assert(0);}
} // switch
} // don't swap
} // 8 bits
} // ch
// advance the source pointer index
wrFrmCnt+=n; wrFrmCnt+=n;
if( fwrite( buf, n*bytesPerSmp*chCnt, 1, p->fp ) != 1) if( fwrite( buf, n*bytesPerSmp*chCnt, 1, p->fp ) != 1)
@ -1319,7 +1448,8 @@ cmRC_t cmAudioFileWriteInt( cmAudioFileH_t h, unsigned frmCnt, unsigned ch
rc = _cmAudioFileError(p,kWriteFailAfRC); rc = _cmAudioFileError(p,kWriteFailAfRC);
break; break;
} }
}
} // while
p->info.frameCnt += wrFrmCnt; p->info.frameCnt += wrFrmCnt;
@ -1329,7 +1459,7 @@ cmRC_t cmAudioFileWriteInt( cmAudioFileH_t h, unsigned frmCnt, unsigned ch
cmRC_t _cmAudioFileWriteRealSamples( cmAudioFileH_t h, unsigned frmCnt, unsigned chCnt, const void* srcPtrPtr, unsigned realSmpByteCnt ) cmRC_t _cmAudioFileWriteRealSamples( cmAudioFileH_t h, unsigned frmCnt, unsigned chCnt, const void* srcPtrPtr, unsigned realSmpByteCnt )
{ {
cmRC_t rc = kOkAfRC; cmRC_t rc = kOkAfRC;
cmAudioFileGuts* p = _cmAudioFileWriteGutsPtr(h,&rc ); cmAf_t* p = _cmAudioFileWriteGutsPtr(h,&rc );
if( rc != kOkAfRC ) if( rc != kOkAfRC )
return rc; return rc;
@ -1428,7 +1558,7 @@ cmRC_t cmAudioFileMinMaxMean( cmAudioFileH_t h, unsigned chIdx, cmSample_t*
*meanPtr = 0; *meanPtr = 0;
cmRC_t rc = kOkAfRC; cmRC_t rc = kOkAfRC;
cmAudioFileGuts* p = _cmAudioFileReadGutsPtr(h,&rc ); cmAf_t* p = _cmAudioFileReadGutsPtr(h,&rc );
if( rc != kOkAfRC ) if( rc != kOkAfRC )
return rc; return rc;
@ -1548,7 +1678,7 @@ cmRC_t cmAudioFileMinMaxMeanFn( const cmChar_t* fn, unsigned chIdx, cmSample_t*
const cmChar_t* cmAudioFileName( cmAudioFileH_t h ) const cmChar_t* cmAudioFileName( cmAudioFileH_t h )
{ {
cmRC_t rc; cmRC_t rc;
cmAudioFileGuts* p = _cmAudioFileReadGutsPtr(h,&rc ); cmAf_t* p = _cmAudioFileReadGutsPtr(h,&rc );
if( rc != kOkAfRC ) if( rc != kOkAfRC )
return NULL; return NULL;
@ -1568,8 +1698,6 @@ const char* cmAudioFileErrorMsg( unsigned rc )
} }
cmRC_t cmAudioFileGetInfo( const cmChar_t* fn, cmAudioFileInfo_t* infoPtr, cmRpt_t* rpt ) cmRC_t cmAudioFileGetInfo( const cmChar_t* fn, cmAudioFileInfo_t* infoPtr, cmRpt_t* rpt )
{ {
cmRC_t rc = kOkAfRC; cmRC_t rc = kOkAfRC;
@ -1626,7 +1754,7 @@ void cmAudioFilePrintInfo( const cmAudioFileInfo_t* infoPtr, cmRpt_t* rpt )
cmRC_t cmAudioFileReport( cmAudioFileH_t h, cmRpt_t* rpt, unsigned frmIdx, unsigned frmCnt ) cmRC_t cmAudioFileReport( cmAudioFileH_t h, cmRpt_t* rpt, unsigned frmIdx, unsigned frmCnt )
{ {
cmRC_t rc = kOkAfRC; cmRC_t rc = kOkAfRC;
cmAudioFileGuts* p = _cmAudioFileReadGutsPtr(h,&rc); cmAf_t* p = _cmAudioFileReadGutsPtr(h,&rc);
if( rc != kOkAfRC ) if( rc != kOkAfRC )
return rc; return rc;
@ -1676,9 +1804,35 @@ cmRC_t cmAudioFileReportFn( const cmChar_t* fn, unsigned frmIdx, unsigned fr
return cmAudioFileDelete(&h); return cmAudioFileDelete(&h);
} }
/// [cmAudioFileExample] cmRC_t cmAudioFileSetSrate( const cmChar_t* fn, unsigned srate )
{
cmRC_t rc = kOkAfRC;
cmAf_t af;
cmAf_t* p = &af;
void cmAudioFileTest( const cmChar_t* audioFn, const cmChar_t* outFn, cmRpt_t* rpt ) memset(&af,0,sizeof(af));
if((rc = _cmAudioFileOpen(p, fn, "r+b")) != kOkAfRC )
goto errLabel;
if( p->info.srate != srate )
{
// change the sample rate
p->info.srate = srate;
// write the file header
if((rc = _cmAudioFileWriteHdr(p)) != kOkAfRC )
goto errLabel;
}
errLabel:
if( p->fp != NULL )
fclose(p->fp);
return rc;
}
void _cmAudioFileTest( const cmChar_t* audioFn, cmRpt_t* rpt )
{ {
cmAudioFileInfo_t afInfo; cmAudioFileInfo_t afInfo;
cmRC_t cmRC; cmRC_t cmRC;
@ -1698,7 +1852,68 @@ void cmAudioFileTest( const cmChar_t* audioFn, const cmChar_t* outFn, cmRpt_t* r
// close and delete the audio file handle // close and delete the audio file handle
cmAudioFileDelete(&afH); cmAudioFileDelete(&afH);
} }
cmRC_t cmAudioFileSine( cmCtx_t* ctx, const cmChar_t* fn, double srate, unsigned bits, double hz, double gain, double secs )
{
cmRC_t rc = kOkAfRC;
unsigned bN = srate * secs;
cmSample_t* b = cmMemAlloc(cmSample_t,bN);
unsigned chCnt = 1;
unsigned i;
for(i=0; i<bN; ++i)
b[i] = gain * sin(2.0*M_PI*hz*i/srate);
if((rc = cmAudioFileWriteFileFloat(fn, srate, bits, bN, chCnt, &b, &ctx->rpt)) != kOkAfRC)
return rc;
return rc;
}
/// [cmAudioFileExample]
void cmAudioFileTest(cmCtx_t* ctx, int argc, const char* argv[])
{
switch( argc )
{
case 3:
//_cmAudioFileTest(argv[2],&ctx->rpt);
cmAudioFileReportFn(argv[2],0,0,&ctx->rpt);
break;
case 4:
{
errno = 0;
long srate = strtol(argv[3], NULL, 10);
if( srate == 0 && errno != 0 )
cmRptPrintf(&ctx->rpt,"Invalid sample rate argument to cmAudioFileTest().");
else
cmAudioFileSetSrate(argv[2],srate);
}
break;
case 8:
{
errno = 0;
double srate = strtod(argv[3],NULL);
unsigned bits = strtol(argv[4],NULL,10);
double hz = strtod(argv[5],NULL);
double gain = strtod(argv[6],NULL);
double secs = strtod(argv[7],NULL);
if( errno != 0 )
cmRptPrintf(&ctx->rpt,"Invalid arg. to cmAudioFileTest().");
cmAudioFileSine( ctx, argv[2], srate, bits, hz, gain, secs );
}
break;
default:
cmRptPrintf(&ctx->rpt,"Invalid argument count to cmAudioFileTest().");
break;
}
}
/// [cmAudioFileExample] /// [cmAudioFileExample]

View File

@ -129,7 +129,8 @@ extern "C" {
/// reopened with the new file. /// reopened with the new file.
cmRC_t cmAudioFileOpen( cmAudioFileH_t h, const cmChar_t* fn, cmAudioFileInfo_t* infoPtr ); cmRC_t cmAudioFileOpen( cmAudioFileH_t h, const cmChar_t* fn, cmAudioFileInfo_t* infoPtr );
/// Open an audio file for writing. /// Open an audio file for writing. The type of the audio file, AIF or WAV
/// is determined by the file name extension.
cmRC_t cmAudioFileCreate( cmRC_t cmAudioFileCreate(
cmAudioFileH_t h, ///< Handle returned from an earlier call to cmAudioFileNewCreate() or cmAudioFileNewOpen(). cmAudioFileH_t h, ///< Handle returned from an earlier call to cmAudioFileNewCreate() or cmAudioFileNewOpen().
const cmChar_t* fn, ///< File name of the new file. const cmChar_t* fn, ///< File name of the new file.
@ -295,9 +296,18 @@ extern "C" {
cmRC_t cmAudioFileReportFn( const cmChar_t* fn, unsigned frmIdx, unsigned frmCnt, cmRpt_t* rpt ); cmRC_t cmAudioFileReportFn( const cmChar_t* fn, unsigned frmIdx, unsigned frmCnt, cmRpt_t* rpt );
///@} ///@}
/// Change the sample rate value in the header. Note that this function does not resample the audio
/// signal it simply changes the value of the sample rate in the header.
cmRC_t cmAudioFileSetSrate( const cmChar_t* audioFn, unsigned srate );
// Generate a sine tone and write it to a file.
cmRC_t cmAudioFileSine( cmCtx_t* ctx, const cmChar_t* fn, double srate, unsigned bits, double hz, double gain, double secs );
/// Testing and example routine for functions in cmAudioFile.h. /// Testing and example routine for functions in cmAudioFile.h.
/// Also see cmProcTest.c cmAudioFileReadWriteTest() /// Also see cmProcTest.c cmAudioFileReadWriteTest()
void cmAudioFileTest( const cmChar_t* audioFn, const cmChar_t* outFn, cmRpt_t* rpt ); void cmAudioFileTest( cmCtx_t* ctx, int argc, const char* argv[] );
#ifdef __cplusplus #ifdef __cplusplus
} }