package com.saq_hei.app; /** * LUHE */ public class ClassSAQ_Filter { ; int outPointer; int inPointer; // Simulation der Eingabe // Funktion erhält die werte (zyklisch und berechnet dieAusgabe in einer geringeren Bitrate (4 mal kleiner) private int NotificationPeriod = (44100 / 32); int inputBuffer_length = NotificationPeriod; int framesPerBuffer = NotificationPeriod; short[] inputBuffer = new short[inputBuffer_length]; // decimation factor = R = 4 int outputBuffer_length = inputBuffer_length; short[] outputBuffer = new short[outputBuffer_length]; Coeffs coeffs = new Coeffs(); /////////////////////////////////////////////////////////////////////////////////////////// // FIR coefficients for decimation/interpolation filters // // Designed with ScopeFIR v3.7a // // Design input: // Filter type = Lowpass // Fs = 44100 Hz // Number of taps = 64 // Grid = 512 // Passband upper = 3000 Hz // Stopband lower = 5512.5 Hz // Passband ripple = 0.16 dB // Stopband attenuation = 90 dB // // Result: // Passband ripple = 0.16 dB // Stopband attenuation = 90.003 dB // // Note! // ScopeFIR's output has a fixed number of decimals so the // coefficients were multiplied by 1.0e8 before exporting // in order to get more valid digits; Don't forget to // compensate for this 160 dB (!) "gain"! public ClassSAQ_Filter() { } int FIR_LEN_CW300H = 428; // LUHE int FIR_LARGEST_LEN = FIR_LEN_CW300H; // *VERY* IMPORTANT! LUHE // double AudioCallback_rLocalOscPhAcc; // int AudioCallback_iFftBufIdxIn = 0; // int AudioCallback_ui44kSampleCounter = 0; // int AudioCallback_iDecimatorIdxIn = 0; // double[] AudioCallback_rDecimatorQueueI = new double[64]; // double[] AudioCallback_rDecimatorQueueQ = new double[64]; // int AudioCallback_iMainFilterIdxIn = 0; // double[] AudioCallback_rMainFilterQueueI = new double[FIR_LARGEST_LEN]; // double[] AudioCallback_rMainFilterQueueQ = new double[FIR_LARGEST_LEN]; // int AudioCallback_iInterpolatorIdxIn = 0; // double[] AudioCallback_rInterpolatorQueueI = new double[16]; // double[] AudioCallback_rInterpolatorQueueQ = new double[16]; /*------------------------------------------------------------------------------ * * Audio stream function * * All audio processing is done in this function! * * */ // public short[] AudioConverte017_8(short[] inputBuffer_1) // { if (inputBuffer_1 != null) { System.arraycopy(inputBuffer_1, 0, inputBuffer, 0, inputBuffer_length); // int inputBuffer_length_= inputBuffer_1.length; int frameCnt; // Zeiger auf ein Element outPointer = 0; // Zeiger auf ein element inPointer = 0; // double leftInput; double rightInput; double rI44k; double rQ44k; double rI11k; double rQ11k; double rTmp; double re; double im; int i; if (inputBuffer == null) { return outputBuffer; } // Read input buffer, process data, and fill output buffer. for (frameCnt = 0; frameCnt < inputBuffer_length/2-2; frameCnt++) { // Get interleaved soundcard samples from input buffer in pointer two times increased // leftInput = inPointer++; leftInput = inputBuffer[inPointer++]; // rightInput = inPointer++; // right channel not used but we must pick it anyway rightInput = inputBuffer[inPointer++]; // Luhe, wieso mächster Wert in rechten Kanal // Check peak signal level rTmp = leftInput; if (rTmp < 0) // faster than calling abs()? { rTmp = -rTmp; } if (rTmp > 0.891251) // 1 dB below full-scale (1.0) { coeffs.q_OverLoad = 1; // Luhe Überlast, 2000 ? nicht ausgewertet } // Run local oscillator (NCO) AudioCallback_rLocalOscPhAcc += coeffs.g_rLoPhInc; if (AudioCallback_rLocalOscPhAcc > (2.0 * coeffs.C_PI)) { AudioCallback_rLocalOscPhAcc -= (2.0 * coeffs.C_PI); } // Half complex mixer rI44k = leftInput * Math.cos(AudioCallback_rLocalOscPhAcc); rQ44k = leftInput * Math.sin(AudioCallback_rLocalOscPhAcc); ///######################################## // FAKED SIGNAL FOR FFT Y SCALING TESTS: // leftInput = cos (rLocalOscPhAcc); // amplitude 1.0 ///######################################## // Decimation filter, R = 4, nTaps = 64 (hardcoded... ;-) if (--AudioCallback_iDecimatorIdxIn < 0) // fill buffer backwards (that convolution stuff...) { AudioCallback_iDecimatorIdxIn = 63; // (well, the filter is symmetrical, but anyway...) } // Half Complex mixer AudioCallback_rDecimatorQueueI[AudioCallback_iDecimatorIdxIn] = rI44k; AudioCallback_rDecimatorQueueQ[AudioCallback_iDecimatorIdxIn] = rQ44k; // Decimate Fs and do the heavy stuff at lower speed /////////////////////////////////////////////////////////////////////////// if (++AudioCallback_ui44kSampleCounter >= 4) // decimation factor = R = 4 { AudioCallback_ui44kSampleCounter = 0; // Fs = 11025 Hz code goes here, first decimation filter: rI11k = rQ11k = 0.0F; // clear MAC accumulators for (i = 0; i < 64; i++) { rTmp = coeffs.rDecIntCoeffs[i]; rI11k += AudioCallback_rDecimatorQueueI[AudioCallback_iDecimatorIdxIn] * rTmp; rQ11k += AudioCallback_rDecimatorQueueQ[AudioCallback_iDecimatorIdxIn] * rTmp; if (++AudioCallback_iDecimatorIdxIn >= 64) { AudioCallback_iDecimatorIdxIn = 0; } } rI11k *= 1.0e-8; // normalize (FIR gain ~10^8 !!!) rQ11k *= 1.0e-8; // rI11k and rQ11k now contains the downsampled complex signal // ready for processing at Fs = 11025 Hz. Passband = +/-3000Hz // with transition bands out to +/-5512.5Hz (stopband). // Main selectivity filters coeffs.g_iFilterBW=33; if ( coeffs.g_iFilterBW == 33) // 0 // coeffs.g_iFilterBW == ) // 0) // NARROW? { if (--AudioCallback_iMainFilterIdxIn < 0) { AudioCallback_iMainFilterIdxIn = FIR_LEN_CW300H - 1; } AudioCallback_rMainFilterQueueI[AudioCallback_iMainFilterIdxIn] = rI11k; AudioCallback_rMainFilterQueueQ[AudioCallback_iMainFilterIdxIn] = rQ11k; rI11k = rQ11k = 0.0F; // clear MAC accumulators for (i = 0; i < FIR_LEN_CW300H; i++) { rI11k += AudioCallback_rMainFilterQueueI[AudioCallback_iMainFilterIdxIn] * coeffs.rCw300HCoeffs_I[i]; rQ11k += AudioCallback_rMainFilterQueueQ[AudioCallback_iMainFilterIdxIn] * coeffs.rCw300HCoeffs_Q[i]; if (++AudioCallback_iMainFilterIdxIn >= FIR_LEN_CW300H) { AudioCallback_iMainFilterIdxIn = 0; } } rI11k *= 1.0e-8; // normalize (FIR gain ~10^8 !!!) rQ11k *= 1.0e-8; } //////////////////////////////////////////////////////////////// /* else { if (coeffs.g_iFilterBW == 1) // MEDIUM? { if (--AudioCallback_iMainFilterIdxIn < 0) { AudioCallback_iMainFilterIdxIn = coeffs.FIR_LEN_CW1K - 1; // <= forgot -1... whattanuglybug! } AudioCallback_rMainFilterQueueI[AudioCallback_iMainFilterIdxIn] = rI11k; AudioCallback_rMainFilterQueueQ[AudioCallback_iMainFilterIdxIn] = rQ11k; rI11k = rQ11k = 0.0F; // clear MAC accumulators for (i = 0; i < coeffs.FIR_LEN_CW1K; i++) { rI11k += AudioCallback_rMainFilterQueueI[AudioCallback_iMainFilterIdxIn] * coeffs.rCw1kCoeffs_I[i]; rQ11k += AudioCallback_rMainFilterQueueQ[AudioCallback_iMainFilterIdxIn] * coeffs.rCw1kCoeffs_Q[i]; if (++AudioCallback_iMainFilterIdxIn >= coeffs.FIR_LEN_CW1K) { AudioCallback_iMainFilterIdxIn = 0; } } rI11k *= 1.0e-8; // normalize (FIR gain ~10^8 !!!) rQ11k *= 1.0e-8; } else // g_iFilterBW == 2 => WIDE! { if (--AudioCallback_iMainFilterIdxIn < 0) { AudioCallback_iMainFilterIdxIn = coeffs.FIR_LEN_SSB2K4 - 1; } AudioCallback_rMainFilterQueueI[AudioCallback_iMainFilterIdxIn] = rI11k; AudioCallback_rMainFilterQueueQ[AudioCallback_iMainFilterIdxIn] = rQ11k; rI11k = rQ11k = 0.0F; // clear MAC accumulators for (i = 0; i < coeffs.FIR_LEN_SSB2K4; i++) { rI11k += AudioCallback_rMainFilterQueueI[AudioCallback_iMainFilterIdxIn] * coeffs.rSsb2k4Coeffs_I[i]; rQ11k += AudioCallback_rMainFilterQueueQ[AudioCallback_iMainFilterIdxIn] * coeffs.rSsb2k4Coeffs_Q[i]; if (++AudioCallback_iMainFilterIdxIn >= coeffs.FIR_LEN_SSB2K4) { AudioCallback_iMainFilterIdxIn = 0; } } rI11k *= 1.0e-8; // normalize (FIR gain ~10^8 !!!) rQ11k *= 1.0e-8; } } */ // "Summing point" of the "Phasing Method Receiver" rI11k += rQ11k; // select USB (subtract for LSB) rQ11k = rI11k; // same signal to both ears (mono output) // Feed a stereo 11k sample to the interpolation filter // (two separate channels are used in case we want // stereo output in the future for binaural I/Q etc.) AudioCallback_iInterpolatorIdxIn--; // backward input, remember that convolution integral... if (AudioCallback_iInterpolatorIdxIn < 0) { AudioCallback_iInterpolatorIdxIn = 15; } AudioCallback_rInterpolatorQueueI[AudioCallback_iInterpolatorIdxIn] = rI11k; AudioCallback_rInterpolatorQueueQ[AudioCallback_iInterpolatorIdxIn] = rQ11k; } // end of if (++ui44kSampleCounter >= 4) //////////////////// // Now we're back in Fs = 44100 Hz business again // Run interpolation filter at Fs = 44k1 // // Note! The interpolator uses the same 64-tap FIR as the decimator but // since 3 out of 4 input samples are zero, only 16 MAC operations need // to be computed. This is the reason why the interpolator buffer size // is only 16. The coefficient set is split into 4 groups (mentally...) // and the filter computation is cycling through these groups, one // group per 44k1 sample: // // 44k1 sample instant 0 (ui44kSampleCounter == 0): // - Put new 11k sample into the interpolator queue // - Compute the filter using coefficients 0, 4, 8 ... 56, 60 // // 44k1 sample instant 1 (ui44kSampleCounter == 1): // - Compute the filter using coefficients 1, 5, 9 ... 57, 61 // // 44k1 sample instant 2 (ui44kSampleCounter == 2): // - Compute the filter using coefficients 2, 6, 10 ... 58, 62 // // 44k1 sample instant 3 (ui44kSampleCounter == 3): // - Compute the filter using coefficients 3, 7, 11 ... 59, 63 // // 44k1 sample instant 4: (now it starts over again!) // - Put new 11k sample into the interpolator queue // - Compute the filter using coefficients 0, 4, 8 ... 56, 60 // // etc. etc. //////////////////////////////////////////////////////////////////////// rI44k = rQ44k = 0.0F; // clear MAC accumulators i = AudioCallback_ui44kSampleCounter; // 0..3, 0 just after a new value has been inserted do // loop from newest to oldest sample (they are stored "backwards") { rTmp = coeffs.rDecIntCoeffs[i]; rI44k += AudioCallback_rInterpolatorQueueI[AudioCallback_iInterpolatorIdxIn] * rTmp; rQ44k += AudioCallback_rInterpolatorQueueQ[AudioCallback_iInterpolatorIdxIn] * rTmp; if (++AudioCallback_iInterpolatorIdxIn >= 16) { AudioCallback_iInterpolatorIdxIn = 0; } i += 4; } while (i < 64); // last i == 60, 61, 62 or 63 depending on coeff' group used /////////////////////////////////////////////////// rI44k *= 4.0e-8; // normalize (FIR gain ~10^8 !!!) rQ44k *= 4.0e-8; // Write interleaved samples to output buffer ////////////////////////////////////////////////////////////////////////////// outputBuffer[outPointer++] = (short) (rI44k * coeffs.g_rOutputGainFactor); // left outputBuffer[outPointer++] = (short) (rQ44k * coeffs.g_rOutputGainFactor); // right // Do FFT of original input signal for the full spectrum view AudioCallback_iFftBufIdxIn &= (coeffs.FFT_SIZE_IN - 1); coeffs.g_fltFftBufRe[AudioCallback_iFftBufIdxIn++] = leftInput; //rI44k leftInput /////////////////////////////////////////////////////////////////////////////////////// // time to do the next FFT but here not used: } // end sample picking for loop } //luhe ///// System.arraycopy(inputBuffer_1, 0, outputBuffer, 0, inputBuffer_length); return outputBuffer; // } }