我正在努力用portaudio输出用户选择的音频音调。我在paex_sine.cpp上有一个函数,并根据这篇文章multi-audio-tones-to-sound-card-using-portaudio进行了一些修改
我可以通过在sinCallBackMethod()中放置sin(n * FREQ * 2 * PI / SAMPLE_RATE)计算来动态生成正弦波,但频率不正确,并且不会根据用户改变输入。 这是我的功能代码。
void CDeepWaveDlg::generateSound(float freq)
{
pitch = tone;
offset = freq;
class Sine
{
public:
Sine() : stream(0), left_phase(0), right_phase(0)
{
}
bool open(PaDeviceIndex index)
{
PaStreamParameters outputParameters;
outputParameters.device = index;
if (outputParameters.device == paNoDevice) {
return false;
}
outputParameters.channelCount = 2; /* stereo output */
outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */
outputParameters.suggestedLatency = Pa_GetDeviceInfo(outputParameters.device)->defaultLowOutputLatency;
outputParameters.hostApiSpecificStreamInfo = NULL;
PaError err = Pa_OpenStream(
&stream,
NULL, /* no input */
&outputParameters,
SAMPLE_RATE,
FRAMES_PER_BUFFER,
paClipOff, /* we won't output out of range samples so don't bother clipping them */
&Sine::paCallback,
this /* Using 'this' for userData so we can cast to Sine* in paCallback method */
);
if (err != paNoError)
{
/* Failed to open stream to device !!! */
return false;
}
err = Pa_SetStreamFinishedCallback(stream, &Sine::paStreamFinished);
if (err != paNoError)
{
Pa_CloseStream(stream);
stream = 0;
return false;
}
return true;
}
bool close()
{
if (stream == 0)
return false;
PaError err = Pa_CloseStream(stream);
stream = 0;
return (err == paNoError);
}
bool start()
{
if (stream == 0)
return false;
PaError err = Pa_StartStream(stream);
return (err == paNoError);
}
bool stop()
{
if (stream == 0)
return false;
PaError err = Pa_StopStream(stream);
return (err == paNoError);
}
private:
/* The instance callback, where we have access to every method/variable in object of class Sine */
int paCallbackMethod(const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags)
{
float *out = (float*)outputBuffer;
unsigned long i;
(void)timeInfo; /* Prevent unused variable warnings. */
(void)statusFlags;
(void)inputBuffer;
for (i = 0; i<framesPerBuffer; i++)
{
float v = sin(i * pitch * 2.0 * M_PI /(float)SAMPLE_RATE);
float v2 = sin(i * (pitch + (float)offset) * 2.0 * M_PI /(float)SAMPLE_RATE);
*out++ = v;
*out++ = v2;
}
return paContinue;
}
/* This routine will be called by the PortAudio engine when audio is needed.
** It may called at interrupt level on some machines so don't do anything
** that could mess up the system like calling malloc() or free().
*/
static int paCallback(const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData)
{
/* Here we cast userData to Sine* type so we can call the instance method paCallbackMethod, we can do that since
we called Pa_OpenStream with 'this' for userData */
return ((Sine*)userData)->paCallbackMethod(inputBuffer, outputBuffer,
framesPerBuffer,
timeInfo,
statusFlags);
}
void paStreamFinishedMethod()
{
printf("Stream Completed: %s\n", message);
}
/*
* This routine is called by portaudio when playback is done.
*/
static void paStreamFinished(void* userData)
{
return ((Sine*)userData)->paStreamFinishedMethod();
}
PaStream *stream;
float sine[TABLE_SIZE];
int left_phase;
int right_phase;
char message[20];
};
PaError err;
Sine sine;
err = Pa_Initialize();
if (err != paNoError) goto error;
if (sine.open(Pa_GetDefaultOutputDevice()))
{
if (sine.start())
{
Pa_Sleep(NUM_SECONDS * 1000);
sine.stop();
}
sine.close();
}
Pa_Terminate();
我能够接近所需音高的唯一方法是增加每个缓冲区的帧数,目前为64,但我仍然遇到根据用户选择不改变频率的问题。 这个问题肯定超出了我的技能水平,但我希望有人能帮我理解这里发生的事情。谢谢你提前寻求帮助。