加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

c – 以编程方式增加麦克风增益

发布时间:2020-12-16 06:53:15 所属栏目:百科 来源:网络整理
导读:我正在尝试为VOIP场景增加麦克风的增益. 我正在使用PortAudio来获取输入流(具有类型为paFloat32的样本),我将这些值乘以浮点数,然后将结果流传递给输出设备. 注意:我将它传递给虚拟输出设备,该设备自动重定向到虚拟输入设备(程序:VB-Cable),VOIP应用程序可
我正在尝试为VOIP场景增加麦克风的增益.
我正在使用PortAudio来获取输入流(具有类型为paFloat32的样本),我将这些值乘以浮点数,然后将结果流传递给输出设备.
注意:我将它传递给虚拟输出设备,该设备自动重定向到虚拟输入设备(程序:VB-Cable),VOIP应用程序可以将其用作应用了增益的麦克风输入.

我想知道是否有更好的方法来增加信号的增益,从而更好地保持质量.

我已经读过,最好先执行这样的增益计算,首先将输入转换为更高精度的格式,以这种格式执行增益乘法,应用裁剪,然后再回到原始格式.
我不知道如何使用PortAudio的paFloat32类型,我已将我的尝试包括在源代码中.当我启用它时,即使增益设置为1,也会出现明显的噪声问题.

依赖关系:tinycon,PortAudio

编译:g main.cpp tinycon.cpp -o main -L./ -lcygportaudio-2 -lrt -lm -pthread -std = c 11

码:

#include "portaudio.h"
#include <iostream>
#include <chrono>
#include <thread>
#include <mutex>
#include "tinycon.h"

#define SAMPLE_RATE       (44100)
#define FRAMES_PER_BUFFER   (441)
#define DITHER_FLAG           (1)

#define PA_SAMPLE_TYPE  paFloat32
#define SAMPLE_SIZE (4)
#define SAMPLE_SILENCE  (0)
#define PRINTF_S_FORMAT "%f"

/*******************************************************************/
double multiplier = 1.0;
double multiplierStep = 0.1;
int main(int argc,char **argv);
int xrun(PaStream *stream,int err,char* sampleBlock);
void error1(PaStream *stream,char* sampleBlock);
void error2(PaStream *stream,int err);
void listDevices();

// Use tinycon and a second thread for non blocking input
class tcon : public tinyConsole
{
public:
        tcon (std::string s): tinyConsole(s) {;}

        int hotkeys(char c)
        {
                if (c == 's') {
                        if (multiplier >= (0+multiplierStep)) {
                                multiplier -= multiplierStep;
                        }
                        printf( "Multiplier: %fn",multiplier );
                        return 1;
                }
                if (c == 'w') {
                        multiplier += multiplierStep;
                        printf( "Multiplier: %fn",multiplier );
                        return 1;
                }
                return 0;
        }
};
int inputThread() {
        tcon tc (std::string(""));
        tc.run();
}

void listDevices() {
        int i,numDevices,defaultDisplayed;
        const PaDeviceInfo *deviceInfo;

        Pa_Initialize();

        numDevices = Pa_GetDeviceCount();

        printf( "Number of devices = %dn",numDevices );
        int isInputDevice = 0;
        for( i=0; i<numDevices; i++ )
        {
                deviceInfo = Pa_GetDeviceInfo( i );
                int isInputDevice = (deviceInfo->maxInputChannels > 0);
                printf( "%sDeviceID: %d,Name: %sn",(isInputDevice ? "Input" : "Output"),i,deviceInfo->name);
        }
        fprintf (stderr,"Press any key to closen");
        getch();
}

