c – 以编程方式增加麦克风增益
我正在尝试为VOIP场景增加麦克风的增益.
我正在使用PortAudio来获取输入流(具有类型为paFloat32的样本),我将这些值乘以浮点数,然后将结果流传递给输出设备. 注意:我将它传递给虚拟输出设备,该设备自动重定向到虚拟输入设备(程序:VB-Cable),VOIP应用程序可以将其用作应用了增益的麦克风输入. 我想知道是否有更好的方法来增加信号的增益,从而更好地保持质量. 我已经读过,最好先执行这样的增益计算,首先将输入转换为更高精度的格式,以这种格式执行增益乘法,应用裁剪,然后再回到原始格式. 依赖关系: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; } } (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |