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 {

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