audio-analysis

安装量: 54
排名: #13814

安装

npx skills add https://github.com/bbeierle12/skill-mcp-claude --skill audio-analysis

Audio Analysis

FFT, frequency extraction, and audio data analysis.

Quick Start import * as Tone from 'tone';

// Create analyzer const analyser = new Tone.Analyser('fft', 256); const player = new Tone.Player('/audio/music.mp3');

player.connect(analyser); player.toDestination();

// Get frequency data const frequencyData = analyser.getValue(); // Float32Array

Analyzer Types FFT Analyzer // FFT (Fast Fourier Transform) - frequency spectrum const fftAnalyser = new Tone.Analyser({ type: 'fft', size: 256, // Must be power of 2: 32, 64, 128, 256, 512, 1024, 2048 smoothing: 0.8 // 0-1, higher = smoother transitions });

// Returns Float32Array of dB values (typically -100 to 0) const fftData = fftAnalyser.getValue();

Waveform Analyzer // Waveform - time domain data const waveformAnalyser = new Tone.Analyser({ type: 'waveform', size: 1024 });

// Returns Float32Array of amplitude values (-1 to 1) const waveformData = waveformAnalyser.getValue();

Meter (Volume Level) // Meter - overall volume level const meter = new Tone.Meter({ smoothing: 0.9, normalRange: false // true for 0-1, false for dB });

player.connect(meter);

// Get current level const level = meter.getValue(); // dB or 0-1

FFT Size Impact Size Frequency Resolution Time Resolution Use Case 32 Low High Beat detection 128 Medium Medium General visualization 256 Good Good Balanced (default) 1024 High Low Detailed spectrum 2048 Very High Very Low Audio analysis tools Frequency Bands Manual Band Extraction const analyser = new Tone.Analyser('fft', 256);

function getFrequencyBands() { const data = analyser.getValue(); const binCount = data.length;

// Define frequency band ranges (approximate for 44.1kHz sample rate) // Each bin = (sampleRate / 2) / binCount Hz const bands = { sub: average(data, 0, Math.floor(binCount * 0.03)), // ~20-60 Hz bass: average(data, Math.floor(binCount * 0.03), Math.floor(binCount * 0.08)), // ~60-250 Hz lowMid: average(data, Math.floor(binCount * 0.08), Math.floor(binCount * 0.15)), // ~250-500 Hz mid: average(data, Math.floor(binCount * 0.15), Math.floor(binCount * 0.3)), // ~500-2000 Hz highMid: average(data, Math.floor(binCount * 0.3), Math.floor(binCount * 0.5)), // ~2000-4000 Hz high: average(data, Math.floor(binCount * 0.5), binCount) // ~4000+ Hz };

return bands; }

function average(data, start, end) { let sum = 0; for (let i = start; i < end; i++) { sum += data[i]; } return sum / (end - start); }

Normalized Band Values function getNormalizedBands() { const bands = getFrequencyBands();

// Convert dB to 0-1 range (assuming -100 to 0 dB range) const normalize = (db) => Math.max(0, Math.min(1, (db + 100) / 100));

return { sub: normalize(bands.sub), bass: normalize(bands.bass), lowMid: normalize(bands.lowMid), mid: normalize(bands.mid), highMid: normalize(bands.highMid), high: normalize(bands.high) }; }

