В этом примере показано, как применить адаптивные фильтры к акустической эхоподавке (AEC).
Автор (ы): Скотт К. Дуглас
Акустическое эхо-подавление важно для аудио телеконференций, когда необходима одновременная связь (или полнодуплексная передача) речи. В акустическом эхоподавлении измеренный сигнал микрофона содержит два сигнала:
Речевой сигнал ближнего конца
Речевой сигнал на дальнем конце с эхом
Цель состоит в том, чтобы удалить речевой сигнал с обратной связью на дальнем конце из микрофонного сигнала, так что передается только речевой сигнал на ближнем конце. В этом примере есть несколько звуковых клипов, поэтому теперь можно настроить громкость компьютера.
Сначала необходимо смоделировать акустику пути громкоговорителя к микрофону, где расположен громкоговоритель. Используйте длинный фильтр конечной импульсной характеристики, чтобы описать характеристики помещения. Следующий код генерирует случайную импульсную характеристику, которая не отличается от того, что выставляет конференц-зал. Примите частоту выборки системы 16000 Гц.
fs = 16000; M = fs/2 + 1; frameSize = 2048; [B,A] = cheby2(4,20,[0.1 0.7]); impulseResponseGenerator = dsp.IIRFilter('Numerator', [zeros(1,6) B], ... 'Denominator', A); FVT = fvtool(impulseResponseGenerator); % Analyze the filter FVT.Color = [1 1 1];
roomImpulseResponse = impulseResponseGenerator( ... (log(0.99*rand(1,M)+0.01).*sign(randn(1,M)).*exp(-0.002*(1:M)))'); roomImpulseResponse = roomImpulseResponse/norm(roomImpulseResponse)*4; room = dsp.FIRFilter('Numerator', roomImpulseResponse'); fig = figure; plot(0:1/fs:0.5, roomImpulseResponse); xlabel('Time (s)'); ylabel('Amplitude'); title('Room Impulse Response'); fig.Color = [1 1 1];
Пользователь системы телеконференций обычно расположен рядом с микрофоном системы. Вот как звучит мужская речь в микрофоне.
load nearspeech player = audioDeviceWriter('SupportVariableSizeInput', true, ... 'BufferSize', 512, 'SampleRate', fs); nearSpeechSrc = dsp.SignalSource('Signal',v,'SamplesPerFrame',frameSize); nearSpeechScope = timescope('SampleRate', fs, 'TimeSpanSource','Property',... 'TimeSpan', 35, 'TimeSpanOverrunAction', 'Scroll', ... 'YLimits', [-1.5 1.5], ... 'BufferLength', length(v), ... 'Title', 'Near-End Speech Signal', ... 'ShowGrid', true); % Stream processing loop while(~isDone(nearSpeechSrc)) % Extract the speech samples from the input signal nearSpeech = nearSpeechSrc(); % Send the speech samples to the output audio device player(nearSpeech); % Plot the signal nearSpeechScope(nearSpeech); end release(nearSpeechScope);
В системе телеконференцсвязи голос выезжает из громкоговорителя, прыгает в комнате, а затем подбирается микрофоном системы. Послушайте, как звучит речь, если она подобрана у микрофона без присутствующей речи ближнего конца.
load farspeech farSpeechSrc = dsp.SignalSource('Signal',x,'SamplesPerFrame',frameSize); farSpeechSink = dsp.SignalSink; farSpeechScope = timescope('SampleRate', fs, 'TimeSpanSource','Property',... 'TimeSpan', 35, 'TimeSpanOverrunAction', 'Scroll', ... 'YLimits', [-0.5 0.5], ... 'BufferLength', length(x), ... 'Title', 'Far-End Speech Signal', ... 'ShowGrid', true); % Stream processing loop while(~isDone(farSpeechSrc)) % Extract the speech samples from the input signal farSpeech = farSpeechSrc(); % Add the room effect to the far-end speech signal farSpeechEcho = room(farSpeech); % Send the speech samples to the output audio device player(farSpeechEcho); % Plot the signal farSpeechScope(farSpeech); % Log the signal for further processing farSpeechSink(farSpeechEcho); end release(farSpeechScope);
Сигнал в микрофоне содержит речь ближнего конца и речь дальнего конца, которая была повторена по всей комнате. Цель акустического эхоподавителя состоит в том, чтобы отменить речь дальнего конца, так что только речь ближнего конца передается назад к прослушивателю дальнего конца.
reset(nearSpeechSrc); farSpeechEchoSrc = dsp.SignalSource('Signal', farSpeechSink.Buffer, ... 'SamplesPerFrame', frameSize); micSink = dsp.SignalSink; micScope = timescope('SampleRate', fs,'TimeSpanSource','Property',... 'TimeSpan', 35, 'TimeSpanOverrunAction', 'Scroll',... 'YLimits', [-1 1], ... 'BufferLength', length(x), ... 'Title', 'Microphone Signal', ... 'ShowGrid', true); % Stream processing loop while(~isDone(farSpeechEchoSrc)) % Microphone signal = echoed far-end + near-end + noise micSignal = farSpeechEchoSrc() + nearSpeechSrc() + ... 0.001*randn(frameSize,1); % Send the speech samples to the output audio device player(micSignal); % Plot the signal micScope(micSignal); % Log the signal micSink(micSignal); end release(micScope);
Алгоритм в этом примере является адаптивным фильтром частотного диапазона (FDAF). Этот алгоритм очень полезен, когда импульсная характеристика системы, которая будет идентифицирована, долга. FDAF использует метод быстрой свертки, чтобы вычислить выход сигнала и фильтра. Этот расчет выполняется быстро в MATLAB ®. Это также имеет быструю эффективность через нормализацию размера шага бина частоты. Выберите некоторые начальные параметры для фильтра и посмотрите, насколько хорошо речь на дальнем конце отменяется в сигнале ошибки.
% Construct the Frequency-Domain Adaptive Filter echoCanceller = dsp.FrequencyDomainAdaptiveFilter('Length', 2048, ... 'StepSize', 0.025, ... 'InitialPower', 0.01, ... 'AveragingFactor', 0.98, ... 'Method', 'Unconstrained FDAF'); AECScope1 = timescope(4, fs, ... 'LayoutDimensions', [4,1],'TimeSpanSource','Property', ... 'TimeSpan', 35, 'TimeSpanOverrunAction', 'Scroll', ... 'BufferLength', length(x)); AECScope1.ActiveDisplay = 1; AECScope1.ShowGrid = true; AECScope1.YLimits = [-1.5 1.5]; AECScope1.Title = 'Near-End Speech Signal'; AECScope1.ActiveDisplay = 2; AECScope1.ShowGrid = true; AECScope1.YLimits = [-1.5 1.5]; AECScope1.Title = 'Microphone Signal'; AECScope1.ActiveDisplay = 3; AECScope1.ShowGrid = true; AECScope1.YLimits = [-1.5 1.5]; AECScope1.Title = 'Output of Acoustic Echo Canceller mu=0.025'; AECScope1.ActiveDisplay = 4; AECScope1.ShowGrid = true; AECScope1.YLimits = [0 50]; AECScope1.YLabel = 'ERLE (dB)'; AECScope1.Title = 'Echo Return Loss Enhancement mu=0.025'; % Near-end speech signal release(nearSpeechSrc); nearSpeechSrc.SamplesPerFrame = frameSize; % Far-end speech signal release(farSpeechSrc); farSpeechSrc.SamplesPerFrame = frameSize; % Far-end speech signal echoed by the room release(farSpeechEchoSrc); farSpeechEchoSrc.SamplesPerFrame = frameSize;
Поскольку у вас есть доступ как к речевым сигналам ближнего, так и дальнего конца, можно вычислить улучшение эхо возврата потерь (ERLE), которое является сглаженной мерой величины (в дБ), которую эхо было ослаблено. Из графика заметьте, что вы достигли около 35 дБ ERLE в конце периода сходимости.
diffAverager = dsp.FIRFilter('Numerator', ones(1,1024)); farEchoAverager = clone(diffAverager); setfilter(FVT,diffAverager); micSrc = dsp.SignalSource('Signal', micSink.Buffer, ... 'SamplesPerFrame', frameSize); % Stream processing loop - adaptive filter step size = 0.025 while(~isDone(nearSpeechSrc)) nearSpeech = nearSpeechSrc(); farSpeech = farSpeechSrc(); farSpeechEcho = farSpeechEchoSrc(); micSignal = micSrc(); % Apply FDAF [y,e] = echoCanceller(farSpeech, micSignal); % Send the speech samples to the output audio device player(e); % Compute ERLE erle = diffAverager((e-nearSpeech).^2)./ farEchoAverager(farSpeechEcho.^2); erledB = -10*log10(erle); % Plot near-end, far-end, microphone, AEC output and ERLE AECScope1(nearSpeech, micSignal, e, erledB); end release(AECScope1);
Чтобы получить более быстрое сходимость, можно попробовать использовать большее значение размера шага. Однако это увеличение вызывает другой эффект: адаптивный фильтр «неправильно скорректирован», в то время как ближний динамик говорит. Послушайте, что происходит, когда вы выбираете размер шага, который на 60% больше, чем раньше.
% Change the step size value in FDAF reset(echoCanceller); echoCanceller.StepSize = 0.04; AECScope2 = clone(AECScope1); AECScope2.ActiveDisplay = 3; AECScope2.Title = 'Output of Acoustic Echo Canceller mu=0.04'; AECScope2.ActiveDisplay = 4; AECScope2.Title = 'Echo Return Loss Enhancement mu=0.04'; reset(nearSpeechSrc); reset(farSpeechSrc); reset(farSpeechEchoSrc); reset(micSrc); reset(diffAverager); reset(farEchoAverager); % Stream processing loop - adaptive filter step size = 0.04 while(~isDone(nearSpeechSrc)) nearSpeech = nearSpeechSrc(); farSpeech = farSpeechSrc(); farSpeechEcho = farSpeechEchoSrc(); micSignal = micSrc(); % Apply FDAF [y,e] = echoCanceller(farSpeech, micSignal); % Send the speech samples to the output audio device player(e); % Compute ERLE erle = diffAverager((e-nearSpeech).^2)./ farEchoAverager(farSpeechEcho.^2); erledB = -10*log10(erle); % Plot near-end, far-end, microphone, AEC output and ERLE AECScope2(nearSpeech, micSignal, e, erledB); end release(nearSpeechSrc); release(farSpeechSrc); release(farSpeechEchoSrc); release(micSrc); release(diffAverager); release(farEchoAverager); release(echoCanceller); release(AECScope2);
При большем размере шага эффективность ERLE не так хороша из-за неправильной настройки, введенной речью ближнего конца. Чтобы справиться с этой трудностью эффективности, акустические эхоподавители включают в себя схему обнаружения, чтобы определить, когда речь ближнего конца присутствует, и снизить значение размера шага в эти периоды. Без таких схем обнаружения эффективность системы с большим размером шага не так хороша, как у первой, как видно из графиков ERLE.
Традиционный FDAF численно более эффективен, чем адаптивная фильтрация во временной области для длинных импульсных характеристик, но он накладывает высокую задержку, потому что размер входного кадра должен быть кратен указанной длине фильтра. Это может быть неприемлемо для многих реальных приложений. Задержка может быть уменьшена при помощи секционированного FDAF, который разбивает импульсную характеристику фильтра на более короткие сегменты, применяет FDAF к каждому сегменту и затем объединяет промежуточные результаты. Формат кадра в этом случае должен быть кратным длине разбиения (блока), тем самым значительно снижая задержку для длинных импульсных характеристик.
% Reduce the frame size from 2048 to 256 frameSize = 256; nearSpeechSrc.SamplesPerFrame = frameSize; farSpeechSrc.SamplesPerFrame = frameSize; farSpeechEchoSrc.SamplesPerFrame = frameSize; micSrc.SamplesPerFrame = frameSize; % Switch the echo canceller to Partitioned constrained FDAF echoCanceller.Method = 'Partitioned constrained FDAF'; % Set the block length to frameSize echoCanceller.BlockLength = frameSize; % Stream processing loop while(~isDone(nearSpeechSrc)) nearSpeech = nearSpeechSrc(); farSpeech = farSpeechSrc(); farSpeechEcho = farSpeechEchoSrc(); micSignal = micSrc(); % Apply FDAF [y,e] = echoCanceller(farSpeech, micSignal); % Send the speech samples to the output audio device player(e); % Compute ERLE erle = diffAverager((e-nearSpeech).^2)./ farEchoAverager(farSpeechEcho.^2); erledB = -10*log10(erle); % Plot near-end, far-end, microphone, AEC output and ERLE AECScope2(nearSpeech, micSignal, e, erledB); end