使用portaudio从用户输入生成特定音调

时间:2016-07-17 19:26:22

标签: c++ windows audio portaudio

我正在努力用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,但我仍然遇到根据用户选择不改变频率的问题。 这个问题肯定超出了我的技能水平,但我希望有人能帮我理解这里发生的事情。谢谢你提前寻求帮助。

0 个答案:

没有答案