C++ 零延迟话筒环回,侧音

C++ 零延迟话筒环回,侧音,c++,windows,audio,portaudio,C++,Windows,Audio,Portaudio,我正试图通过编程方式创建一个零延迟麦克风环回扬声器或输出。这用于为耳机生成侧音。我相信任何读者都知道,侧音必须为零延迟,否则,听到自己的延迟会导致你失去大部分连贯的说话能力 我尝试用C语言中的Naudio和C++的PARTAUDIO来创建解决方案。我在PortAudio方面运气很好,但是我无法实现我所需要的零延迟侧音。Portaudio会产生5毫秒左右的延迟,这是可以检测到的,并且会导致我的语音持续减慢 我知道windows提供了一个麦克风环回,我已经测试过了,但是即使是windows环回也有足

我正试图通过编程方式创建一个零延迟麦克风环回扬声器或输出。这用于为耳机生成侧音。我相信任何读者都知道,侧音必须为零延迟,否则,听到自己的延迟会导致你失去大部分连贯的说话能力

我尝试用C语言中的Naudio和C++的PARTAUDIO来创建解决方案。我在PortAudio方面运气很好,但是我无法实现我所需要的零延迟侧音。Portaudio会产生5毫秒左右的延迟,这是可以检测到的,并且会导致我的语音持续减慢

我知道windows提供了一个麦克风环回,我已经测试过了,但是即使是windows环回也有足够的延迟,足以让人恼火

我的问题分为两部分

1.)这是硬件/软件的限制吗?实现零音频延迟实际上是不可克服的吗

这是我的C++代码,使用PARTAUDIO。有没有什么方法可以比我现有的方法更有效地减少延迟?(请原谅我可能的草率代码,我对C++是很新的,我还在学习)

class vC\u侧音{
公众:
无效启用(整数输入,整数输出);
无效禁用();
vC_侧音(int输入设备,int输出设备){
st_inputDevice=inputDevice;
st_outputDevice=输出设备;
}
int st_instanceCallBack(常量无效*输入缓冲区,
void*outputBuffer,
未签名的长帧缓冲,
const Pastream CallbackTimeInfo*timeInfo,
PaStreamCallbackFlags状态标志);
私人:
int st_输入设备;
int st_输出设备;
PaError st_错误;
PaStream*st_stream=NULL;
};
PaStreamParameters vC_getParam(PaDeviceIndex dev、int ch、PaSampleFormat smplFormat){
参数参数;
param.device=dev;
param.channelCount=ch;
param.sampleFormat=smplFormat;
//param.suggestedLatency=Pa_GetDeviceInfo(dev)->defaultLowInputLatency;
param.suggestedLatency=0.000;//这里是调整延迟的好地方
param.hostApiSpecificStreamInfo=NULL;
返回参数;
}
void vC_sidetone::enable(int-in_-ch,int-out-ch){
int framesPerBuffer=1;
PaSampleFormat smplFormat=PA32;
int smplRate=44100;
PaStreamParameters inParam=vC_getParam(st_inputDevice,in_ch,smplFormat);
PaStreamParameters outParam=vC_getParam(st_outputDevice,out_ch,smplFormat);
st_error=Pa_Initialize();
//使用回调打开并启动流:
st_error=Pa_OpenStream(
&圣河,
&因帕兰,
&奥帕兰,
SMPRrate,
framesPerBuffer,
帕利波夫,
st_gblCallBack,
这
);
st_error=Pa_StartStream(st_stream);
}
void vC_sidetone::disable(){
st_错误=Pa_停止流(st_流);
巴乌河(圣乌河);
巴乌河(圣乌河);
Pa_Terminate();
}
int vC_sidetone::st_instanceCallBack(const void*inputBuffer,
void*outputBuffer,
未签名的长帧缓冲,
const Pastream CallbackTimeInfo*timeInfo,
PaStreamCallbackFlags状态标志){
(void)timeInfo;//防止未使用的变量警告。
(作废)状态标志;
//将数据强制转换为浮动:
浮点*输出=(浮点*)输出缓冲区;
浮点*输入=(浮点*)输入缓冲区;
无符号长i;
//对于(i=0;ist_instanceCallBack(inputBuffer,outputBuffer,
framesPerBuffer,
时间信息,
状态标志);
}

