Last active
May 14, 2024 09:57
-
-
Save Joehoel/edfa6667dc1f93d711f088212db7ab8b to your computer and use it in GitHub Desktop.
hoorsimulatie.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { FFT } from 'fft.js'; | |
import { decode } from 'wav-decoder'; | |
import { encode } from 'wav-encoder'; | |
import * as fs from 'fs'; | |
// Constants | |
const TWO_PI = 2 * Math.PI; | |
const SAMPLE_RATE = 44100; | |
const FFT_SIZE = 2048; | |
const HALF_FFT_SIZE = FFT_SIZE / 2; | |
const TAP_SIZE = HALF_FFT_SIZE; | |
// Frequency and smearing values | |
const FREQUENCIES = [125, 187, 250, 375, 500, 750, 1000, 1500, 2000, 3000, 4000, 6000, 8000, 12000, 16000]; | |
const SMEARING_VALUES = [0.4, 0.5, 0.9, 1.2, 1.5, 1.8, 2.0, 2.3, 3.1, 3.9]; | |
// Utility Functions | |
function createLowPassFilter(cutoffFrequency: number, size: number): Float64Array { | |
const filter = new Float64Array(size); | |
const M = size - 1; | |
let sum = 0; | |
for (let i = 0; i < size; i++) { | |
const x = i - M / 2; | |
if (x === 0) { | |
filter[i] = cutoffFrequency * TWO_PI; | |
} else { | |
filter[i] = Math.sin(cutoffFrequency * TWO_PI * x) / x; | |
} | |
filter[i] *= 0.42 - 0.5 * Math.cos(TWO_PI * i / M) + 0.08 * Math.cos(2 * TWO_PI * i / M); | |
sum += filter[i]; | |
} | |
for (let i = 0; i < size; i++) { | |
filter[i] /= sum; | |
} | |
return filter; | |
} | |
function applyFilter(fft: FFT, signal: Float64Array, filter: Float64Array): Float64Array { | |
const freqSignal = fft.createComplexArray(); | |
fft.realTransform(freqSignal, signal); | |
fft.complete(freqSignal); | |
for (let i = 0; i < filter.length; i++) { | |
freqSignal[i * 2] *= filter[i]; | |
freqSignal[i * 2 + 1] *= filter[i]; | |
} | |
const filteredSignal = fft.createComplexArray(); | |
fft.inverseTransform(filteredSignal, freqSignal); | |
return filteredSignal.subarray(0, signal.length); | |
} | |
function createSmearingFilter(size: number, bandwidth: number): Float64Array { | |
const filter = new Float64Array(size); | |
const halfSize = size / 2; | |
let sum = 0; | |
for (let i = 0; i < size; i++) { | |
const x = (i - halfSize) / bandwidth; | |
filter[i] = Math.exp(-Math.PI * x * x); | |
sum += filter[i]; | |
} | |
for (let i = 0; i < size; i++) { | |
filter[i] /= sum; | |
} | |
return filter; | |
} | |
async function processAudio(filePath: string, outputFilePath: string, ear: number) { | |
const buffer = fs.readFileSync(filePath); | |
const audioData = await decode(buffer); | |
const channelData = audioData.channelData[0]; // assuming mono audio | |
const fft = new FFT(FFT_SIZE); | |
const lowPassFilter = createLowPassFilter(60 / HALF_FFT_SIZE, TAP_SIZE); | |
const smearingFilter = createSmearingFilter(FFT_SIZE, SMEARING_VALUES[Math.floor(averagePTA / 10)] * HALF_FFT_SIZE / Math.log2(SAMPLE_RATE)); | |
const processedData = new Float64Array(channelData.length); | |
for (let i = 0; i < channelData.length; i += FFT_SIZE) { | |
const segment = channelData.subarray(i, i + FFT_SIZE); | |
const filteredSegment = applyFilter(fft, segment, lowPassFilter); | |
const smearedSegment = applyFilter(fft, filteredSegment, smearingFilter); | |
processedData.set(smearedSegment, i); | |
} | |
const encodedWav = await encode({ | |
sampleRate: SAMPLE_RATE, | |
channelData: [processedData] | |
}); | |
fs.writeFileSync(outputFilePath, Buffer.from(encodedWav)); | |
} | |
processAudio('input.wav', 'output.wav', 1).then(() => { | |
console.log('Processing completed.'); | |
}).catch(err => { | |
console.error('Error processing audio:', err); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment