Skip to main content
Glama

analyze_rhythm

Analyze musical rhythms to identify patterns and structures for music generation and live coding applications.

Instructions

Rhythm analysis

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault

No arguments

Implementation Reference

  • 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' };
  • 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 }; }
  • 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; }
  • 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); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/williamzujkowski/strudel-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server