analyze_rhythm
Analyze musical rhythms to identify patterns and structures for music generation and live coding applications.
Instructions
Rhythm analysis
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Implementation Reference
- src/server/EnhancedMCPServerFixed.ts:276-280 (registration)Tool registration definition for 'analyze_rhythm' in the getTools() array.{ name: 'analyze_rhythm', description: 'Rhythm analysis', inputSchema: { type: 'object', properties: {} } },
- MCP tool handler in executeTool switch statement (currently stubbed, calls analyzeAudio but returns placeholder).case 'analyze_rhythm': if (!this.isInitialized) { return 'Browser not initialized. Run init first.'; } const analysis = await this.controller.analyzeAudio(); return { isPlaying: analysis.features?.isPlaying, tempo: 'Analysis pending implementation', pattern: 'Rhythm pattern analysis' };
- src/AudioAnalyzer.ts:609-712 (handler)Core rhythm analysis implementation in AudioAnalyzer class, performing onset detection, complexity analysis, syncopation detection, etc.async analyzeRhythm(page: Page): Promise<RhythmAnalysis> { // Get analyzer object from browser const analyzer = await page.evaluate(() => { return (window as any).strudelAudioAnalyzer; }); if (!analyzer || !analyzer.isConnected) { return { pattern: 'X...', complexity: 0, density: 0, syncopation: 0, onsets: [], isRegular: true }; } let onsets: number[]; // Check if this is a mock with pre-calculated onset times (for testing) if (typeof analyzer.analyze === 'function') { const analysis = analyzer.analyze(); if (analysis?.features?.onsets) { onsets = analysis.features.onsets; } else if (analysis?.features?.onsetTimes) { onsets = analysis.features.onsetTimes; } else { // No mock data, use real-time detection const fftData = new Uint8Array(analyzer.dataArray); const flux = this.calculateSpectralFlux(fftData); if (flux > this.ONSET_THRESHOLD) { this._onsetHistory.push(Date.now()); if (this._onsetHistory.length > this.MAX_HISTORY_LENGTH) { this._onsetHistory.shift(); } } onsets = [...this._onsetHistory]; } } else { // No analyze function, use real-time detection const fftData = new Uint8Array(analyzer.dataArray); const flux = this.calculateSpectralFlux(fftData); if (flux > this.ONSET_THRESHOLD) { this._onsetHistory.push(Date.now()); if (this._onsetHistory.length > this.MAX_HISTORY_LENGTH) { this._onsetHistory.shift(); } } onsets = [...this._onsetHistory]; } // Need at least 2 onsets for rhythm analysis if (onsets.length < 2) { return { pattern: 'X...', complexity: 0, density: 0, syncopation: 0, onsets: [], isRegular: true }; } // Calculate intervals const intervals = this.calculateIntervals(onsets); // Calculate density (events per second) const duration = (onsets[onsets.length - 1] - onsets[0]) / 1000; const density = duration > 0 ? (onsets.length - 1) / duration : 0; // Calculate complexity from interval variance const meanInterval = intervals.reduce((a, b) => a + b, 0) / intervals.length; const variance = this.calculateVariance(intervals, meanInterval); const coefficientOfVariation = Math.sqrt(variance) / meanInterval; // Analyze subdivisions const subdivisionScore = this.analyzeSubdivisions(intervals); // Combine variance and subdivision complexity with higher sensitivity const varianceComponent = Math.min(1, coefficientOfVariation * 5); const complexity = Math.min(1, varianceComponent * 0.8 + subdivisionScore * 0.2); // Calculate syncopation (off-beat events) const syncopation = this.detectSyncopation(onsets, meanInterval); // Determine regularity const isRegular = coefficientOfVariation < 0.2; // Generate pattern string const pattern = this.generatePatternString(onsets, meanInterval); return { pattern, complexity, density, syncopation, onsets, isRegular }; }
- src/types/AudioAnalysis.ts:18-25 (schema)TypeScript interface defining the RhythmAnalysis return type (schema).export interface RhythmAnalysis { pattern: string; complexity: number; // 0-1 scale density: number; // events per second syncopation: number; // 0-1 scale onsets: number[]; isRegular: boolean; }
- src/AudioAnalyzer.ts:717-734 (helper)Helper function for subdivision complexity calculation used in analyzeRhythm.private analyzeSubdivisions(intervals: number[]): number { if (intervals.length === 0) return 0; const meanInterval = intervals.reduce((a, b) => a + b, 0) / intervals.length; // Count how many different subdivision levels are present const subdivisions = new Set<number>(); for (const interval of intervals) { const ratio = interval / meanInterval; // Quantize to common subdivisions (1, 0.5, 0.25, 0.75, 0.33, etc.) const quantized = Math.round(ratio * 8) / 8; // Higher resolution subdivisions.add(quantized); } // More subdivision levels = more complex (more aggressive scaling) return Math.min(1, subdivisions.size / 4); }