NEW 129333
WaveShaperNode interpolation does not match specification
https://bugs.webkit.org/show_bug.cgi?id=129333
Summary WaveShaperNode interpolation does not match specification
Andy Harman
Reported 2014-02-25 13:43:08 PST
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.
Attachments
HTML page that demonstates the bug (updated to work in Firefox too). (3.08 KB, text/html)
2014-02-25 13:43 PST, Andy Harman
no flags
Andy Harman
Comment 1 2014-02-28 01:35:38 PST
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>
Note You need to log in before you can comment on or make changes to this bug.