int main (int argc,char **argv)
{
        int c;
        int inputDeviceId = -1;
        int outputDeviceId = -1;
        opterr = 0;
        const char* helpMessage =
                "-h : show this help messagen"
                "-i <int> : select the INPUT DEVICE by idn"
                "-o <int> : select the OUPUT DEVICE by idn"
                "-m <double> : SIGNAL MULTIPLIERn"
                "-s <double> : SIGNAL MULTIPLIER STEP (press w or s while console focused to go up and down by this ammount.n"
                "-d : list devicesn";

        while ((c = getopt (argc,argv,"i:o:l:m:s:hd")) != -1) {
                switch (c) {
                        case 'i':
                                inputDeviceId = atoi(optarg);
                                break;
                        case 'o':
                                outputDeviceId = atoi(optarg);
                                break;
                        case 'm':
                                multiplier = atof(optarg);
                                break;
                        case 's':
                                multiplierStep = atof(optarg);
                                break;
                        case 'd':
                                listDevices();
                                return 0;
                        case '?':
                                if (isprint (optopt))
                                        fprintf (stderr,"Unknown option `-%c'.n",optopt);
                                else
                                        fprintf (stderr,"Unknown option character `x%x'.n",optopt);
                        case 'h':
                                fprintf (stderr,helpMessage);
                                fprintf (stderr,"Press any key to closen");
                                getch();
                                return 1;
                        default:
                        abort ();
                }
        }

        // Start non blocking input thread
        std::thread nonBlockingInputThread(inputThread);


    PaStreamParameters inputParameters,outputParameters;
    PaStream *stream = NULL;
    PaError err;
    const PaDeviceInfo* inputInfo;
    const PaDeviceInfo* outputInfo;
    char *sampleBlock = NULL;
    int i;
    int numBytes;
    int numChannels;

    err = Pa_Initialize();
    if( err != paNoError ) error2(stream,err);

    inputParameters.device = (inputDeviceId == -1) ? Pa_GetDefaultInputDevice() : inputDeviceId; /* default input device */
    inputInfo = Pa_GetDeviceInfo( inputParameters.device );
    outputParameters.device = (outputDeviceId == -1) ? Pa_GetDefaultOutputDevice() : outputDeviceId; /* default output device */
    outputInfo = Pa_GetDeviceInfo( outputParameters.device );

        numChannels = inputInfo->maxInputChannels < outputInfo->maxOutputChannels
                ? inputInfo->maxInputChannels : outputInfo->maxOutputChannels;

    inputParameters.channelCount = numChannels;
    inputParameters.sampleFormat = PA_SAMPLE_TYPE;
    inputParameters.suggestedLatency = inputInfo->defaultHighInputLatency ;
    inputParameters.hostApiSpecificStreamInfo = NULL;
    printf( "Input device # %d.n",inputParameters.device );
    printf( "    Name: %sn",inputInfo->name );


        outputParameters.channelCount = numChannels;
        outputParameters.sampleFormat = PA_SAMPLE_TYPE;
        outputParameters.suggestedLatency = outputInfo->defaultHighOutputLatency;
        outputParameters.hostApiSpecificStreamInfo = NULL;
    printf( "Output device # %d.n",outputParameters.device );
    printf( "    Name: %sn",outputInfo->name );

    /* -- setup -- */

    err = Pa_OpenStream(
              &stream,&inputParameters,&outputParameters,SAMPLE_RATE,FRAMES_PER_BUFFER,paClipOff,/* we won't output out of range samples so don't bother clipping them */
              NULL,/* no callback,use blocking API */
              NULL ); /* no callback,so no callback userData */
    if( err != paNoError ) error2(stream,err);

    numBytes = FRAMES_PER_BUFFER * numChannels * SAMPLE_SIZE ;
    sampleBlock = (char *) malloc( numBytes );
    if( sampleBlock == NULL )
    {
        printf("Could not allocate record array.n");
        error1(stream,sampleBlock);
    }

    err = Pa_StartStream( stream );
    if( err != paNoError ) error1(stream,sampleBlock);


        while (1) {
        // You may get underruns or overruns if the output is not primed by PortAudio.
        err = Pa_ReadStream( stream,sampleBlock,FRAMES_PER_BUFFER );
        if( err ) xrun(stream,err,sampleBlock);

                int blockIndex;
                float* sampleBlockShort = (float*)sampleBlock;
                for (blockIndex = 0; blockIndex < FRAMES_PER_BUFFER; blockIndex++) {
                        /*
                        double dSample = (double)sampleBlockShort[blockIndex];
                        dSample *= multiplier;
                        if (dSample > 32767.0) dSample = 32767.0;
                        if (dSample < -32768.0) dSample = -32768.0;
                        sampleBlockShort[blockIndex] = (short)dSample;
                        */
                        sampleBlockShort[blockIndex] *= multiplier;
                }

                err = Pa_WriteStream( stream,FRAMES_PER_BUFFER );
                if( err ) xrun(stream,sampleBlock);
    }
    printf("Wire off.n"); fflush(stdout);

    err = Pa_StopStream( stream );
    if( err != paNoError ) error1(stream,sampleBlock);

    free( sampleBlock );

    Pa_Terminate();
    return 0;

}

