Mercurial > hg > audio-degradation-toolbox
diff AudioDegradationToolbox/degradationUnits/degradationUnit_adaptiveEqualizer.m @ 28:76f45f5c9afd DoP tip
- added units
* adaptiveEqualizer
* applyMfccMeanAdaption
- added corresponding data files for presets
- modified applyImpulseReponse to use the estimated average group delay to adjust
the output audio and keep the timestamps as is (was vice versa before)
- added new units demos, incl one for applyLowpass
author | SebastianEwert |
---|---|
date | Tue, 21 Jan 2014 18:08:28 +0000 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/AudioDegradationToolbox/degradationUnits/degradationUnit_adaptiveEqualizer.m Tue Jan 21 18:08:28 2014 +0000 @@ -0,0 +1,275 @@ +function [f_audio_out,timepositions_afterDegr] = degradationUnit_adaptiveEqualizer(f_audio, samplingFreq, timepositions_beforeDegr, parameter) +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Name: degradationUnit_adaptiveEqualizer +% Date of Revision: 2013-01-23 +% Programmer: Sebastian Ewert +% +% Description: +% - designs a filter such that the mean spectrum of f_audio becomes similar +% to a given mean spectrum +% - mean spectra are specified using a decibel scale, i.e. if x is a magnitude +% spectrum, then apply x -> 20*log10(x) +% - there are four ways to specify the destination mean spectrum: 1. by +% loading example files provided with the toolbox, 2. by using specific +% noise "color" profiles, 3. by providing the destination mean spectrum +% using the parameter destMagFreqResp, 4. by providing audio data from +% which the destination mean spectrum is computed. +% - if mean spectra are computed, this is done by computing a magnitude +% spectrogram, deriving the corresponding values in dB, and then averaging +% over all time frames. +% - the same is done for f_audio and the two mean spectral vectors are +% compared +% - Then a filter is designed such that f_audio's mean spectral vector +% becomes similar to destMagFreqResp +% - filtering is done using a linear-phase FIR filter +% - this unit normalizes the output +% +% Notes: +% - note that the mean spectrum is often a good approximation of what is +% sometimes referred to as the mean spectral shape (however, this term is not +% defined for general sound recordings). In this sense, the function +% modifies the mean spectral shape to the desired one. +% +% Input: +% f_audio - audio signal \in [-1,1]^{NxC} with C being the number of +% channels +% samplingFreq - sampling frequency of f_audio +% timepositions_beforeDegr - some degradations delay the input signal. If +% some points in time are given via this +% parameter, timepositions_afterDegr will +% return the corresponding positions in the +% output. Set to [] if unavailable. Set f_audio +% and samplingFreq to [] to compute only +% timepositions_afterDegr. +% +% Input (optional): parameter +% .loadInternalMagFreqResp=1 - loads one of the destMagFreqResp provided +% by the toolbox +% .internalMagFreqResp='Beatles_NorwegianWood' +% .computeMagFreqRespFromAudio - computes destMagFreqResp from given +% audio data +% .computeMagFreqRespFromAudio_audioData +% - audio data for .computeMagFreqRespFromAudio +% .computeMagFreqRespFromAudio_sf - sampl freq for .computeMagFreqRespFromAudio +% .destMagFreqResp = [] - in db. See above. +% .destMagFreqResp_freqs = [] - must have same length as destMagFreqResp. +% In Hertz +% .fftLength = 2 ^ nextpow2(0.02 * samplingFreq); - fft length to +% calculate spectrogram of f_audio. +% +% Output: +% f_audio_out - audio signal \in [-1,1]^{NxC} with C being the number +% of channels +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Audio Degradation Toolbox +% +% Centre for Digital Music, Queen Mary University of London. +% This file copyright 2013 Sebastian Ewert, Matthias Mauch and QMUL. +% +% This program is free software; you can redistribute it and/or +% modify it under the terms of the GNU General Public License as +% published by the Free Software Foundation; either version 2 of the +% License, or (at your option) any later version. See the file +% COPYING included with this distribution for more information. +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Check parameters +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +if nargin<4 + parameter=[]; +end +if nargin<3 + timepositions_beforeDegr=[]; +end +if nargin<2 + error('Please specify input data'); +end + +if isfield(parameter,'loadInternalMagFreqResp')==0 + parameter.loadInternalMagFreqResp = 1; +end +if isfield(parameter,'loadNoiseColorPreset')==0 + parameter.loadNoiseColorPreset = 0; +end +if isfield(parameter,'computeMagFreqRespFromAudio')==0 + parameter.computeMagFreqRespFromAudio = 0; +end + +if isfield(parameter,'internalMagFreqResp')==0 + parameter.internalMagFreqResp = 'Beethoven_Appasionata_Rwc'; +end +if isfield(parameter,'noiseColorPreset')==0 + parameter.noiseColorPreset = 'pink'; +end +if isfield(parameter,'computeMagFreqRespFromAudio_audioData')==0 + parameter.computeMagFreqRespFromAudio_audioData = []; +end +if isfield(parameter,'computeMagFreqRespFromAudio_sf')==0 + parameter.computeMagFreqRespFromAudio_sf = []; +end +if isfield(parameter,'destMagFreqResp')==0 + parameter.destMagFreqResp = []; +end +if isfield(parameter,'destMagFreqResp_freqs')==0 + parameter.destMagFreqResp_freqs = []; +end +if isfield(parameter,'fftLength')==0 + parameter.fftLength = 2 ^ nextpow2(0.02 * samplingFreq); +end +if isfield(parameter,'filterOrder')==0 + parameter.filterOrder = round(parameter.fftLength/2); +end +if isfield(parameter,'visualizations')==0 + parameter.visualizations = 0; +end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Main program +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +if isempty(f_audio) + % we design a linear-phase filter. Such filters have the property that + % the group delay for every frequency is always equal to + % parameter.filterOrder/2. Therefore, although we built a signal + % adaptive filter, we can adjust for that delay. That means that the + % timepositions_beforeDegr don't require any adjustment. + timepositions_afterDegr = timepositions_beforeDegr; + return; +end + +% load/compute destMagFreqResp +[destMagFreqResp,destMagFreqResp_freqs] = internal_getDestMagFreqResp(parameter); + +% compute mean spectral vector for f_audio +[meanMagSpec,meanMagSpec_freqs] = internal_computeMeanSpectralVector(f_audio,samplingFreq,parameter.fftLength); +meanMagSpec = internal_standardizeMagFreqResp(meanMagSpec); + +% compute magnitude response for the filter to be designed +destMagFreqResp_org = destMagFreqResp; +destMagFreqResp_freqs_org = destMagFreqResp_freqs; +if ~((length(destMagFreqResp_freqs)==length(meanMagSpec_freqs)) && (all(meanMagSpec_freqs(:) == destMagFreqResp_freqs(:)))) + % in this case we interpolate the frequency response using a + % spline interpolation + destMagFreqResp = spline(destMagFreqResp,destMagFreqResp_freqs,meanMagSpec_freqs); +end +filter_magFreqResp = destMagFreqResp(:) - meanMagSpec(:); + +% design filter (fir2 is linear phase) +filter_magFreqResp_linear = 10 .^ (filter_magFreqResp/20); +b = fir2(parameter.filterOrder,meanMagSpec_freqs/(samplingFreq/2),filter_magFreqResp_linear); + +% apply filter +parameterApplyImpulseResponse.loadInternalIR = 0; +parameterApplyImpulseResponse.impulseResponse = b; +parameterApplyImpulseResponse.impulseResponseSampFreq = samplingFreq; +parameterApplyImpulseResponse.normalizeOutputAudio = 1; +parameterApplyImpulseResponse.averageGroupDelayOfFilter = round(parameter.filterOrder/2); +[f_audio_out,timepositions_afterDegr] = degradationUnit_applyImpulseResponse(f_audio, samplingFreq, timepositions_beforeDegr, parameterApplyImpulseResponse); + +if parameter.visualizations + fvtool(b,1); + + [meanMagSpecOut,meanMagSpecOut_freqs] = internal_computeMeanSpectralVector(f_audio_out,samplingFreq,parameter.fftLength); + meanMagSpecOut = internal_standardizeMagFreqResp(meanMagSpecOut); + + figure; + plot(destMagFreqResp_freqs_org,destMagFreqResp_org,'y'); + hold on; + plot(meanMagSpecOut_freqs,meanMagSpecOut,'k'); + title('Comparison: destMagFreqResp(y) and mean spectral vector of output(k)') +end + + +end + +function [f_meanmagspec_db,freqs] = internal_computeMeanSpectralVector(f_audio,fs,fftLength) + +f_audio = mean(f_audio,2); + +[f_spec,freqs,time] = spectrogram(f_audio,hanning(fftLength),fftLength/2,fftLength,fs); + +f_magspec_db = 20 * log10(abs(f_spec)); + +f_magspec_db(:,isinf(sum(abs(f_magspec_db),1))) = []; % ignore columns with -inf/inf entries +f_magspec_db(:,isnan(sum(abs(f_magspec_db),1))) = []; + +f_meanmagspec_db = mean(f_magspec_db,2); + +end + +function magFreqResp = internal_standardizeMagFreqResp(magFreqResp) + +temp = magFreqResp(~isinf(magFreqResp)); +temp = temp(~isnan(magFreqResp)); +maxRobust = max(temp); + +magFreqResp = magFreqResp - maxRobust; + +magFreqResp(magFreqResp > 0) = 0; % remaining positive inf +magFreqResp(magFreqResp < -80) = -80; % remaining positive inf + +end + +function [destMagFreqResp,destMagFreqResp_freqs,fftLength] = internal_getDestMagFreqResp(parameter) +if parameter.loadInternalMagFreqResp + % load example included in toolbox + + fullFilenameMfile = mfilename('fullpath'); + [pathstr,name,ext] = fileparts(fullFilenameMfile); + dirRootIRs = fullfile(pathstr,'../degradationData'); + + names_internal = {'Beatles_NorwegianWood','Beethoven_Appasionata_Rwc'}; + indexInternal = find(strcmpi(names_internal,parameter.internalMagFreqResp), 1); + if isempty(indexInternal) + error('Please specify a valid internal name') + end + + switch indexInternal + case 1 + file = fullfile(dirRootIRs,'SpecEnvelopes/Beatles_NorwegianWood.mat'); + case 2 + file = fullfile(dirRootIRs,'SpecEnvelopes/Beethoven_Appasionata_Rwc.mat'); + end + load(file, 'destMagFreqResp', 'destMagFreqResp_freqs'); + +elseif parameter.loadNoiseColorPreset + switch(lower( parameter.noiseColorPreset)) + case 'white' + freqExponent = 0; + case 'pink' + freqExponent = 0.5; + case 'brown' + freqExponent = 1; + case 'blue' + freqExponent = -0.5; + case 'violet' + freqExponent = -1; + end + + lengthMagResp = parameter.fftLength/2+1; + destMagFreqResp_freqs = linspace(0,samplingFreq/2,lengthMagResp); + magResp = 1./destMagFreqResp_freqs.^freqExponent; + magResp(1) = 1; + destMagFreqResp = 20 * log10(magResp); + +elseif parameter.computeMagFreqRespFromAudio + % compute destMagFreqResp as mean spectral vector from given audio data + [destMagFreqResp,destMagFreqResp_freqs] = internal_computeMeanSpectralVector(... + parameter.computeMagFreqRespFromAudio_audioData,parameter.computeMagFreqRespFromAudio_sf,parameter.fftLength); +else + destMagFreqResp = parameter.destMagFreqResp; + destMagFreqResp_freqs = parameter.destMagFreqResp_freqs; +end + +% standardize destMagFreqResp +destMagFreqResp = internal_standardizeMagFreqResp(destMagFreqResp); +end + + + + + +