Программное увеличение усиления микрофона

Я пытаюсь увеличить усиление моего микрофона для сценариев VOIP. Я использую PortAudio для получения входного потока (с образцами типа paFloat32), я умножаю эти значения на число с плавающей запятой, а затем передаю результирующий поток на устройство вывода. Примечание. Я передаю его на виртуальное устройство вывода, которое автоматически перенаправляет на виртуальное устройство ввода (программа: VB-Cable), которое приложения VOIP могут использовать в качестве микрофонного входа с примененным усилением.

Мне интересно, есть ли лучшие способы увеличить усиление сигнала, которые лучше сохраняют качество.

Я читал, что лучше выполнять такие вычисления усиления, сначала преобразовывая входные данные в формат с более высокой точностью, выполняя умножение усиления в этом формате, применяя отсечение, а затем возвращаясь к исходному. Я не уверен, как это сделать с типом paFloat32 PortAudio, я включил свою попытку, закомментированную в исходный код. Когда я включаю его, возникают заметные проблемы с шумом даже при усилении 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: %f\n", multiplier );
                        return 1;
                }
                if (c == 'w') {
                        multiplier += multiplierStep;
                        printf( "Multiplier: %f\n", 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 = %d\n", numDevices );
        int isInputDevice = 0;
        for( i=0; i<numDevices; i++ )
        {
                deviceInfo = Pa_GetDeviceInfo( i );
                int isInputDevice = (deviceInfo->maxInputChannels > 0);
                printf( "%sDeviceID: %d, Name: %s\n", (isInputDevice ? "Input" : "Output"), i, deviceInfo->name);
        }
        fprintf (stderr, "Press any key to close\n");
        getch();
}

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

        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 close\n");
                                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: %s\n", 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: %s\n", 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, sampleBlock, FRAMES_PER_BUFFER );
                if( err ) xrun(stream, err, 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, int err, char* sampleBlock) {
    printf("err = %d\n", 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 stream\n" );
    fprintf( stderr, "Error number: %d\n", err );
    fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
    exit(-1);
}

person Modf    schedule 12.03.2017    source источник
comment
Предположим, что отсечение с плавающей запятой должно ограничить умножение до -1,0, если оно меньше -1,0, и до 1,0, если оно больше 1,0. Так же, как и вы для краткости. Вы пробовали копировать входные данные в выходные без всякого умножения? Вы должны без проблем слышать хороший голос. И вы знаете, что, увеличивая громкость таким же образом, вы также увеличиваете шум входного сигнала.   -  person alexander    schedule 12.03.2017
comment
@alexander Прямое копирование ввода в вывод не вызывает проблем с качеством. Я знал, что увеличение усиления приведет к увеличению шума, но я не думал об этом с точки зрения усиления уже присутствующего шума, что имеет гораздо больший смысл.   -  person Modf    schedule 13.03.2017
comment
Portaudio использует формат данных с чередованием в вашей конфигурации. Итак, если 'numChannels ›1' вы перебираете часть буфера, и некоторые образцы остаются неизменными. Таким образом, вы можете услышать проблемы с шумом. Измените условие цикла на 'blockIndex ‹FRAMES_PER_BUFFER * numChannels'. Закомментированная попытка увеличения усиления выглядит нормально. И текущая попытка выглядит нормально, за исключением потери отсечения (-1.0 .. 1.0). Я бы использовал ваше первое решение для увеличения прироста, потому что, используя paFloat32, вы добавляете дополнительную работу portaudio для преобразования из int16 в paFloat32 и снова в int16 без каких-либо преимуществ.   -  person alexander    schedule 13.03.2017
comment
Ой! Полагаю, это означает, что я применил усиление только к первой половине обоих каналов буфера, удивившись, что позволил этому проскользнуть.   -  person Modf    schedule 14.03.2017
comment
And current attempt looks fine except lost clipping (-1.0 .. 1.0). Так что я должен сделать что-то вроде: float sampleWithGain = sampleBlockShort[blockIndex]; sampleWithGain *= multiplier; sampleWithGain = (sampleWithGain > 1) ? 1 : (sampleWithGain < 1) ? -1 : sampleWithGain; sampleBlokcShort[blockIndex] = sampleWithGain;   -  person Modf    schedule 14.03.2017
comment
using paFloat32 you add extra work to portaudio to convert from int16 to paFloat32 Аудиосистемы обычно работают с int16, и когда я говорю PortAudio использовать paFlot32, я делаю дополнительные преобразования? Если это так, это имеет для меня смысл, иначе я понятия не имею, что вы заметили. Также, если я использую paInt16, мои множители должны быть короткими (а не плавающими). Это дает меньше контроля.   -  person Modf    schedule 14.03.2017
comment
Также, если я использую paInt16, мои множители должны быть короткими (а не плавающими). Вы можете использовать арифметику с фиксированной запятой для представления дробных множителей. Например, 2.3 может быть выражено как 2.3 * 256 = 588 (256 равно 1 ‹---------------- 8). Затем вы можете рассчитать каждый образец как snew = (sample * 588) ›› 8.   -  person alexander    schedule 14.03.2017
comment
Аудиосистемы обычно работают с int16, и когда я говорю PortAudio использовать paFlot32, я делаю дополнительные преобразования? Я думаю, что да. Я думаю, что portaudio не будет конвертировать int16 в paFlot32, если он этого не попросит. Например, сигнал с микрофона будет иметь вид int16.   -  person alexander    schedule 14.03.2017


Ответы (1)


Я обнаружил, что для этого также можно использовать библиотеку webrtc. Он поставляется с подавлением шума, что очень удобно. Я не понимаю, что на самом деле делают сжатие_gain_db и target_level_dbfs, но установка их на самые высокие значения, кажется, дает наибольший выигрыш. Работа в int16, предложенная @alexander, решила многие проблемы с моим индивидуальным решением, и исправление цикла для обхода всего буфера также помогло. Как решение 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, см. здесь

Компиляция: 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, char **argv);
int xrun(PaStream *stream, int err, char* sampleBlock);
void error1(PaStream *stream, char* sampleBlock);
void error2(PaStream *stream, 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: %d\n", 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: %d\n", 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: %d\n", 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: %d\n", target_level_dbfs );
                        return 1;
                }
                if (c == 'g') {
                        webrtcGainControlEnabled = !webrtcGainControlEnabled;
                        printf("webrtcGainControlEnabled: %s\n", (webrtcGainControlEnabled) ? "true" : "false");
                        return 1;
                }
                if (c == 'h') {
                        if (customGainFactor >= (1+customGainFactorStep)) {
                                customGainFactor -= customGainFactorStep;
                        }
                        printf( "customGainFactor: %d\n", customGainFactor );
                        return 1;
                }
                if (c == 'j') {
                        customGainFactor += customGainFactorStep;
                        printf( "customGainFactor: %d\n", customGainFactor );
                        return 1;
                }
                if (c == 'k') {
                        customGainControlEnabled = !customGainControlEnabled;
                        printf("customGainControlEnabled: %s\n", (customGainControlEnabled) ? "true" : "false");
                        return 1;
                }
                if (c == 'q') {
                        if (webrtcNoiseSuppressionLevel <= (3-1)) {
                                webrtcNoiseSuppressionLevel += 1;
                        }
                        printf( "webrtcNoiseSuppressionLevel: %d\n", webrtcNoiseSuppressionLevel );
                        return 1;
                }
                if (c == 'w') {
                        if (webrtcNoiseSuppressionLevel >= (0+1)) {
                                webrtcNoiseSuppressionLevel -= 1;
                        }
                        printf( "webrtcNoiseSuppressionLevel: %d\n", webrtcNoiseSuppressionLevel );
                        return 1;
                }
                if (c == 'e') {
                        webrtcNoiseSuppressionEnabled = !webrtcNoiseSuppressionEnabled;
                        printf("webrtcNoiseSuppressionEnabled: %s\n", (webrtcNoiseSuppressionEnabled) ? "true" : "false");
                        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 = %d\n", numDevices );
        int isInputDevice = 0;
        for( i=0; i<numDevices; i++ )
        {
                deviceInfo = Pa_GetDeviceInfo( i );
                int isInputDevice = (deviceInfo->maxInputChannels > 0);
                printf( "%sDeviceID: %d, Name: %s\n", (isInputDevice ? "Input" : "Output"), i, deviceInfo->name);
        }
        fprintf (stderr, "Press any key to close\n");
        getch();
}