int xrun(PaStream *stream,char* sampleBlock) {
    printf("err = %dn",err); fflush(stdout);
    if( stream ) {
       Pa_AbortStream( stream );
       Pa_CloseStream( stream );
    }
    free( sampleBlock );
    Pa_Terminate();
    if( err & paInputOverflow )
       fprintf( stderr,"Input Overflow.n" );
    if( err & paOutputUnderflow )
       fprintf( stderr,"Output Underflow.n" );
    return -2;
}

void error1(PaStream *stream,char* sampleBlock) {
    free( sampleBlock );
    exit(-1);
}

void error2(PaStream *stream,int err) {
    if( stream ) {
       Pa_AbortStream( stream );
       Pa_CloseStream( stream );
    }
    Pa_Terminate();
    fprintf( stderr,"An error occured while using the portaudio streamn" );
    fprintf( stderr,"Error number: %dn",err );
    fprintf( stderr,"Error message: %sn",Pa_GetErrorText( err ) );
    exit(-1);
}

解决方法

我发现你也可以使用webrtc库.它具有噪音抑制功能,非常方便.我不明白compress_gain_db和target_level_dbfs实际上做了什么,但将它们设置为最高值似乎应用了最大的收益.按照@alexander的建议在int16中工作解决了我的自定义解决方案的许多问题,并修复了循环以覆盖整个缓冲区. webrtc的解决方案和我自己的解决方案都可以通过以下示例代码实时播放.

代码示例包括在下面.

$./main.exe -h
-h : show this help message
-i <int> : select the INPUT DEVICE by id
-o <int> : select the OUPUT DEVICE by id
-c <int [0,90]> : compression_gain_db
-t <int [0,31]> : target_level_dbfs
-g <0 or 1> : toggle webrtc gain control on and off (1 by default)
-k <0 or 1> : toggle custom gain control on and off (1 by default)
-f <int [1,maxInt]> : customGainControlFactor
-q <int [0,3]> : webrtc noise supression level,high is more suppression
-e <0 or 1> : toggle webrtc noise suppression on and off (1 by default)
-d : list devices

Real time controls:
compression_gain_db            UP_KEY='a' DOWN_KEY='s'
target_dbfs_level              UP_KEY='d' DOWN_KEY='f'
webrtcGainControlEnabled                           TOGGLE_KEY='g'
webrtcNoiseSuppressionLevel    UP_KEY='q' DOWN_KEY='w'
webrtcNoiseSuppressionEnabled                      TOGGLE_KEY='e'
customGainFactor               UP_KEY='h' DOWN_KEY='j'
customGainFactorEnabled                            TOGGLE_KEY='k'
Press any key to close

依赖关系:tinycon,PortAudio,libwebrtc-audio-processing-devel

注意:我正在使用cygwin,如果你遇到libwebrtc问题,请参阅here

编译:g main.cpp tinycon.cpp -o main -L./ -lcygportaudio-2 -lrt -lm -pthread -I /usr/include / webrtc_audio_processing / -DWEBRTC_WIN -DWEBRTC

main.cpp中

#include "portaudio.h"
#include <iostream>
#include <limits>
#include <chrono>
#include <thread>
#include <mutex>
#include "tinycon.h"

#include "webrtc/modules/audio_processing/include/audio_processing.h"
#include "webrtc/modules/interface/module_common_types.h"
#include "webrtc/system_wrappers/include/trace.h"
using webrtc::AudioProcessing;
using webrtc::AudioFrame;
using webrtc::GainControl;
using webrtc::NoiseSuppression;

#define SAMPLE_RATE       (32000)
#define FRAMES_PER_BUFFER   (320)
#define DITHER_FLAG           (0)

#define PA_SAMPLE_TYPE paInt16
#define SAMPLE_SIZE (2)
#define SAMPLE_SILENCE  (0)
#define PRINTF_S_FORMAT "%d"

/*******************************************************************/
int customGainFactor = 1;
int customGainFactorStep = 1;
bool customGainControlEnabled = true;
int compression_gain_db = 1;
int compression_gain_dbStep = 1;
int target_level_dbfs = 1;
int target_level_dbfsStep = 1;
bool webrtcGainControlEnabled = true;
bool webrtcNoiseSuppressionEnabled = true;
int webrtcNoiseSuppressionLevel = 1;
int main(int argc,int err);
void listDevices();
webrtc::NoiseSuppression::Level webrtcNoiseSuppressionLevelToEnum(int level);
// Use tinycon and a second thread for non blocking input
class tcon : public tinyConsole
{
public:
        tcon (std::string s): tinyConsole(s) {;}

