├── .gitignore ├── .gitmodules ├── .travis.yml ├── MANIFEST.in ├── README.md ├── examples ├── wav_agc.py └── wav_ns.py ├── setup.cfg ├── setup.py ├── src ├── Makefile ├── __init__.py ├── audio_processing_module.cpp ├── audio_processing_module.h └── webrtc_audio_processing.i └── tests └── test_ns.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | local_settings.py 55 | 56 | # Flask stuff: 57 | instance/ 58 | .webassets-cache 59 | 60 | # Scrapy stuff: 61 | .scrapy 62 | 63 | # Sphinx documentation 64 | docs/_build/ 65 | 66 | # PyBuilder 67 | target/ 68 | 69 | # IPython Notebook 70 | .ipynb_checkpoints 71 | 72 | # pyenv 73 | .python-version 74 | 75 | # celery beat schedule file 76 | celerybeat-schedule 77 | 78 | # dotenv 79 | .env 80 | 81 | # virtualenv 82 | venv/ 83 | ENV/ 84 | 85 | # Spyder project settings 86 | .spyderproject 87 | 88 | # Rope project settings 89 | .ropeproject 90 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "webrtc-audio-processing"] 2 | path = webrtc-audio-processing 3 | url = https://gitlab.freedesktop.org/pulseaudio/webrtc-audio-processing.git 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # This file was autogenerated and will overwrite each time you run travis_pypi_setup.py 2 | before_install: 3 | - sudo apt-get -qq update 4 | - sudo apt-get install -qq build-essential python-dev swig 5 | deploy: 6 | true: 7 | python: 2.7 8 | branch: master 9 | tags: true 10 | distributions: sdist 11 | password: 12 | secure: V2jeEN5mj2kReyyzSE4krixN0bewhIT4VcmEQuDrIobXyeZuqbgn/8Dz91XbkGlRSqdh7ux8TACDxK2AVtu7xnrOPsVKoXnvPvYM6G6Ha+6sBYc6lFjsNR1KRTGIfSLRw00ACMsLkgHS1rZPaQHPJB256dp50iibM+AmXQv3LjbpGHQmI+N/gIw9jzKXxDDmPtjm5pSrS1z9kUazus+I0MIZcYQbJ0QGgBUud1VDRAUfv5y2nPIipYP1onnUUBTXhXIbtuvplmpQynM2EtSSv5Hv0ZI+EWWaFq7Y817h/Ag8evw4zh8GFWA4xkTE4wwj8eoiNuUoWYZRlrHv2BAbgqeFSNwSdAtCAd1FAIqaxI55bCoGj/y+p8jzFEi2ieIvNulTHZ9hVx2mao0CJJ4UWzqRoeIcQXYoY9c/qoYB7Bh8UqIoLwWYe1oAD4mwXgYaSkRcX9PqymeGRoRhR3r5RzYDFd+UQZRYasnjzcJm0Oytj4fi72JYzk1fZP6FlNZEd7TUx0SpCiziO0t+i+65lgBTbWvV1h8YjmR+I2Hp9okRX7P0CKlPhThALGOyGtgTiuxHSLnvYAfjC7FuUIS4E0MBkbHH8HLnBYjxc8ikzM82NcgWyeO7+M0kjk8uEiLQIkL4gZ+r09x91ONCq3GICN2GdG0LOJ6BXUeh2oU2OBU= 13 | provider: pypi 14 | skip_cleanup: true 15 | user: yihui 16 | install: 17 | - python setup.py build 18 | - pip install . 19 | language: python 20 | python: 21 | - '2.6' 22 | - '2.7' 23 | - '3.4' 24 | - '3.5' 25 | - '3.6' 26 | script: pytest 27 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.md 2 | 3 | exclude .gitignore .gitmodules 4 | 5 | recursive-include webrtc-audio-processing *.c *.cc *.h 6 | recursive-include src *.c *.cc *.h 7 | 8 | recursive-exclude Makefile 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WebRTC Audio Processing for Python 2 | 3 | [![Build Status](https://travis-ci.org/xiongyihui/python-webrtc-audio-processing.svg?branch=master)](https://travis-ci.org/xiongyihui/python-webrtc-audio-processing) 4 | [![Pypi](https://img.shields.io/pypi/v/webrtc_audio_processing.svg)](https://pypi.python.org/pypi/webrtc_audio_processing) 5 | 6 | Python binding of WebRTC Audio Processing. 7 | 8 | ## Requirements 9 | + swig 10 | + compile toolchain 11 | + python 12 | 13 | ## Build 14 | There are two way to build the package. 15 | 16 | 1. using setup.py 17 | 18 | ```bash 19 | git clone https://github.com/xiongyihui/python-webrtc-audio-processing.git 20 | cd python-webrtc-audio-processing 21 | git submodule init && git submodule update 22 | python setup.py build 23 | sudo python setup.py install 24 | ``` 25 | 26 | 2. using Makefile 27 | 28 | ```bash 29 | git clone https://github.com/xiongyihui/python-webrtc-audio-processing.git 30 | cd python-webrtc-audio-processing 31 | git submodule init && git submodule update 32 | cd webrtc-audio-processing 33 | ./autogen.sh 34 | ./configure --with-pic 35 | make 36 | cd ../src 37 | make 38 | ``` 39 | 40 | ## Usage 41 | ```python 42 | from webrtc_audio_processing import AudioProcessingModule as AP 43 | 44 | ap = AP(enable_vad=True, enable_ns=True) 45 | ap.set_stream_format(16000, 1) # set sample rate and channels 46 | ap.set_ns_level(1) # NS level from 0 to 3 47 | ap.set_vad_level(1) # VAD level from 0 to 3 48 | 49 | audio_10ms = '\0' * 160 * 2 # 10ms, 16000 sample rate, 16 bits, 1 channel 50 | 51 | # only support processing 10ms audio data each time 52 | audio_out = ap.process_stream(audio_10ms) 53 | print('voice: {}'.format(ap.has_voice())) 54 | ``` 55 | -------------------------------------------------------------------------------- /examples/wav_agc.py: -------------------------------------------------------------------------------- 1 | 2 | import sys 3 | import wave 4 | from webrtc_audio_processing import AP 5 | 6 | 7 | if len(sys.argv) < 3: 8 | print('Usage: {} audio.wav out.wav'.format(sys.argv[0])) 9 | sys.exit(1) 10 | 11 | wav = wave.open(sys.argv[1], 'rb') 12 | rate = wav.getframerate() 13 | width = wav.getsampwidth() 14 | channels = wav.getnchannels() 15 | 16 | out = wave.open(sys.argv[2], 'wb') 17 | out.setnchannels(channels) 18 | out.setsampwidth(width) 19 | out.setframerate(rate) 20 | 21 | ap = AP(agc_type=1) 22 | 23 | # set input/output stream format 24 | ap.set_stream_format(rate, channels, rate, channels) 25 | 26 | frames_size = int(rate * 10 / 1000) 27 | frames_bytes = frames_size * width * channels 28 | 29 | ap.set_agc_target(-20) 30 | 31 | 32 | while True: 33 | data = wav.readframes(frames_size) 34 | if len(data) != frames_bytes: 35 | break 36 | 37 | data_out = ap.process_stream(data) 38 | 39 | out.writeframes(data_out) 40 | 41 | wav.close() 42 | out.close() -------------------------------------------------------------------------------- /examples/wav_ns.py: -------------------------------------------------------------------------------- 1 | 2 | import sys 3 | import wave 4 | from webrtc_audio_processing import AP 5 | 6 | 7 | if len(sys.argv) < 3: 8 | print('Usage: {} audio.wav out.wav'.format(sys.argv[0])) 9 | sys.exit(1) 10 | 11 | wav = wave.open(sys.argv[1], 'rb') 12 | rate = wav.getframerate() 13 | width = wav.getsampwidth() 14 | channels = wav.getnchannels() 15 | 16 | out = wave.open(sys.argv[2], 'wb') 17 | out.setnchannels(channels) 18 | out.setsampwidth(width) 19 | out.setframerate(rate) 20 | 21 | ap = AP(enable_ns=True) 22 | 23 | # set input/output stream format 24 | ap.set_stream_format(rate, channels, rate, channels) 25 | 26 | frames_size = int(rate * 10 / 1000) # only support processing 10ms audio each time 27 | frames_bytes = frames_size * width * channels 28 | 29 | 30 | 31 | 32 | while True: 33 | data = wav.readframes(frames_size) 34 | if len(data) != frames_bytes: 35 | break 36 | 37 | data_out = ap.process_stream(data) 38 | 39 | out.writeframes(data_out) 40 | 41 | wav.close() 42 | out.close() -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [bdist_wheel] 2 | universal = 1 3 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | Python bindings of webrtc audio processing 5 | """ 6 | 7 | from glob import glob 8 | import platform 9 | import sys 10 | from setuptools import setup, Extension 11 | import os 12 | 13 | with open('README.md') as f: 14 | long_description = f.read() 15 | 16 | include_dirs = ['src', 'webrtc-audio-processing'] 17 | libraries = ['pthread', 'stdc++'] 18 | define_macros = [ 19 | ('WEBRTC_LINUX', None), 20 | ('WEBRTC_POSIX', None), 21 | ('WEBRTC_NS_FLOAT', None), 22 | ('WEBRTC_AUDIO_PROCESSING_ONLY_BUILD', None) 23 | ] 24 | extra_compile_args = ['-std=c++11'] 25 | 26 | ap_sources = [] 27 | ap_dir_prefix = 'webrtc-audio-processing/webrtc/' 28 | for i in range(8): 29 | ap_sources += glob(ap_dir_prefix + '*.c*') 30 | ap_dir_prefix += '*/' 31 | 32 | rw_lock_generic_path = os.path.join('webrtc-audio-processing', 'webrtc', 'system_wrappers', 'source', 'rw_lock_generic.cc') 33 | condition_variable_path = os.path.join('webrtc-audio-processing', 'webrtc', 'system_wrappers', 'source', 'condition_variable.cc') 34 | 35 | if rw_lock_generic_path in ap_sources: 36 | ap_sources.remove(rw_lock_generic_path) 37 | 38 | if condition_variable_path in ap_sources: 39 | ap_sources.remove(condition_variable_path) 40 | 41 | def get_yocto_var(var_name): 42 | val = os.environ.get(var_name, None) 43 | if val is None: 44 | raise Exception(f'Bitbake build detected, i.e. BB_CURRENT_TASK is set, but {var_name} is not set. Please do export {var_name} in your bitbake recipe.') 45 | return val 46 | 47 | def process_arch(arch, set_compile_flags=False): 48 | global ap_sources, define_macros 49 | if arch.find('arm') >= 0: 50 | ap_sources = [src for src in ap_sources if src.find('mips.') < 0 and src.find('sse') < 0] 51 | define_macros.append(('WEBRTC_HAS_NEON', None)) 52 | if arch.find('arm64') >= 0: 53 | define_macros.remove(('WEBRTC_LINUX', None)) 54 | define_macros.append(('WEBRTC_MAC', None)) 55 | define_macros.append(('WEBRTC_ARCH_ARM64', None)) 56 | define_macros.append(('WEBRTC_CLOCK_TYPE_REALTIME', None)) 57 | extra_compile_args.clear() 58 | else: 59 | if set_compile_flags: 60 | extra_compile_args.append('-mfloat-abi=hard') 61 | extra_compile_args.append('-mfpu=neon') 62 | elif arch.find('aarch64') >= 0: 63 | ap_sources = [src for src in ap_sources if src.find('mips.') < 0 and src.find('sse') < 0] 64 | define_macros.append(('WEBRTC_HAS_NEON', None)) 65 | define_macros.append(('WEBRTC_ARCH_ARM64', None)) 66 | elif arch.find('x86') >= 0: 67 | ap_sources = [src for src in ap_sources if src.find('mips.') < 0 and src.find('neon.') < 0] 68 | elif arch.find('mips') >= 0: 69 | ap_sources = [src for src in ap_sources if src.find('mips.') < 0 and src.find('neon.') < 0] 70 | else: 71 | raise Exception('Unsupported arch: %s' % arch) 72 | 73 | if 'BITBAKE_BUILD' in os.environ: 74 | print('Building with bitbake build system') 75 | cc_args = get_yocto_var('TARGET_CC_ARCH') 76 | extra_compile_args += cc_args.split() 77 | 78 | target_sys = get_yocto_var('TARGET_SYS') 79 | process_arch(target_sys) 80 | else: 81 | process_arch(platform.machine(), set_compile_flags=True) 82 | 83 | sources = ( 84 | ap_sources + 85 | ['src/audio_processing_module.cpp', 'src/webrtc_audio_processing.i'] 86 | ) 87 | 88 | swig_opts = ( 89 | ['-c++'] + 90 | ['-I' + h for h in include_dirs] 91 | ) 92 | 93 | setup( 94 | name='webrtc_audio_processing', 95 | version='0.1.3', 96 | description='Python bindings of webrtc audio processing', 97 | long_description=long_description, 98 | long_description_content_type='text/markdown', 99 | author='Yihui Xiong', 100 | author_email='yihui.xiong@hotmail.com', 101 | url='https://github.com/xiongyihui/python-webrtc-audio-processing', 102 | download_url='https://pypi.python.org/pypi/webrtc_audio_processing', 103 | packages=['webrtc_audio_processing'], 104 | ext_modules=[ 105 | Extension( 106 | name='webrtc_audio_processing._webrtc_audio_processing', 107 | sources=sources, 108 | swig_opts=swig_opts, 109 | include_dirs=include_dirs, 110 | libraries=libraries, 111 | define_macros=define_macros, 112 | extra_compile_args=extra_compile_args 113 | ) 114 | ], 115 | classifiers=[ 116 | 'Development Status :: 2 - Pre-Alpha', 117 | 'License :: OSI Approved :: BSD License', 118 | 'Operating System :: POSIX :: Linux', 119 | 'Programming Language :: Python :: 2', 120 | 'Programming Language :: Python :: 2.6', 121 | 'Programming Language :: Python :: 2.7', 122 | 'Programming Language :: Python :: 3', 123 | 'Programming Language :: Python :: 3.2', 124 | 'Programming Language :: Python :: 3.3', 125 | 'Programming Language :: Python :: 3.4', 126 | 'Programming Language :: Python :: 3.5', 127 | 'Programming Language :: C++' 128 | ], 129 | license='BSD', 130 | keywords=['webrtc audioprocessing', 'voice activity detection', 'noise suppression', 'automatic gain control'], 131 | platforms=['Linux'], 132 | package_dir={ 133 | 'webrtc_audio_processing': 'src' 134 | }, 135 | package_data={ 136 | 'webrtc_audio_processing': ['webrtc_audio_processing.py'] 137 | } 138 | ) -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | 2 | 3 | SWIG := swig 4 | 5 | WEBRTCLIBFILE = ../webrtc-audio-processing/webrtc/modules/audio_processing/.libs/libwebrtc_audio_processing.a 6 | 7 | CXXFLAGS := -fPIC -std=c++11 -I. -I../webrtc-audio-processing $(shell python-config --cflags) -DWEBRTC_AUDIO_PROCESSING_ONLY_BUILD -DWEBRTC_LINUX -DWEBRTC_POSIX -DWEBRTC_NS_FLOAT 8 | LDFLAGS := -shared $(shell python-config --ldflags) -lpthread 9 | CXX := g++ 10 | 11 | 12 | all: _webrtc_audio_processing.so 13 | 14 | webrtc_audio_processing_wrap.cpp: webrtc_audio_processing.i 15 | $(SWIG) -I. -c++ -python -o $@ $^ 16 | 17 | _webrtc_audio_processing.so: webrtc_audio_processing_wrap.o audio_processing_module.o 18 | $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $^ $(WEBRTCLIBFILE) 19 | 20 | clean: 21 | -rm -f webrtc_audio_processing_wrap.cpp *.o _webrtc_audio_processing.so webrtc_audio_processing.py *.pyc 22 | -------------------------------------------------------------------------------- /src/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | from .webrtc_audio_processing import AudioProcessingModule 3 | 4 | AP = AudioProcessingModule 5 | -------------------------------------------------------------------------------- /src/audio_processing_module.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | #include "audio_processing_module.h" 4 | 5 | #include "webrtc/modules/audio_processing/include/audio_processing.h" 6 | #include "webrtc/common_audio/channel_buffer.h" 7 | 8 | #include 9 | #include 10 | 11 | 12 | AudioProcessingModule::AudioProcessingModule(int aec_type, bool enable_ns, int agc_type, bool enable_vad) 13 | { 14 | system_delay = 0; 15 | 16 | Config config; 17 | config.Set(new ExperimentalNs(false)); 18 | config.Set(new Intelligibility(false)); 19 | config.Set(new ExperimentalAgc(false)); 20 | 21 | // if (true) { 22 | // std::vector array_geometry; 23 | // array_geometry.push_back(webrtc::Point(-0.05, 0, 0)); 24 | // array_geometry.push_back(webrtc::Point(0.05, 0, 0)); 25 | // config.Set( 26 | // new Beamforming(true, 27 | // array_geometry, 28 | // SphericalPointf(DegreesToRadians(90), 0.f, 1.f))); 29 | // } 30 | 31 | 32 | ap = AudioProcessing::Create(config); 33 | 34 | if (1 == aec_type) { 35 | ap->echo_control_mobile()->Enable(true); 36 | ap->echo_control_mobile()->set_routing_mode(webrtc::EchoControlMobile::kLoudSpeakerphone); 37 | } else if (2 == aec_type) { 38 | ap->echo_cancellation()->Enable(true); 39 | ap->echo_cancellation()->set_suppression_level(EchoCancellation::kLowSuppression); 40 | } else if (3 == aec_type) { 41 | // AudioProcessing::Config config; 42 | // config.high_pass_filter.enabled = true; 43 | // config.echo_canceller3.enabled = true; 44 | // ap->ApplyConfig(config); 45 | } 46 | 47 | 48 | if (enable_ns) { 49 | ap->noise_suppression()->Enable(true); 50 | ap->noise_suppression()->set_level(static_cast(0)); 51 | } 52 | 53 | if (agc_type) { 54 | ap->gain_control()->Enable(true); 55 | if (agc_type == 1) { 56 | // Adaptive Digital AGC 57 | ap->gain_control()->set_mode(GainControl::kAdaptiveDigital); 58 | ap->gain_control()->set_target_level_dbfs(30); 59 | } else { 60 | // Adaptive Analog AGC 61 | ap->gain_control()->set_mode(webrtc::GainControl::kAdaptiveAnalog); 62 | ap->gain_control()->set_analog_level_limits(0, 100); 63 | ap->gain_control()->set_target_level_dbfs(30); 64 | ap->gain_control()->set_compression_gain_db(0); 65 | } 66 | } 67 | 68 | if (enable_vad) { 69 | ap->voice_detection()->Enable(true); 70 | ap->voice_detection()->set_likelihood(VoiceDetection::kVeryLowLikelihood); 71 | ap->voice_detection()->set_frame_size_ms(frame_size_ms); 72 | } 73 | 74 | nearend_config = new StreamConfig(default_rate, default_channels, false); 75 | nearend_filtered_config = new StreamConfig(default_rate, default_channels, false); 76 | 77 | int num_frames = nearend_config->num_frames(); 78 | 79 | nearend_fbuf = new float[num_frames * default_channels]; 80 | nearend_ibuf = new int16_t[num_frames * default_channels]; 81 | nearend_cbuf = new ChannelBuffer(num_frames, default_channels); 82 | nearend_filtered_cbuf = new ChannelBuffer(num_frames, default_channels); 83 | 84 | farend_config = new StreamConfig(default_rate, default_channels, false); 85 | 86 | num_frames = farend_config->num_frames(); 87 | 88 | farend_fbuf = new float[num_frames * default_channels]; 89 | farend_cbuf = new ChannelBuffer(num_frames, default_channels); 90 | } 91 | 92 | void AudioProcessingModule::set_stream_format(int rate, int channels, int out_rate, int out_channels) 93 | { 94 | delete nearend_fbuf; 95 | delete nearend_ibuf; 96 | delete nearend_cbuf; 97 | delete nearend_filtered_cbuf; 98 | 99 | nearend_config->set_sample_rate_hz(rate); 100 | nearend_config->set_num_channels(channels); 101 | 102 | nearend_filtered_config->set_sample_rate_hz(out_rate); 103 | nearend_filtered_config->set_num_channels(out_channels); 104 | 105 | int num_frames = nearend_config->num_frames(); 106 | 107 | nearend_fbuf = new float[num_frames * channels]; 108 | nearend_ibuf = new int16_t[num_frames * channels]; 109 | nearend_cbuf = new ChannelBuffer(num_frames, channels); 110 | nearend_filtered_cbuf = new ChannelBuffer(num_frames, channels); 111 | } 112 | 113 | void AudioProcessingModule::set_reverse_stream_format(int rate, int channels) 114 | { 115 | delete farend_fbuf; 116 | delete farend_cbuf; 117 | 118 | farend_config->set_sample_rate_hz(rate); 119 | farend_config->set_num_channels(channels); 120 | 121 | int num_frames = farend_config->num_frames(); 122 | 123 | farend_fbuf = new float[num_frames * channels]; 124 | farend_cbuf = new ChannelBuffer(num_frames, channels); 125 | } 126 | 127 | string AudioProcessingModule::process_stream(const string& nearend) 128 | { 129 | const int16_t *y = (const int16_t *)(nearend.data()); 130 | int frames = nearend_config->num_frames(); 131 | int channels = nearend_config->num_channels(); 132 | 133 | S16ToFloat(y, frames * channels, nearend_fbuf); 134 | Deinterleave(nearend_fbuf, frames, channels, nearend_cbuf->channels()); 135 | 136 | ap->ProcessStream(nearend_cbuf->channels(), 137 | *nearend_config, 138 | *nearend_filtered_config, 139 | nearend_filtered_cbuf->channels()); 140 | 141 | channels = nearend_filtered_config->num_channels(); 142 | 143 | Interleave(nearend_filtered_cbuf->channels(), frames, channels, nearend_fbuf); 144 | FloatToS16(nearend_fbuf, frames * channels, nearend_ibuf); 145 | 146 | return string((const char *)nearend_ibuf, frames * channels * sizeof(int16_t)); 147 | } 148 | 149 | void AudioProcessingModule::process_reverse_stream(const string& farend) 150 | { 151 | const int16_t *x = (const int16_t *)(farend.data()); 152 | int frames = farend_config->num_frames(); 153 | int channels = farend_config->num_channels(); 154 | 155 | S16ToFloat(x, frames * channels, farend_fbuf); 156 | Deinterleave(farend_fbuf, frames, channels, farend_cbuf->channels()); 157 | 158 | ap->ProcessReverseStream(farend_cbuf->channels(), 159 | *farend_config, 160 | *farend_config, 161 | farend_cbuf->channels()); 162 | 163 | ap->set_stream_delay_ms(system_delay); 164 | } 165 | 166 | void AudioProcessingModule::set_system_delay(int delay) 167 | { 168 | system_delay = delay; 169 | } 170 | 171 | bool AudioProcessingModule::has_echo() 172 | { 173 | return ap->echo_cancellation()->stream_has_echo(); 174 | } 175 | 176 | bool AudioProcessingModule::has_voice() 177 | { 178 | return ap->voice_detection()->stream_has_voice(); 179 | } 180 | 181 | int AudioProcessingModule::vad_level() 182 | { 183 | return ap->voice_detection()->likelihood(); 184 | } 185 | 186 | void AudioProcessingModule::set_vad_level(int level) 187 | { 188 | if (level < 0 || level > 3) { 189 | return; 190 | } 191 | ap->voice_detection()->set_likelihood(static_cast(level)); 192 | } 193 | 194 | int AudioProcessingModule::agc_level() 195 | { 196 | return ap->gain_control()->stream_analog_level(); 197 | } 198 | 199 | void AudioProcessingModule::set_agc_level(int level) 200 | { 201 | if (level < 0 || level > 100) { 202 | return; 203 | } 204 | ap->gain_control()->set_stream_analog_level(level); 205 | } 206 | 207 | void AudioProcessingModule::set_agc_target(int dbfs) 208 | { 209 | if (dbfs < 0) { 210 | dbfs = -dbfs; 211 | } 212 | 213 | if (dbfs > 31) { 214 | dbfs = 31; 215 | } 216 | 217 | ap->gain_control()->set_target_level_dbfs(dbfs); 218 | } 219 | 220 | int AudioProcessingModule::ns_level() 221 | { 222 | return ap->noise_suppression()->level(); 223 | } 224 | 225 | void AudioProcessingModule::set_ns_level(int level) 226 | { 227 | if (level < 0 || level > 3) { 228 | return; 229 | } 230 | ap->noise_suppression()->set_level(static_cast(level)); 231 | } 232 | 233 | int AudioProcessingModule::aec_level() 234 | { 235 | return ap->echo_cancellation()->suppression_level(); 236 | } 237 | 238 | void AudioProcessingModule::set_aec_level(int level) 239 | { 240 | if (level < 0 || level > 2) { 241 | return; 242 | } 243 | ap->echo_cancellation()->set_suppression_level(static_cast(level)); 244 | } 245 | 246 | AudioProcessingModule::~AudioProcessingModule() 247 | { 248 | delete nearend_config; 249 | delete farend_config; 250 | delete nearend_fbuf; 251 | delete nearend_ibuf; 252 | delete farend_fbuf; 253 | delete nearend_cbuf; 254 | delete nearend_filtered_cbuf; 255 | delete farend_cbuf; 256 | 257 | delete ap; 258 | } 259 | -------------------------------------------------------------------------------- /src/audio_processing_module.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef __AUDIO_PROCESSING_MODULE_H__ 3 | #define __AUDIO_PROCESSING_MODULE_H__ 4 | 5 | #include 6 | 7 | namespace webrtc 8 | { 9 | class AudioProcessing; 10 | class StreamConfig; 11 | template class ChannelBuffer; 12 | } 13 | 14 | using namespace std; 15 | using namespace webrtc; 16 | 17 | class AudioProcessingModule 18 | { 19 | public: 20 | AudioProcessingModule(int aec_type=0, bool enable_ns=false, int agc_type=0, bool enable_vad=false); 21 | 22 | string process_stream(const string& stream); 23 | void process_reverse_stream(const string& data); 24 | 25 | bool has_echo(); 26 | 27 | bool has_voice(); 28 | 29 | void set_system_delay(int delay); 30 | 31 | void set_stream_format(int rate, int channels, int out_rate=16000, int out_channels=1); 32 | void set_reverse_stream_format(int rate, int channels); 33 | 34 | int vad_level(); 35 | void set_vad_level(int level); 36 | 37 | int ns_level(); 38 | void set_ns_level(int level); 39 | 40 | int aec_level(); 41 | void set_aec_level(int level); 42 | 43 | void set_agc_level(int level); 44 | int agc_level(); 45 | 46 | // Sets the target peak |level| (or envelope) of the AGC in dBFs (decibels 47 | // from digital full-scale). The convention is to use positive values. For 48 | // instance, passing in a value of -3 corresponds to -3 dBFs, or a target 49 | // level 3 dB below full-scale. Limited to [-31, 0]. 50 | void set_agc_target(int dbfs); 51 | 52 | ~AudioProcessingModule(); 53 | 54 | private: 55 | static const int default_rate = 16000; 56 | static const int default_channels = 1; 57 | static const int frame_size_ms = 10; 58 | 59 | AudioProcessing *ap; 60 | StreamConfig *nearend_config; 61 | StreamConfig *nearend_filtered_config; 62 | StreamConfig *farend_config; 63 | int system_delay; 64 | float *nearend_fbuf; 65 | int16_t *nearend_ibuf; 66 | float *farend_fbuf; 67 | ChannelBuffer *nearend_cbuf; 68 | ChannelBuffer *nearend_filtered_cbuf; 69 | ChannelBuffer *farend_cbuf; 70 | }; 71 | 72 | 73 | #endif // __AUDIO_PROCESSING_MODULE_H__ 74 | -------------------------------------------------------------------------------- /src/webrtc_audio_processing.i: -------------------------------------------------------------------------------- 1 | // webrtc_audio_processing.i 2 | 3 | %module webrtc_audio_processing 4 | 5 | %begin %{ 6 | #define SWIG_PYTHON_STRICT_BYTE_CHAR 7 | %} 8 | 9 | %include "std_string.i" 10 | 11 | %{ 12 | #include "audio_processing_module.h" 13 | %} 14 | 15 | %include "audio_processing_module.h" 16 | 17 | -------------------------------------------------------------------------------- /tests/test_ns.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | from webrtc_audio_processing import AP 4 | 5 | 6 | def test_ns(): 7 | ap = AP(enable_ns=True) 8 | ap.set_ns_level(1) 9 | ap.set_stream_format(16000, 1) 10 | 11 | chunk = '\0\0' * 1600 12 | for _ in range(16): 13 | out = ap.process_stream(chunk) --------------------------------------------------------------------------------