cwAudioFile.h/cpp : Add floating point file format.

This commit is contained in:
kevin 2021-05-08 08:15:06 -04:00
parent a511b00493
commit eed27b2e49
2 changed files with 431 additions and 68 deletions

View File

@ -42,7 +42,22 @@ namespace cw
kWavChkId = 'EVAW',
};
enum { kWriteAudioGutsFl=0x01 };
enum
{
kWriteAudioGutsFl = 0x01,
kWriteFloatFl = 0x02
};
enum
{
kIntegerFmtId = 1,
kFloatFmtId = 3,
kBitsPerByte = 8,
kMax24Bits = 0x007fffff,
kMax16Bits = 0x00007fff,
kMax32Bits = 0x7fffffff
};
typedef struct audiofile_str
{
@ -373,11 +388,12 @@ namespace cw
p->info.chCnt = chCnt;
p->info.bits = bits;
p->info.srate = srate;
p->flags = fmtId == kFloatFmtId ? kWriteFloatFl : 0;
// if the 'data' chunk was read before the 'fmt' chunk then info.frameCnt
// holds the number of bytes in the data chunk
if( p->info.frameCnt != 0 )
p->info.frameCnt = p->info.frameCnt / (p->info.chCnt * p->info.bits/8);
p->info.frameCnt = p->info.frameCnt / (p->info.chCnt * (p->info.bits/kBitsPerByte));
return rc;
}
@ -386,7 +402,7 @@ namespace cw
{
// if the 'fmt' chunk was read before the 'data' chunk then info.chCnt is non-zero
if( p->info.chCnt != 0 )
p->info.frameCnt = chkByteCnt / (p->info.chCnt * p->info.bits/8);
p->info.frameCnt = chkByteCnt / (p->info.chCnt * (p->info.bits/kBitsPerByte));
else
p->info.frameCnt = chkByteCnt;
@ -544,7 +560,7 @@ namespace cw
doubleToX80( p->info.srate, srateX80 );
unsigned hdrByteCnt = 54;
unsigned ssndByteCnt = 8 + (p->info.chCnt * p->info.frameCnt * (p->info.bits/8));
unsigned ssndByteCnt = 8 + (p->info.chCnt * p->info.frameCnt * (p->info.bits/kBitsPerByte));
unsigned formByteCnt = hdrByteCnt + ssndByteCnt - 8;
unsigned commByteCnt = 18;
unsigned ssndSmpOffs = 0;
@ -583,8 +599,8 @@ namespace cw
unsigned frmCnt = p->info.frameCnt;
short bits = p->info.bits;
unsigned srate = p->info.srate;
short fmtId = 1;
unsigned bytesPerSmp = bits/8;
short fmtId = p->flags & kWriteFloatFl ? kFloatFmtId : kIntegerFmtId;
unsigned bytesPerSmp = bits/kBitsPerByte;
unsigned hdrByteCnt = 36;
unsigned fmtByteCnt = 16;
unsigned blockAlignCnt = chCnt * bytesPerSmp;
@ -674,13 +690,16 @@ namespace cw
rc_t rc = kOkRC;
af_t* p = _handleToPtr(h);
//if( p->flags & kWriteFloatFl )
// return cwLogError(kOpFailRC,"Conversion from floating point samples to integer output is not implemented.");
if( chIdx+chCnt > p->info.chCnt )
return cwLogError(kInvalidArgRC,"Invalid channel index on read. %i > %i",chIdx+chCnt,chCnt);
if( actualFrmCntPtr != NULL )
*actualFrmCntPtr = 0;
unsigned bps = p->info.bits / 8; // bytes per sample
unsigned bps = p->info.bits / kBitsPerByte; // bytes per sample
unsigned bpf = bps * p->info.chCnt; // bytes per file frame
unsigned bufFrmCnt = std::min(totalFrmCnt,(unsigned)cwAudioFile_MAX_FRAME_READ_CNT);
unsigned bytesPerBuf = bufFrmCnt * bpf;
@ -842,7 +861,6 @@ namespace cw
if( actualFrmCntPtr != NULL )
*actualFrmCntPtr = 0;
unsigned totalReadCnt = 0;
unsigned bufFrmCnt = std::min( totalFrmCnt, (unsigned)cwAudioFile_MAX_FRAME_READ_CNT );
unsigned bufSmpCnt = bufFrmCnt * chCnt;
@ -877,7 +895,6 @@ namespace cw
if( fbuf != NULL )
fPtrBuf[i] = fbuf[i];
}
//
@ -886,19 +903,74 @@ namespace cw
unsigned actualReadFrmCnt = 0;
frmCnt = std::min( p->info.frameCnt - p->curFrmIdx, std::min( totalFrmCnt-totalReadCnt, bufFrmCnt ) );
// fill the integer audio buffer from the file
// Fill the integer audio buffer from the file.
// Note that the input format may be float but this is ok since there size is the same and _readInt() does not processes the values.
if((rc = _readInt( h, frmCnt, chIdx, chCnt, ptrBuf, &actualReadFrmCnt, false )) != kOkRC )
return rc;
if( actualFrmCntPtr != NULL )
*actualFrmCntPtr += actualReadFrmCnt;
// convert the integer buffer to floating point
// Convert the integer buffer to floating point
for(i=0; i<chCnt; ++i)
{
// if the input is already in float format
if( p->flags & kWriteFloatFl )
{
float* sp = (float*)ptrBuf[i];
// if output is in float (not double) format
if( fbuf != NULL )
{
float* dp = fPtrBuf[i];
float* ep = dp + frmCnt;
if( sumFl )
{
for(; dp<ep; ++dp,++sp)
*dp += *sp;
}
else
{
for(; dp<ep; ++dp,++sp)
*dp = *sp;
}
assert( dp <= fbuf[i] + totalFrmCnt );
fPtrBuf[i] = dp;
}
else // uf output is in double (not float) format
{
double* dp = dPtrBuf[i];
double* ep = dp + frmCnt;
if( sumFl )
{
for(; dp<ep; ++dp,++sp)
*dp += ((double)*sp);
}
else
{
for(; dp<ep; ++dp,++sp)
*dp = ((double)*sp);
}
assert( dp <= dbuf[i] + totalFrmCnt );
dPtrBuf[i] = dp;
}
}
else // if input is an integer format (not float format)
{
int* sp = ptrBuf[i];
// if output is float (not double)
if( fbuf != NULL )
{
@ -921,7 +993,7 @@ namespace cw
fPtrBuf[i] = dp;
}
else
else // output is double (not float)
{
double* dp = dPtrBuf[i];
double* ep = dp + frmCnt;
@ -940,7 +1012,7 @@ namespace cw
assert( dp <= dbuf[i] + totalFrmCnt );
dPtrBuf[i] = dp;
}
}
}
}
@ -1011,6 +1083,225 @@ namespace cw
return rc;
}
rc_t _write_samples_to_file( af_t* p, unsigned bytesPerSmp, unsigned bufSmpCnt, void* buf )
{
rc_t rc = kOkRC;
if( fwrite( buf, bufSmpCnt*bytesPerSmp, 1, p->fp ) != 1)
rc = cwLogError(kWriteFailRC,"Audio file write failed on '%s'.",cwStringNullGuard(p->fn));
return rc;
}
//
// Write 8 bit samples
//
// sample writer: int->uint8_t
rc_t _write_samples( af_t* p, unsigned bufSmpCnt, const int* sbuf, int8_t* dbuf )
{
for(unsigned i=0; i<bufSmpCnt; ++i)
dbuf[i] = (((long)sbuf[i]*255) / ((long)INT_MAX - INT_MIN)) + 127;
return _write_samples_to_file(p,sizeof(dbuf[0]),bufSmpCnt,dbuf);
}
// sample writer: float->uint8_t
rc_t _write_samples( af_t* p, unsigned bufSmpCnt, const float* sbuf, int8_t* dbuf )
{
for(unsigned i=0; i<bufSmpCnt; ++i)
dbuf[i] = (uint8_t)(sbuf[i] * 128) + 127;
return _write_samples_to_file(p,sizeof(dbuf[0]),bufSmpCnt,dbuf);
}
// sample writer: double->uint8_t
rc_t _write_samples( af_t* p, unsigned bufSmpCnt, const double* sbuf, int8_t* dbuf )
{
for(unsigned i=0; i<bufSmpCnt; ++i)
dbuf[i] = (uint8_t)(sbuf[i] * 128) + 127;
return _write_samples_to_file(p,sizeof(dbuf[0]),bufSmpCnt,dbuf);
}
//
// Write 16 bit samples
//
// sample writer: int->short
rc_t _write_samples( af_t* p, unsigned bufSmpCnt, const int* sbuf, short* dbuf )
{
for(unsigned i=0; i<bufSmpCnt; ++i)
dbuf[i] = ((long)sbuf[i]*2*SHRT_MAX) / ((long)INT_MAX - INT_MIN);
return _write_samples_to_file(p,sizeof(dbuf[0]),bufSmpCnt,dbuf);
}
// sample writer: float->short
rc_t _write_samples( af_t* p, unsigned bufSmpCnt, const float* sbuf, short* dbuf )
{
for(unsigned i=0; i<bufSmpCnt; ++i)
dbuf[i] = (short)(sbuf[i] * SHRT_MAX);
return _write_samples_to_file(p,sizeof(dbuf[0]),bufSmpCnt,dbuf);
}
// sample writer: double->short
rc_t _write_samples( af_t* p, unsigned bufSmpCnt, const double* sbuf, short* dbuf )
{
for(unsigned i=0; i<bufSmpCnt; ++i)
dbuf[i] = (short)(sbuf[i] * SHRT_MAX);
return _write_samples_to_file(p,sizeof(dbuf[0]),bufSmpCnt,dbuf);
}
//
// Write 24 bit samples
//
inline void int_to_24( int v, uint8_t* d )
{
uint8_t* s = (uint8_t*)(&v);
d[0] = s[0];
d[1] = s[1];
d[2] = s[2];
}
// sample writer: int->int24
rc_t _write_samples( af_t* p, unsigned bufSmpCnt, const int* sbuf, uint32_t* dbuf )
{
uint8_t* dbp = (uint8_t*)dbuf;
for(unsigned i=0; i<bufSmpCnt; dbp+=3, ++i)
int_to_24(sbuf[i],dbp);
return _write_samples_to_file(p,3,bufSmpCnt,dbuf);
}
// sample writer: float->int24
rc_t _write_samples( af_t* p, unsigned bufSmpCnt, const float* sbuf, uint32_t* dbuf )
{
uint8_t* dbp = (uint8_t*)dbuf;
for(unsigned i=0; i<bufSmpCnt; dbp+=3, ++i)
{
int ismp = sbuf[i]*kMax24Bits;
int_to_24(ismp,dbp);
}
return _write_samples_to_file(p,sizeof(dbuf[0]),bufSmpCnt,dbuf);
}
// sample writer: double->int24
rc_t _write_samples( af_t* p, unsigned bufSmpCnt, const double* sbuf, uint32_t* dbuf )
{
uint8_t* dbp = (uint8_t*)dbuf;
for(unsigned i=0; i<bufSmpCnt; dbp+=3, ++i)
{
int ismp = sbuf[i]*kMax24Bits;
int_to_24(ismp,dbp);
}
return _write_samples_to_file(p,sizeof(dbuf[0]),bufSmpCnt,dbuf);
}
//
// Write 32 bit samples
//
// sample writer: int->int
rc_t _write_samples( af_t* p, unsigned bufSmpCnt, const int* sbuf, int* dbuf )
{
return _write_samples_to_file(p,sizeof(dbuf[0]),bufSmpCnt,dbuf);
}
// float->int
rc_t _write_samples( af_t* p, unsigned bufSmpCnt, const float* sbuf, int* dbuf )
{
for(unsigned i=0; i<bufSmpCnt; ++i)
dbuf[i] = (int)(sbuf[i] * INT_MAX);
return _write_samples_to_file(p,sizeof(dbuf[0]),bufSmpCnt,dbuf);
}
// double->int
rc_t _write_samples( af_t* p, unsigned bufSmpCnt, const double* sbuf, int* dbuf )
{
for(unsigned i=0; i<bufSmpCnt; ++i)
dbuf[i] = (int)(sbuf[i] * INT_MAX);
return _write_samples_to_file(p,sizeof(dbuf[0]),bufSmpCnt,dbuf);
}
//
// Write float samples
//
// int->float
rc_t _write_samples( af_t* p, unsigned bufSmpCnt, const int* sbuf, float* dbuf )
{
for(unsigned i=0; i<bufSmpCnt; ++i)
dbuf[i] = sbuf[i]/INT_MAX;
return _write_samples_to_file(p,sizeof(dbuf[0]),bufSmpCnt,dbuf);
}
// float->float
rc_t _write_samples( af_t* p, unsigned bufSmpCnt, const float* sbuf, float* dbuf )
{
memcpy(dbuf,sbuf,bufSmpCnt*sizeof(float));
return _write_samples_to_file(p,sizeof(dbuf[0]),bufSmpCnt,dbuf);
}
// double->float
rc_t _write_samples( af_t* p, unsigned bufSmpCnt, const double* sbuf, float* dbuf )
{
for(unsigned i=0; i<bufSmpCnt; ++i)
dbuf[i] = (float)(sbuf[i]);
return _write_samples_to_file(p,sizeof(dbuf[0]),bufSmpCnt,dbuf);
}
template< typename S, typename D >
rc_t _write_samples( af_t* p, unsigned bufSmpCnt, const S* sbuf, D* dbuf)
{
return cwLogError(kInvalidDataTypeRC,"The source and desintation sample types are not supported by the audio file writer.");
}
template< typename S, typename D >
rc_t _write_audio( af_t* p, unsigned srcBufFrmCnt, unsigned chCnt, const S* const * srcPtrPtr )
{
rc_t rc = kOkRC;
// Determine the size of the temporary buffer used for deinterleaving, type conversion and file writing
unsigned bufFrmCnt = std::min(srcBufFrmCnt,4096u);
unsigned bufSmpCnt = bufFrmCnt*chCnt;
// allocate the temp. buffers
S sbuf[ bufSmpCnt ];
D dbuf[ bufSmpCnt ];
// for each frame in the source
for(unsigned sfi=0; sfi<srcBufFrmCnt; )
{
// copy as many samples as are available into the temp. buffer
unsigned copyFrmN = std::min( bufFrmCnt, srcBufFrmCnt - sfi );
// deinterleave the source channel signal arrays into the temp buffer
for(unsigned fi=0,si=0; fi<copyFrmN; ++fi)
for(unsigned sci=0; sci<chCnt; ++sci,++si)
sbuf[si] = srcPtrPtr[sci][sfi+fi];
// convert the sample data types and write the result to the output file
if((rc = _write_samples(p,copyFrmN*chCnt,sbuf,dbuf)) != kOkRC )
break;
p->info.frameCnt += copyFrmN;
sfi += copyFrmN;
}
return rc;
}
/*
rc_t _writeRealSamples( handle_t h, unsigned frmCnt, unsigned chCnt, const void* srcPtrPtr, unsigned realSmpByteCnt )
{
rc_t rc = kOkRC;
@ -1093,6 +1384,7 @@ namespace cw
return rc;
}
*/
void _test( const char* audioFn )
{
@ -1181,12 +1473,13 @@ cw::rc_t cw::audiofile::create( handle_t& h, const char* fn, double srate, unsig
}
else
{
p->fn = mem::duplStr( fn );
p->info.srate = srate;
p->info.bits = bits;
p->info.bits = bits==0 ? sizeof(float)*kBitsPerByte : bits;
p->info.chCnt = chCnt;
p->info.frameCnt = 0;
p->flags = kWriteAudioGutsFl;
p->flags = kWriteAudioGutsFl + (bits==0 ? kWriteFloatFl : 0);
// set the swap flags
bool swapFl = cwIsFlag(p->info.flags,kWavAfFl) ? _cmWavSwapFl : _cmAifSwapFl;
@ -1250,7 +1543,7 @@ cw::rc_t cw::audiofile::seek( handle_t h, unsigned frmIdx )
rc_t rc = kOkRC;
af_t* p = _handleToPtr(h);
if((rc = _seek(p,p->smpByteOffs + (frmIdx * p->info.chCnt * (p->info.bits/8)), SEEK_SET)) != kOkRC )
if((rc = _seek(p,p->smpByteOffs + (frmIdx * p->info.chCnt * (p->info.bits/kBitsPerByte)), SEEK_SET)) != kOkRC )
return rc;
p->curFrmIdx = frmIdx;
@ -1355,11 +1648,11 @@ cw::rc_t cw::audiofile::freeFloatBuf( float** floatBuf, unsigned chCnt )
}
cw::rc_t cw::audiofile::writeInt( handle_t h, unsigned frmCnt, unsigned chCnt, int** srcPtrPtr )
cw::rc_t cw::audiofile::writeInt( handle_t h, unsigned frmCnt, unsigned chCnt, const int* const* srcPtrPtr )
{
rc_t rc = kOkRC;
af_t* p = _handleToPtr(h);
unsigned bytesPerSmp = p->info.bits / 8;
unsigned bytesPerSmp = p->info.bits / kBitsPerByte;
unsigned bufFrmCnt = 1024;
unsigned bufByteCnt = bufFrmCnt * bytesPerSmp;
unsigned ci;
@ -1484,11 +1777,78 @@ cw::rc_t cw::audiofile::writeInt( handle_t h, unsigned frmCnt, unsigned ch
}
cw::rc_t cw::audiofile::writeFloat( handle_t h, unsigned frmCnt, unsigned chCnt, float** bufPtrPtr )
{ return _writeRealSamples(h,frmCnt,chCnt,bufPtrPtr,sizeof(float)); }
cw::rc_t cw::audiofile::writeFloat( handle_t h, unsigned frmCnt, unsigned chCnt, const float* const* srcPtrPtr )
{
rc_t rc = kOkRC;
af_t* p = _handleToPtr(h);
cw::rc_t cw::audiofile::writeDouble( handle_t h, unsigned frmCnt, unsigned chCnt, double** bufPtrPtr )
{ return _writeRealSamples(h,frmCnt,chCnt,bufPtrPtr,sizeof(double)); }
if( p->flags & kWriteFloatFl )
rc = _write_audio<float,float>( p, frmCnt, chCnt, srcPtrPtr );
else
{
switch(p->info.bits)
{
case 8:
rc = _write_audio<float,uint8_t>( p, frmCnt, chCnt, srcPtrPtr );
break;
case 16:
rc = _write_audio<float,short>( p, frmCnt, chCnt, srcPtrPtr );
break;
case 24:
break;
case 32:
rc = _write_audio<float,int>( p, frmCnt, chCnt, srcPtrPtr );
break;
default:
cwLogError(kInvalidArgRC,"Invalid bit depth:%i",p->info.bits);
}
}
return rc;
//return _writeRealSamples(h,frmCnt,chCnt,bufPtrPtr,sizeof(float));
}
cw::rc_t cw::audiofile::writeDouble( handle_t h, unsigned frmCnt, unsigned chCnt, const double* const* srcPtrPtr )
{
rc_t rc = kOkRC;
af_t* p = _handleToPtr(h);
if( p->flags & kWriteFloatFl )
rc = _write_audio<double,float>( p, frmCnt, chCnt, srcPtrPtr );
else
{
switch(p->info.bits)
{
case 8:
rc = _write_audio<double,uint8_t>( p, frmCnt, chCnt, srcPtrPtr );
break;
case 16:
rc = _write_audio<double,short>( p, frmCnt, chCnt, srcPtrPtr );
break;
case 24:
break;
case 32:
rc = _write_audio<double,int>( p, frmCnt, chCnt, srcPtrPtr );
break;
default:
cwLogError(kInvalidArgRC,"Invalid bit depth:%i",p->info.bits);
}
}
return rc;
//return _writeRealSamples(h,frmCnt,chCnt,bufPtrPtr,sizeof(double));
}
@ -1547,7 +1907,7 @@ cw::rc_t cw::audiofile::minMaxMean( handle_t h, unsigned chIdx, float* minPtr
}
cw::rc_t cw::audiofile::writeFileInt( const char* fn, double srate, unsigned bits, unsigned frmCnt, unsigned chCnt, int** bufPtrPtr )
cw::rc_t cw::audiofile::writeFileInt( const char* fn, double srate, unsigned bits, unsigned frmCnt, unsigned chCnt, const int* const* bufPtrPtr )
{
rc_t rc;
handle_t h;
@ -1562,7 +1922,7 @@ cw::rc_t cw::audiofile::writeFileInt( const char* fn, double srate, unsign
return rc;
}
cw::rc_t cw::audiofile::writeFileFloat( const char* fn, double srate, unsigned bits, unsigned frmCnt, unsigned chCnt, float** bufPtrPtr )
cw::rc_t cw::audiofile::writeFileFloat( const char* fn, double srate, unsigned bits, unsigned frmCnt, unsigned chCnt, const float* const* bufPtrPtr )
{
rc_t rc;
handle_t h;
@ -1577,7 +1937,7 @@ cw::rc_t cw::audiofile::writeFileFloat( const char* fn, double srate, unsign
return rc;
}
cw::rc_t cw::audiofile::writeFileDouble( const char* fn, double srate, unsigned bits, unsigned frmCnt, unsigned chCnt, double** bufPtrPtr )
cw::rc_t cw::audiofile::writeFileDouble( const char* fn, double srate, unsigned bits, unsigned frmCnt, unsigned chCnt, const double* const* bufPtrPtr )
{
rc_t rc;
handle_t h;

View File

@ -22,7 +22,7 @@ namespace cw
kWavAfFl = 0x02, // this is a WAV file
kSwapAfFl = 0x04, // file header bytes must be swapped
kAifcAfFl = 0x08, // this is an AIFC file
kSwapSamplesAfFl = 0x10 // file sample bytes must be swapped
kSwapSamplesAfFl = 0x10, // file sample bytes must be swapped
};
@ -76,6 +76,7 @@ namespace cw
rc_t open( handle_t& h, const char* fn, info_t* info );
// Set bits to 0 to write the file in IEEE float format.
rc_t create( handle_t& h, const char* fn, double srate, unsigned bits, unsigned chN );
rc_t close( handle_t& h );
@ -134,13 +135,15 @@ namespace cw
rc_t freeFloatBuf( float** floatBufRef, unsigned chCnt );
// Sample Writing Functions
rc_t writeInt( handle_t h, unsigned frmCnt, unsigned chCnt, int** bufPtrPtr );
rc_t writeFloat( handle_t h, unsigned frmCnt, unsigned chCnt, float** bufPtrPtr );
rc_t writeDouble( handle_t h, unsigned frmCnt, unsigned chCnt, double** bufPtrPtr );
rc_t writeInt( handle_t h, unsigned frmCnt, unsigned chCnt, const int* const* bufPtrPtr );
rc_t writeFloat( handle_t h, unsigned frmCnt, unsigned chCnt, const float* const* bufPtrPtr );
rc_t writeDouble( handle_t h, unsigned frmCnt, unsigned chCnt, const double* const* bufPtrPtr );
rc_t writeFileInt( const char* fn, double srate, unsigned bit, unsigned frmCnt, unsigned chCnt, int** bufPtrPtr);
rc_t writeFileFloat( const char* fn, double srate, unsigned bit, unsigned frmCnt, unsigned chCnt, float** bufPtrPtr);
rc_t writeFileDouble( const char* fn, double srate, unsigned bit, unsigned frmCnt, unsigned chCnt, double** bufPtrPtr);
// File Writing Functions
// Set 'bit' to 0 to write the file in IEEE float format.
rc_t writeFileInt( const char* fn, double srate, unsigned bit, unsigned frmCnt, unsigned chCnt, const int* const * bufPtrPtr);
rc_t writeFileFloat( const char* fn, double srate, unsigned bit, unsigned frmCnt, unsigned chCnt, const float* const* bufPtrPtr);
rc_t writeFileDouble( const char* fn, double srate, unsigned bit, unsigned frmCnt, unsigned chCnt, const double* const* bufPtrPtr);
// Scan an entire audio file and return the minimum, maximum and mean sample value.