Please bookmark this page to avoid losing your image tool!

Image Audio Pitch Splitter

(Free & Supports Bulk Upload)

Drag & drop your images here or

The result will appear here...
You can edit the below JavaScript code to customize the image tool.
/**
 * Processes an audio file from a URL, splitting it into multiple frequency bands.
 * The input image is not used in this process, as the core task is audio manipulation.
 *
 * @param {Image} originalImg - An Image object. This parameter is required but not used for the audio processing logic.
 * @param {string} audioUrl - The URL of the audio file to process. Must be accessible via fetch (CORS headers may be required for cross-domain URLs).
 * @param {string} cutoffFrequencies - A comma-separated string of numbers representing the frequencies (in Hz) at which to split the audio. For example, "250,4000" will create three bands: <250Hz, 250-4000Hz, and >4000Hz.
 * @returns {Promise<HTMLDivElement>} A promise that resolves to a div element containing audio players for each frequency band.
 */
async function processImage(originalImg, audioUrl = '', cutoffFrequencies = '250,4000') {
    const container = document.createElement('div');
    container.style.fontFamily = 'sans-serif';
    container.style.padding = '1rem';

    if (!audioUrl) {
        container.textContent = 'Error: Please provide a valid audio URL.';
        return container;
    }
    
    // Helper function to encode an AudioBuffer to a WAV file (as a Blob)
    const bufferToWaveBlob = (audioBuffer) => {
        const numOfChan = audioBuffer.numberOfChannels;
        const length = audioBuffer.length * numOfChan * 2 + 44;
        const buffer = new ArrayBuffer(length);
        const view = new DataView(buffer);
        const channels = [];
        let pos = 0;

        const setUint16 = (data) => {
            view.setUint16(pos, data, true);
            pos += 2;
        };

        const setUint32 = (data) => {
            view.setUint32(pos, data, true);
            pos += 4;
        };

        // RIFF header
        setUint32(0x46464952); // "RIFF"
        setUint32(length - 8);
        setUint32(0x45564157); // "WAVE"

        // FMT sub-chunk
        setUint32(0x20746d66); // "fmt "
        setUint32(16); // PCM chunk size
        setUint16(1); // PCM format code
        setUint16(numOfChan);
        setUint32(audioBuffer.sampleRate);
        setUint32(audioBuffer.sampleRate * 2 * numOfChan); // byte rate
        setUint16(numOfChan * 2); // block align
        setUint16(16); // bits per sample

        // Data sub-chunk
        setUint32(0x61746164); // "data"
        setUint32(length - pos - 4);

        for (let i = 0; i < audioBuffer.numberOfChannels; i++) {
            channels.push(audioBuffer.getChannelData(i));
        }

        let offset = 0;
        while (pos < length) {
            for (let i = 0; i < numOfChan; i++) {
                // clamp and scale to 16-bit signed integer
                let sample = Math.max(-1, Math.min(1, channels[i][offset])); 
                sample = sample < 0 ? sample * 0x8000 : sample * 0x7FFF;
                view.setInt16(pos, sample, true);
                pos += 2;
            }
            offset++;
        }

        return new Blob([buffer], { type: "audio/wav" });
    };


    let tempAudioContext;
    try {
        const loadingIndicator = document.createElement('p');
        loadingIndicator.textContent = 'Loading and processing audio...';
        container.appendChild(loadingIndicator);

        tempAudioContext = new(window.AudioContext || window.webkitAudioContext)();

        const response = await fetch(audioUrl);
        if (!response.ok) {
            throw new Error(`Failed to fetch audio. Status: ${response.status}`);
        }
        const arrayBuffer = await response.arrayBuffer();
        const originalBuffer = await tempAudioContext.decodeAudioData(arrayBuffer);

        const cutoffs = cutoffFrequencies
            .split(',')
            .map(f => parseFloat(f.trim()))
            .filter(f => !isNaN(f) && f > 0 && f < tempAudioContext.sampleRate / 2)
            .sort((a, b) => a - b);
            
        if (cutoffs.length === 0) {
            throw new Error('Invalid or no cutoff frequencies provided. Please provide comma-separated numbers (e.g., "250,4000").');
        }

        const numBands = cutoffs.length + 1;
        const processingPromises = [];

        for (let i = 0; i < numBands; i++) {
            const promise = (async () => {
                const offlineCtx = new OfflineAudioContext(originalBuffer.numberOfChannels, originalBuffer.length, originalBuffer.sampleRate);
                const source = offlineCtx.createBufferSource();
                source.buffer = originalBuffer;

                let lastNode = source;
                let label = '';
                
                if (i === 0) { // First band (low-pass)
                    const lowpass = offlineCtx.createBiquadFilter();
                    lowpass.type = 'lowpass';
                    lowpass.frequency.value = cutoffs[0];
                    lowpass.Q.value = 1;
                    lastNode.connect(lowpass);
                    lastNode = lowpass;
                    label = `Low Pass (< ${cutoffs[0]} Hz)`;
                } else if (i === cutoffs.length) { // Last band (high-pass)
                    const highpass = offlineCtx.createBiquadFilter();
                    highpass.type = 'highpass';
                    highpass.frequency.value = cutoffs[i - 1];
                    highpass.Q.value = 1;
                    lastNode.connect(highpass);
                    lastNode = highpass;
                    label = `High Pass (> ${cutoffs[i - 1]} Hz)`;
                } else { // Intermediate bands (band-pass)
                    const highPassFreq = cutoffs[i - 1];
                    const lowPassFreq = cutoffs[i];
                    
                    const highpass = offlineCtx.createBiquadFilter();
                    highpass.type = 'highpass';
                    highpass.frequency.value = highPassFreq;
                    highpass.Q.value = 1;

                    const lowpass = offlineCtx.createBiquadFilter();
                    lowpass.type = 'lowpass';
                    lowpass.frequency.value = lowPassFreq;
                    lowpass.Q.value = 1;

                    lastNode.connect(highpass);
                    highpass.connect(lowpass);
                    lastNode = lowpass;
                    label = `Band Pass (${highPassFreq} Hz - ${lowPassFreq} Hz)`;
                }
                
                lastNode.connect(offlineCtx.destination);
                source.start(0);
                
                const renderedBuffer = await offlineCtx.startRendering();
                return { buffer: renderedBuffer, label: label };
            })();
            processingPromises.push(promise);
        }

        const results = await Promise.all(processingPromises);
        
        container.removeChild(loadingIndicator); // Remove loading text
        const title = document.createElement('h3');
        title.textContent = 'Split Audio Tracks';
        title.style.margin = "0 0 1rem 0";
        container.appendChild(title);
        
        results.forEach(result => {
            const bandContainer = document.createElement('div');
            bandContainer.style.marginBottom = '10px';
            bandContainer.style.padding = '10px';
            bandContainer.style.border = '1px solid #ccc';
            bandContainer.style.borderRadius = '5px';

            const labelEl = document.createElement('p');
            labelEl.textContent = result.label;
            labelEl.style.margin = '0 0 5px 0';
            labelEl.style.fontWeight = 'bold';

            const audioEl = document.createElement('audio');
            audioEl.controls = true;
            audioEl.style.width = '100%';
            
            const wavBlob = bufferToWaveBlob(result.buffer);
            const blobUrl = URL.createObjectURL(wavBlob);
            
            audioEl.src = blobUrl;

            bandContainer.appendChild(labelEl);
            bandContainer.appendChild(audioEl);
            container.appendChild(bandContainer);
        });

    } catch (error) {
        console.error('Audio processing failed:', error);
        container.innerHTML = `<p style="color: red;"><strong>Error:</strong> Failed to process audio. ${error.message}.</p><p>Please check the audio URL and ensure it's accessible (cross-domain requests require CORS headers on the server).</p>`;
    } finally {
        if (tempAudioContext && tempAudioContext.state !== 'closed') {
           tempAudioContext.close();
        }
    }
    
    const imgInfo = document.createElement('p');
    imgInfo.textContent = `(Input image dimensions: ${originalImg.width}x${originalImg.height}. Image is not used by this audio tool.)`;
    imgInfo.style.fontSize = '0.8em';
    imgInfo.style.color = '#777';
    imgInfo.style.marginTop = '20px';
    imgInfo.style.borderTop = '1px solid #eee';
    imgInfo.style.paddingTop = '10px';
    container.appendChild(imgInfo);

    return container;
}

Free Image Tool Creator

Can't find the image tool you're looking for?
Create one based on your own needs now!

Description

The Image Audio Pitch Splitter is a web tool designed to process audio files by splitting them into multiple frequency bands. Users can provide a valid audio URL and specify cutoff frequencies to create different audio segments based on those frequencies. This tool is useful for musicians, audio engineers, and sound designers who want to isolate or analyze specific frequency ranges in audio tracks, such as separating bass from treble, or focusing on particular vocal ranges. The output includes playable audio players for each frequency band created, allowing users to listen to the results directly.

Leave a Reply

Your email address will not be published. Required fields are marked *