CoolFace
Apppublic

ashuworkspace/binaural

sourceHugging Faceupdated 8mo agoView on Hugging Face
0likes
audioEngineClass.js197 linesDownload Raw Back to root
1/**2 * @fileoverview Core AudioEngine class for the Binaural Studio3 * @description Manages all audio layers, state, noise generators, and buffer synthesis.4 * Supports multi-mode generation with phase-accurate oscillators and high bit-depth output.5 */6 7const { CONFIG, BIT_DEPTH, SAMPLE_RATE, CHANNELS, CHUNK_SIZE } = require('./config');8const SimpleDelay = require('./simpleDelay');9const { processDrone } = require('./drone');10const { processBinaural } = require('./binaural');11const { processWeather } = require('./weather');12const { processFire } = require('./fire');13const { processBirds } = require('./birds');14const { processChimes } = require('./chimes');15 16/**17 * Main audio processing engine18 * @class19 */20class AudioEngine {21    constructor() {22        // --- CONSTANTS ---23        /** @type {number} */24        this.SAMPLE_RATE = SAMPLE_RATE;25        /** @type {number} */26        this.BIT_DEPTH = BIT_DEPTH;27        /** @type {number} */28        this.CHANNELS = CHANNELS;29        /** @type {number} */30        this.CHUNK_SIZE = CHUNK_SIZE;31 32        // --- GLOBAL STATE ---33        /** @type {number} Total samples processed */34        this.time = 0;35        /** @type {number} Base carrier frequency in Hz */36        this.baseFreq = CONFIG.base;37        /** @type {number} Binaural beat difference in Hz */38        this.beatFreq = CONFIG.beat;39        /** @type {number} Current wind panning position (0-1) */40        this.windPan = 0.5;41        /** @type {number} Slow-moving atmospheric modulation (0-1) */42        this.atmosphereFlow = 0.5;43 44        // --- PHASE ACCUMULATORS ---45        /** @type {number} Left binaural oscillator phase */46        this.binPhaseL = 0;47        /** @type {number} Right binaural oscillator phase */48        this.binPhaseR = 0;49        /** @type {number} Drone oscillator 1 phase */50        this.dronePhase1 = 0;51        /** @type {number} Drone oscillator 2 phase */52        this.dronePhase2 = 0;53        /** @type {number} Drone oscillator 3 phase */54        this.dronePhase3 = 0;55        /** @type {number} Bird chirp FM oscillator phase */56        this.birdPhase = 0;57        /** @type {number[]} Chime oscillator phases */58        this.chimePhases = [0, 0];59 60        // --- NOISE GENERATOR STATE ---61        /** @type {number} Last brown noise sample for filtering */62        this.lastBrown = 0;63        /** @type {number} Last pink noise sample for filtering */64        this.lastPink = 0;65        /** @type {number} Rain low-pass filter state */66        this.rainFilter = 0;67 68        // --- EVENT STATE ---69        /** @type {number} Countdown for fire crackle events */70        this.fireCrackleTimer = 0;71        /** @type {boolean} Is a bird chirp currently active? */72        this.birdActive = false;73        /** @type {number} Countdown for bird chirp duration */74        this.birdTimer = 0;75        /** @type {number} Base frequency for the current bird chirp */76        this.birdBaseFreq = 2000;77        /** @type {number} Volume envelope for bird chirp */78        this.birdEnv = 0;79        /** @type {number} Next timestamp for chime triggering */80        this.nextChimeTime = this.SAMPLE_RATE * 5;81        /** @type {boolean} Are chimes currently active? */82        this.chimeActive = false;83        /** @type {number[]} Envelopes for active chime notes */84        this.chimeEnvs = [0, 0];85        /** @type {number[]} Frequencies for active chime notes */86        this.currentChimeMode = [];87 88        // --- SPATIAL EFFECTS ---89        /** @type {SimpleDelay} Delay line for rain stereo width */90        this.rainDelayL = new SimpleDelay(15, 0);91        /** @type {SimpleDelay} Delay line for chime depth */92        this.chimeDelay = new SimpleDelay(450, 0.45);93        /** @type {SimpleDelay} Delay line for bird echo */94        this.birdDelay = new SimpleDelay(120, 0.3);95    }96 97    /**98     * Generate pink noise via low-pass filtering white noise99     * @returns {number} Pink noise sample (-1 to 1)100     */101    getPinkNoise() {102        const white = Math.random() * 2 - 1;103        const pink = (this.lastPink + 0.02 * white) / 1.02;104        this.lastPink = pink;105        return pink;106    }107 108    /**109     * Generate brown noise via integration of white noise110     * @returns {number} Brown noise sample (-1 to 1)111     */112    getBrownNoise() {113        const white = Math.random() * 2 - 1;114        const brown = (this.lastBrown + 0.02 * white) / 1.02;115        this.lastBrown = brown;116        return brown * 3.5;117    }118 119    /**120     * Writes a 24-bit signed integer to a buffer (Little Endian)121     * @param {Buffer} buffer - Target buffer122     * @param {number} value - Signed integer value123     * @param {number} offset - Buffer offset124     */125    writeInt24LE(buffer, value, offset) {126        const signedValue = value < 0 ? value + 0x1000000 : value;127        buffer.writeUInt8(signedValue & 0xFF, offset);128        buffer.writeUInt8((signedValue >> 8) & 0xFF, offset + 1);129        buffer.writeUInt8((signedValue >> 16) & 0xFF, offset + 2);130    }131 132    /**133     * Synthesizes and returns a chunk of PCM audio data134     * @returns {Buffer} Raw PCM audio buffer135     */136    generateChunk() {137        const bytesPerSample = this.BIT_DEPTH / 8;138        const buffer = Buffer.alloc(this.CHUNK_SIZE * this.CHANNELS * bytesPerSample);139 140        // Slow modulation of nature density over time141        this.atmosphereFlow = 0.5 + Math.sin(this.time * 0.00001) * 0.5;142 143        for (let i = 0; i < this.CHUNK_SIZE; i++) {144            // 1. Foundation Layers145            const { droneL, droneR } = processDrone(this);146            const { binL, binR } = processBinaural(this);147 148            // 2. Texture Layers149            const { rainL, rainR, windL, windR } = processWeather(this);150            const { fireMix } = processFire(this);151 152            // 3. Event Layers153            const { birdMix } = processBirds(this);154            const { cL, cR } = processChimes(this);155 156            // Apply Echoes157            const bL = birdMix + this.birdDelay.process(birdMix) * 0.2;158            const bR = birdMix + this.birdDelay.process(birdMix) * 0.4;159 160            // 4. Mix Stages161            const foundationL = droneL + binL;162            const foundationR = droneR + binR;163            const textureL = rainL + windL + fireMix * 0.5;164            const textureR = rainR + windR + fireMix * 0.5;165            const eventL = bL + cL;166            const eventR = bR + cR;167 168            const { foundation, texture, events } = CONFIG.mixGains;169            let mixL = foundationL * foundation + textureL * texture + eventL * events;170            let mixR = foundationR * foundation + textureR * texture + eventR * events;171 172            // 5. Soft Limiting (Tanh Compression)173            mixL = Math.tanh(mixL);174            mixR = Math.tanh(mixR);175 176            // 6. Output Processing177            const offset = i * this.CHANNELS * bytesPerSample;178            if (this.BIT_DEPTH === 24) {179                const lInt = Math.round(mixL * 8388607);180                const rInt = Math.round(mixR * 8388607);181                this.writeInt24LE(buffer, lInt, offset);182                this.writeInt24LE(buffer, rInt, offset + 3);183            } else {184                const lInt = Math.round(mixL * 32767);185                const rInt = Math.round(mixR * 32767);186                buffer.writeInt16LE(lInt, offset);187                buffer.writeInt16LE(rInt, offset + 2);188            }189 190            this.time++;191        }192        return buffer;193    }194}195 196module.exports = { AudioEngine };197