I have piano-like application, and when I'm pressing piano key, it plays a sound. In my app I wrote my own sine wave generator. App is wrote in Qt. I think that problem is with portAudio, but I can't find any solution for this.
I've recorded for you how my problem sounds: https://vocaroo.com/i/s1yiWjaJffTU
And here is my generator class:
soundEngine.h
#ifndef SOUNDENGINE_H
#define SOUNDENGINE_H
#include <QThread>
#include <math.h>
#include "portaudio.h"
#define SAMPLE_RATE (44100)
#define FRAMES_PER_BUFFER (64)
#define FREQUENCY 220
#ifndef M_PI
#define M_PI (3.14159265)
#endif
#define TABLE_SIZE (200)
typedef struct
{
float sine[TABLE_SIZE];
int phase;
}
paTestData;
class SoundEngine : public QThread
{
Q_OBJECT
public:
bool turnOFF;
void run();
static int patestCallback( const void *inputBuffer, void *outputBuffer,unsigned long framesPerBuffer,const PaStreamCallbackTimeInfo* timeInfo,PaStreamCallbackFlags statusFlags,void *userData );
void generateSine();
void removeSine();
private:
paTestData data;
PaStream *stream;
PaError err;
bool isPressed;
};
#endif // SOUNDENGINE_H
soundEngine.cpp
#include "soundengine.h"
#include <QDebug>
void SoundEngine::run()
{
PaStreamParameters outputParameters;
int i;
double t;
turnOFF = false;
isPressed = false;
static unsigned long n=0;
for( i=0; i<TABLE_SIZE; i++, n++ )
{
t = (double)i/(double)SAMPLE_RATE;
data.sine[i] = 0;
//data.sine[i] = 0.3*sin(2 * M_PI * FREQUENCY * t);
/*data.sine[i] *= 1.0/2;
data.sine[i] += 0.5*sin(2 * M_PI * (FREQUENCY+110) * t);
data.sine[i] *= 2.0/3;
data.sine[i] += (1.0/3)*sin(2 * M_PI * (FREQUENCY+60) * t);
data.sine[i] *= 3.0/4;
data.sine[i] += (1.0/4)*sin(2 * M_PI * (FREQUENCY+160) * t);*/
}
data.phase = 0;
err = Pa_Initialize();
if(err != paNoError) qDebug()<<"Błąd przy inicjalizacji strumienia:"<<Pa_GetErrorText(err);
outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
if (outputParameters.device == paNoDevice) qDebug()<<"Błąd: Brak domyślnego urządzenia wyjścia!";
outputParameters.channelCount = 2; /* stereo output */
outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */
outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
outputParameters.hostApiSpecificStreamInfo = NULL;
err = Pa_OpenStream(
&stream,
NULL, /* no input */
&outputParameters,
SAMPLE_RATE,
FRAMES_PER_BUFFER,
paClipOff, /*paNoFlag we won't output out of range samples so don't bother clipping them */
patestCallback,
&data );
if(err != paNoError) qDebug()<<"Błąd przy otwieraniu strumienia:"<<Pa_GetErrorText(err);
//err = Pa_StartStream( stream );
if(err != paNoError) qDebug()<<"Błąd przy starcie strumienia:"<<Pa_GetErrorText(err);
while (turnOFF == false) {
Pa_Sleep(500);
}
//err = Pa_StopStream( stream );
if(err != paNoError) qDebug()<<"Błąd przy zatrzymywaniu strumienia:"<<Pa_GetErrorText(err);
err = Pa_CloseStream( stream );
if(err != paNoError) qDebug()<<"Błąd przy zamykaniu strumienia:"<<Pa_GetErrorText(err);
Pa_Terminate();
}
int SoundEngine::patestCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags, void *userData)
{
paTestData *callData = (paTestData*)userData;
float *out = (float*)outputBuffer;
float sample;
unsigned long i;
(void) timeInfo; /* Prevent unused variable warnings. */
(void) statusFlags;
(void) inputBuffer;
for( i=0; i<framesPerBuffer; i++ )
{
sample = callData->sine[callData->phase++];
*out++ = sample; /* left */
*out++ = sample; /* right */
if( callData->phase >= TABLE_SIZE ) callData->phase -= TABLE_SIZE;
}
return paContinue;
}
void SoundEngine::generateSine()
{
if(isPressed == false)
{
for(int i=0; i<TABLE_SIZE; i++)
{
data.sine[i] += 0.3*sin(2 * M_PI * 440 * ((double)i/(double)SAMPLE_RATE));
}
isPressed = true;
err = Pa_StartStream( stream );
}
}
void SoundEngine::removeSine()
{
err = Pa_StopStream( stream );
for(int i=0; i<TABLE_SIZE; i++)
{
data.sine[i] -= 0.3*sin(2 * M_PI * 440 * ((double)i/(double)SAMPLE_RATE));
}
isPressed = false;
}
When I'm pressing button, function
void SoundEngine::generateSine()
is running - it generates sound. When I release the button, method
void SoundEngine::removeSine()
removes the sound.
There's nothing wrong with either your sound or PortAudio. The sound you're hearing at the end is just the result of the audio being abruptly stopped. Take a look at the following image of a sound that has a constant amplitude through the entire duration. This sound will have an audible pop at the end.
Conversely, if we attenuate the amplitude by modifying the waveform's envelope (the same sound as in image #1) so that it resembles the sound in image #2, we won't hear any abrupt change(s) in the sound at the end.
In conclusion, if your goal is to completely eliminate the pops that you're hearing, fade out (or fade in) your sound(s).