ashuworkspace/binaural
0
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 