analyze_wav_samples
Analyze WAV files to detect drum sample issues like header problems, metadata inconsistencies, and compatibility errors for DecentSampler drum kits.
Instructions
Analyze WAV files to detect common issues in drum kit samples.
This tool checks for:
Non-standard WAV headers that may cause playback issues
Metadata inconsistencies that could affect multi-mic setups
Sample rate and bit depth compatibility
Channel configuration issues
File size and format validation
Error Handling:
Reports detailed header format issues
Identifies metadata inconsistencies between related samples
Flags potential playback compatibility problems
Returns specific error messages for each issue type
Success Response: Returns detailed analysis including:
WAV header information
Sample metadata
Potential compatibility issues
Recommendations for fixes
IMPORTANT: Always use absolute paths (e.g., 'C:/Users/username/Documents/Samples/kick.wav') rather than relative paths.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| paths | Yes | Array of absolute paths to WAV files to analyze (e.g., ['C:/Users/username/Documents/Samples/kick.wav']) |
Implementation Reference
- src/wav-analysis.ts:122-157 (handler)Core implementation of WAV file analysis: reads file, parses and validates WAV header, calculates sample length, extracts metadata (sampleRate, channels, bitDepth), returns WavAnalysis object.export async function analyzeWavFile(path: string): Promise<WavAnalysis> { try { const buffer = await fs.readFile(path); if (buffer.length < 44) { // Minimum size for WAV header throw new McpError( ErrorCode.InvalidRequest, 'File too small to be a valid WAV file' ); } const header = await parseWavHeader(buffer); // Calculate sample length from data chunk size const bytesPerSample = header.bitsPerSample / 8; const samplesPerChannel = header.dataSize! / (bytesPerSample * header.numChannels); return { path, sampleLength: Math.round(samplesPerChannel), sampleRate: header.sampleRate, channels: header.numChannels, bitDepth: header.bitsPerSample }; } catch (error: unknown) { if (error instanceof McpError) { throw error; // Re-throw validation errors } // Handle unexpected errors console.error('WAV analysis error:', error instanceof Error ? error.message : error); const message = error instanceof Error ? error.message : JSON.stringify(error); throw new McpError( ErrorCode.InternalError, `Failed to analyze WAV file ${path}: ${message}` ); } }
- src/wav-analysis.ts:5-11 (schema)TypeScript interface defining the output format of the WAV analysis results.export interface WavAnalysis { path: string; sampleLength: number; // For end marker sampleRate: number; channels: number; bitDepth: number; }
- src/index.ts:248-283 (registration)Tool registration in ListTools handler: defines name, detailed description, and input schema requiring array of absolute WAV file paths.name: "analyze_wav_samples", description: `Analyze WAV files to detect common issues in drum kit samples. This tool checks for: - Non-standard WAV headers that may cause playback issues - Metadata inconsistencies that could affect multi-mic setups - Sample rate and bit depth compatibility - Channel configuration issues - File size and format validation Error Handling: - Reports detailed header format issues - Identifies metadata inconsistencies between related samples - Flags potential playback compatibility problems - Returns specific error messages for each issue type Success Response: Returns detailed analysis including: - WAV header information - Sample metadata - Potential compatibility issues - Recommendations for fixes IMPORTANT: Always use absolute paths (e.g., 'C:/Users/username/Documents/Samples/kick.wav') rather than relative paths.`, inputSchema: { type: "object", properties: { paths: { type: "array", items: { type: "string" }, description: "Array of absolute paths to WAV files to analyze (e.g., ['C:/Users/username/Documents/Samples/kick.wav'])" } }, required: ["paths"] } },
- src/index.ts:600-628 (handler)MCP CallTool handler: validates input, calls analyzeWavFile on each path in parallel, serializes results as JSON text response.case "analyze_wav_samples": { const args = request.params.arguments; if (!args || !Array.isArray(args.paths)) { throw new McpError( ErrorCode.InvalidParams, "Invalid arguments: expected array of paths" ); } try { const analyses = await Promise.all( args.paths.map(path => analyzeWavFile(path)) ); return { content: [{ type: "text", text: JSON.stringify(analyses, null, 2) }] }; } catch (error: unknown) { if (error instanceof McpError) throw error; const message = error instanceof Error ? error.message : String(error); throw new McpError( ErrorCode.InternalError, `Failed to analyze WAV files: ${message}` ); } }
- src/wav-analysis.ts:29-120 (helper)Helper function to parse and validate WAV header fields from file buffer.async function parseWavHeader(buffer: Buffer): Promise<WavHeader> { const invalidReasons: string[] = []; // RIFF header const riffHeader = buffer.toString('ascii', 0, 4); if (riffHeader !== 'RIFF') { invalidReasons.push('Missing RIFF header'); } // File size const fileSize = buffer.readUInt32LE(4); // WAVE header const waveHeader = buffer.toString('ascii', 8, 12); if (waveHeader !== 'WAVE') { invalidReasons.push('Missing WAVE format marker'); } // fmt chunk const fmtHeader = buffer.toString('ascii', 12, 16); if (fmtHeader !== 'fmt ') { invalidReasons.push('Missing fmt chunk'); } const fmtChunkSize = buffer.readUInt32LE(16); const audioFormat = buffer.readUInt16LE(20); if (audioFormat !== 1) { invalidReasons.push(`Unsupported audio format: ${audioFormat} (only PCM supported)`); } const numChannels = buffer.readUInt16LE(22); const sampleRate = buffer.readUInt32LE(24); const byteRate = buffer.readUInt32LE(28); const blockAlign = buffer.readUInt16LE(32); const bitsPerSample = buffer.readUInt16LE(34); // Validate format values if (numChannels === 0) { invalidReasons.push('Invalid number of channels: 0'); } if (sampleRate === 0) { invalidReasons.push('Invalid sample rate: 0'); } if (bitsPerSample === 0) { invalidReasons.push('Invalid bits per sample: 0'); } // Look for data chunk let dataHeader: string | undefined; let dataSize: number | undefined; let offset = 36; // Skip any non-data chunks while (offset < buffer.length - 8) { const chunkHeader = buffer.toString('ascii', offset, offset + 4); const chunkSize = buffer.readUInt32LE(offset + 4); if (chunkHeader === 'data') { dataHeader = chunkHeader; dataSize = chunkSize; break; } offset += 8 + chunkSize; } if (!dataHeader || !dataSize) { invalidReasons.push('Missing data chunk'); } if (invalidReasons.length > 0) { throw new McpError( ErrorCode.InvalidRequest, `WAV file validation failed: ${invalidReasons.join(', ')}` ); } return { riffHeader, fileSize, waveHeader, fmtHeader, fmtChunkSize, audioFormat, numChannels, sampleRate, byteRate, blockAlign, bitsPerSample, dataHeader, dataSize }; }