Like Shazam - but much shitter
The Nyquist Theorem states that in order to adequately reproduce a signal it should be periodically sampled at a rate that is 2X the highest frequency you wish to record
sample rate = 16000
FFT size = 1024
bin count = 1024/2 = 512
bin size = 8000 / 512 = 15.625
bin size = 16000/1024 = 15.625
frequency = 4000
bin index = 4000/15.625 = 256
Example
https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API
https://www.w3.org/TR/webaudio/#ModularRouting
const findTopBins = frequencyData => {
const binValues = Array.from(frequencyData)
const zipped = binValues.map((binValue, index) => ({ binValue, index }))
return zipped.sort((a, b) => b.binValue - a.binValue)
}
const DURATION = 1
const SAMPLE_RATE = 44100
const FFT_SIZE = 1024
const options = {
length: DURATION * SAMPLE_RATE,
sampleRate: SAMPLE_RATE
}
const audioContext = new OfflineAudioContext(options)
const frequency = 440
const source = new OscillatorNode(audioContext, { frequency })
const analyserNode = new AnalyserNode(audioContext, { fftSize: FFT_SIZE })
source.connect(analyserNode)
analyserNode.connect(audioContext.destination)
source.start()
source.stop(DURATION)
await audioContext.startRendering()
const frequencyData = new Uint8Array(analyserNode.frequencyBinCount)
analyserNode.getByteFrequencyData(frequencyData)
const binSize = SAMPLE_RATE / FFT_SIZE
const topBinIndex = Math.round(frequency / binSize)
const topBins = findTopBins(frequencyData)
chai.expect(topBins[0].index).to.equal(topBinIndex)
https://www.ee.columbia.edu/~dpwe/papers/Wang03-shazam.pdf
| 12 bits | 12 bits | 8 bits |
|---|---|---|
| f1 | f2 | dt |
export const FREQUENCY_BANDS = [
0,
100,
200,
400,
800,
1600,
8000
]
const binSize = audioBuffer.sampleRate / C.FFT_SIZE
const binBands = R.aperture(2, C.FREQUENCY_BANDS.map(f =>
Math.round(f / binSize)))https://www.ee.columbia.edu/~dpwe/papers/Wang03-shazam.pdf
CREATE TABLE IF NOT EXISTS track_metadata (
id serial PRIMARY KEY,
album_title VARCHAR (128) NOT NULL,
track_title VARCHAR (128) NOT NULL,
artist VARCHAR (128) NOT NULL,
artwork VARCHAR (128) NOT NULL
);
CREATE TABLE IF NOT EXISTS track_hashes (
id serial PRIMARY KEY,
tuple int NOT NULL,
t1 int NOT NULL,
track_metadata_id INTEGER REFERENCES track_metadata(id)
);
CREATE INDEX IF NOT EXISTS track_hashes_tuple_idx
ON track_hashes USING HASH (tuple);
SELECT
track_hashes.track_metadata_id,
(track_hashes.t1 - samples.t1) AS offset,
COUNT (track_hashes.t1 - samples.t1) AS count
FROM track_hashes
INNER JOIN samples ON track_hashes.tuple = samples.tuple
GROUP BY
track_hashes.track_metadata_id,
(track_hashes.t1 - samples.t1)
HAVING COUNT (track_hashes.t1 - samples.t1) >= 50
ORDER BY count DESC
LIMIT 1
(everthing else is just fluff and boilerplate)