I have developed a service for simulating hearing impairment based on an audiogram.
However, I've encountered a problem: I can only process the entire audio signal without distortions.
Unfortunately, due to insufficient RAM when performing direct FFT, I cannot handle a sufficiently long audio signal in one go. While dividing the audio into parts seems like a solution, I face distortions when doing so.
I present three scenarios on Google Disk:
- The original audio (
original_video.webm
). - The entire processed audio at once (Please increase the volume if you can't hear it).
Contains frequencies before 1_000 Hz (the desired result) (handled_video_without_chunking.webm
). - The same audio with a "hammering" effect, where the repeatability of that effect depends on the chunk size. (
handled_video_with_chunking.webm
)
My goal is to eliminate the "thumping" effect and achieve a result as if the sound was processed as a whole at once, rather than in parts.
I also attach Java code that processes the audio:
Processing the entire signal at once (This is the working code):
private float[] processWithoutChunking(AuditoryGraph auditoryGraph) {
float[] audioSamples = convertAudioBytesToAudioSamples(this.audioBytes);
FloatFFT_1D fft = new FloatFFT_1D(audioSamples.length);
fft.realForward(audioSamples);
for (int i = 0; i < audioSamples.length; i++) {
float frequency = (float) i * sampleRate / audioSamples.length;
AuditoryPoint point = auditoryGraph.getPointAt(frequency);
float attenuation = point.getAttenuation();
audioSamples[i] *= attenuation;
}
fft.realInverse(audioSamples, true);
return audioSamples;
}
Processing the signal with chunking (This code creates a hammering effect):
private float[] processWithChunking(AuditoryGraph auditoryGraph) {
float[] audioSamples = convertAudioBytesToAudioSamples(this.audioBytes);
final int chunkSize = (int) (sampleRate);
List<float[]> chunkedSamples = new ArrayList<>();
for (int i = 0; i < audioSamples.length; i += chunkSize) {
float[] chunk = new float[Math.min(chunkSize, audioSamples.length - i)];
System.arraycopy(audioSamples, i, chunk, 0, chunk.length);
chunkedSamples.add(chunk);
}
for (float[] chunk : chunkedSamples) {
FloatFFT_1D fft = new FloatFFT_1D(chunk.length);
fft.realForward(chunk);
for (int i = 0; i < chunk.length; i++) {
float frequency = (float) i * sampleRate / chunk.length;
AuditoryPoint point = auditoryGraph.getPointAt(frequency);
float attenuation = point.getAttenuation();
chunk[i] *= attenuation;
}
fft.realInverse(chunk, true);
}
float[] handledAudioSamples = concatChunkListToArray(chunkedSamples);
return handledAudioSamples;
}
I consulted ChatGPT for advice, and it suggested using a Hanning window with overlaps. However, ChatGPT couldn't provide a working implementation, and since I have limited understanding in audio processing, I would like to know a practical approach in words on how to achieve this and whether it's possible to generate audio without distortions.