2015-10-17 20:46:58 -04:00
/*
File : CASpectralProcessor . cpp
Abstract : CASpectralProcessor . h
Version : 1.1
2015-10-26 14:35:06 -04:00
2015-10-17 20:46:58 -04:00
Disclaimer : IMPORTANT : This Apple software is supplied to you by Apple
Inc . ( " Apple " ) in consideration of your agreement to the following
terms , and your use , installation , modification or redistribution of
this Apple software constitutes acceptance of these terms . If you do
not agree with these terms , please do not use , install , modify or
redistribute this Apple software .
2015-10-26 14:35:06 -04:00
2015-10-17 20:46:58 -04:00
In consideration of your agreement to abide by the following terms , and
subject to these terms , Apple grants you a personal , non - exclusive
license , under Apple ' s copyrights in this original Apple software ( the
" Apple Software " ) , to use , reproduce , modify and redistribute the Apple
Software , with or without modifications , in source and / or binary forms ;
provided that if you redistribute the Apple Software in its entirety and
without modifications , you must retain this notice and the following
text and disclaimers in all such redistributions of the Apple Software .
Neither the name , trademarks , service marks or logos of Apple Inc . may
be used to endorse or promote products derived from the Apple Software
without specific prior written permission from Apple . Except as
expressly stated in this notice , no other rights or licenses , express or
implied , are granted by Apple herein , including but not limited to any
patent rights that may be infringed by your derivative works or by other
works in which the Apple Software may be incorporated .
2015-10-26 14:35:06 -04:00
2015-10-17 20:46:58 -04:00
The Apple Software is provided by Apple on an " AS IS " basis . APPLE
MAKES NO WARRANTIES , EXPRESS OR IMPLIED , INCLUDING WITHOUT LIMITATION
THE IMPLIED WARRANTIES OF NON - INFRINGEMENT , MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE , REGARDING THE APPLE SOFTWARE OR ITS USE AND
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS .
2015-10-26 14:35:06 -04:00
2015-10-17 20:46:58 -04:00
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL , INDIRECT , INCIDENTAL
OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS
INTERRUPTION ) ARISING IN ANY WAY OUT OF THE USE , REPRODUCTION ,
MODIFICATION AND / OR DISTRIBUTION OF THE APPLE SOFTWARE , HOWEVER CAUSED
AND WHETHER UNDER THEORY OF CONTRACT , TORT ( INCLUDING NEGLIGENCE ) ,
STRICT LIABILITY OR OTHERWISE , EVEN IF APPLE HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE .
2015-10-26 14:35:06 -04:00
2015-10-17 20:46:58 -04:00
Copyright ( C ) 2014 Apple Inc . All Rights Reserved .
2015-10-26 14:35:06 -04:00
2015-10-17 20:46:58 -04:00
*/
2015-10-26 14:35:06 -04:00
2015-10-17 20:46:58 -04:00
//#include "AudioFormulas.h"
# include "CASpectralProcessor.h"
# include "CABitOperations.h"
# include <Accelerate/Accelerate.h>
# define OFFSETOF(class, field)((size_t)&((class*)0)->field)
CASpectralProcessor : : CASpectralProcessor ( UInt32 inFFTSize , UInt32 inHopSize , UInt32 inNumChannels , UInt32 inMaxFrames )
: mFFTSize ( inFFTSize ) , mHopSize ( inHopSize ) , mNumChannels ( inNumChannels ) , mMaxFrames ( inMaxFrames ) ,
2015-10-26 14:35:06 -04:00
mLog2FFTSize ( Log2Ceil ( mFFTSize ) ) ,
2015-10-17 20:46:58 -04:00
mFFTMask ( mFFTSize - 1 ) ,
mFFTByteSize ( mFFTSize * sizeof ( Float32 ) ) ,
mIOBufSize ( NextPowerOfTwo ( mFFTSize + mMaxFrames ) ) ,
mIOMask ( mIOBufSize - 1 ) ,
mInputSize ( 0 ) ,
2015-10-26 14:35:06 -04:00
mInputPos ( 0 ) , mOutputPos ( - mFFTSize & mIOMask ) ,
2015-10-17 20:46:58 -04:00
mInFFTPos ( 0 ) , mOutFFTPos ( 0 ) ,
mSpectralFunction ( 0 ) , mUserData ( 0 )
{
mWindow . alloc ( mFFTSize , false ) ;
SineWindow ( ) ; // set default window.
2015-10-26 14:35:06 -04:00
2015-10-17 20:46:58 -04:00
mChannels . alloc ( mNumChannels ) ;
mSpectralBufferList . allocBytes ( OFFSETOF ( SpectralBufferList , mDSPSplitComplex [ mNumChannels ] ) , true ) ;
mSpectralBufferList - > mNumberSpectra = mNumChannels ;
2015-10-26 14:35:06 -04:00
for ( UInt32 i = 0 ; i < mNumChannels ; + + i )
2015-10-17 20:46:58 -04:00
{
mChannels [ i ] . mInputBuf . alloc ( mIOBufSize , true ) ;
mChannels [ i ] . mOutputBuf . alloc ( mIOBufSize , true ) ;
mChannels [ i ] . mFFTBuf . alloc ( mFFTSize , true ) ;
mChannels [ i ] . mSplitFFTBuf . alloc ( mFFTSize , true ) ;
mSpectralBufferList - > mDSPSplitComplex [ i ] . realp = mChannels [ i ] . mSplitFFTBuf ( ) ;
mSpectralBufferList - > mDSPSplitComplex [ i ] . imagp = mChannels [ i ] . mSplitFFTBuf ( ) + ( mFFTSize > > 1 ) ;
}
mFFTSetup = vDSP_create_fftsetup ( mLog2FFTSize , FFT_RADIX2 ) ;
2015-10-26 14:35:06 -04:00
2015-10-17 20:46:58 -04:00
}
CASpectralProcessor : : ~ CASpectralProcessor ( )
{
mWindow . free ( ) ;
mChannels . free ( ) ;
mSpectralBufferList . free ( ) ;
vDSP_destroy_fftsetup ( mFFTSetup ) ;
}
void CASpectralProcessor : : Reset ( )
{
mInputPos = 0 ;
mOutputPos = - mFFTSize & mIOMask ;
mInFFTPos = 0 ;
mOutFFTPos = 0 ;
2015-10-26 14:35:06 -04:00
for ( UInt32 i = 0 ; i < mNumChannels ; + + i )
2015-10-17 20:46:58 -04:00
{
memset ( mChannels [ i ] . mInputBuf ( ) , 0 , mIOBufSize * sizeof ( Float32 ) ) ;
memset ( mChannels [ i ] . mOutputBuf ( ) , 0 , mIOBufSize * sizeof ( Float32 ) ) ;
memset ( mChannels [ i ] . mFFTBuf ( ) , 0 , mFFTSize * sizeof ( Float32 ) ) ;
}
}
const double two_pi = 2. * M_PI ;
void CASpectralProcessor : : HanningWindow ( )
2015-10-26 14:35:06 -04:00
{
2015-10-17 20:46:58 -04:00
// this is also vector optimized
double w = two_pi / ( double ) ( mFFTSize - 1 ) ;
for ( UInt32 i = 0 ; i < mFFTSize ; + + i )
{
2015-10-26 14:35:06 -04:00
mWindow [ i ] = ( 0.5 - 0.5 * cos ( w * ( double ) i ) ) ;
2015-10-17 20:46:58 -04:00
}
}
void CASpectralProcessor : : SineWindow ( )
{
double w = M_PI / ( double ) ( mFFTSize - 1 ) ;
for ( UInt32 i = 0 ; i < mFFTSize ; + + i )
{
mWindow [ i ] = sin ( w * ( double ) i ) ;
}
}
void CASpectralProcessor : : Process ( UInt32 inNumFrames , AudioBufferList * inInput , AudioBufferList * outOutput )
{
// copy from buffer list to input buffer
CopyInput ( inNumFrames , inInput ) ;
2015-10-26 14:35:06 -04:00
2015-10-17 20:46:58 -04:00
// if enough input to process, then process.
2015-10-26 14:35:06 -04:00
while ( mInputSize > = mFFTSize )
2015-10-17 20:46:58 -04:00
{
CopyInputToFFT ( ) ; // copy from input buffer to fft buffer
DoWindowing ( ) ;
DoFwdFFT ( ) ;
ProcessSpectrum ( mFFTSize , mSpectralBufferList ( ) ) ;
DoInvFFT ( ) ;
DoWindowing ( ) ;
OverlapAddOutput ( ) ;
}
// copy from output buffer to buffer list
CopyOutput ( inNumFrames , outOutput ) ;
}
void CASpectralProcessor : : DoWindowing ( )
{
Float32 * win = mWindow ( ) ;
if ( ! win ) return ;
for ( UInt32 i = 0 ; i < mNumChannels ; + + i ) {
Float32 * x = mChannels [ i ] . mFFTBuf ( ) ;
vDSP_vmul ( x , 1 , win , 1 , x , 1 , mFFTSize ) ;
}
//printf("DoWindowing %g %g\n", mChannels[0].mFFTBuf()[0], mChannels[0].mFFTBuf()[200]);
}
void CASpectralProcessor : : CopyInput ( UInt32 inNumFrames , AudioBufferList * inInput )
{
UInt32 numBytes = inNumFrames * sizeof ( Float32 ) ;
UInt32 firstPart = mIOBufSize - mInputPos ;
2015-10-26 14:35:06 -04:00
2015-10-17 20:46:58 -04:00
if ( firstPart < inNumFrames ) {
UInt32 firstPartBytes = firstPart * sizeof ( Float32 ) ;
UInt32 secondPartBytes = numBytes - firstPartBytes ;
2015-10-26 14:35:06 -04:00
for ( UInt32 i = 0 ; i < mNumChannels ; + + i ) {
2015-10-17 20:46:58 -04:00
memcpy ( mChannels [ i ] . mInputBuf + mInputPos , inInput - > mBuffers [ i ] . mData , firstPartBytes ) ;
memcpy ( mChannels [ i ] . mInputBuf , ( UInt8 * ) inInput - > mBuffers [ i ] . mData + firstPartBytes , secondPartBytes ) ;
}
} else {
UInt32 numBytes = inNumFrames * sizeof ( Float32 ) ;
2015-10-26 14:35:06 -04:00
for ( UInt32 i = 0 ; i < mNumChannels ; + + i ) {
2015-10-17 20:46:58 -04:00
memcpy ( mChannels [ i ] . mInputBuf + mInputPos , inInput - > mBuffers [ i ] . mData , numBytes ) ;
}
}
//printf("CopyInput %g %g\n", mChannels[0].mInputBuf[mInputPos], mChannels[0].mInputBuf[(mInputPos + 200) & mIOMask]);
//printf("CopyInput mInputPos %u mIOBufSize %u\n", (unsigned)mInputPos, (unsigned)mIOBufSize);
mInputSize + = inNumFrames ;
mInputPos = ( mInputPos + inNumFrames ) & mIOMask ;
}
void CASpectralProcessor : : CopyOutput ( UInt32 inNumFrames , AudioBufferList * outOutput )
{
//printf("->CopyOutput %g %g\n", mChannels[0].mOutputBuf[mOutputPos], mChannels[0].mOutputBuf[(mOutputPos + 200) & mIOMask]);
//printf("CopyOutput mOutputPos %u\n", (unsigned)mOutputPos);
UInt32 numBytes = inNumFrames * sizeof ( Float32 ) ;
UInt32 firstPart = mIOBufSize - mOutputPos ;
if ( firstPart < inNumFrames ) {
UInt32 firstPartBytes = firstPart * sizeof ( Float32 ) ;
UInt32 secondPartBytes = numBytes - firstPartBytes ;
for ( UInt32 i = 0 ; i < mNumChannels ; + + i ) {
memcpy ( outOutput - > mBuffers [ i ] . mData , mChannels [ i ] . mOutputBuf + mOutputPos , firstPartBytes ) ;
memcpy ( ( UInt8 * ) outOutput - > mBuffers [ i ] . mData + firstPartBytes , mChannels [ i ] . mOutputBuf , secondPartBytes ) ;
memset ( mChannels [ i ] . mOutputBuf + mOutputPos , 0 , firstPartBytes ) ;
memset ( mChannels [ i ] . mOutputBuf , 0 , secondPartBytes ) ;
}
} else {
for ( UInt32 i = 0 ; i < mNumChannels ; + + i ) {
memcpy ( outOutput - > mBuffers [ i ] . mData , mChannels [ i ] . mOutputBuf + mOutputPos , numBytes ) ;
memset ( mChannels [ i ] . mOutputBuf + mOutputPos , 0 , numBytes ) ;
}
}
//printf("<-CopyOutput %g %g\n", ((Float32*)outOutput->mBuffers[0].mData)[0], ((Float32*)outOutput->mBuffers[0].mData)[200]);
mOutputPos = ( mOutputPos + inNumFrames ) & mIOMask ;
}
void CASpectralProcessor : : PrintSpectralBufferList ( )
{
UInt32 half = mFFTSize > > 1 ;
for ( UInt32 i = 0 ; i < mNumChannels ; + + i ) {
DSPSplitComplex & freqData = mSpectralBufferList - > mDSPSplitComplex [ i ] ;
2015-10-26 14:35:06 -04:00
2015-10-17 20:46:58 -04:00
for ( UInt32 j = 0 ; j < half ; j + + ) {
printf ( " bin[%d]: %lf + %lfi \n " , ( int ) j , freqData . realp [ j ] , freqData . imagp [ j ] ) ;
}
}
}
void CASpectralProcessor : : CopyInputToFFT ( )
{
//printf("CopyInputToFFT mInFFTPos %u\n", (unsigned)mInFFTPos);
UInt32 firstPart = mIOBufSize - mInFFTPos ;
UInt32 firstPartBytes = firstPart * sizeof ( Float32 ) ;
if ( firstPartBytes < mFFTByteSize ) {
UInt32 secondPartBytes = mFFTByteSize - firstPartBytes ;
for ( UInt32 i = 0 ; i < mNumChannels ; + + i ) {
memcpy ( mChannels [ i ] . mFFTBuf ( ) , mChannels [ i ] . mInputBuf ( ) + mInFFTPos , firstPartBytes ) ;
memcpy ( ( UInt8 * ) mChannels [ i ] . mFFTBuf ( ) + firstPartBytes , mChannels [ i ] . mInputBuf ( ) , secondPartBytes ) ;
}
} else {
for ( UInt32 i = 0 ; i < mNumChannels ; + + i ) {
memcpy ( mChannels [ i ] . mFFTBuf ( ) , mChannels [ i ] . mInputBuf ( ) + mInFFTPos , mFFTByteSize ) ;
}
}
mInputSize - = mHopSize ;
mInFFTPos = ( mInFFTPos + mHopSize ) & mIOMask ;
//printf("CopyInputToFFT %g %g\n", mChannels[0].mFFTBuf()[0], mChannels[0].mFFTBuf()[200]);
}
void CASpectralProcessor : : OverlapAddOutput ( )
{
//printf("OverlapAddOutput mOutFFTPos %u\n", (unsigned)mOutFFTPos);
UInt32 firstPart = mIOBufSize - mOutFFTPos ;
if ( firstPart < mFFTSize ) {
UInt32 secondPart = mFFTSize - firstPart ;
for ( UInt32 i = 0 ; i < mNumChannels ; + + i ) {
float * out1 = mChannels [ i ] . mOutputBuf ( ) + mOutFFTPos ;
vDSP_vadd ( out1 , 1 , mChannels [ i ] . mFFTBuf ( ) , 1 , out1 , 1 , firstPart ) ;
float * out2 = mChannels [ i ] . mOutputBuf ( ) ;
vDSP_vadd ( out2 , 1 , mChannels [ i ] . mFFTBuf ( ) + firstPart , 1 , out2 , 1 , secondPart ) ;
}
} else {
for ( UInt32 i = 0 ; i < mNumChannels ; + + i ) {
float * out1 = mChannels [ i ] . mOutputBuf ( ) + mOutFFTPos ;
vDSP_vadd ( out1 , 1 , mChannels [ i ] . mFFTBuf ( ) , 1 , out1 , 1 , mFFTSize ) ;
}
}
//printf("OverlapAddOutput %g %g\n", mChannels[0].mOutputBuf[mOutFFTPos], mChannels[0].mOutputBuf[(mOutFFTPos + 200) & mIOMask]);
mOutFFTPos = ( mOutFFTPos + mHopSize ) & mIOMask ;
}
void CASpectralProcessor : : DoFwdFFT ( )
{
//printf("->DoFwdFFT %g %g\n", mChannels[0].mFFTBuf()[0], mChannels[0].mFFTBuf()[200]);
UInt32 half = mFFTSize > > 1 ;
2015-10-26 14:35:06 -04:00
for ( UInt32 i = 0 ; i < mNumChannels ; + + i )
2015-10-17 20:46:58 -04:00
{
vDSP_ctoz ( ( DSPComplex * ) mChannels [ i ] . mFFTBuf ( ) , 2 , & mSpectralBufferList - > mDSPSplitComplex [ i ] , 1 , half ) ;
vDSP_fft_zrip ( mFFTSetup , & mSpectralBufferList - > mDSPSplitComplex [ i ] , 1 , mLog2FFTSize , FFT_FORWARD ) ;
}
//printf("<-DoFwdFFT %g %g\n", direction, mChannels[0].mFFTBuf()[0], mChannels[0].mFFTBuf()[200]);
}
void CASpectralProcessor : : DoInvFFT ( )
{
//printf("->DoInvFFT %g %g\n", mChannels[0].mFFTBuf()[0], mChannels[0].mFFTBuf()[200]);
UInt32 half = mFFTSize > > 1 ;
2015-10-26 14:35:06 -04:00
for ( UInt32 i = 0 ; i < mNumChannels ; + + i )
2015-10-17 20:46:58 -04:00
{
vDSP_fft_zrip ( mFFTSetup , & mSpectralBufferList - > mDSPSplitComplex [ i ] , 1 , mLog2FFTSize , FFT_INVERSE ) ;
2015-10-26 14:35:06 -04:00
vDSP_ztoc ( & mSpectralBufferList - > mDSPSplitComplex [ i ] , 1 , ( DSPComplex * ) mChannels [ i ] . mFFTBuf ( ) , 2 , half ) ;
2015-10-17 20:46:58 -04:00
float scale = 0.5 / mFFTSize ;
vDSP_vsmul ( mChannels [ i ] . mFFTBuf ( ) , 1 , & scale , mChannels [ i ] . mFFTBuf ( ) , 1 , mFFTSize ) ;
}
//printf("<-DoInvFFT %g %g\n", direction, mChannels[0].mFFTBuf()[0], mChannels[0].mFFTBuf()[200]);
}
void CASpectralProcessor : : SetSpectralFunction ( SpectralFunction inFunction , void * inUserData )
{
2015-10-26 14:35:06 -04:00
mSpectralFunction = inFunction ;
2015-10-17 20:46:58 -04:00
mUserData = inUserData ;
}
void CASpectralProcessor : : ProcessSpectrum ( UInt32 inFFTSize , SpectralBufferList * inSpectra )
{
if ( mSpectralFunction )
( mSpectralFunction ) ( inSpectra , mUserData ) ;
}
# pragma mark ___Utility___
2015-10-26 14:35:06 -04:00
void CASpectralProcessor : : GetMagnitude ( AudioBufferList * list , Float32 * min , Float32 * max )
{
UInt32 half = mFFTSize > > 1 ;
2015-10-17 20:46:58 -04:00
for ( UInt32 i = 0 ; i < mNumChannels ; + + i ) {
2015-10-26 14:35:06 -04:00
DSPSplitComplex & freqData = mSpectralBufferList - > mDSPSplitComplex [ i ] ;
2015-10-17 20:46:58 -04:00
Float32 * b = ( Float32 * ) list - > mBuffers [ i ] . mData ;
2015-10-26 14:35:06 -04:00
vDSP_zvabs ( & freqData , 1 , b , 1 , half ) ;
vDSP_maxmgv ( b , 1 , & max [ i ] , half ) ;
vDSP_minmgv ( b , 1 , & min [ i ] , half ) ;
}
2015-10-17 20:46:58 -04:00
}
void CASpectralProcessor : : GetFrequencies ( Float32 * freqs , Float32 sampleRate )
{
2015-10-26 14:35:06 -04:00
UInt32 half = mFFTSize > > 1 ;
2015-10-17 20:46:58 -04:00
for ( UInt32 i = 0 ; i < half ; i + + ) {
2015-10-26 14:35:06 -04:00
freqs [ i ] = ( ( Float32 ) ( i ) ) * sampleRate / ( ( Float32 ) mFFTSize ) ;
2015-10-17 20:46:58 -04:00
}
}
bool CASpectralProcessor : : ProcessForwards ( UInt32 inNumFrames , AudioBufferList * inInput )
{
// copy from buffer list to input buffer
CopyInput ( inNumFrames , inInput ) ;
2015-10-26 14:35:06 -04:00
2015-10-17 20:46:58 -04:00
bool processed = false ;
// if enough input to process, then process.
2015-10-26 14:35:06 -04:00
while ( mInputSize > = mFFTSize )
2015-10-17 20:46:58 -04:00
{
CopyInputToFFT ( ) ; // copy from input buffer to fft buffer
DoWindowing ( ) ;
DoFwdFFT ( ) ;
ProcessSpectrum ( mFFTSize , mSpectralBufferList ( ) ) ; // here you would copy the fft results out to a buffer indicated in mUserData, say for sonogram drawing
processed = true ;
}
2015-10-26 14:35:06 -04:00
2015-10-17 20:46:58 -04:00
return processed ;
}
bool CASpectralProcessor : : ProcessBackwards ( UInt32 inNumFrames , AudioBufferList * outOutput )
2015-10-26 14:35:06 -04:00
{
2015-10-17 20:46:58 -04:00
ProcessSpectrum ( mFFTSize , mSpectralBufferList ( ) ) ;
DoInvFFT ( ) ;
DoWindowing ( ) ;
2015-10-26 14:35:06 -04:00
OverlapAddOutput ( ) ;
2015-10-17 20:46:58 -04:00
// copy from output buffer to buffer list
CopyOutput ( inNumFrames , outOutput ) ;
2015-10-26 14:35:06 -04:00
2015-10-17 20:46:58 -04:00
return true ;
}