Beat Detection Simple Peak Detection class BeatDetector { constructor(threshold = 0.7, decay = 0.98) { this.threshold = threshold; this.decay = decay; this.peak = 0; this.lastBeat = 0; this.minInterval = 200; // Minimum ms between beats }

detect(analyser) { const data = analyser.getValue();

// Focus on bass frequencies for beat detection
const bassEnergy = this.getBassEnergy(data);

// Decay the peak
this.peak *= this.decay;

// Update peak if higher
if (bassEnergy > this.peak) {
  this.peak = bassEnergy;
}

// Detect beat
const now = Date.now();
const threshold = this.peak * this.threshold;

if (bassEnergy > threshold && now - this.lastBeat > this.minInterval) {
  this.lastBeat = now;
  return true;
}

return false;

}

getBassEnergy(data) { // Average of low frequency bins let sum = 0; const bassRange = Math.floor(data.length * 0.1); for (let i = 0; i < bassRange; i++) { // Convert dB to linear and sum sum += Math.pow(10, data[i] / 20); } return sum / bassRange; } }

// Usage const beatDetector = new BeatDetector(); const analyser = new Tone.Analyser('fft', 256);

function update() { if (beatDetector.detect(analyser)) { console.log('Beat!'); // Trigger visual effect } requestAnimationFrame(update); }

Energy History Beat Detection class EnergyBeatDetector { constructor(historySize = 43, sensitivity = 1.3) { this.history = new Array(historySize).fill(0); this.sensitivity = sensitivity; this.historyIndex = 0; }

detect(analyser) { const data = analyser.getValue(); const currentEnergy = this.calculateEnergy(data);

// Calculate average energy from history
const avgEnergy = this.history.reduce((a, b) => a + b) / this.history.length;

// Update history
this.history[this.historyIndex] = currentEnergy;
this.historyIndex = (this.historyIndex + 1) % this.history.length;

// Beat if current energy exceeds average by sensitivity factor
return currentEnergy > avgEnergy * this.sensitivity;

}

calculateEnergy(data) { let energy = 0; for (let i = 0; i < data.length; i++) { const amplitude = Math.pow(10, data[i] / 20); energy += amplitude * amplitude; } return energy; } }

Amplitude Analysis RMS (Root Mean Square) function getRMS(analyser) { const waveform = analyser.getValue(); // Waveform analyzer

let sum = 0; for (let i = 0; i < waveform.length; i++) { sum += waveform[i] * waveform[i]; }

return Math.sqrt(sum / waveform.length); }

Peak Amplitude function getPeakAmplitude(analyser) { const waveform = analyser.getValue(); let peak = 0;

for (let i = 0; i < waveform.length; i++) { const abs = Math.abs(waveform[i]); if (abs > peak) peak = abs; }

return peak; }

Smoothing Techniques Exponential Smoothing class SmoothValue { constructor(smoothing = 0.9) { this.value = 0; this.smoothing = smoothing; }

update(newValue) { this.value = this.smoothing * this.value + (1 - this.smoothing) * newValue; return this.value; } }

// Usage const smoothBass = new SmoothValue(0.85); const bassLevel = smoothBass.update(rawBassLevel);

Moving Average class MovingAverage { constructor(size = 10) { this.size = size; this.values = []; }

update(value) { this.values.push(value); if (this.values.length > this.size) { this.values.shift(); } return this.values.reduce((a, b) => a + b) / this.values.length; } }

Complete Analysis System class AudioAnalysisSystem { constructor() { this.fftAnalyser = new Tone.Analyser('fft', 256); this.waveformAnalyser = new Tone.Analyser('waveform', 1024); this.meter = new Tone.Meter({ smoothing: 0.9 });

this.smoothers = {
  bass: new SmoothValue(0.85),
  mid: new SmoothValue(0.9),
  high: new SmoothValue(0.9),
  volume: new SmoothValue(0.95)
};

this.beatDetector = new BeatDetector();

}

connect(source) { source.connect(this.fftAnalyser); source.connect(this.waveformAnalyser); source.connect(this.meter); source.toDestination(); }

getAnalysis() { const fft = this.fftAnalyser.getValue(); const waveform = this.waveformAnalyser.getValue(); const volume = this.meter.getValue();

const bands = this.extractBands(fft);

return {
  // Raw data
  fft,
  waveform,

  // Smoothed bands (0-1)
  bass: this.smoothers.bass.update(bands.bass),
  mid: this.smoothers.mid.update(bands.mid),
  high: this.smoothers.high.update(bands.high),

  // Volume
  volume: this.smoothers.volume.update(this.normalizeDb(volume)),
  volumeDb: volume,

  // Beat
  isBeat: this.beatDetector.detect(this.fftAnalyser),

  // Waveform metrics
  rms: this.getRMS(waveform),
  peak: this.getPeak(waveform)
};

}

extractBands(fft) { const len = fft.length; return { bass: this.normalizeDb(this.avgRange(fft, 0, len * 0.1)), mid: this.normalizeDb(this.avgRange(fft, len * 0.1, len * 0.5)), high: this.normalizeDb(this.avgRange(fft, len * 0.5, len)) }; }

avgRange(data, start, end) { let sum = 0; const s = Math.floor(start); const e = Math.floor(end); for (let i = s; i < e; i++) sum += data[i]; return sum / (e - s); }

normalizeDb(db) { return Math.max(0, Math.min(1, (db + 100) / 100)); }

getRMS(waveform) { let sum = 0; for (let i = 0; i < waveform.length; i++) { sum += waveform[i] * waveform[i]; } return Math.sqrt(sum / waveform.length); }

getPeak(waveform) { let peak = 0; for (let i = 0; i < waveform.length; i++) { const abs = Math.abs(waveform[i]); if (abs > peak) peak = abs; } return peak; }

dispose() { this.fftAnalyser.dispose(); this.waveformAnalyser.dispose(); this.meter.dispose(); } }

Temporal Collapse Usage class TemporalAudioAnalysis extends AudioAnalysisSystem { getCountdownData() { const analysis = this.getAnalysis();

return {
  // For bloom intensity
  glowIntensity: analysis.bass * 0.5 + analysis.volume * 0.5,

  // For particle speed
  particleEnergy: analysis.mid,

  // For chromatic aberration
  distortion: analysis.high * 0.3,

  // For digit pulse
  pulse: analysis.isBeat ? 1 : 0,

  // For background intensity
  ambientLevel: analysis.rms
};

} }

Performance Tips // 1. Use appropriate FFT size const analyser = new Tone.Analyser('fft', 128); // Smaller = faster

// 2. Don't analyze every frame if not needed let frameCount = 0; function update() { if (frameCount % 2 === 0) { // Every other frame const data = analyser.getValue(); } frameCount++; }

// 3. Reuse arrays const dataArray = new Float32Array(256); analyser.getValue(dataArray); // Pass in array to avoid allocation

// 4. Use smoothing to reduce visual jitter const smoothedValue = smoother.update(rawValue);

Reference See audio-playback for loading and playing audio See audio-reactive for connecting analysis to visuals See audio-router for audio domain routing

返回排行榜