        int hotkeys(char c)
        {
                if (c == 'a') {
                        if (compression_gain_db >= (0+compression_gain_dbStep)) {
                                compression_gain_db -= compression_gain_dbStep;
                        }
                        printf( "Compression_gain_db: %dn",compression_gain_db );
                        return 1;
                }
                if (c == 's') {
                        if (compression_gain_db <= (90-compression_gain_dbStep)) {
                                compression_gain_db += compression_gain_dbStep;
                        }
                        printf( "Compression_gain_db: %dn",compression_gain_db );
                        return 1;
                }
                if (c == 'd') {
                        if (target_level_dbfs >= (0+target_level_dbfsStep)) {
                                target_level_dbfs -= target_level_dbfsStep;
                        }
                        printf( "target_level_dbfs: %dn",target_level_dbfs );
                        return 1;
                }
                if (c == 'f') {
                        if (target_level_dbfs <= (31-target_level_dbfsStep)) {
                                target_level_dbfs += target_level_dbfsStep;
                        }
                        printf( "target_level_dbfs: %dn",target_level_dbfs );
                        return 1;
                }
                if (c == 'g') {
                        webrtcGainControlEnabled = !webrtcGainControlEnabled;
                        printf("webrtcGainControlEnabled: %sn",(webrtcGainControlEnabled) ? "true" : "false");
                        return 1;
                }
                if (c == 'h') {
                        if (customGainFactor >= (1+customGainFactorStep)) {
                                customGainFactor -= customGainFactorStep;
                        }
                        printf( "customGainFactor: %dn",customGainFactor );
                        return 1;
                }
                if (c == 'j') {
                        customGainFactor += customGainFactorStep;
                        printf( "customGainFactor: %dn",customGainFactor );
                        return 1;
                }
                if (c == 'k') {
                        customGainControlEnabled = !customGainControlEnabled;
                        printf("customGainControlEnabled: %sn",(customGainControlEnabled) ? "true" : "false");
                        return 1;
                }
                if (c == 'q') {
                        if (webrtcNoiseSuppressionLevel <= (3-1)) {
                                webrtcNoiseSuppressionLevel += 1;
                        }
                        printf( "webrtcNoiseSuppressionLevel: %dn",webrtcNoiseSuppressionLevel );
                        return 1;
                }
                if (c == 'w') {
                        if (webrtcNoiseSuppressionLevel >= (0+1)) {
                                webrtcNoiseSuppressionLevel -= 1;
                        }
                        printf( "webrtcNoiseSuppressionLevel: %dn",webrtcNoiseSuppressionLevel );
                        return 1;
                }
                if (c == 'e') {
                        webrtcNoiseSuppressionEnabled = !webrtcNoiseSuppressionEnabled;
                        printf("webrtcNoiseSuppressionEnabled: %sn",(webrtcNoiseSuppressionEnabled) ? "true" : "false");
                        return 1;
                }
                return 0;
        }
};
int inputThread() {
        tcon tc (std::string(""));
        tc.run();
}