抽象层只能增加延迟,而不能减少延迟。很明显,时间旅行是不可能的。PulseAudio不是Windows上的本机API,它构建在本机音频架构之上


当前的本机Windows API是WASAPI。它以低延迟著称。但它也是底层硬件上的抽象层。例如USB也有延迟。没有比这更好的了。

由于输入和输出硬件以及底层软件堆栈(不是PortAudio,而是DirectSound或它使用的任何东西)的工作方式,您想要的通常是无法实现的。数据在大小大于1的缓冲区中传递,每个采样时间为1/44000秒。因此,例如,仅88个样本的缓冲区意味着播放(以及再次记录)的最小2ms延迟。关于Q2,PortAudio文档建议您将framesPerBuffer设置为零,以避免额外的缓冲层增加延迟。
class vC_sidetone {
public:
    void enable(int in_ch, int out_ch);
    void disable();

    vC_sidetone(int inputDevice, int outputDevice){
        st_inputDevice = inputDevice;
        st_outputDevice = outputDevice;
    }

    int st_instanceCallBack(const void *inputBuffer,
        void *outputBuffer,
        unsigned long framesPerBuffer,
        const PaStreamCallbackTimeInfo *timeInfo,
        PaStreamCallbackFlags statusFlags);

private:
    int st_inputDevice;
    int st_outputDevice;
    PaError st_error;
    PaStream *st_stream = NULL;
};

PaStreamParameters vC_getParam(PaDeviceIndex dev, int ch, PaSampleFormat smplFormat){

    PaStreamParameters param;
    param.device = dev;
    param.channelCount = ch;
    param.sampleFormat = smplFormat;
    //param.suggestedLatency = Pa_GetDeviceInfo(dev)->defaultLowInputLatency;
    param.suggestedLatency = 0.000; //here is a good place to tweak latency
    param.hostApiSpecificStreamInfo = NULL;

    return param;
}

void vC_sidetone::enable(int in_ch, int out_ch){
    int framesPerBuffer = 1;
    PaSampleFormat smplFormat = paFloat32;
    int smplRate = 44100;

    PaStreamParameters inParam = vC_getParam(st_inputDevice, in_ch, smplFormat);
    PaStreamParameters outParam = vC_getParam(st_outputDevice, out_ch, smplFormat);

    st_error = Pa_Initialize();

    // Open and start stream using callback:
    st_error = Pa_OpenStream(
        &st_stream,
        &inParam,
        &outParam,
        smplRate,
        framesPerBuffer,
        paClipOff,
        st_gblCallBack,
        this
        );

    st_error = Pa_StartStream(st_stream);
}

void vC_sidetone::disable(){

    st_error = Pa_StopStream(st_stream);
    Pa_AbortStream(st_stream);
    Pa_CloseStream(st_stream);
    Pa_Terminate();
}

int vC_sidetone::st_instanceCallBack(const void *inputBuffer,
                            void *outputBuffer,
                            unsigned long framesPerBuffer,
                            const PaStreamCallbackTimeInfo *timeInfo,
                            PaStreamCallbackFlags statusFlags){

    (void)timeInfo; // Prevent unused variable warnings.
    (void)statusFlags;


    // Cast data to floats:
    float *out = (float*)outputBuffer;
    float *in = (float*)inputBuffer;
    unsigned long i;

    //for (i = 0; i < framesPerBuffer*NUM_CHANNELS; i++)
    //  out[i] = in[i];
    for (i = 0; i < framesPerBuffer; i++)  //another good place for latency
        out[i] = in[i];

    return paContinue;
}

//this is actually not part of the vC_sidetone class, I was getting linker errors
static int st_gblCallBack(const void *inputBuffer,
    void *outputBuffer,
    unsigned long framesPerBuffer,
    const PaStreamCallbackTimeInfo *timeInfo,
    PaStreamCallbackFlags statusFlags,
    void *userData){

    return ((vC_sidetone*)userData)->st_instanceCallBack(inputBuffer, outputBuffer,
        framesPerBuffer,
        timeInfo,
        statusFlags);
}