Skip to main content
Glama
DeveloperZo

MCP Audio Tweaker

by DeveloperZo

generate_variations

Creates multiple audio variations from one file by adjusting pitch, volume, and spectral characteristics to build sound families for batch processing.

Instructions

Generate multiple variations of a sound from a single input - perfect for creating sound families

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
inputFileYesPath to input audio file
outputDirectoryYesDirectory for output variations
countNoNumber of variations to generate (1-20)
pitchRangeNoPitch variation range in semitones (±)
volumeRangeNoVolume variation range in dB (±)
spectralRangeNoSpectral variation range in dB (±)
seedNoRandom seed for reproducible variations
overwriteNoWhether to overwrite existing output files

Implementation Reference

  • Core handler function that generates multiple audio file variations by applying randomized pitch shifts, spectral changes, and other effects using FFmpeg.
    async generateVariations(
      inputFile: string,
      outputDirectory: string,
      variations: VariationOperation,
      baseOperations?: AudioOperations,
      overwrite: boolean = false
    ): Promise<ProcessingResult[]> {
      const results: ProcessingResult[] = [];
      
      try {
        await validateInputFile(inputFile);
        await ensureOutputDirectory(outputDirectory);
        
        const seed = variations.seed || Math.floor(Math.random() * 1000000);
        const rng = this.createSeededRandom(seed);
        
        for (let i = 0; i < variations.count; i++) {
          const variationOps: AudioOperations = {
            ...baseOperations,
            advanced: {
              ...baseOperations?.advanced,
              ...this.generateRandomVariation(variations, rng)
            }
          };
          
          const outputFile = path.join(outputDirectory, 
            `${path.parse(inputFile).name}_var${i + 1}${path.extname(inputFile)}`
          );
          
          const result = await this.processAudioFile(
            inputFile,
            outputFile,
            variationOps,
            overwrite
          );
          
          results.push(result);
        }
        
        return results;
        
      } catch (error) {
        throw new FFmpegError(`Variation generation failed: ${(error as Error).message}`);
      }
    }
  • Tool definition including input schema for validating parameters like inputFile, outputDirectory, count, pitchRange, etc.
    export const generateVariationsTool: Tool = {
      name: 'generate_variations',
      description: 'Generate multiple variations of a sound from a single input - perfect for creating sound families',
      inputSchema: {
        type: 'object',
        properties: {
          inputFile: {
            type: 'string',
            description: 'Path to input audio file'
          },
          outputDirectory: {
            type: 'string',
            description: 'Directory for output variations'
          },
          count: {
            type: 'number',
            description: 'Number of variations to generate (1-20)',
            minimum: 1,
            maximum: 20,
            default: 5
          },
          pitchRange: {
            type: 'number',
            description: 'Pitch variation range in semitones (±)',
            minimum: 0,
            maximum: 12,
            default: 2
          },
          volumeRange: {
            type: 'number',
            description: 'Volume variation range in dB (±)',
            minimum: 0,
            maximum: 10,
            default: 3
          },
          spectralRange: {
            type: 'number',
            description: 'Spectral variation range in dB (±)',
            minimum: 0,
            maximum: 6,
            default: 2
          },
          seed: {
            type: 'number',
            description: 'Random seed for reproducible variations',
            optional: true
          },
          overwrite: {
            type: 'boolean',
            description: 'Whether to overwrite existing output files',
            default: false
          }
        },
        required: ['inputFile', 'outputDirectory']
      }
    };
  • MCP tool request handler case that dispatches 'generate_variations' calls to the AdvancedAudioProcessor.generateVariations method.
    case 'generate_variations': {
      const input = args as any;
      const variations = {
        count: input.count || 5,
        pitchRange: input.pitchRange || 2,
        volumeRange: input.volumeRange || 3,
        spectralRange: input.spectralRange || 2,
        seed: input.seed
      };
      
      const results = await advancedProcessor.generateVariations(
        input.inputFile,
        input.outputDirectory,
        variations,
        undefined,
        input.overwrite || false
      );
      
      return {
        content: [
          {
            type: 'text',
            text: JSON.stringify({
              success: true,
              variationsGenerated: results.length,
              results: results
            }, null, 2)
          }
        ]
      };
    }
  • Array of all available tools including generateVariationsTool, used for tool listing/discovery.
    export const tools = [
      processAudioFileTool,
      batchProcessAudioTool,
      applyPresetTool,
      listPresetsTool,
      getQueueStatusTool,
      generateVariationsTool,
      createHarmonicsTool,
      advancedProcessTool,
      layerSoundsTool
    ];
  • Helper method that generates randomized audio effect parameters for each variation based on specified ranges.
    }
    
    /**
     * Layer multiple sounds together with advanced blending
     */
    async layerSounds(
      inputFiles: string[],
      outputFile: string,
      layering: LayeringOperation,
      overwrite: boolean = false
    ): Promise<ProcessingResult> {
      const startTime = Date.now();
      
      try {
        if (inputFiles.length === 0) {
          throw new FFmpegError('No input files provided for layering');
        }
        
        // Validate all input files
        for (const file of inputFiles) {
          await validateInputFile(file);
        }
        
        await ensureOutputDirectory(outputFile);
        await handleExistingOutput(outputFile, overwrite);
        
        const command = ffmpeg();
        
        // Add all input files
        inputFiles.forEach(file => command.input(file));
        
        // Build complex filter for layering
        const filterGraph = this.buildLayeringFilter(inputFiles, layering);
        command.complexFilter(filterGraph);
        
        // Execute command
        await executeFFmpegCommand(command, outputFile);
        
        return {
          success: true,
          inputFile: inputFiles.join(', '),
          outputFile,
          processingTime: Date.now() - startTime,
          operations: { advanced: { layering } }
        };
        
      } catch (error) {
        return {
          success: false,
          inputFile: inputFiles.join(', '),
          outputFile,
          processingTime: Date.now() - startTime,
          operations: { advanced: { layering } },
          error: error instanceof Error ? error.message : 'Unknown error'
        };
      }
    }
    
    /**
     * Create harmonic variations by adding octaves and intervals
     */
    async createHarmonicVariations(
      inputFile: string,
      outputDirectory: string,
      harmonics: HarmonicsOperation,
      overwrite: boolean = false
    ): Promise<ProcessingResult[]> {
      const results: ProcessingResult[] = [];
      const baseName = path.parse(inputFile).name;
      
      const harmonicIntervals = [
        { name: 'octave_up', semitones: 12, mix: harmonics.octaveUp },
        { name: 'octave_down', semitones: -12, mix: harmonics.octaveDown },
        { name: 'fifth_up', semitones: 7, mix: harmonics.fifthUp },
        { name: 'third_up', semitones: 4, mix: harmonics.thirdUp }
      ];
      
      for (const interval of harmonicIntervals) {
        if (interval.mix && interval.mix > 0) {
          const outputFile = path.join(outputDirectory, 
            `${baseName}_${interval.name}${path.extname(inputFile)}`
          );
          
          const operations: AudioOperations = {
            advanced: {
              pitch: { semitones: interval.semitones },
              layering: {
                layers: [
                  { blend: 'mix', volume: 1 - interval.mix },
                  { blend: 'add', volume: interval.mix, pitch: interval.semitones }
                ]
              }
            }
          };
          
          const result = await this.processAudioFile(
            inputFile,
            outputFile,
            operations,
            overwrite
          );
          
          results.push(result);
        }
      }
      
      return results;
    }
    
    /**
     * Override the base applyOperationsToCommand to include advanced effects
     */
    protected applyOperationsToCommand(command: any, operations: AudioOperations): void {
      // Apply base operations first (volume, format, basic effects)
      super.applyOperationsToCommand(command, operations);
      
      // Apply advanced operations
      if (operations.advanced) {
        this.applyAdvancedOperations(command, operations.advanced);
      }
    }
    
    /**
     * Apply advanced operations to FFmpeg command
     */
    protected applyAdvancedOperations(command: any, advanced: AdvancedEffectsOperation): void {
      const filters: string[] = [];
      
      // Pitch shifting
      if (advanced.pitch) {
        filters.push(this.buildPitchFilter(advanced.pitch));
      }
      
      // Tempo adjustment
      if (advanced.tempo) {
        filters.push(this.buildTempoFilter(advanced.tempo));
      }
      
      // Spectral processing
      if (advanced.spectral) {
        const spectralFilter = this.buildSpectralFilter(advanced.spectral);
        if (spectralFilter) {
          filters.push(spectralFilter);
        }
      }
      
      // Dynamics processing
      if (advanced.dynamics) {
        filters.push(...this.buildDynamicsFilters(advanced.dynamics));
      }
      
      // Spatial processing
      if (advanced.spatial) {
        filters.push(...this.buildSpatialFilters(advanced.spatial));
      }
      
      // Modulation effects
      if (advanced.modulation) {
        filters.push(...this.buildModulationFilters(advanced.modulation));
      }
      
      // Apply all filters
      if (filters.length > 0) {
        command.audioFilters(filters);
      }
    }
    
    /**
     * Build pitch shifting filter
     */
    private buildPitchFilter(pitch: PitchOperation): string {
      const totalCents = (pitch.semitones * 100) + (pitch.cents || 0);
      const ratio = Math.pow(2, totalCents / 1200);
      
      if (pitch.preserveFormants) {
        return `asetrate=44100*${ratio},aresample=44100,atempo=${1/ratio}`;
      } else {
        return `asetrate=44100*${ratio},aresample=44100`;
      }
    }
    
    /**
     * Build tempo adjustment filter
     */
    private buildTempoFilter(tempo: TempoOperation): string {
      if (tempo.preservePitch) {
        return `atempo=${tempo.factor}`;
      } else {
        return `asetrate=44100*${tempo.factor},aresample=44100`;
      }
    }
    
    /**
     * Build spectral processing filter
     */
    private buildSpectralFilter(spectral: SpectralOperation): string {
      const eqBands: string[] = [];
      
      if (spectral.bassBoost !== undefined) {
        eqBands.push(`bass=g=${spectral.bassBoost}`);
      }
      
      if (spectral.trebleBoost !== undefined) {
        eqBands.push(`treble=g=${spectral.trebleBoost}`);
      }
      
      if (spectral.midCut !== undefined) {
        eqBands.push(`equalizer=f=1000:width=500:g=${-Math.abs(spectral.midCut)}`);
      }
      
      if (spectral.warmth !== undefined) {
        // Add subtle low-mid boost for warmth
        const warmthGain = spectral.warmth * 3;
        eqBands.push(`equalizer=f=200:width=100:g=${warmthGain}`);
      }
      
      if (spectral.brightness !== undefined) {
        // Add high frequency presence
        const brightnessGain = spectral.brightness * 4;
        eqBands.push(`equalizer=f=8000:width=2000:g=${brightnessGain}`);
      }
      
      return eqBands.join(',');
    }
    
    /**
     * Build dynamics processing filters
     */
    private buildDynamicsFilters(dynamics: DynamicsOperation): string[] {
      const filters: string[] = [];
      
      if (dynamics.compressor) {
        const comp = dynamics.compressor;
        filters.push(
          `acompressor=threshold=${comp.threshold}dB:ratio=${comp.ratio}:attack=${comp.attack}:release=${comp.release}${comp.knee ? `:knee=${comp.knee}` : ''}`
        );
      }
      
      if (dynamics.gate) {
        const gate = dynamics.gate;
        filters.push(
          `agate=threshold=${gate.threshold}dB:ratio=${gate.ratio}${gate.attack ? `:attack=${gate.attack}` : ''}${gate.release ? `:release=${gate.release}` : ''}`
        );
      }
      
      if (dynamics.limiter) {
        const limiter = dynamics.limiter;
        filters.push(
          `alimiter=limit=${limiter.threshold}dB${limiter.release ? `:release=${limiter.release}` : ''}`
        );
      }
      
      return filters;
    }
    
    /**
     * Build spatial processing filters
     */
    private buildSpatialFilters(spatial: SpatialOperation): string[] {
      const filters: string[] = [];
      
      if (spatial.stereoWidth !== undefined) {
        filters.push(`extrastereo=m=${spatial.stereoWidth}`);
      }
      
      if (spatial.panPosition !== undefined) {
        filters.push(`pan=stereo|c0=${1 - Math.abs(Math.min(0, spatial.panPosition))}*c0+${Math.max(0, -spatial.panPosition)}*c1|c1=${1 - Math.abs(Math.max(0, spatial.panPosition))}*c1+${Math.max(0, spatial.panPosition)}*c0`);
      }
      
      if (spatial.delayTime !== undefined) {
        const delaySeconds = spatial.delayTime / 1000;
        const feedback = spatial.delayFeedback || 0.3;
        filters.push(`adelay=${spatial.delayTime}|${spatial.delayTime}`);
        if (feedback > 0) {
          filters.push(`afeedback=feedback=${feedback}`);
        }
      }
      
      if (spatial.reverbSend !== undefined) {
        // Simple reverb using allpass filters
        filters.push(`aecho=0.8:0.9:${Math.floor(spatial.reverbSend * 1000)}:${spatial.reverbSend}`);
      }
      
      return filters;
    }
    
    /**
     * Build modulation effect filters
     */
    private buildModulationFilters(modulation: ModulationOperation): string[] {
      const filters: string[] = [];
      
      if (modulation.tremolo) {
        const trem = modulation.tremolo;
        filters.push(`tremolo=f=${trem.rate}:d=${trem.depth}`);
      }
      
      if (modulation.vibrato) {
        const vib = modulation.vibrato;
        filters.push(`vibrato=f=${vib.rate}:d=${vib.depth}`);
      }
      
      if (modulation.chorus) {
        const chorus = modulation.chorus;
        const delayMs = chorus.delay;
        filters.push(`chorus=0.7:0.9:${delayMs}:0.25:${chorus.rate}:${chorus.depth}:t`);
      }
      
      return filters;
    }
    
    /**
     * Build layering filter for complex mixing
     */
    private buildLayeringFilter(inputFiles: string[], layering: LayeringOperation): string {
      const layers = layering.layers;
      
      let filterGraph = '';
      
      // Apply individual layer processing
      layers.forEach((layer, i) => {
        if (i < inputFiles.length) {
          let layerFilter = `[${i}:a]`;
          
          // Apply layer-specific effects
          if (layer.delay) {
            layerFilter += `adelay=${layer.delay}[delayed${i}];[delayed${i}]`;
          }
          
          if (layer.pitch) {
            const ratio = Math.pow(2, layer.pitch / 12);
            layerFilter += `asetrate=44100*${ratio},aresample=44100[pitched${i}];[pitched${i}]`;
          }
          
          if (layer.volume !== undefined) {
            layerFilter += `volume=${layer.volume}[vol${i}];[vol${i}]`;
          }
          
          if (layer.pan !== undefined) {
            layerFilter += `pan=stereo|c0=${1 - Math.abs(Math.min(0, layer.pan))}*c0+${Math.max(0, -layer.pan)}*c1|c1=${1 - Math.abs(Math.max(0, layer.pan))}*c1+${Math.max(0, layer.pan)}*c0[pan${i}];[pan${i}]`;
          }
          
          filterGraph += layerFilter + `[processed${i}];`;
        }
      });
      
      // Mix all layers together
      const mixInputs = layers.map((_, i) => `[processed${i}]`).join('');
      filterGraph += `${mixInputs}amix=inputs=${layers.length}:duration=longest:dropout_transition=2[final]`;
      
      return filterGraph;
    }
    
    /**
     * Generate random variation parameters
     */
    private generateRandomVariation(variations: VariationOperation, rng: () => number): AdvancedEffectsOperation {
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure. It mentions the tool generates variations but lacks details on permissions, rate limits, file formats, error handling, or output specifics (e.g., file naming, format). This is inadequate for a tool with 8 parameters and file operations, leaving significant gaps in understanding its behavior.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, efficient sentence that front-loads the core purpose. It wastes no words and is appropriately sized for the tool's complexity, making it easy for an agent to parse quickly.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (8 parameters, file operations, no annotations, no output schema), the description is insufficient. It doesn't cover behavioral aspects like file handling, output details, or error conditions. While concise, it fails to provide the completeness needed for an agent to confidently invoke this tool without additional context.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema description coverage is 100%, so all parameters are documented in the schema. The description adds no additional parameter semantics beyond implying variation generation. It doesn't explain how parameters like 'pitchRange' or 'spectralRange' interact or affect the output, so it relies entirely on the schema, meeting the baseline for high coverage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: 'Generate multiple variations of a sound from a single input'. It specifies the verb ('generate'), resource ('variations of a sound'), and outcome ('creating sound families'). However, it doesn't explicitly differentiate from sibling tools like 'batch_process_audio' or 'process_audio_file', which might have overlapping functionality.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides minimal usage guidance: 'perfect for creating sound families' suggests a use case but doesn't specify when to use this tool versus alternatives like 'batch_process_audio' or 'create_harmonics'. No explicit when-not-to-use scenarios or prerequisites are mentioned, leaving the agent with little contextual direction.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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/DeveloperZo/mcp-audio-tweaker'

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