sc_play_synth
Generate synthesized audio from natural language descriptions. Create sounds like bells, bass tones, or plucked strings by describing them in English.
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 execution logic for the sc_play_synth tool: parses natural language input, generates and executes inline SuperCollider synthesis code for various sound types.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-94 (registration)Tool registration in the tools list, including name, description, and input schema.{ 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/index.ts:85-94 (schema)JSON schema defining the input parameters for the sc_play_synth tool.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)Helper function parseSynthDescription that interprets the natural language description to select synth type and extract parameters like frequency, duration, amplitude, 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 }; }