matthiasm@0
|
1 function [f_audio_out,timepositions_afterDegr] = degradationUnit_applyWowResampling(f_audio, samplingFreq, timepositions_beforeDegr, parameter)
|
matthiasm@0
|
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
matthiasm@0
|
3 % Name: degradation_applyWowResampling
|
matthiasm@0
|
4 % Date of Revision: 2013-03
|
matthiasm@0
|
5 % Programmer: Sebastian Ewert
|
matthiasm@0
|
6 %
|
matthiasm@0
|
7 % Description:
|
matthiasm@0
|
8 % - This is useful for wow and flutter simulations.
|
matthiasm@0
|
9 % - a playback speed of 1 is modulated by a cos signal via
|
matthiasm@0
|
10 % f(x) = a_m * cos(2*pi*f_m*x)+1
|
matthiasm@0
|
11 % - the function mapping a position in the original recording
|
matthiasm@0
|
12 % to a position in the generated recording:
|
matthiasm@0
|
13 % F(x) = x + a_m * sin(2*pi*f_m*x) / (2*pi*f_m)
|
matthiasm@0
|
14 % - an optimal solution now would employ a sinc reconstruction of f_audio,
|
matthiasm@0
|
15 % sinc_fa, and sampling sinc_fa(F^-1(y)) equidistantly.
|
matthiasm@0
|
16 % - This implementation employs only an approximation by first upsampling
|
matthiasm@0
|
17 % f_audio and then sampling that using nearest neighbors
|
matthiasm@0
|
18 %
|
matthiasm@0
|
19 % Input:
|
matthiasm@0
|
20 % f_audio - audio signal \in [-1,1]^{NxC} with C being the number of
|
matthiasm@0
|
21 % channels
|
matthiasm@0
|
22 % samplingFreq - sampling frequency of f_audio
|
matthiasm@0
|
23 % timepositions_beforeDegr - some degradations delay the input signal. If
|
matthiasm@0
|
24 % some points in time are given via this
|
matthiasm@0
|
25 % parameter, timepositions_afterDegr will
|
matthiasm@0
|
26 % return the corresponding positions in the
|
matthiasm@0
|
27 % output. Set to [] if unavailable. Set f_audio
|
matthiasm@0
|
28 % and samplingFreq to [] to compute only
|
matthiasm@0
|
29 % timepositions_afterDegr.
|
matthiasm@0
|
30 %
|
matthiasm@0
|
31 % Input (optional): parameter
|
matthiasm@0
|
32 % .intensityOfChange = 1.5 - a_m = parameter.intensityOfChange/100; stay below 100
|
matthiasm@0
|
33 % .frequencyOfChange = 0.5 - f_m
|
matthiasm@0
|
34 % .upsamplingFactor = 5 - the higher the better the quality (and the more memory)
|
matthiasm@0
|
35 % .normalizeOutputAudio = 0 - normalize audio
|
matthiasm@0
|
36 %
|
matthiasm@0
|
37 % Output:
|
matthiasm@0
|
38 % f_audio_out - audio signal \in [-1,1]^{NxC} with C being the number
|
matthiasm@0
|
39 % of channels
|
matthiasm@0
|
40 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
matthiasm@0
|
41
|
matthiasm@0
|
42 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
matthiasm@0
|
43 % Audio Degradation Toolbox
|
matthiasm@0
|
44 %
|
matthiasm@0
|
45 % Centre for Digital Music, Queen Mary University of London.
|
matthiasm@0
|
46 % This file copyright 2013 Sebastian Ewert, Matthias Mauch and QMUL.
|
matthiasm@0
|
47 %
|
matthiasm@0
|
48 % This program is free software; you can redistribute it and/or
|
matthiasm@0
|
49 % modify it under the terms of the GNU General Public License as
|
matthiasm@0
|
50 % published by the Free Software Foundation; either version 2 of the
|
matthiasm@0
|
51 % License, or (at your option) any later version. See the file
|
matthiasm@0
|
52 % COPYING included with this distribution for more information.
|
matthiasm@0
|
53 %
|
matthiasm@0
|
54 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
matthiasm@0
|
55
|
matthiasm@0
|
56 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
matthiasm@0
|
57 % Check parameters
|
matthiasm@0
|
58 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
matthiasm@0
|
59 if nargin<4
|
matthiasm@0
|
60 parameter=[];
|
matthiasm@0
|
61 end
|
matthiasm@0
|
62 if nargin<3
|
matthiasm@0
|
63 timepositions_beforeDegr=[];
|
matthiasm@0
|
64 end
|
matthiasm@0
|
65 if nargin<2
|
matthiasm@0
|
66 error('Please specify input data');
|
matthiasm@0
|
67 end
|
matthiasm@0
|
68
|
matthiasm@0
|
69 if isfield(parameter,'intensityOfChange')==0
|
matthiasm@0
|
70 parameter.intensityOfChange = 1.5; % a_m = parameter.intensityOfChange/100; %stay below 100
|
matthiasm@0
|
71 end
|
matthiasm@0
|
72 if isfield(parameter,'frequencyOfChange')==0
|
matthiasm@0
|
73 parameter.frequencyOfChange = 0.5; % f_m
|
matthiasm@0
|
74 end
|
matthiasm@0
|
75 if isfield(parameter,'upsamplingFactor')==0
|
matthiasm@0
|
76 parameter.upsamplingFactor = 5; % the higher the better the quality (and the more memory)
|
matthiasm@0
|
77 end
|
matthiasm@0
|
78 if isfield(parameter,'normalizeOutputAudio')==0
|
matthiasm@0
|
79 parameter.normalizeOutputAudio = 0;
|
matthiasm@0
|
80 end
|
matthiasm@0
|
81
|
matthiasm@0
|
82 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
matthiasm@0
|
83 % Main program
|
matthiasm@0
|
84 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
matthiasm@0
|
85 fs = samplingFreq;
|
matthiasm@0
|
86 fsOverSampled = fs * parameter.upsamplingFactor;
|
matthiasm@0
|
87 a_m = parameter.intensityOfChange / 100;
|
matthiasm@0
|
88 f_m = parameter.frequencyOfChange;
|
matthiasm@0
|
89
|
matthiasm@0
|
90 f_audio_out = f_audio;
|
matthiasm@0
|
91 if ~isempty(f_audio)
|
matthiasm@0
|
92 numSamples = size(f_audio,1);
|
matthiasm@0
|
93 numChannels = size(f_audio,2);
|
matthiasm@0
|
94 length_sec = numSamples/fs;
|
matthiasm@0
|
95 numFullPeriods = floor(length_sec * f_m);
|
matthiasm@0
|
96 numSamplesToWarp = round(numFullPeriods *fs / f_m);
|
matthiasm@0
|
97
|
matthiasm@0
|
98 oldSamplePositions_to_newOversampledPositions = round(timeAssignment_newToOld([1:numSamplesToWarp]/fs,a_m,f_m)*fsOverSampled);
|
matthiasm@0
|
99
|
matthiasm@0
|
100 for ch=1:numChannels
|
matthiasm@0
|
101 audioUpsampled = resample(f_audio(:,ch),parameter.upsamplingFactor,1);
|
matthiasm@0
|
102
|
matthiasm@0
|
103 f_audio_out(1:numSamplesToWarp,ch) = audioUpsampled(oldSamplePositions_to_newOversampledPositions);
|
matthiasm@0
|
104
|
matthiasm@0
|
105 clear audioUpsampled
|
matthiasm@0
|
106 end
|
matthiasm@0
|
107
|
matthiasm@0
|
108 if parameter.normalizeOutputAudio
|
matthiasm@0
|
109 f_audio_out = adthelper_normalizeAudio(f_audio_out, samplingFreq);
|
matthiasm@0
|
110 end
|
matthiasm@0
|
111
|
matthiasm@0
|
112 end
|
matthiasm@0
|
113
|
matthiasm@0
|
114 %pitchShifted_semitones = getPitchShiftAtPositionsInNewFile([0:0.1:length_sec],a_m,f_m); % not used yet but might be useful later on
|
matthiasm@0
|
115
|
matthiasm@0
|
116 %selftest(length_sec,a_m,f_m);
|
matthiasm@0
|
117
|
matthiasm@0
|
118 % This degradation does impose a complex temporal distortion
|
matthiasm@0
|
119 timepositions_afterDegr = [];
|
matthiasm@0
|
120 if ~isempty(timepositions_beforeDegr)
|
matthiasm@0
|
121 timepositions_afterDegr = timeAssignment_oldToNew(timepositions_beforeDegr,a_m,f_m);
|
matthiasm@0
|
122 end
|
matthiasm@0
|
123
|
matthiasm@0
|
124 end
|
matthiasm@0
|
125
|
matthiasm@0
|
126
|
matthiasm@0
|
127 function timeAssigned = timeAssignment_oldToNew(x,a_m,f_m)
|
matthiasm@0
|
128 % vectorized
|
matthiasm@0
|
129
|
matthiasm@0
|
130 timeAssigned = x + a_m * sin(2*pi*f_m*x) / (2*pi*f_m);
|
matthiasm@0
|
131
|
matthiasm@0
|
132 end
|
matthiasm@0
|
133
|
matthiasm@0
|
134 function timeAssigned = timeAssignment_newToOld(y,a_m,f_m)
|
matthiasm@0
|
135 % vectorized
|
matthiasm@0
|
136 % SE: non linear equation: solved numerically (we are lucky:
|
matthiasm@0
|
137 % As G(x) := x - (F(x)-y) is a contraction for a_m<1 we can simply employ the Banach fixpoint theorem)
|
matthiasm@0
|
138
|
matthiasm@0
|
139 timeAssigned = y;
|
matthiasm@0
|
140 for k=1:40
|
matthiasm@0
|
141 timeAssigned = y - a_m * sin(2*pi*f_m*timeAssigned) / (2*pi*f_m);
|
matthiasm@0
|
142 end
|
matthiasm@0
|
143
|
matthiasm@0
|
144 end
|
matthiasm@0
|
145
|
matthiasm@0
|
146 function pitchShifted_semitones = getPitchShiftAtPositionsInNewFile(y,a_m,f_m)
|
matthiasm@0
|
147 % vectorized
|
matthiasm@0
|
148
|
matthiasm@0
|
149 % maps times from new to old and takes a look at the value of the derivative
|
matthiasm@0
|
150 % there
|
matthiasm@0
|
151
|
matthiasm@0
|
152 timeAssigned = timeAssignment_newToOld(y,a_m,f_m);
|
matthiasm@0
|
153
|
matthiasm@0
|
154 derivative = 1 + a_m * cos(2*pi*f_m*timeAssigned);
|
matthiasm@0
|
155
|
matthiasm@0
|
156 pitchShifted_semitones = log2(1./derivative)*12;
|
matthiasm@0
|
157
|
matthiasm@0
|
158 end
|
matthiasm@0
|
159
|
matthiasm@0
|
160 function selftest(length_sec,a_m,f_m)
|
matthiasm@0
|
161
|
matthiasm@0
|
162 x0 = rand(10000,1) * length_sec;
|
matthiasm@0
|
163
|
matthiasm@0
|
164 timeAssigned1 = timeAssignment_oldToNew(x0,a_m,f_m);
|
matthiasm@0
|
165 timeAssigned2 = timeAssignment_newToOld(timeAssigned1,a_m,f_m);
|
matthiasm@0
|
166
|
matthiasm@0
|
167 figure;
|
matthiasm@0
|
168 plot(x0 - timeAssigned2);
|
matthiasm@0
|
169
|
matthiasm@0
|
170 end
|
matthiasm@0
|
171
|
matthiasm@0
|
172
|
matthiasm@0
|
173
|
matthiasm@0
|
174
|
matthiasm@0
|
175
|
matthiasm@0
|
176
|