Summary: | WaveShaperNode interpolation does not match specification | ||||||
---|---|---|---|---|---|---|---|
Product: | WebKit | Reporter: | Andy Harman <webaudio.at.pendragon> | ||||
Component: | Web Audio | Assignee: | Nobody <webkit-unassigned> | ||||
Status: | NEW --- | ||||||
Severity: | Normal | CC: | crogers, webaudio.at.pendragon | ||||
Priority: | P2 | ||||||
Version: | 528+ (Nightly build) | ||||||
Hardware: | PC | ||||||
OS: | Windows XP | ||||||
Attachments: |
|
Comment on attachment 225181 [details]
HTML page that demonstates the bug (updated to work in Firefox too).
<html>
<body>
<pre>
The interpolation for the Web Audio WaveShaperNode does not work properly.
I have created the follow JavaScript to demonstrate the 2 problems:
var audioContextClass=window.OfflineAudioContext||webkitOfflineAudioContext
var c=new audioContextClass(1, 23, 48000);
//Create input values between -1.1 and + 1.1.
var b=c.createBuffer(1, 23, 48000);
var d=b.getChannelData(0);
for (var i=0; i<23; i++){ d[i]=(i*.1)-1.1; }
var src=c.createBufferSource();
src.buffer=b;
src.start(0);
//Simple wave-shaper. It should map -1 to 0, 0 to 1, and +1 to 0 - and interpolate all
//points between.
var ws=c.createWaveShaper();
var curve=new Float32Array(3);
curve[0]=0;
curve[1]=1;
curve[2]=0;
ws.curve=curve;
src.connect(ws);
ws.connect(c.destination);
//Execute.
c.oncomplete=function(ev){
var inputData=b.getChannelData(0);
var outputData=ev.renderedBuffer.getChannelData(0);
var msgItems=[];
for (var i=0; i<23; i++){
msgItems.push("Input value " + inputData[i].toFixed(4) +
" => output value " + outputData[i].toFixed(4));
}
alert(msgItems.join("\n"));
};
c.startRendering();
The specification says "Any sample value less than -1 will correspond to the first value in the curve array."
* In Chrome, an input of -1.1 returns a value -0.1500, it should return 0.0000
The specification says "Each input sample within this range will index into the shaping curve
with a signal level of zero corresponding to the center value of the curve array."
* In Chrome and FireFox, an input of 0 returns a value of 0.5000, it should return 1.0000
* In Chrome and FireFox, an input of 0.4 returns a value of 0.0000, it should return 0.8000
The problem appears to be in WaveShaperDSPKernel.cpp in the webkit source code - I think it should be
something like this:
double virtualIndex = 0.5 * (input + 1) * curveLength;
double output;
if (virtualIndex <= -1) {
output = curve[0];
} else if (virtualIndex >= curve[curveLength-1]) {
output = curve[curveLength-1];
} else {
int index1 = static_cast<int>(virtualIndex);
double value1 = curve[index1];
double value2 = curve[index1+1];
double interpolationFactor = virtualIndex - index1;
output = (1.0 - interpolationFactor) * value1 + interpolationFactor * value2;
}
The current webkit test HTML page only tests input values that fall between -1 and +1. The
error from the mid-point and end-point of the curve are disguised by the large number of
elements in the tested curve array.
</pre>
<script type="text/javascript">
var audioContextClass=(window.OfflineAudioContext||webkitOfflineAudioContext);
var c=new audioContextClass(1, 23, 48000);
//Create input values between -1.1 and + 1.1.
var b=c.createBuffer(1, 23, 48000);
var d=b.getChannelData(0);
for (var i=0; i<23; i++){ d[i]=(i*.1)-1.1; }
var src=c.createBufferSource();
src.buffer=b;
src.start(0);
//Simple wave-shaper.
var ws=c.createWaveShaper();
var curve=new Float32Array(3);
curve[0]=0;
curve[1]=1;
curve[2]=0;
ws.curve=curve;
src.connect(ws);
ws.connect(c.destination);
//Execute.
c.oncomplete=function(ev){
var inputData=b.getChannelData(0);
var outputData=ev.renderedBuffer.getChannelData(0);
var msgItems=[];
for (var i=0; i<23; i++){
msgItems.push("Input value " + inputData[i].toFixed(4) +
" => output value " + outputData[i].toFixed(4));
}
alert(msgItems.join("\n"));
};
c.startRendering();
</script>
</body>
</html>
|
Created attachment 225181 [details] HTML page that demonstates the bug (updated to work in Firefox too). The interpolation for the Web Audio WaveShaperNode does not work properly. I have created the follow JavaScript to demonstrate the 2 problems: var c=new webkitOfflineAudioContext(1, 23, 48000); //Create input values between -1.1 and + 1.1. var b=c.createBuffer(1, 23, 48000); var d=b.getChannelData(0); for (var i=0; i<23; i++){ d[i]=(i*.1)-1.1; } var src=c.createBufferSource(); src.buffer=b; src.start(0); //Simple wave-shaper. It should map -1 to 0, 0 to 1, and +1 to 0 - and interpolate all //points between. var ws=c.createWaveShaper(); var curve=new Float32Array(3); curve[0]=0; curve[1]=1; curve[2]=0; ws.curve=curve; src.connect(ws); ws.connect(c.destination); //Execute. c.oncomplete=function(ev){ var outputData=ev.renderedBuffer.getChannelData(0); var msgItems=[]; for (var i=0; i<23; i++){ msgItems.push("Input value " + d[i].toFixed(4) + " => output value " + outputData[i].toFixed(4)); } alert(msgItems.join("\n")); }; c.startRendering(); The specification says "Any sample value less than -1 will correspond to the first value in the curve array." * An input of -1.1 returns a value -0.1500, it should return 0.0000 The specification says "Each input sample within this range will index into the shaping curve with a signal level of zero corresponding to the center value of the curve array." * An input of 0 returns a value of 0.5000, it should return 1.0000 * An input of 0.4 returns a value of 0.0000, it should return 0.8000 The problem appears to be in WaveShaperDSPKernel.cpp - I think it should be changed to something like: double virtualIndex = 0.5 * (input + 1) * curveLength; double output; if (virtualIndex <= -1) { output = curve[0]; } else if (virtualIndex >= curve[curveLength-1]) { output = curve[curveLength-1]; } else { var index1 = (int)virtualIndex; var value1 = curve[index1]; var value2 = curve[index1+1]; var interpolationFactor = virtualIndex - index1; output = (1.0 - interpolationFactor) * value1 + interpolationFactor * value2; } The current test HTML page only tests input values that fall between -1 and +1 - and the error from the mid-point and end-point of the curve are disguised by the large number of elements in the curve array.