// Note: this function will have half-buffer phase shift effect void processInput(short *input, short *output, short channel) { int i; // Read in the input buffer for (i = 0; i < ARRAY_SIZE; i++) { inputBuffer[channel][ARRAY_SIZE + i] = input[i]; } // Apply windows to the midpoint buffer and input buffer applyWindow(inputBuffer[channel] + (ARRAY_SIZE / 2), windowedCrossSampleArray[channel]); applyWindow(inputBuffer[channel] + ARRAY_SIZE, windowedInputArray[channel]); frequencyScale(windowedCrossSampleArray[channel], frequencyScaledSamples[channel][1], ARRAY_SIZE, frequencyScalars); frequencyScale(windowedInputArray[channel], frequencyScaledSamples[channel][2], ARRAY_SIZE, frequencyScalars); // Sum overlaps to produce the output array for (i = 0; i < ARRAY_SIZE / 2; i++) { output[i] = frequencyScaledSamples[channel][FSS_PREVIOUS_INPUT][ARRAY_SIZE / 2 + i] + frequencyScaledSamples[channel][FSS_CROSS_INPUT][i]; } for (i = ARRAY_SIZE / 2; i < ARRAY_SIZE; i++) { // float -> short conversion should be handled automatically output[i] = frequencyScaledSamples[channel][FSS_CROSS_INPUT][i] + frequencyScaledSamples[channel][FSS_THIS_INPUT][i - (ARRAY_SIZE / 2)]; } // Store input and scaled version of input for use in next iteration for (i = 0; i < ARRAY_SIZE; i++) { inputBuffer[channel][i] = inputBuffer[channel][i + ARRAY_SIZE]; frequencyScaledSamples[channel][FSS_PREVIOUS_INPUT][i] = frequencyScaledSamples[channel][FSS_THIS_INPUT][i]; } /* for (i = 0; i < ARRAY_SIZE / 2; i++) { printf("%d, %d\n", input[i], output[i + ARRAY_SIZE / 2]); } printf("\n\n"); */ }
void RealtimeAnalyser::doFFTAnalysis() { DCHECK(isMainThread()); // Unroll the input buffer into a temporary buffer, where we'll apply an // analysis window followed by an FFT. size_t fftSize = this->fftSize(); AudioFloatArray temporaryBuffer(fftSize); float* inputBuffer = m_inputBuffer.data(); float* tempP = temporaryBuffer.data(); // Take the previous fftSize values from the input buffer and copy into the // temporary buffer. unsigned writeIndex = m_writeIndex; if (writeIndex < fftSize) { memcpy(tempP, inputBuffer + writeIndex - fftSize + InputBufferSize, sizeof(*tempP) * (fftSize - writeIndex)); memcpy(tempP + fftSize - writeIndex, inputBuffer, sizeof(*tempP) * writeIndex); } else { memcpy(tempP, inputBuffer + writeIndex - fftSize, sizeof(*tempP) * fftSize); } // Window the input samples. applyWindow(tempP, fftSize); // Do the analysis. m_analysisFrame->doFFT(tempP); float* realP = m_analysisFrame->realData(); float* imagP = m_analysisFrame->imagData(); // Blow away the packed nyquist component. imagP[0] = 0; // Normalize so than an input sine wave at 0dBfs registers as 0dBfs (undo FFT // scaling factor). const double magnitudeScale = 1.0 / fftSize; // A value of 0 does no averaging with the previous result. Larger values // produce slower, but smoother changes. double k = m_smoothingTimeConstant; k = std::max(0.0, k); k = std::min(1.0, k); // Convert the analysis data from complex to magnitude and average with the // previous result. float* destination = magnitudeBuffer().data(); size_t n = magnitudeBuffer().size(); for (size_t i = 0; i < n; ++i) { std::complex<double> c(realP[i], imagP[i]); double scalarMagnitude = abs(c) * magnitudeScale; destination[i] = float(k * destination[i] + (1 - k) * scalarMagnitude); } }
void RealtimeAnalyser::doFFTAnalysis() { ASSERT(isMainThread()); // Unroll the input buffer into a temporary buffer, where we'll apply an analysis window followed by an FFT. size_t fftSize = this->fftSize(); AudioFloatArray temporaryBuffer(fftSize); float* inputBuffer = m_inputBuffer.data(); float* tempP = temporaryBuffer.data(); // Take the previous fftSize values from the input buffer and copy into the temporary buffer. // FIXME : optimize with memcpy(). unsigned writeIndex = m_writeIndex; for (unsigned i = 0; i < fftSize; ++i) tempP[i] = inputBuffer[(i + writeIndex - fftSize + InputBufferSize) % InputBufferSize]; // Window the input samples. applyWindow(tempP, fftSize); // Do the analysis. m_analysisFrame->doFFT(tempP); size_t n = DefaultFFTSize / 2; float* realP = m_analysisFrame->realData(); float* imagP = m_analysisFrame->imagData(); // Blow away the packed nyquist component. imagP[0] = 0.0f; // Normalize so than an input sine wave at 0dBfs registers as 0dBfs (undo FFT scaling factor). const double MagnitudeScale = 1.0 / DefaultFFTSize; // A value of 0 does no averaging with the previous result. Larger values produce slower, but smoother changes. double k = m_smoothingTimeConstant; k = max(0.0, k); k = min(1.0, k); // Convert the analysis data from complex to magnitude and average with the previous result. float* destination = magnitudeBuffer().data(); for (unsigned i = 0; i < n; ++i) { Complex c(realP[i], imagP[i]); double scalarMagnitude = abs(c) * MagnitudeScale; destination[i] = float(k * destination[i] + (1.0 - k) * scalarMagnitude); } }
/* -- main function -- */ int main( int argc, char **argv ) { PaStreamParameters inputParameters; float a[2], b[3], mem1[4], mem2[4]; float data[FFT_SIZE]; float datai[FFT_SIZE]; float window[FFT_SIZE]; float freqTable[FFT_SIZE]; char * noteNameTable[FFT_SIZE]; float notePitchTable[FFT_SIZE]; void * fft = NULL; PaStream *stream = NULL; PaError err = 0; struct sigaction action; // add signal listen so we know when to exit: action.sa_handler = signalHandler; sigemptyset (&action.sa_mask); action.sa_flags = 0; sigaction (SIGINT, &action, NULL); sigaction (SIGHUP, &action, NULL); sigaction (SIGTERM, &action, NULL); // build the window, fft, etc /* buildHanWindow( window, 30 ); for( int i=0; i<30; ++i ) { for( int j=0; j<window[i]*50; ++j ) printf( "*" ); printf("\n"); } exit(0); */ buildHanWindow( window, FFT_SIZE ); fft = initfft( FFT_EXP_SIZE ); computeSecondOrderLowPassParameters( SAMPLE_RATE, 330, a, b ); mem1[0] = 0; mem1[1] = 0; mem1[2] = 0; mem1[3] = 0; mem2[0] = 0; mem2[1] = 0; mem2[2] = 0; mem2[3] = 0; //freq/note tables for( int i=0; i<FFT_SIZE; ++i ) { freqTable[i] = ( SAMPLE_RATE * i ) / (float) ( FFT_SIZE ); } for( int i=0; i<FFT_SIZE; ++i ) { noteNameTable[i] = NULL; notePitchTable[i] = -1; } for( int i=0; i<127; ++i ) { float pitch = ( 440.0 / 32.0 ) * pow( 2, (i-9.0)/12.0 ) ; if( pitch > SAMPLE_RATE / 2.0 ) break; //find the closest frequency using brute force. float min = 1000000000.0; int index = -1; for( int j=0; j<FFT_SIZE; ++j ) { if( fabsf( freqTable[j]-pitch ) < min ) { min = fabsf( freqTable[j]-pitch ); index = j; } } noteNameTable[index] = NOTES[i%12]; notePitchTable[index] = pitch; //printf( "%f %d %s\n", pitch, index, noteNameTable[index] ); } // initialize portaudio err = Pa_Initialize(); if( err != paNoError ) goto error; inputParameters.device = Pa_GetDefaultInputDevice(); inputParameters.channelCount = 1; inputParameters.sampleFormat = paFloat32; inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultHighInputLatency ; inputParameters.hostApiSpecificStreamInfo = NULL; printf( "Opening %s\n", Pa_GetDeviceInfo( inputParameters.device )->name ); err = Pa_OpenStream( &stream, &inputParameters, NULL, //no output SAMPLE_RATE, FFT_SIZE, paClipOff, NULL, NULL ); if( err != paNoError ) goto error; err = Pa_StartStream( stream ); if( err != paNoError ) goto error; // this is the main loop where we listen to and // process audio. while( running ) { // read some data err = Pa_ReadStream( stream, data, FFT_SIZE ); if( err ) goto error; //FIXME: we don't want to err on xrun // low-pass //for( int i=0; i<FFT_SIZE; ++i ) // printf( "in %f\n", data[i] ); for( int j=0; j<FFT_SIZE; ++j ) { data[j] = processSecondOrderFilter( data[j], mem1, a, b ); data[j] = processSecondOrderFilter( data[j], mem2, a, b ); } // window applyWindow( window, data, FFT_SIZE ); // do the fft for( int j=0; j<FFT_SIZE; ++j ) datai[j] = 0; applyfft( fft, data, datai, false ); //find the peak float maxVal = -1; int maxIndex = -1; for( int j=0; j<FFT_SIZE/2; ++j ) { float v = data[j] * data[j] + datai[j] * datai[j] ; /* printf( "%d: ", j*SAMPLE_RATE/(2*FFT_SIZE) ); for( int i=0; i<sqrt(v)*100000000; ++i ) printf( "*" ); printf( "\n" ); */ if( v > maxVal ) { maxVal = v; maxIndex = j; } } float freq = freqTable[maxIndex]; //find the nearest note: int nearestNoteDelta=0; while( true ) { if( nearestNoteDelta < maxIndex && noteNameTable[maxIndex-nearestNoteDelta] != NULL ) { nearestNoteDelta = -nearestNoteDelta; break; } else if( nearestNoteDelta + maxIndex < FFT_SIZE && noteNameTable[maxIndex+nearestNoteDelta] != NULL ) { break; } ++nearestNoteDelta; } char * nearestNoteName = noteNameTable[maxIndex+nearestNoteDelta]; float nearestNotePitch = notePitchTable[maxIndex+nearestNoteDelta]; float centsSharp = 1200 * log( freq / nearestNotePitch ) / log( 2.0 ); // now output the results: printf("\033[2J\033[1;1H"); //clear screen, go to top left fflush(stdout); printf( "Tuner listening. Control-C to exit.\n" ); printf( "%f Hz, %d : %f\n", freq, maxIndex, maxVal*1000 ); printf( "Nearest Note: %s\n", nearestNoteName ); if( nearestNoteDelta != 0 ) { if( centsSharp > 0 ) printf( "%f cents sharp.\n", centsSharp ); if( centsSharp < 0 ) printf( "%f cents flat.\n", -centsSharp ); } else { printf( "in tune!\n" ); } printf( "\n" ); int chars = 30; if( nearestNoteDelta == 0 || centsSharp >= 0 ) { for( int i=0; i<chars; ++i ) printf( " " ); } else { for( int i=0; i<chars+centsSharp; ++i ) printf( " " ); for( int i=chars+centsSharp<0?0:chars+centsSharp; i<chars; ++i ) printf( "=" ); } printf( " %2s ", nearestNoteName ); if( nearestNoteDelta != 0 ) for( int i=0; i<chars && i<centsSharp; ++i ) printf( "=" ); printf("\n"); } err = Pa_StopStream( stream ); if( err != paNoError ) goto error; // cleanup destroyfft( fft ); Pa_Terminate(); return 0; error: if( stream ) { Pa_AbortStream( stream ); Pa_CloseStream( stream ); } destroyfft( fft ); Pa_Terminate(); fprintf( stderr, "An error occured while using the portaudio stream\n" ); fprintf( stderr, "Error number: %d\n", err ); fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); return 1; }