Changeset 106621 in webkit


Ignore:
Timestamp:
Feb 2, 2012 8:08:50 PM (12 years ago)
Author:
commit-queue@webkit.org
Message:

Check parameters to biquad filters
https://bugs.webkit.org/show_bug.cgi?id=71413

Source/WebCore:

Patch by Raymond Toy <Raymond Toy> on 2012-02-02
Reviewed by Kenneth Russell.

Tests added for each filter type and for the limiting cases for
each filter type.

  • platform/audio/Biquad.cpp:

(WebCore::Biquad::setLowpassParams):
(WebCore::Biquad::setHighpassParams):
(WebCore::Biquad::setLowShelfParams):
(WebCore::Biquad::setHighShelfParams):
(WebCore::Biquad::setPeakingParams):
(WebCore::Biquad::setAllpassParams):
(WebCore::Biquad::setNotchParams):
(WebCore::Biquad::setBandpassParams):
Check for invalid parameters and clip them to something sensible.
Also check for the limiting cases and try to use the limiting form
of the z-transform for the biquad. Some issues cannot be
consistently handled because the z-transform is not continuous as
the parameters approach the limit.

LayoutTests:

Patch by Raymond Toy <rtoy@chromium.org> on 2012-02-02
Reviewed by Kenneth Russell.

  • webaudio/biquad-allpass-expected.txt: Added
  • webaudio/biquad-allpass.html: Added
  • webaudio/biquad-bandpass-expected.txt: Added
  • webaudio/biquad-bandpass.html: Added
  • webaudio/biquad-highpass-expected.txt: Added
  • webaudio/biquad-highpass.html: Added
  • webaudio/biquad-highshelf-expected.txt: Added
  • webaudio/biquad-highshelf.html: Added
  • webaudio/biquad-lowpass-expected.txt: Added
  • webaudio/biquad-lowpass.html: Added
  • webaudio/biquad-lowshelf-expected.txt: Added
  • webaudio/biquad-lowshelf.html: Added
  • webaudio/biquad-notch-expected.txt: Added
  • webaudio/biquad-notch.html: Added
  • webaudio/biquad-peaking-expected.txt: Added
  • webaudio/biquad-peaking.html: Added
  • webaudio/resources/biquad-testing.js: Added
