package com.talixa.specan.dsp;
import java.io.IOException;
import com.talixa.audio.riff.exceptions.RiffFormatException;
import com.talixa.audio.wav.WaveFile;
import com.talixa.audio.wav.WaveGenerator;
import com.talixa.audio.wav.WaveReader;
import com.talixa.specan.fft.Complex;
import com.talixa.specan.fft.FFT;
// TODO What effect would dropping every other value have? How about adding new values between existing ones?
public class FrequencyTranslator {
/*
* To make a band pass filter:
* 1) Run FFT on input data
* 2) Zero all data above & below the pass range
* 3) DeFFT the data
* 4) Output
*/
private static final int FFT_LEN = 8192*8;
private static final int SHIFT_VALUE = FFT_LEN;
private static short[] shift(short[] data, int shift) {
int numberSamples = data.length;
short[] shiftedData = new short[numberSamples];
Complex[] amplitudeData = new Complex[FFT_LEN];
Complex[] frequencyData = new Complex[FFT_LEN];
for(int baseAddress = 0; (baseAddress)+FFT_LEN < numberSamples; baseAddress+=SHIFT_VALUE) {
// fill the data array
for (int offset = 0; offset < FFT_LEN; offset++) {
int desiredSampleIndex = (baseAddress)+offset;
short signedSample = data[desiredSampleIndex];
double sample = ((double) signedSample) / (Short.MAX_VALUE);
amplitudeData[offset] = new Complex(sample,0);
}
// run fft
frequencyData = FFT.fft(amplitudeData);
if (shift < 0) {
// downshift
// calculate new 0
int newZero = SharedDSPFunctions.getSampleNumberByFrequency(FFT_LEN, shift*-1);
// shift new zero to zero
for(int i = 0; i < FFT_LEN/2; ++i) {
Complex value;
if (i + newZero < FFT_LEN/2) {
value = frequencyData[i+newZero];
} else {
value = new Complex(0,0);
}
frequencyData[i] = value;
frequencyData[FFT_LEN-i-1] = value;
}
} else {
// calculate where to put the old 0
int oldZero = SharedDSPFunctions.getSampleNumberByFrequency(FFT_LEN, shift);
// upshift
for(int i = FFT_LEN/2-1; i > 0 ; --i) {
Complex value;
if (i - oldZero > 0) {
value = frequencyData[i-oldZero];
} else {
value = new Complex(0,0);
}
frequencyData[i] = value;
frequencyData[FFT_LEN-i-1] = value;
}
}
// undo fft
Complex[] filterFft = FFT.ifft(frequencyData);
// create new data array
for(int i = 0; i < FFT_LEN; ++i) {
shiftedData[baseAddress+i] = (short)(filterFft[i].re() * Short.MAX_VALUE);
}
}
return shiftedData;
}
public static short[] doDoubleShift(short[] data, int frequency) {
// single pass causes amplitude variations on constant carrier
// two passes, shifted 50% and averaged together works well
// first shift
short[] shifted1 = shift(data, frequency);
// offset 50% and shift again
short[] data2 = new short[data.length - FFT_LEN/2];
for(int i = 0; i+FFT_LEN/2 < data.length; ++i) {
data2[i] = data[i+FFT_LEN/2];
}
short[] shifted2 = shift(data2, frequency);
// line up data, and average values
short[] outputData = new short[shifted2.length];
for(int i = 0; i < outputData.length; ++i) {
outputData[i] = (short)((shifted1[i+FFT_LEN/2] + shifted2[i]) / 2);
}
// return shifted data
return outputData;
}
public static short[] shiftDown(short[] data, int frequency) {
return doDoubleShift(data, frequency * -1);
}
public static void shiftDown(short[] data, int frequency, String outputFile) throws IOException {
short[] shifted = shiftDown(data, frequency);
byte[] byteData = SharedDSPFunctions.shortArrayToByteArray(shifted);
WaveFile out = WaveGenerator.generateWaveFromRaw16bitPcm(byteData);
out.outputToFile(outputFile);
}
public static short[] shiftUp(short[] data, int frequency) {
return doDoubleShift(data, frequency);
}
public static void shiftUp(short[] data, int frequency, String outputFile) throws IOException {
short[] shifted = shiftUp(data, frequency);
byte[] byteData = SharedDSPFunctions.shortArrayToByteArray(shifted);
WaveFile out = WaveGenerator.generateWaveFromRaw16bitPcm(byteData);
out.outputToFile(outputFile);
}
public static void main(String args[]) {
String testFile = "/home/thomas/audio/1000Hz.wav";
String downFile = "/home/thomas/audio/900Hz.wav";
String upFile = "/home/thomas/audio/1100Hz.wav";
try {
WaveFile waveFile = WaveReader.readFromFile(testFile);
short[] data = SharedDSPFunctions.extractWaveFileData(waveFile);
shiftDown(data, 100, downFile);
shiftUp(data, 100, upFile);
} catch (IOException e) {
e.printStackTrace();
} catch (RiffFormatException e) {
e.printStackTrace();
}
}
}