package audio.processing.eq; import triana.types.audio.AudioEffect16Bit; /** * A Comb-delay Effect which allows user to add a delayed signal onto the original by creating a feedforward and * feedback Comb Filter based on a building block of a reverb device. The user can set the attenuation the level of the * delayed signal and also adjust the delay time by adjusting the settings using the GUI.CombDelayEffect exends * AudioEffect 16bit to allow for chunked data. * * @author Eddie Al-Shakarchi * @version $Revision: 4052 $ * @see triana.audio.processing.delay.HighPass * @see triana.audio.AudioEffect16Bit */ public class BandPassEffect extends AudioEffect16Bit { double frequencyPeak; int delaySamples; double delayedInput; double delayedInput2; double delayedOutput; double delayedOutput2; boolean chunkedData; float samplingFrequency; float q; float dbGainLevel; // Create buffer arrays short[] output; short[] outputData; /** * Creates an comb filter delay effect with a particular forward memory size. * * @param forwardMemSize the size of the forward memory to be used by AudioEffect16bit class. * @param feedback the level of attenutation of the delayed signal in the the feedback comb filter. Should be * divided by 100 to give value in percent. * @param delayOffset the size of the delay in samples. Used to calculate appropriate buffer size * @param filterType sets which type of filter to use. Can be Standard Feedback Filter or Feedback Loop Only * @param chunked boolean value to indicate if the user is using chunked data. Used by process method to * choose suitable method. */ public BandPassEffect(int forwardMemSize, float frequency, boolean chunked, float delayInMs, float sampleRate, float Q, float dbGain) { super(forwardMemSize); samplingFrequency = sampleRate; setFrequency(frequency); setDelayOffset(delayInMs); setChunked(chunked); setQ(Q); setDBGain(dbGain); } /** * Sets the feedback factor. To be specific this sets how loud the delayed signal will be. * * @param feed the level of feedback. This value must be divided by 100 in order to be used, as the feedback factor * must be less than 1. */ public void setFrequency(float freq) { frequencyPeak = freq; System.out.println("freq = " + freq); } /** * Sets the delay size in samples that the delayed signal will be delayed by * * @param offset this is the size of the delay in samples. This is used to set the forward buffer when using chunks, * and also to calculate the size of the final output array. */ public void setDelayOffset(float offset) { //delaySamples = ((int)(offset * (float)samplingFrequency / (float)1000)); delaySamples = 1; System.out.println("delaySamples = " + delaySamples); } /** * Sets the delay size in samples that the delayed signal will be delayed by * * @param offset this is the size of the delay in samples. This is used to set the forward buffer when using chunks, * and also to calculate the size of the final output array. */ public void setQ(float qValue) { //delaySamples = ((int)(offset * (float)samplingFrequency / (float)1000)); q = qValue; } /** * Sets the delay size in samples that the delayed signal will be delayed by * * @param qValue this is the size of the delay in samples. This is used to set the forward buffer when using chunks, * and also to calculate the size of the final output array. */ public void setDBGain(float gain) { //delaySamples = ((int)(offset * (float)samplingFrequency / (float)1000)); dbGainLevel = gain; } /** * Determines whether chunked data is being used or not. * * @param chunk returns true if the user has indicated that chunked data is being used. */ public void setChunked(boolean chunk) { chunkedData = chunk; } /** * Process method calls appropriate combProcess or combProcessChunked method depending on the users decision to use or * not chunked data. * * @param input short array containing the input data to be passed on to respective method */ public short[] process(short[] input) { if (chunkedData == false) { outputData = bandPassProcess(input); System.out.println("chunked off"); } else { outputData = bandPassProcessChunked(input); System.out.println("chunked on"); } return outputData; } /** * Method for non chunked data. Creates a delayed signal after the original data, and also acts as a feedback delay * creating further delayed signals Works in 32bit (ints) to avoid any (unintentional) clipping from using shorts. * * @param input short array containing the input data to be manipulated by algorithm */ public short[] bandPassProcess(short input[]) { short[] output = new short[input.length + delaySamples]; double outputSample = 0; double Fs = samplingFrequency; double f0 = (double) frequencyPeak; double Q = q; System.out.println("Q = " + Q); double w0 = 2 * (Math.PI * f0) / Fs; System.out.println("w0 = " + w0); double alpha = Math.sin(w0) / (2 * Q); System.out.println("alpha = " + alpha); // Computer the coefficients for the filter double b0 = alpha; double b1 = 0; double b2 = -alpha; double a0 = 1 + alpha; double a1 = -2 * (Math.cos(w0)); double a2 = 1 - alpha; // For each sample in the output array for (int n = 0; n < output.length; n++) { // delayedPosition is only set once n is large enough int delayedPosition = n - delaySamples; int delayedPosition2 = n - 2; if (delayedPosition < 0) { delayedInput = 0; delayedOutput = 0; } else if (delayedPosition2 > 2) { delayedInput = input[delayedPosition]; // This is x[n-1] delayedOutput = output[delayedPosition]; // This is y[n-1] delayedInput2 = input[delayedPosition2]; // This is x[n-2] delayedOutput2 = output[delayedPosition2]; // This is y[n-2] } double inputSample = 0; if (n < input.length) { inputSample = input[n]; } // This is the algorithm to create a comb filter with a feedback loop. This represents an IIR // recursive delay comb filter. outputSample = ((b0 / a0) * inputSample) + ((b1 / a0) * delayedInput) + ((b2 / a0) * delayedInput2) - ((a1 / a0) * delayedOutput) - ((a2 / a0) * delayedOutput2); // Limits outputSample to max 16bit (short) value double limit = outputSample; if (limit > 32767) { limit = 32767; } else if (limit < -32767) { limit = -32767; } // Turns int back into short value in output array after manipulation and limiting output[n] = (short) limit; //System.out.println("output[n] = " + output[n]); }// End of for loop return output; } /** * Method for chunked data. Creates a delayed signal after the original data, and also acts as a feedback delay * creating further delayed signals Works in 32bit (ints) to avoid any (unintentional) clipping from using shorts. * * @param input short array containing the input data to be manipulated by algorithm */ public short[] bandPassProcessChunked(short input[]) { // inserts current data into array which comprises of current data + forwardBuffer // from the previous chunk super.preProcess(input); short[] forwardBuffer = new short[delaySamples]; short[] output = new short[input.length + delaySamples]; int outputSample = 0; // For each sample in the output array for (int n = 0; n < output.length; n++) { // delayedPosition is only set once n is large enough int delayedPosition = n - delaySamples; if (delayedPosition < 0) { delayedInput = 0; delayedOutput = 0; } else { delayedInput = input[delayedPosition]; delayedOutput = output[delayedPosition]; } short inputSample = 0; if (n < input.length) { inputSample = input[n]; } // Limits outputSample to max 16bit (short) value int limit = outputSample; if (limit > 32767) { limit = 32767; } else if (limit < -32767) { limit = -32767; } // Turns int back into short value in output array after manipulation and limiting output[n] = (short) limit; }// End of for loop // Copies the echoes into the forward buffer, to be added next time by the preProcess method System.arraycopy(output, input.length, forwardBuffer, 0, forwardBuffer.length); try { super.initialiseForward(forwardBuffer); // this will set up the forward data so that it gets added to the 'allData' in AudioEffect16bit // next time preProcess is called } catch (Exception ee) { ee.printStackTrace(); } return output; } }