CoolFace
Apppublic

sourav-das/stem-separator

sourceHugging Facemitupdated 6mo agoView on Hugging Face
3likes
decoder.js83 linesDownload Raw Back to dist
1var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {2    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }3    return new (P || (P = Promise))(function (resolve, reject) {4        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }5        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }6        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }7        step((generator = generator.apply(thisArg, _arguments || [])).next());8    });9};10/** Decode an array buffer into an audio buffer */11function decode(audioData, sampleRate) {12    return __awaiter(this, void 0, void 0, function* () {13        const audioCtx = new AudioContext({ sampleRate });14        try {15            return yield audioCtx.decodeAudioData(audioData);16        }17        finally {18            // Ensure AudioContext is always closed, even on synchronous errors19            audioCtx.close();20        }21    });22}23/** Normalize peaks to -1..1 */24function normalize(channelData) {25    const firstChannel = channelData[0];26    if (firstChannel.some((n) => n > 1 || n < -1)) {27        const length = firstChannel.length;28        let max = 0;29        for (let i = 0; i < length; i++) {30            const absN = Math.abs(firstChannel[i]);31            if (absN > max)32                max = absN;33        }34        for (const channel of channelData) {35            for (let i = 0; i < length; i++) {36                channel[i] /= max;37            }38        }39    }40    return channelData;41}42/** Create an audio buffer from pre-decoded audio data */43function createBuffer(channelData, duration) {44    // Validate inputs45    if (!channelData || channelData.length === 0) {46        throw new Error('channelData must be a non-empty array');47    }48    if (duration <= 0) {49        throw new Error('duration must be greater than 0');50    }51    // If a single array of numbers is passed, make it an array of arrays52    if (typeof channelData[0] === 'number')53        channelData = [channelData];54    // Validate channel data after conversion55    if (!channelData[0] || channelData[0].length === 0) {56        throw new Error('channelData must contain non-empty channel arrays');57    }58    // Normalize to -1..159    normalize(channelData);60    // Convert to Float32Array for consistency61    const float32Channels = channelData.map((channel) => channel instanceof Float32Array ? channel : Float32Array.from(channel));62    return {63        duration,64        length: float32Channels[0].length,65        sampleRate: float32Channels[0].length / duration,66        numberOfChannels: float32Channels.length,67        getChannelData: (i) => {68            const channel = float32Channels[i];69            if (!channel) {70                throw new Error(`Channel ${i} not found`);71            }72            return channel;73        },74        copyFromChannel: AudioBuffer.prototype.copyFromChannel,75        copyToChannel: AudioBuffer.prototype.copyToChannel,76    };77}78const Decoder = {79    decode,80    createBuffer,81};82export default Decoder;83