int main (int argc, char **argv)
{
        int c;
        int inputDeviceId = -1;
        int outputDeviceId = -1;
        opterr = 0;
        const char* helpMessage =
                "-h : show this help message\n"
                "-i <int> : select the INPUT DEVICE by id\n"
                "-o <int> : select the OUPUT DEVICE by id\n"
                "-c <int [0,90]> : compression_gain_db\n"
                "-t <int [0, 31]> : target_level_dbfs\n"
                "-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]> : customGainControlFactor\n"
                "-q <int [0, 5]> : webrtc noise supression level, high is more suppression\n"
                "-e <0 or 1> : toggle webrtc noise suppression on and off (1 by default)\n"
                "-d : list devices\n"
                "\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, argv, "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, "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 close\n");
                                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: %s\n", 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: %s\n", 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);
    }

        // 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, FRAMES_PER_BUFFER );
        if( err ) xrun(stream, err, 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_, sampleBlock, numBytes);
                if ((webrtcErr = apm->ProcessStream(&frame)) < 0) {
                        printf("Error Code: %d\n", webrtcErr); fflush(stdout);
                        return -1;
                }
                memcpy(sampleBlock, frame.data_, numBytes);

                err = Pa_WriteStream( stream, sampleBlock, FRAMES_PER_BUFFER );
                if( err ) xrun(stream, err, 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, int err, char* sampleBlock) {
    printf("err = %d\n", 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 stream\n" );
    fprintf( stderr, "Error number: %d\n", err );
    fprintf( stderr, "Error message: %s\n", 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;
        }
}
person Modf    schedule 14.03.2017