sc_play_synth
Generate synthesized sounds using natural language descriptions. Describe the sound you want to create, such as "bell at C5" or "deep bass tone for 2 seconds," and the tool produces the corresponding audio output.
Instructions
Play a synthesized sound based on a natural language description. This is the primary tool for sound synthesis. Examples: "play a bell sound at C4", "play a low bass tone for 2 seconds", "play a plucked string sound"
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| description | Yes | Natural language description of the sound to create (e.g., "bell at C5", "deep bass", "short snare") |
Implementation Reference
- src/index.ts:310-349 (handler)Main handler logic for the sc_play_synth tool. Validates input, checks server status, parses the natural language description using parseSynthDescription, generates inline SuperCollider code for specific synth types (bell, kick, snare, hihat, default sine), executes the code on the SC server, and returns a confirmation message.case 'sc_play_synth': { const schema = z.object({ description: z.string() }); const { description } = schema.parse(args); if (!scServer.getBooted() || !synthDefsLoaded) { return { content: [{ type: 'text', text: 'Error: SuperCollider server is not running. Call sc_boot first.' }], isError: true, }; } const { synthName, params } = parseSynthDescription(description); // Use inline function syntax for simple sounds let code = ''; if (synthName === 'bell') { code = `{ var sig = SinOsc.ar(${params.freq} + SinOsc.ar(${params.freq! * 2.4}, 0, ${params.freq! * 0.8}), 0, ${params.amp}) * EnvGen.kr(Env.perc(0.01, ${params.duration}, 1, -4), doneAction: 2); Pan2.ar(sig, ${params.pan}) }.play;`; } else if (synthName === 'kick') { code = `{ var sig = SinOsc.ar(EnvGen.kr(Env.perc(0.001, 0.3), 1, 60, 50), 0, ${params.amp}) * EnvGen.kr(Env.perc(0.001, 0.5), doneAction: 2); Pan2.ar(sig, ${params.pan || 0}) }.play;`; } else if (synthName === 'snare') { code = `{ var sig = (WhiteNoise.ar(${params.amp}) + SinOsc.ar(180, 0, ${params.amp})) * EnvGen.kr(Env.perc(0.001, 0.2), doneAction: 2); Pan2.ar(sig, ${params.pan || 0}) }.play;`; } else if (synthName === 'hihat') { code = `{ var sig = HPF.ar(WhiteNoise.ar(${params.amp}), 8000) * EnvGen.kr(Env.perc(0.001, 0.1), doneAction: 2); Pan2.ar(sig, ${params.pan || 0}) }.play;`; } else { // Default to simple sine wave code = `{ SinOsc.ar(${params.freq}, 0, ${params.amp}) * EnvGen.kr(Env.perc(0.01, ${params.duration}), doneAction: 2) }.play;`; } await scServer.executeCode(code); return { content: [ { type: 'text', text: `Playing ${synthName} synth: ${JSON.stringify(params)}`, }, ], }; }
- src/index.ts:82-95 (schema)Tool definition including name, description, and input schema for the 'description' parameter (Zod validation occurs in handler). This is used for tool listing and validation.{ name: 'sc_play_synth', description: 'Play a synthesized sound based on a natural language description. This is the primary tool for sound synthesis. Examples: "play a bell sound at C4", "play a low bass tone for 2 seconds", "play a plucked string sound"', inputSchema: { type: 'object', properties: { description: { type: 'string', description: 'Natural language description of the sound to create (e.g., "bell at C5", "deep bass", "short snare")', }, }, required: ['description'], }, },
- src/synth-library.ts:134-222 (helper)Key helper function that parses the natural language 'description' input into a synthName and parameter object (SynthParams), used by the handler to determine sound characteristics from keywords like 'bell', 'bass', pitches (C4), durations, volumes, etc.export function parseSynthDescription(description: string): { synthName: string; params: SynthParams; } { const lowerDesc = description.toLowerCase(); // Default params const params: SynthParams = { freq: 440, amp: 0.3, duration: 1, pan: 0, }; // Determine synth type let synthName = 'sine'; if (lowerDesc.includes('bell') || lowerDesc.includes('chime')) { synthName = 'bell'; } else if (lowerDesc.includes('pluck') || lowerDesc.includes('guitar') || lowerDesc.includes('string')) { synthName = 'pluck'; params.decay = 2; } else if (lowerDesc.includes('bass') || lowerDesc.includes('low')) { synthName = 'bass'; params.freq = 110; params.cutoff = 800; } else if (lowerDesc.includes('pad') || lowerDesc.includes('ambient') || lowerDesc.includes('warm')) { synthName = 'pad'; params.freq = 220; params.cutoff = 2000; } else if (lowerDesc.includes('kick') || lowerDesc.includes('drum')) { synthName = 'kick'; } else if (lowerDesc.includes('snare')) { synthName = 'snare'; } else if (lowerDesc.includes('hihat') || lowerDesc.includes('hi-hat')) { synthName = 'hihat'; } else if (lowerDesc.includes('atmosphere') || lowerDesc.includes('noise') || lowerDesc.includes('wind')) { synthName = 'atmosphere'; } else if (lowerDesc.includes('sweep') || lowerDesc.includes('riser') || lowerDesc.includes('swell')) { synthName = 'sweep'; } // Parse frequency/pitch const noteMatch = lowerDesc.match(/\b([a-g]#?)\s*(\d+)\b/i); if (noteMatch) { const note = noteMatch[1]; const octave = parseInt(noteMatch[2]); params.freq = noteToFreq(note, octave); } else { // Look for frequency in Hz const freqMatch = lowerDesc.match(/(\d+)\s*hz/i); if (freqMatch) { params.freq = parseInt(freqMatch[1]); } } // Parse descriptive pitch terms if (lowerDesc.includes('high') || lowerDesc.includes('bright')) { params.freq = (params.freq || 440) * 2; } else if (lowerDesc.includes('low') || lowerDesc.includes('deep')) { params.freq = (params.freq || 440) / 2; } // Parse duration const durMatch = lowerDesc.match(/(\d+(?:\.\d+)?)\s*(?:second|sec|s)\b/i); if (durMatch) { params.duration = parseFloat(durMatch[1]); } else if (lowerDesc.includes('short')) { params.duration = 0.3; } else if (lowerDesc.includes('long')) { params.duration = 3; } // Parse amplitude/volume if (lowerDesc.includes('loud') || lowerDesc.includes('forte')) { params.amp = 0.6; } else if (lowerDesc.includes('quiet') || lowerDesc.includes('soft') || lowerDesc.includes('piano')) { params.amp = 0.15; } // Parse panning if (lowerDesc.includes('left')) { params.pan = -0.7; } else if (lowerDesc.includes('right')) { params.pan = 0.7; } return { synthName, params }; }
- src/synth-library.ts:6-12 (schema)TypeScript interface defining the parameter shape returned by parseSynthDescription and used in synth code generation.export interface SynthParams { freq?: number; amp?: number; duration?: number; pan?: number; [key: string]: number | undefined; }