analyze_spectrum
Analyze audio frequency content using FFT to support music pattern creation and audio processing in Strudel live coding environments.
Instructions
FFT spectrum analysis
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Implementation Reference
- The main tool handler for 'analyze_spectrum'. Checks if browser is initialized, then delegates to StrudelController.analyzeAudio() and returns the spectrum features or full analysis result.case 'analyze_spectrum': if (!this.isInitialized) { return 'Browser not initialized. Run init first.'; } const spectrum = await this.controller.analyzeAudio(); return spectrum.features || spectrum;
- Tool schema definition including name, description ('FFT spectrum analysis'), and input schema (no parameters required).name: 'analyze_spectrum', description: 'FFT spectrum analysis', inputSchema: { type: 'object', properties: {} }
- src/server/EnhancedMCPServerFixed.ts:571-573 (registration)Tool registration via setRequestHandler for ListToolsRequestSchema, which returns the full tools list including analyze_spectrum from getTools().this.server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: this.getTools() }));
- src/StrudelController.ts:269-273 (helper)StrudelController.analyzeAudio() method delegated by the tool handler, which forwards to AudioAnalyzer.getAnalysis().async analyzeAudio(): Promise<AudioAnalysisResult> { if (!this._page) throw new Error('Browser not initialized. Run init tool first.'); return await this.analyzer.getAnalysis(this._page); }
- src/AudioAnalyzer.ts:179-219 (helper)AudioAnalyzer.getAnalysis() retrieves the browser-side spectrum analysis by calling the injected analyzer.analyze() which performs FFT and computes features.async getAnalysis(page: Page): Promise<AudioAnalysisResult> { // Client-side caching with local fallback const now = Date.now(); if (this._analysisCache && (now - this._cacheTimestamp) < this.ANALYSIS_CACHE_TTL) { return this._analysisCache; } const result = await page.evaluate(() => { const analyzer = (window as any).strudelAudioAnalyzer; if (!analyzer) { return { connected: false, error: 'Analyzer not initialized. Audio context may not have started yet.', hint: 'Try playing a pattern first to initialize the audio context.' }; } if (!analyzer.isConnected) { return { connected: false, error: 'Analyzer not connected to audio output.', hint: 'Play a pattern to connect the analyzer to Strudel audio output.' }; } const analysis = analyzer.analyze(); // Add diagnostic info if no audio detected if (analysis.features && analysis.features.isSilent) { analysis.hint = 'Audio analyzer connected but no audio detected. Ensure pattern is playing.'; } return analysis; }); // Update cache this._analysisCache = result; this._cacheTimestamp = now; return result; }
- src/AudioAnalyzer.ts:82-167 (helper)Browser-injected analyzer.analyze() function: core FFT spectrum analysis using getByteFrequencyData, computes key spectrum features like average dB, peak freq, spectral centroid, EQ bands.analyze() { if (!this.analyser || !this.isConnected) { return { connected: false, error: 'Analyzer not connected' }; } // Cache-based throttling const now = Date.now(); if (this.lastAnalysis && (now - this.lastAnalysisTime) < 50) { return this.lastAnalysis; } this.analyser.getByteFrequencyData(this.dataArray); // Optimized analysis using typed array operations const dataArray = this.dataArray; const length = dataArray.length; // Single-pass computation for better performance let sum = 0; let peak = 0; let peakIndex = 0; let weightedSum = 0; // Frequency band accumulators let bassSum = 0, lowMidSum = 0, midSum = 0, highMidSum = 0, trebleSum = 0; for (let i = 0; i < length; i++) { const value = dataArray[i]; sum += value; weightedSum += i * value; if (value > peak) { peak = value; peakIndex = i; } // Frequency bands (adjusted for 1024 FFT) if (i < 4) bassSum += value; else if (i < 16) lowMidSum += value; else if (i < 64) midSum += value; else if (i < 128) highMidSum += value; else if (i < 256) trebleSum += value; } const average = sum / length; const centroid = sum > 0 ? weightedSum / sum : 0; const peakFreq = (peakIndex / length) * 22050; const bass = bassSum / 4; const lowMid = lowMidSum / 12; const mid = midSum / 48; const highMid = highMidSum / 64; const treble = trebleSum / 128; const result = { connected: true, timestamp: now, features: { average: Math.round(average * 10) / 10, peak, peakFrequency: Math.round(peakFreq), centroid: Math.round(centroid * 10) / 10, bass: Math.round(bass), lowMid: Math.round(lowMid), mid: Math.round(mid), highMid: Math.round(highMid), treble: Math.round(treble), isPlaying: average > 5, isSilent: average < 1, bassToTrebleRatio: treble > 0 ? (bass / treble).toFixed(2) : 'N/A', brightness: centroid > 500 ? 'bright' : centroid > 200 ? 'balanced' : 'dark' } }; // Cache result this.lastAnalysis = result; this.lastAnalysisTime = now; return result; }