Location:
trunk
Files:
17 added
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r106619 r106621  
     12012-02-02  Raymond Toy  <rtoy@chromium.org>
     2
     3        Check parameters to biquad filters
     4        https://bugs.webkit.org/show_bug.cgi?id=71413
     5
     6        Reviewed by Kenneth Russell.
     7       
     8        * webaudio/biquad-allpass-expected.txt: Added
     9        * webaudio/biquad-allpass.html: Added
     10        * webaudio/biquad-bandpass-expected.txt: Added
     11        * webaudio/biquad-bandpass.html: Added
     12        * webaudio/biquad-highpass-expected.txt: Added
     13        * webaudio/biquad-highpass.html: Added
     14        * webaudio/biquad-highshelf-expected.txt: Added
     15        * webaudio/biquad-highshelf.html: Added
     16        * webaudio/biquad-lowpass-expected.txt: Added
     17        * webaudio/biquad-lowpass.html: Added
     18        * webaudio/biquad-lowshelf-expected.txt: Added
     19        * webaudio/biquad-lowshelf.html: Added
     20        * webaudio/biquad-notch-expected.txt: Added
     21        * webaudio/biquad-notch.html: Added
     22        * webaudio/biquad-peaking-expected.txt: Added
     23        * webaudio/biquad-peaking.html: Added
     24        * webaudio/resources/biquad-testing.js: Added
     25
    1262012-02-02  Shinya Kawanaka  <shinyak@google.com>
    227
  • trunk/Source/WebCore/ChangeLog

    r106620 r106621  
     12012-02-02  Raymond Toy  <rtoy@google.com>
     2
     3        Check parameters to biquad filters
     4        https://bugs.webkit.org/show_bug.cgi?id=71413
     5
     6        Reviewed by Kenneth Russell.
     7
     8        Tests added for each filter type and for the limiting cases for
     9        each filter type.
     10
     11        * platform/audio/Biquad.cpp:
     12        (WebCore::Biquad::setLowpassParams):
     13        (WebCore::Biquad::setHighpassParams):
     14        (WebCore::Biquad::setLowShelfParams):
     15        (WebCore::Biquad::setHighShelfParams):
     16        (WebCore::Biquad::setPeakingParams):
     17        (WebCore::Biquad::setAllpassParams):
     18        (WebCore::Biquad::setNotchParams):
     19        (WebCore::Biquad::setBandpassParams):
     20        Check for invalid parameters and clip them to something sensible.
     21        Also check for the limiting cases and try to use the limiting form
     22        of the z-transform for the biquad.  Some issues cannot be
     23        consistently handled because the z-transform is not continuous as
     24        the parameters approach the limit.
     25
    1262012-02-02  No'am Rosenthal  <noam.rosenthal@nokia.com>
    227
  • trunk/Source/WebCore/platform/audio/Biquad.cpp

    r99337 r106621  
    189189{
    190190    resonance = std::max(0.0, resonance); // can't go negative
    191 
     191    // Limit cutoff to 0 to 1.
     192    cutoff = std::max(0.0, std::min(cutoff, 1.0));
     193   
    192194    double g = pow(10.0, 0.05 * resonance);
    193195    double d = sqrt((4 - sqrt(16 - 16 / (g * g))) / 2);
    194196
    195     // Compute biquad coefficients for lopass filter
    196     double theta = piDouble * cutoff;
    197     double sn = 0.5 * d * sin(theta);
    198     double beta = 0.5 * (1 - sn) / (1 + sn);
    199     double gamma = (0.5 + beta) * cos(theta);
    200     double alpha = 0.25 * (0.5 + beta - gamma);
    201 
    202     m_b0 = 2 * alpha;
    203     m_b1 = 2 * 2 * alpha;
    204     m_b2 = 2 * alpha;
    205     m_a1 = 2 * -gamma;
    206     m_a2 = 2 * beta;
     197    if (cutoff == 1) {
     198        // When cutoff is 1, the z-transform is 1.
     199        m_b0 = 1;
     200        m_b1 = 0;
     201        m_b2 = 0;
     202        m_a1 = 0;
     203        m_a2 = 0;
     204    } else if (cutoff > 0) {
     205        // Compute biquad coefficients for lowpass filter
     206        double theta = piDouble * cutoff;
     207        double sn = 0.5 * d * sin(theta);
     208        double beta = 0.5 * (1 - sn) / (1 + sn);
     209        double gamma = (0.5 + beta) * cos(theta);
     210        double alpha = 0.25 * (0.5 + beta - gamma);
     211
     212        m_b0 = 2 * alpha;
     213        m_b1 = 2 * 2 * alpha;
     214        m_b2 = 2 * alpha;
     215        m_a1 = 2 * -gamma;
     216        m_a2 = 2 * beta;
     217    } else {
     218        // When cutoff is zero, nothing gets through the filter, so set
     219        // coefficients up correctly.
     220        m_b0 = 0;
     221        m_b1 = 0;
     222        m_b2 = 0;
     223        m_a1 = 0;
     224        m_a2 = 0;
     225    }
    207226}
    208227
     
    210229{
    211230    resonance = std::max(0.0, resonance); // can't go negative
     231
     232    // Limit cutoff to 0 to 1.
     233    cutoff = std::max(0.0, std::min(cutoff, 1.0));
    212234
    213235    double g = pow(10.0, 0.05 * resonance);
    214236    double d = sqrt((4 - sqrt(16 - 16 / (g * g))) / 2);
    215237
    216     // Compute biquad coefficients for highpass filter
    217     double theta = piDouble * cutoff;
    218     double sn = 0.5 * d * sin(theta);
    219     double beta = 0.5 * (1 - sn) / (1 + sn);
    220     double gamma = (0.5 + beta) * cos(theta);
    221     double alpha = 0.25 * (0.5 + beta + gamma);
    222 
    223     m_b0 = 2 * alpha;
    224     m_b1 = 2 * -2 * alpha;
    225     m_b2 = 2 * alpha;
    226     m_a1 = 2 * -gamma;
    227     m_a2 = 2 * beta;
     238    if (cutoff == 1) {
     239        // The z-transform is 0.
     240        m_b0 = 0;
     241        m_b1 = 0;
     242        m_b2 = 0;
     243        m_a1 = 0;
     244        m_a2 = 0;
     245    } else if (cutoff > 0) {
     246        // Compute biquad coefficients for highpass filter
     247        double theta = piDouble * cutoff;
     248        double sn = 0.5 * d * sin(theta);
     249        double beta = 0.5 * (1 - sn) / (1 + sn);
     250        double gamma = (0.5 + beta) * cos(theta);
     251        double alpha = 0.25 * (0.5 + beta + gamma);
     252
     253        m_b0 = 2 * alpha;
     254        m_b1 = 2 * -2 * alpha;
     255        m_b2 = 2 * alpha;
     256        m_a1 = 2 * -gamma;
     257        m_a2 = 2 * beta;
     258    } else {
     259      // When cutoff is zero, we need to be careful because the above
     260      // gives a quadratic divided by the same quadratic, with poles
     261      // and zeros on the unit circle in the same place. When cutoff
     262      // is zero, the z-transform is 1.
     263      m_b0 = 1;
     264      m_b1 = 0;
     265      m_b2 = 0;
     266      m_a1 = 0;
     267      m_a2 = 0;
     268    }
    228269}
    229270
     
    241282void Biquad::setLowShelfParams(double frequency, double dbGain)
    242283{
    243     double w0 = piDouble * frequency;
    244 
     284    // Clip frequencies to between 0 and 1, inclusive.
     285    frequency = std::max(0.0, std::min(frequency, 1.0));
     286   
    245287    double A = pow(10.0, dbGain / 40);
    246     double S = 1; // filter slope (1 is max value)
    247     double alpha = 0.5 * sin(w0) * sqrt((A + 1 / A) * (1 / S - 1) + 2);
    248 
    249     double k = cos(w0);
    250     double k2 = 2 * sqrt(A) * alpha;
    251 
    252     double aPlusOne = A + 1;
    253     double aMinusOne = A - 1;
     288
     289    if (frequency == 1) {
     290        // The z-transform is a constant gain.
     291        setNormalizedCoefficients(A * A, 0, 0,
     292                                  1, 0, 0);
     293    } else if (frequency > 0) {
     294        double w0 = piDouble * frequency;
     295        double S = 1; // filter slope (1 is max value)
     296        double alpha = 0.5 * sin(w0) * sqrt((A + 1 / A) * (1 / S - 1) + 2);
     297        double k = cos(w0);
     298        double k2 = 2 * sqrt(A) * alpha;
     299        double aPlusOne = A + 1;
     300        double aMinusOne = A - 1;
     301
     302        double b0 = A * (aPlusOne - aMinusOne * k + k2);
     303        double b1 = 2 * A * (aMinusOne - aPlusOne * k);
     304        double b2 = A * (aPlusOne - aMinusOne * k - k2);
     305        double a0 = aPlusOne + aMinusOne * k + k2;
     306        double a1 = -2 * (aMinusOne + aPlusOne * k);
     307        double a2 = aPlusOne + aMinusOne * k - k2;
     308
     309        setNormalizedCoefficients(b0, b1, b2, a0, a1, a2);
     310    } else {
     311        // When frequency is 0, the z-transform is 1.
     312        setNormalizedCoefficients(1, 0, 0,
     313                                  1, 0, 0);
     314    }
     315}
     316
     317void Biquad::setHighShelfParams(double frequency, double dbGain)
     318{
     319    // Clip frequencies to between 0 and 1, inclusive.
     320    frequency = std::max(0.0, std::min(frequency, 1.0));
     321
     322    double A = pow(10.0, dbGain / 40);
     323
     324    if (frequency == 1) {
     325        // The z-transform is 1.
     326        setNormalizedCoefficients(1, 0, 0,
     327                                  1, 0, 0);
     328    } else if (frequency > 0) {
     329        double w0 = piDouble * frequency;
     330        double S = 1; // filter slope (1 is max value)
     331        double alpha = 0.5 * sin(w0) * sqrt((A + 1 / A) * (1 / S - 1) + 2);
     332        double k = cos(w0);
     333        double k2 = 2 * sqrt(A) * alpha;
     334        double aPlusOne = A + 1;
     335        double aMinusOne = A - 1;
     336
     337        double b0 = A * (aPlusOne + aMinusOne * k + k2);
     338        double b1 = -2 * A * (aMinusOne + aPlusOne * k);
     339        double b2 = A * (aPlusOne + aMinusOne * k - k2);
     340        double a0 = aPlusOne - aMinusOne * k + k2;
     341        double a1 = 2 * (aMinusOne - aPlusOne * k);
     342        double a2 = aPlusOne - aMinusOne * k - k2;
     343
     344        setNormalizedCoefficients(b0, b1, b2, a0, a1, a2);
     345    } else {
     346        // When frequency = 0, the filter is just a gain, A^2.
     347        setNormalizedCoefficients(A * A, 0, 0,
     348                                  1, 0, 0);
     349    }
     350}
     351
     352void Biquad::setPeakingParams(double frequency, double Q, double dbGain)
     353{
     354    // Clip frequencies to between 0 and 1, inclusive.
     355    frequency = std::max(0.0, std::min(frequency, 1.0));
     356
     357    // Don't let Q go negative, which causes an unstable filter.
     358    Q = std::max(0.0, Q);
     359
     360    double A = pow(10.0, dbGain / 40);
     361
     362    if (frequency > 0 && frequency < 1) {
     363        if (Q > 0) {
     364            double w0 = piDouble * frequency;
     365            double alpha = sin(w0) / (2 * Q);
     366            double k = cos(w0);
     367
     368            double b0 = 1 + alpha * A;
     369            double b1 = -2 * k;
     370            double b2 = 1 - alpha * A;
     371            double a0 = 1 + alpha / A;
     372            double a1 = -2 * k;
     373            double a2 = 1 - alpha / A;
     374
     375            setNormalizedCoefficients(b0, b1, b2, a0, a1, a2);
     376        } else {
     377            // When Q = 0, the above formulas have problems. If we look at
     378            // the z-transform, we can see that the limit as Q->0 is A^2, so
     379            // set the filter that way.
     380            setNormalizedCoefficients(A * A, 0, 0,
     381                                      1, 0, 0);
     382        }
     383    } else {
     384        // When frequency is 0 or 1, the z-transform is 1.
     385        setNormalizedCoefficients(1, 0, 0,
     386                                  1, 0, 0);
     387    }
     388}
     389
     390void Biquad::setAllpassParams(double frequency, double Q)
     391{
     392    // Clip frequencies to between 0 and 1, inclusive.
     393    frequency = std::max(0.0, std::min(frequency, 1.0));
     394
     395    // Don't let Q go negative, which causes an unstable filter.
     396    Q = std::max(0.0, Q);
     397
     398    if (frequency > 0 && frequency < 1) {
     399        if (Q > 0) {
     400            double w0 = piDouble * frequency;
     401            double alpha = sin(w0) / (2 * Q);
     402            double k = cos(w0);
     403
     404            double b0 = 1 - alpha;
     405            double b1 = -2 * k;
     406            double b2 = 1 + alpha;
     407            double a0 = 1 + alpha;
     408            double a1 = -2 * k;
     409            double a2 = 1 - alpha;
     410
     411            setNormalizedCoefficients(b0, b1, b2, a0, a1, a2);
     412        } else {
     413            // When Q = 0, the above formulas have problems. If we look at
     414            // the z-transform, we can see that the limit as Q->0 is -1, so
     415            // set the filter that way.
     416            setNormalizedCoefficients(-1, 0, 0,
     417                                      1, 0, 0);
     418        }
     419    } else {
     420        // When frequency is 0 or 1, the z-transform is 1.
     421        setNormalizedCoefficients(1, 0, 0,
     422                                  1, 0, 0);
     423    }
     424}
     425
     426void Biquad::setNotchParams(double frequency, double Q)
     427{
     428    // Clip frequencies to between 0 and 1, inclusive.
     429    frequency = std::max(0.0, std::min(frequency, 1.0));
     430
     431    // Don't let Q go negative, which causes an unstable filter.
     432    Q = std::max(0.0, Q);
     433
     434    if (frequency > 0 && frequency < 1) {
     435        if (Q > 0) {
     436            double w0 = piDouble * frequency;
     437            double alpha = sin(w0) / (2 * Q);
     438            double k = cos(w0);
     439
     440            double b0 = 1;
     441            double b1 = -2 * k;
     442            double b2 = 1;
     443            double a0 = 1 + alpha;
     444            double a1 = -2 * k;
     445            double a2 = 1 - alpha;
     446
     447            setNormalizedCoefficients(b0, b1, b2, a0, a1, a2);
     448        } else {
     449            // When Q = 0, the above formulas have problems. If we look at
     450            // the z-transform, we can see that the limit as Q->0 is 0, so
     451            // set the filter that way.
     452            setNormalizedCoefficients(0, 0, 0,
     453                                      1, 0, 0);
     454        }
     455    } else {
     456        // When frequency is 0 or 1, the z-transform is 1.
     457        setNormalizedCoefficients(1, 0, 0,
     458                                  1, 0, 0);
     459    }
     460}
     461
     462void Biquad::setBandpassParams(double frequency, double Q)
     463{
     464    // No negative frequencies allowed.
     465    frequency = std::max(0.0, frequency);
     466
     467    // Don't let Q go negative, which causes an unstable filter.
     468    Q = std::max(0.0, Q);
     469
     470    if (frequency > 0 && frequency < 1) {
     471        double w0 = piDouble * frequency;
     472        if (Q > 0) {
     473            double alpha = sin(w0) / (2 * Q);
     474            double k = cos(w0);
    254475   
    255     double b0 = A * (aPlusOne - aMinusOne * k + k2);
    256     double b1 = 2 * A * (aMinusOne - aPlusOne * k);
    257     double b2 = A * (aPlusOne - aMinusOne * k - k2);
    258     double a0 = aPlusOne + aMinusOne * k + k2;
    259     double a1 = -2 * (aMinusOne + aPlusOne * k);
    260     double a2 = aPlusOne + aMinusOne * k - k2;
    261 
    262     setNormalizedCoefficients(b0, b1, b2, a0, a1, a2);
    263 }
    264 
    265 void Biquad::setHighShelfParams(double frequency, double dbGain)
    266 {
    267     double w0 = piDouble * frequency;
    268 
    269     double A = pow(10.0, dbGain / 40);
    270     double S = 1; // filter slope (1 is max value)
    271     double alpha = 0.5 * sin(w0) * sqrt((A + 1 / A) * (1 / S - 1) + 2);
    272 
    273     double k = cos(w0);
    274     double k2 = 2 * sqrt(A) * alpha;
    275 
    276     double aPlusOne = A + 1;
    277     double aMinusOne = A - 1;
    278    
    279     double b0 = A * (aPlusOne + aMinusOne * k + k2);
    280     double b1 = -2 * A * (aMinusOne + aPlusOne * k);
    281     double b2 = A * (aPlusOne + aMinusOne * k - k2);
    282     double a0 = aPlusOne - aMinusOne * k + k2;
    283     double a1 = 2 * (aMinusOne - aPlusOne * k);
    284     double a2 = aPlusOne - aMinusOne * k - k2;
    285 
    286     setNormalizedCoefficients(b0, b1, b2, a0, a1, a2);
    287 }
    288 
    289 void Biquad::setPeakingParams(double frequency, double Q, double dbGain)
    290 {
    291     double w0 = piDouble * frequency;
    292     double alpha = sin(w0) / (2 * Q);
    293     double A = pow(10.0, dbGain / 40);
    294 
    295     double k = cos(w0);
    296 
    297     double b0 = 1 + alpha * A;
    298     double b1 = -2 * k;
    299     double b2 = 1 - alpha * A;
    300     double a0 = 1 + alpha / A;
    301     double a1 = -2 * k;
    302     double a2 = 1 - alpha / A;
    303 
    304     setNormalizedCoefficients(b0, b1, b2, a0, a1, a2);
    305 }
    306 
    307 void Biquad::setAllpassParams(double frequency, double Q)
    308 {
    309     double w0 = piDouble * frequency;
    310     double alpha = sin(w0) / (2 * Q);
    311 
    312     double k = cos(w0);
    313 
    314     double b0 = 1 - alpha;
    315     double b1 = -2 * k;
    316     double b2 = 1 + alpha;
    317     double a0 = 1 + alpha;
    318     double a1 = -2 * k;
    319     double a2 = 1 - alpha;
    320 
    321     setNormalizedCoefficients(b0, b1, b2, a0, a1, a2);
    322 }
    323 
    324 void Biquad::setNotchParams(double frequency, double Q)
    325 {
    326     double w0 = piDouble * frequency;
    327     double alpha = sin(w0) / (2 * Q);
    328 
    329     double k = cos(w0);
    330 
    331     double b0 = 1;
    332     double b1 = -2 * k;
    333     double b2 = 1;
    334     double a0 = 1 + alpha;
    335     double a1 = -2 * k;
    336     double a2 = 1 - alpha;
    337 
    338     setNormalizedCoefficients(b0, b1, b2, a0, a1, a2);
    339 }
    340 
    341 void Biquad::setBandpassParams(double frequency, double Q)
    342 {
    343     double w0 = piDouble * frequency;
    344     double alpha = sin(w0) / (2 * Q);
    345 
    346     double k = cos(w0);
    347    
    348     double b0 = alpha;
    349     double b1 = 0;
    350     double b2 = -alpha;
    351     double a0 = 1 + alpha;
    352     double a1 = -2 * k;
    353     double a2 = 1 - alpha;
    354 
    355     setNormalizedCoefficients(b0, b1, b2, a0, a1, a2);
     476            double b0 = alpha;
     477            double b1 = 0;
     478            double b2 = -alpha;
     479            double a0 = 1 + alpha;
     480            double a1 = -2 * k;
     481            double a2 = 1 - alpha;
     482
     483            setNormalizedCoefficients(b0, b1, b2, a0, a1, a2);
     484        } else {
     485            // When Q = 0, the above formulas have problems. If we look at
     486            // the z-transform, we can see that the limit as Q->0 is 1, so
     487            // set the filter that way.
     488            setNormalizedCoefficients(1, 0, 0,
     489                                      1, 0, 0);
     490        }
     491    } else {
     492        // When the cutoff is zero, the z-transform approaches 0, if Q
     493        // > 0. When both Q and cutoff are zero, the z-transform is
     494        // pretty much undefined. What should we do in this case?
     495        // For now, just make the filter 0. When the cutoff is 1, the
     496        // z-transform also approaches 0.
     497        setNormalizedCoefficients(0, 0, 0,
     498                                  1, 0, 0);
     499    }
    356500}
    357501
Note: See TracChangeset for help on using the changeset viewer.