summaryrefslogtreecommitdiff
path: root/libs/appleutility/CoreAudio/PublicUtility/CASpectralProcessor.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libs/appleutility/CoreAudio/PublicUtility/CASpectralProcessor.cpp')
-rw-r--r--libs/appleutility/CoreAudio/PublicUtility/CASpectralProcessor.cpp376
1 files changed, 376 insertions, 0 deletions
diff --git a/libs/appleutility/CoreAudio/PublicUtility/CASpectralProcessor.cpp b/libs/appleutility/CoreAudio/PublicUtility/CASpectralProcessor.cpp
new file mode 100644
index 0000000000..ffb2da8a83
--- /dev/null
+++ b/libs/appleutility/CoreAudio/PublicUtility/CASpectralProcessor.cpp
@@ -0,0 +1,376 @@
+/*
+ File: CASpectralProcessor.cpp
+ Abstract: CASpectralProcessor.h
+ Version: 1.1
+
+ 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.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ Copyright (C) 2014 Apple Inc. All Rights Reserved.
+
+*/
+
+//#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),
+ mLog2FFTSize(Log2Ceil(mFFTSize)),
+ mFFTMask(mFFTSize - 1),
+ mFFTByteSize(mFFTSize * sizeof(Float32)),
+ mIOBufSize(NextPowerOfTwo(mFFTSize + mMaxFrames)),
+ mIOMask(mIOBufSize - 1),
+ mInputSize(0),
+ mInputPos(0), mOutputPos(-mFFTSize & mIOMask),
+ mInFFTPos(0), mOutFFTPos(0),
+ mSpectralFunction(0), mUserData(0)
+{
+ mWindow.alloc(mFFTSize, false);
+ SineWindow(); // set default window.
+
+ mChannels.alloc(mNumChannels);
+ mSpectralBufferList.allocBytes(OFFSETOF(SpectralBufferList, mDSPSplitComplex[mNumChannels]), true);
+ mSpectralBufferList->mNumberSpectra = mNumChannels;
+ for (UInt32 i = 0; i < mNumChannels; ++i)
+ {
+ 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);
+
+}
+
+CASpectralProcessor::~CASpectralProcessor()
+{
+ mWindow.free();
+ mChannels.free();
+ mSpectralBufferList.free();
+ vDSP_destroy_fftsetup(mFFTSetup);
+}
+
+void CASpectralProcessor::Reset()
+{
+ mInputPos = 0;
+ mOutputPos = -mFFTSize & mIOMask;
+ mInFFTPos = 0;
+ mOutFFTPos = 0;
+
+ for (UInt32 i = 0; i < mNumChannels; ++i)
+ {
+ 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()
+{
+ // this is also vector optimized
+
+ double w = two_pi / (double)(mFFTSize - 1);
+ for (UInt32 i = 0; i < mFFTSize; ++i)
+ {
+ mWindow[i] = (0.5 - 0.5 * cos(w * (double)i));
+ }
+}
+
+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);
+
+ // if enough input to process, then process.
+ while (mInputSize >= mFFTSize)
+ {
+ 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;
+
+
+ if (firstPart < inNumFrames) {
+ UInt32 firstPartBytes = firstPart * sizeof(Float32);
+ UInt32 secondPartBytes = numBytes - firstPartBytes;
+ for (UInt32 i=0; i<mNumChannels; ++i) {
+ 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);
+ for (UInt32 i=0; i<mNumChannels; ++i) {
+ 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];
+
+ 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;
+ for (UInt32 i=0; i<mNumChannels; ++i)
+ {
+ 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;
+ for (UInt32 i=0; i<mNumChannels; ++i)
+ {
+ vDSP_fft_zrip(mFFTSetup, &mSpectralBufferList->mDSPSplitComplex[i], 1, mLog2FFTSize, FFT_INVERSE);
+ vDSP_ztoc(&mSpectralBufferList->mDSPSplitComplex[i], 1, (DSPComplex*)mChannels[i].mFFTBuf(), 2, half);
+ 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)
+{
+ mSpectralFunction = inFunction;
+ mUserData = inUserData;
+}
+
+void CASpectralProcessor::ProcessSpectrum(UInt32 inFFTSize, SpectralBufferList* inSpectra)
+{
+ if (mSpectralFunction)
+ (mSpectralFunction)(inSpectra, mUserData);
+}
+
+#pragma mark ___Utility___
+
+void CASpectralProcessor::GetMagnitude(AudioBufferList* list, Float32* min, Float32* max)
+{
+ UInt32 half = mFFTSize >> 1;
+ for (UInt32 i=0; i<mNumChannels; ++i) {
+ DSPSplitComplex &freqData = mSpectralBufferList->mDSPSplitComplex[i];
+
+ Float32* b = (Float32*) list->mBuffers[i].mData;
+
+ vDSP_zvabs(&freqData,1,b,1,half);
+
+ vDSP_maxmgv(b, 1, &max[i], half);
+ vDSP_minmgv(b, 1, &min[i], half);
+
+ }
+}
+
+
+void CASpectralProcessor::GetFrequencies(Float32* freqs, Float32 sampleRate)
+{
+ UInt32 half = mFFTSize >> 1;
+
+ for (UInt32 i=0; i< half; i++){
+ freqs[i] = ((Float32)(i))*sampleRate/((Float32)mFFTSize);
+ }
+}
+
+
+bool CASpectralProcessor::ProcessForwards(UInt32 inNumFrames, AudioBufferList* inInput)
+{
+ // copy from buffer list to input buffer
+ CopyInput(inNumFrames, inInput);
+
+ bool processed = false;
+ // if enough input to process, then process.
+ while (mInputSize >= mFFTSize)
+ {
+ 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;
+ }
+
+ return processed;
+}
+
+bool CASpectralProcessor::ProcessBackwards(UInt32 inNumFrames, AudioBufferList* outOutput)
+{
+
+ ProcessSpectrum(mFFTSize, mSpectralBufferList());
+ DoInvFFT();
+ DoWindowing();
+ OverlapAddOutput();
+
+ // copy from output buffer to buffer list
+ CopyOutput(inNumFrames, outOutput);
+
+ return true;
+}
+
+