void listDevices() {
        int i,char **argv)
{
        int c;
        int inputDeviceId = -1;
        int outputDeviceId = -1;
        opterr = 0;
        const char* helpMessage =
                "-h : show this help messagen"
                "-i <int> : select the INPUT DEVICE by idn"
                "-o <int> : select the OUPUT DEVICE by idn"
                "-c <int [0,90]> : compression_gain_dbn"
                "-t <int [0,31]> : target_level_dbfsn"
                "-g <0 or 1> : toggle webrtc gain control on and off (1 by default)n"
                "-k <0 or 1> : toggle custom gain control on and off (1 by default)n"
                "-f <int [1,maxInt]> : customGainControlFactorn"
                "-q <int [0,5]> : webrtc noise supression level,high is more suppressionn"
                "-e <0 or 1> : toggle webrtc noise suppression on and off (1 by default)n"
                "-d : list devicesn"
                "n"
                "Real time controls:n"
                "compression_gain_db            UP_KEY='a' DOWN_KEY='s'n"
                "target_dbfs_level              UP_KEY='d' DOWN_KEY='f'n"
                "webrtcGainControlEnabled                           TOGGLE_KEY='g'n"
                "webrtcNoiseSuppressionLevel    UP_KEY='q' DOWN_KEY='w'n"
                "webrtcNoiseSuppressionEnabled                      TOGGLE_KEY='e'n"
                "customGainFactor               UP_KEY='h' DOWN_KEY='j'n"
                "customGainFactorEnabled                            TOGGLE_KEY='k'n";

        while ((c = getopt (argc,"i:o:c:t:g:k:f:w:q:hd")) != -1) {
                switch (c) {
                        case 'i':
                                inputDeviceId = atoi(optarg);
                                break;
                        case 'o':
                                outputDeviceId = atoi(optarg);
                                break;
                        case 'c':
                                compression_gain_db = atoi(optarg);
                                break;
                        case 't':
                                target_level_dbfs = atoi(optarg);
                                break;
                        case 'g':
                                webrtcGainControlEnabled = (atoi(optarg) == 1) ? true : false;
                                break;
                        case 'f':
                                customGainFactor = atoi(optarg);
                                break;
                        case 'k':
                                customGainControlEnabled = (atoi(optarg) == 1) ? true : false;
                                break;
                        case 'w':
                                webrtcNoiseSuppressionLevel = atoi(optarg);
                                break;
                        case 'e':
                                webrtcNoiseSuppressionEnabled = (atoi(optarg) == 1) ? true : false;
                                break;
                        case 'd':
                                listDevices();
                                return 0;
                        case '?':
                                if (isprint (optopt))
                                        fprintf (stderr,sampleBlock);
    }

        // Configure webrtc::audioprocessing
        int webrtcErr;
        AudioProcessing* apm = AudioProcessing::Create();

        apm->high_pass_filter()->Enable(true);

        apm->noise_suppression()->set_level(webrtcNoiseSuppressionLevelToEnum(webrtcNoiseSuppressionLevel));
        apm->noise_suppression()->Enable(webrtcNoiseSuppressionEnabled);

        apm->gain_control()->set_mode(apm->gain_control()->kFixedDigital);
        apm->gain_control()->set_compression_gain_db(compression_gain_db);
        apm->gain_control()->set_target_level_dbfs(target_level_dbfs);
        apm->gain_control()->Enable(webrtcGainControlEnabled);

    err = Pa_StartStream( stream );
    if( err != paNoError ) error1(stream,sampleBlock);

        while (1) {
        // You may get underruns or overruns if the output is not primed by PortAudio.
        err = Pa_ReadStream( stream,sampleBlock);

                // Run custom gain solution
                if (customGainControlEnabled) {
                        int blockIndex;
                        short* sampleBlockShort = (short*)sampleBlock;
                        for (blockIndex = 0; blockIndex < FRAMES_PER_BUFFER*numChannels; blockIndex++) {
                                        int iSample = (int)sampleBlockShort[blockIndex];
                                        iSample *= customGainFactor;
                                        if (iSample > std::numeric_limits<short>::max())
                                        iSample =
                                                  (iSample > std::numeric_limits<short>::max()) ? std::numeric_limits<short>::max()
                                                : (iSample < std::numeric_limits<short>::min()) ? std::numeric_limits<short>::min()
                                                : iSample;
                                        sampleBlockShort[blockIndex] = (short)iSample;
                        }
                }

                // Apply webrtc gain and noise suppression
                apm->noise_suppression()->set_level(webrtcNoiseSuppressionLevelToEnum(webrtcNoiseSuppressionLevel));
                apm->noise_suppression()->Enable(webrtcNoiseSuppressionEnabled);
                apm->gain_control()->set_compression_gain_db(compression_gain_db);
                apm->gain_control()->set_target_level_dbfs(target_level_dbfs);
                apm->gain_control()->Enable(webrtcGainControlEnabled);

                webrtc::AudioFrame frame;
                frame.num_channels_ = numChannels;
                frame.sample_rate_hz_ = SAMPLE_RATE;
                frame.samples_per_channel_ = FRAMES_PER_BUFFER;

                memcpy(frame.data_,numBytes);
                if ((webrtcErr = apm->ProcessStream(&frame)) < 0) {
                        printf("Error Code: %dn",webrtcErr); fflush(stdout);
                        return -1;
                }
                memcpy(sampleBlock,frame.data_,numBytes);

                err = Pa_WriteStream( stream,Pa_GetErrorText( err ) );
    exit(-1);
}

webrtc::NoiseSuppression::Level webrtcNoiseSuppressionLevelToEnum(int level) {
        switch (level) {
                case 0 : return webrtc::NoiseSuppression::Level::kLow;
                case 1 : return webrtc::NoiseSuppression::Level::kModerate;
                case 2 : return webrtc::NoiseSuppression::Level::kHigh;
                case 3 : return webrtc::NoiseSuppression::Level::kVeryHigh;
        }
}

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读