matthiasm@0
|
1 function [f_audio_out,timepositions_afterDegr] = degradationUnit_applyDynamicRangeCompression(f_audio, samplingFreq, timepositions_beforeDegr, parameter)
|
matthiasm@0
|
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
matthiasm@0
|
3 % Name: degradation_applyDynamicRangeCompression
|
matthiasm@0
|
4 % Version: 1
|
matthiasm@0
|
5 % Date: 2013-01-23
|
matthiasm@0
|
6 % Programmer: Matthias Mauch, Sebastian Ewert
|
matthiasm@0
|
7 %
|
matthiasm@0
|
8 % Description:
|
matthiasm@0
|
9 % - applies dynamic range compression to a signal
|
matthiasm@0
|
10 % - f_audio_out is the compressed audio
|
matthiasm@0
|
11 %
|
matthiasm@0
|
12 % Input:
|
matthiasm@0
|
13 % f_audio - audio signal \in [-1,1]^{NxC} with C being the number of
|
matthiasm@0
|
14 % channels
|
matthiasm@0
|
15 % samplingFreq - sampling frequency of f_audio
|
matthiasm@0
|
16 % timepositions_beforeDegr - some degradations delay the input signal. If
|
matthiasm@0
|
17 % some points in time are given via this
|
matthiasm@0
|
18 % parameter, timepositions_afterDegr will
|
matthiasm@0
|
19 % return the corresponding positions in the
|
matthiasm@0
|
20 % output. Set to [] if unavailable. Set f_audio
|
matthiasm@0
|
21 % and samplingFreq to [] to compute only
|
matthiasm@0
|
22 % timepositions_afterDegr.
|
matthiasm@0
|
23 %
|
matthiasm@0
|
24 % Input (optional): parameter
|
matthiasm@0
|
25 % .preNormalization = 0; % db for 95% RMS quantile / 0 means "off"
|
matthiasm@0
|
26 % .forgettingTime = 0.1; % seconds
|
matthiasm@0
|
27 % .compressorThreshold = -40; % dB
|
matthiasm@0
|
28 % .compressorSlope = 0.9;
|
matthiasm@0
|
29 % .attackTime = 0.01; % seconds
|
matthiasm@0
|
30 % .releaseTime = 0.01; % seconds
|
matthiasm@0
|
31 % .delayTime = 0.01; % seconds
|
matthiasm@0
|
32 % .normalizeOutputAudio = 1;
|
matthiasm@0
|
33 %
|
matthiasm@0
|
34 % Output:
|
matthiasm@0
|
35 % f_audio_out - audio output signal
|
matthiasm@0
|
36 %
|
matthiasm@0
|
37 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
matthiasm@0
|
38
|
matthiasm@0
|
39 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
matthiasm@0
|
40 % Audio Degradation Toolbox
|
matthiasm@0
|
41 %
|
matthiasm@0
|
42 % Centre for Digital Music, Queen Mary University of London.
|
matthiasm@0
|
43 % This file copyright 2013 Sebastian Ewert, Matthias Mauch and QMUL.
|
matthiasm@0
|
44 %
|
matthiasm@0
|
45 % This program is free software; you can redistribute it and/or
|
matthiasm@0
|
46 % modify it under the terms of the GNU General Public License as
|
matthiasm@0
|
47 % published by the Free Software Foundation; either version 2 of the
|
matthiasm@0
|
48 % License, or (at your option) any later version. See the file
|
matthiasm@0
|
49 % COPYING included with this distribution for more information.
|
matthiasm@0
|
50 %
|
matthiasm@0
|
51 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
matthiasm@0
|
52
|
matthiasm@0
|
53 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
matthiasm@0
|
54 % Check parameters
|
matthiasm@0
|
55 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
matthiasm@0
|
56 if nargin<4
|
matthiasm@0
|
57 parameter=[];
|
matthiasm@0
|
58 end
|
matthiasm@0
|
59 if nargin<3
|
matthiasm@0
|
60 timepositions_beforeDegr=[];
|
matthiasm@0
|
61 end
|
matthiasm@0
|
62 if nargin<2
|
matthiasm@0
|
63 error('Please specify input data');
|
matthiasm@0
|
64 end
|
matthiasm@0
|
65
|
matthiasm@0
|
66 if isfield(parameter,'preNormalization')==0
|
matthiasm@0
|
67 parameter.preNormalization = 0; % db for 95% RMS quantile / 0 means "off"
|
matthiasm@0
|
68 end
|
matthiasm@0
|
69 if isfield(parameter,'forgettingTime')==0
|
matthiasm@0
|
70 parameter.forgettingTime = 0.1; % seconds
|
matthiasm@0
|
71 end
|
matthiasm@0
|
72 if isfield(parameter,'compressorThreshold')==0
|
matthiasm@0
|
73 parameter.compressorThreshold = -40; % dB
|
matthiasm@0
|
74 end
|
matthiasm@0
|
75 if isfield(parameter,'compressorSlope')==0
|
matthiasm@0
|
76 parameter.compressorSlope = 0.9;
|
matthiasm@0
|
77 end
|
matthiasm@0
|
78 if isfield(parameter,'attackTime')==0
|
matthiasm@0
|
79 parameter.attackTime = 0.01; % seconds
|
matthiasm@0
|
80 end
|
matthiasm@0
|
81 if isfield(parameter,'releaseTime')==0
|
matthiasm@0
|
82 parameter.releaseTime = 0.01; % seconds
|
matthiasm@0
|
83 end
|
matthiasm@0
|
84 if isfield(parameter,'delayTime')==0
|
matthiasm@0
|
85 parameter.delayTime = 0.01; % seconds
|
matthiasm@0
|
86 end
|
matthiasm@0
|
87 if isfield(parameter,'normalizeOutputAudio')==0
|
matthiasm@0
|
88 parameter.normalizeOutputAudio = 1;
|
matthiasm@0
|
89 end
|
matthiasm@0
|
90
|
matthiasm@0
|
91 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
matthiasm@0
|
92 % Main program
|
matthiasm@0
|
93 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
matthiasm@0
|
94
|
matthiasm@0
|
95 f_audio_out = [];
|
matthiasm@0
|
96 if ~isempty(f_audio)
|
matthiasm@0
|
97
|
matthiasm@0
|
98 %% secondary parameters
|
matthiasm@0
|
99
|
matthiasm@0
|
100 AT = 1 - exp(-2.2/(samplingFreq*parameter.attackTime)); % attack parameter
|
matthiasm@0
|
101 RT = 1 - exp(-2.2/(samplingFreq*parameter.releaseTime)); % release parameter
|
matthiasm@0
|
102 FT = 1 - exp(-2.2/(samplingFreq*parameter.forgettingTime)); % forgetting parameter
|
matthiasm@0
|
103
|
matthiasm@0
|
104 delay = floor(parameter.delayTime * samplingFreq);
|
matthiasm@0
|
105
|
matthiasm@0
|
106 mono_audio = mean(f_audio, 2);
|
matthiasm@0
|
107
|
matthiasm@0
|
108 if parameter.preNormalization < 0
|
matthiasm@0
|
109 quantMeasured = max(quantile(abs(mono_audio), 0.95),eps);
|
matthiasm@0
|
110 quantWanted = db2mag(parameter.preNormalization);
|
matthiasm@0
|
111 f_audio = f_audio * quantWanted / quantMeasured;
|
matthiasm@0
|
112 mono_audio = mono_audio * quantWanted / quantMeasured;
|
matthiasm@0
|
113 end
|
matthiasm@0
|
114
|
matthiasm@0
|
115 %%
|
matthiasm@0
|
116
|
matthiasm@0
|
117 nSample = size(f_audio, 1);
|
matthiasm@0
|
118 nChannel = size(f_audio, 2);
|
matthiasm@0
|
119
|
matthiasm@0
|
120 if AT == RT
|
matthiasm@0
|
121 % Vectorized version
|
matthiasm@0
|
122 %%
|
matthiasm@0
|
123 runningMS_all = filter(FT,[1 -(1-FT)],mono_audio.^2);
|
matthiasm@0
|
124 runningRMSdB_all = 10 * log10(runningMS_all);
|
matthiasm@0
|
125 gainDB_all = min([zeros(1,length(runningRMSdB_all));...
|
matthiasm@0
|
126 parameter.compressorSlope * (parameter.compressorThreshold - runningRMSdB_all(:)')]);
|
matthiasm@0
|
127 preGain_all = 10.^(gainDB_all/20);
|
matthiasm@0
|
128 gain_all = filter(AT,[1 -(1-AT)],[1/AT,preGain_all(2:end)]);
|
matthiasm@0
|
129 % The next line is equivalent to the behaviour of the serial version. Is that a bug?
|
matthiasm@0
|
130 %f_audio_out = repmat(gain_all(:),1,nChannel) .* [zeros(delay+1,nChannel);f_audio(1:end-(delay+1),:)];
|
matthiasm@0
|
131 f_audio_out = repmat(gain_all(:),1,nChannel) .* [zeros(delay,nChannel);f_audio(1:end-delay,:)];
|
matthiasm@0
|
132 else
|
matthiasm@0
|
133 % Serial version (The non-linearity 'if preGain < gain' does not seem to allow for a vectorization in all cases)
|
matthiasm@0
|
134 %%
|
matthiasm@0
|
135 runningMS = mono_audio(1)^2 * FT;
|
matthiasm@0
|
136 gain = 1;
|
matthiasm@0
|
137 buffer = zeros(delay + 1, nChannel);
|
matthiasm@0
|
138 f_audio_out = zeros(size(f_audio));
|
matthiasm@0
|
139
|
matthiasm@0
|
140 for iSample = 2:nSample
|
matthiasm@0
|
141 runningMS = ...
|
matthiasm@0
|
142 runningMS * (1-FT) +...
|
matthiasm@0
|
143 mono_audio(iSample)^2 * FT;
|
matthiasm@0
|
144 runningRMSdB = 10 * log10(runningMS);
|
matthiasm@0
|
145
|
matthiasm@0
|
146 gainDB = min([0, ...
|
matthiasm@0
|
147 parameter.compressorSlope * (parameter.compressorThreshold - runningRMSdB)]);
|
matthiasm@0
|
148 preGain = 10^(gainDB/20);
|
matthiasm@0
|
149
|
matthiasm@0
|
150 if preGain < gain % "gain" being old gain
|
matthiasm@0
|
151 coeff = AT; % we're in the attack phase
|
matthiasm@0
|
152 else
|
matthiasm@0
|
153 coeff = RT; % we're in the release phase
|
matthiasm@0
|
154 end
|
matthiasm@0
|
155
|
matthiasm@0
|
156 % calculate new gain as mix of current gain (preGain) and old gain
|
matthiasm@0
|
157 gain = (1-coeff) * gain + coeff * preGain;
|
matthiasm@0
|
158
|
matthiasm@0
|
159 f_audio_out(iSample, :) = gain * buffer(end,:);
|
matthiasm@0
|
160 if delay > 1
|
matthiasm@0
|
161 buffer = [f_audio(iSample, :); buffer(1:end-1,:)];
|
matthiasm@0
|
162 else
|
matthiasm@0
|
163 buffer = f_audio(iSample, :);
|
matthiasm@0
|
164 end
|
matthiasm@0
|
165 end
|
matthiasm@0
|
166 end
|
matthiasm@0
|
167
|
matthiasm@0
|
168 if parameter.normalizeOutputAudio
|
matthiasm@0
|
169 f_audio_out = adthelper_normalizeAudio(f_audio_out, samplingFreq);
|
matthiasm@0
|
170 end
|
matthiasm@0
|
171
|
matthiasm@0
|
172 end
|
matthiasm@0
|
173
|
matthiasm@0
|
174 % This degradation does impose a temporal distortion
|
matthiasm@0
|
175 timepositions_afterDegr = timepositions_beforeDegr + parameter.delayTime;
|
matthiasm@0
|
176
|
matthiasm@0
|
177 end
|
matthiasm@0
|
178
|
matthiasm@0
|
179
|
matthiasm@0
|
180
|
matthiasm@0
|
181
|