| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418 |
- #include "config.h"
- #include "nfcfilter.h"
- #include "alu.h"
- /* Near-field control filters are the basis for handling the near-field effect.
- * The near-field effect is a bass-boost present in the directional components
- * of a recorded signal, created as a result of the wavefront curvature (itself
- * a function of sound distance). Proper reproduction dictates this be
- * compensated for using a bass-cut given the playback speaker distance, to
- * avoid excessive bass in the playback.
- *
- * For real-time rendered audio, emulating the near-field effect based on the
- * sound source's distance, and subsequently compensating for it at output
- * based on the speaker distances, can create a more realistic perception of
- * sound distance beyond a simple 1/r attenuation.
- *
- * These filters do just that. Each one applies a low-shelf filter, created as
- * the combination of a bass-boost for a given sound source distance (near-
- * field emulation) along with a bass-cut for a given control/speaker distance
- * (near-field compensation).
- *
- * Note that it is necessary to apply a cut along with the boost, since the
- * boost alone is unstable in higher-order ambisonics as it causes an infinite
- * DC gain (even first-order ambisonics requires there to be no DC offset for
- * the boost to work). Consequently, ambisonics requires a control parameter to
- * be used to avoid an unstable boost-only filter. NFC-HOA defines this control
- * as a reference delay, calculated with:
- *
- * reference_delay = control_distance / speed_of_sound
- *
- * This means w0 (for input) or w1 (for output) should be set to:
- *
- * wN = 1 / (reference_delay * sample_rate)
- *
- * when dealing with NFC-HOA content. For FOA input content, which does not
- * specify a reference_delay variable, w0 should be set to 0 to apply only
- * near-field compensation for output. It's important that w1 be a finite,
- * positive, non-0 value or else the bass-boost will become unstable again.
- * Also, w0 should not be too large compared to w1, to avoid excessively loud
- * low frequencies.
- */
- static const float B[4][3] = {
- { 0.0f },
- { 1.0f },
- { 3.0f, 3.0f },
- { 3.6778f, 6.4595f, 2.3222f },
- /*{ 4.2076f, 11.4877f, 5.7924f, 9.1401f }*/
- };
- void NfcFilterCreate1(NfcFilter *nfc, const float w0, const float w1)
- {
- float b_00, g_0;
- float r;
- memset(nfc, 0, sizeof(*nfc));
- nfc->g = 1.0f;
- nfc->coeffs[0] = 1.0f;
- /* Calculate bass-boost coefficients. */
- r = 0.5f * w0;
- b_00 = B[1][0] * r;
- g_0 = 1.0f + b_00;
- nfc->coeffs[0] *= g_0;
- nfc->coeffs[1] = (2.0f * b_00) / g_0;
- /* Calculate bass-cut coefficients. */
- r = 0.5f * w1;
- b_00 = B[1][0] * r;
- g_0 = 1.0f + b_00;
- nfc->g /= g_0;
- nfc->coeffs[0] /= g_0;
- nfc->coeffs[1+1] = (2.0f * b_00) / g_0;
- }
- void NfcFilterAdjust1(NfcFilter *nfc, const float w0)
- {
- float b_00, g_0;
- float r;
- r = 0.5f * w0;
- b_00 = B[1][0] * r;
- g_0 = 1.0f + b_00;
- nfc->coeffs[0] = nfc->g * g_0;
- nfc->coeffs[1] = (2.0f * b_00) / g_0;
- }
- void NfcFilterUpdate1(NfcFilter *nfc, ALfloat *restrict dst, const float *restrict src, const int count)
- {
- const float b0 = nfc->coeffs[0];
- const float a0 = nfc->coeffs[1];
- const float a1 = nfc->coeffs[2];
- float z1 = nfc->history[0];
- int i;
- for(i = 0;i < count;i++)
- {
- float out = src[i] * b0;
- float y;
- y = out - (a1*z1);
- out = y + (a0*z1);
- z1 += y;
- dst[i] = out;
- }
- nfc->history[0] = z1;
- }
- void NfcFilterCreate2(NfcFilter *nfc, const float w0, const float w1)
- {
- float b_10, b_11, g_1;
- float r;
- memset(nfc, 0, sizeof(*nfc));
- nfc->g = 1.0f;
- nfc->coeffs[0] = 1.0f;
- /* Calculate bass-boost coefficients. */
- r = 0.5f * w0;
- b_10 = B[2][0] * r;
- b_11 = B[2][1] * r * r;
- g_1 = 1.0f + b_10 + b_11;
- nfc->coeffs[0] *= g_1;
- nfc->coeffs[1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1;
- nfc->coeffs[2] = (4.0f * b_11) / g_1;
- /* Calculate bass-cut coefficients. */
- r = 0.5f * w1;
- b_10 = B[2][0] * r;
- b_11 = B[2][1] * r * r;
- g_1 = 1.0f + b_10 + b_11;
- nfc->g /= g_1;
- nfc->coeffs[0] /= g_1;
- nfc->coeffs[2+1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1;
- nfc->coeffs[2+2] = (4.0f * b_11) / g_1;
- }
- void NfcFilterAdjust2(NfcFilter *nfc, const float w0)
- {
- float b_10, b_11, g_1;
- float r;
- r = 0.5f * w0;
- b_10 = B[2][0] * r;
- b_11 = B[2][1] * r * r;
- g_1 = 1.0f + b_10 + b_11;
- nfc->coeffs[0] = nfc->g * g_1;
- nfc->coeffs[1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1;
- nfc->coeffs[2] = (4.0f * b_11) / g_1;
- }
- void NfcFilterUpdate2(NfcFilter *nfc, ALfloat *restrict dst, const float *restrict src, const int count)
- {
- const float b0 = nfc->coeffs[0];
- const float a00 = nfc->coeffs[1];
- const float a01 = nfc->coeffs[2];
- const float a10 = nfc->coeffs[3];
- const float a11 = nfc->coeffs[4];
- float z1 = nfc->history[0];
- float z2 = nfc->history[1];
- int i;
- for(i = 0;i < count;i++)
- {
- float out = src[i] * b0;
- float y;
- y = out - (a10*z1) - (a11*z2);
- out = y + (a00*z1) + (a01*z2);
- z2 += z1;
- z1 += y;
- dst[i] = out;
- }
- nfc->history[0] = z1;
- nfc->history[1] = z2;
- }
- void NfcFilterCreate3(NfcFilter *nfc, const float w0, const float w1)
- {
- float b_10, b_11, g_1;
- float b_00, g_0;
- float r;
- memset(nfc, 0, sizeof(*nfc));
- nfc->g = 1.0f;
- nfc->coeffs[0] = 1.0f;
- /* Calculate bass-boost coefficients. */
- r = 0.5f * w0;
- b_10 = B[3][0] * r;
- b_11 = B[3][1] * r * r;
- g_1 = 1.0f + b_10 + b_11;
- nfc->coeffs[0] *= g_1;
- nfc->coeffs[1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1;
- nfc->coeffs[2] = (4.0f * b_11) / g_1;
- b_00 = B[3][2] * r;
- g_0 = 1.0f + b_00;
- nfc->coeffs[0] *= g_0;
- nfc->coeffs[2+1] = (2.0f * b_00) / g_0;
- /* Calculate bass-cut coefficients. */
- r = 0.5f * w1;
- b_10 = B[3][0] * r;
- b_11 = B[3][1] * r * r;
- g_1 = 1.0f + b_10 + b_11;
- nfc->g /= g_1;
- nfc->coeffs[0] /= g_1;
- nfc->coeffs[3+1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1;
- nfc->coeffs[3+2] = (4.0f * b_11) / g_1;
-
- b_00 = B[3][2] * r;
- g_0 = 1.0f + b_00;
- nfc->g /= g_0;
- nfc->coeffs[0] /= g_0;
- nfc->coeffs[3+2+1] = (2.0f * b_00) / g_0;
- }
- void NfcFilterAdjust3(NfcFilter *nfc, const float w0)
- {
- float b_10, b_11, g_1;
- float b_00, g_0;
- float r;
- r = 0.5f * w0;
- b_10 = B[3][0] * r;
- b_11 = B[3][1] * r * r;
- g_1 = 1.0f + b_10 + b_11;
- nfc->coeffs[0] = nfc->g * g_1;
- nfc->coeffs[1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1;
- nfc->coeffs[2] = (4.0f * b_11) / g_1;
- b_00 = B[3][2] * r;
- g_0 = 1.0f + b_00;
- nfc->coeffs[0] *= g_0;
- nfc->coeffs[2+1] = (2.0f * b_00) / g_0;
- }
- void NfcFilterUpdate3(NfcFilter *nfc, ALfloat *restrict dst, const float *restrict src, const int count)
- {
- const float b0 = nfc->coeffs[0];
- const float a00 = nfc->coeffs[1];
- const float a01 = nfc->coeffs[2];
- const float a02 = nfc->coeffs[3];
- const float a10 = nfc->coeffs[4];
- const float a11 = nfc->coeffs[5];
- const float a12 = nfc->coeffs[6];
- float z1 = nfc->history[0];
- float z2 = nfc->history[1];
- float z3 = nfc->history[2];
- int i;
- for(i = 0;i < count;i++)
- {
- float out = src[i] * b0;
- float y;
- y = out - (a10*z1) - (a11*z2);
- out = y + (a00*z1) + (a01*z2);
- z2 += z1;
- z1 += y;
- y = out - (a12*z3);
- out = y + (a02*z3);
- z3 += y;
- dst[i] = out;
- }
- nfc->history[0] = z1;
- nfc->history[1] = z2;
- nfc->history[2] = z3;
- }
- #if 0 /* Original methods the above are derived from. */
- static void NfcFilterCreate(NfcFilter *nfc, const ALsizei order, const float src_dist, const float ctl_dist, const float rate)
- {
- static const float B[4][5] = {
- { },
- { 1.0f },
- { 3.0f, 3.0f },
- { 3.6778f, 6.4595f, 2.3222f },
- { 4.2076f, 11.4877f, 5.7924f, 9.1401f }
- };
- float w0 = SPEEDOFSOUNDMETRESPERSEC / (src_dist * rate);
- float w1 = SPEEDOFSOUNDMETRESPERSEC / (ctl_dist * rate);
- ALsizei i;
- float r;
- nfc->g = 1.0f;
- nfc->coeffs[0] = 1.0f;
- /* NOTE: Slight adjustment from the literature to raise the center
- * frequency a bit (0.5 -> 1.0).
- */
- r = 1.0f * w0;
- for(i = 0; i < (order-1);i += 2)
- {
- float b_10 = B[order][i ] * r;
- float b_11 = B[order][i+1] * r * r;
- float g_1 = 1.0f + b_10 + b_11;
- nfc->b[i] = b_10;
- nfc->b[i + 1] = b_11;
- nfc->coeffs[0] *= g_1;
- nfc->coeffs[i+1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1;
- nfc->coeffs[i+2] = (4.0f * b_11) / g_1;
- }
- if(i < order)
- {
- float b_00 = B[order][i] * r;
- float g_0 = 1.0f + b_00;
- nfc->b[i] = b_00;
- nfc->coeffs[0] *= g_0;
- nfc->coeffs[i+1] = (2.0f * b_00) / g_0;
- }
- r = 1.0f * w1;
- for(i = 0;i < (order-1);i += 2)
- {
- float b_10 = B[order][i ] * r;
- float b_11 = B[order][i+1] * r * r;
- float g_1 = 1.0f + b_10 + b_11;
- nfc->g /= g_1;
- nfc->coeffs[0] /= g_1;
- nfc->coeffs[order+i+1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1;
- nfc->coeffs[order+i+2] = (4.0f * b_11) / g_1;
- }
- if(i < order)
- {
- float b_00 = B[order][i] * r;
- float g_0 = 1.0f + b_00;
- nfc->g /= g_0;
- nfc->coeffs[0] /= g_0;
- nfc->coeffs[order+i+1] = (2.0f * b_00) / g_0;
- }
- for(i = 0; i < MAX_AMBI_ORDER; i++)
- nfc->history[i] = 0.0f;
- }
- static void NfcFilterAdjust(NfcFilter *nfc, const float distance)
- {
- int i;
- nfc->coeffs[0] = nfc->g;
- for(i = 0;i < (nfc->order-1);i += 2)
- {
- float b_10 = nfc->b[i] / distance;
- float b_11 = nfc->b[i+1] / (distance * distance);
- float g_1 = 1.0f + b_10 + b_11;
- nfc->coeffs[0] *= g_1;
- nfc->coeffs[i+1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1;
- nfc->coeffs[i+2] = (4.0f * b_11) / g_1;
- }
- if(i < nfc->order)
- {
- float b_00 = nfc->b[i] / distance;
- float g_0 = 1.0f + b_00;
- nfc->coeffs[0] *= g_0;
- nfc->coeffs[i+1] = (2.0f * b_00) / g_0;
- }
- }
- static float NfcFilterUpdate(const float in, NfcFilter *nfc)
- {
- int i;
- float out = in * nfc->coeffs[0];
- for(i = 0;i < (nfc->order-1);i += 2)
- {
- float y = out - (nfc->coeffs[nfc->order+i+1] * nfc->history[i]) -
- (nfc->coeffs[nfc->order+i+2] * nfc->history[i+1]) + 1.0e-30f;
- out = y + (nfc->coeffs[i+1]*nfc->history[i]) + (nfc->coeffs[i+2]*nfc->history[i+1]);
- nfc->history[i+1] += nfc->history[i];
- nfc->history[i] += y;
- }
- if(i < nfc->order)
- {
- float y = out - (nfc->coeffs[nfc->order+i+1] * nfc->history[i]) + 1.0e-30f;
- out = y + (nfc->coeffs[i+1] * nfc->history[i]);
- nfc->history[i] += y;
- }
- return out;
- }
- #endif
|