Source identity
| Field | Recorded value |
|---|---|
| Source type | archive member |
| Original record | synthesizer.js |
| Date | Not specified in source |
| Path or archive member | archives/libsrc-1c3f88063121/NETLIFY_AI_RECOVERY_2026-10-14_21-10-49/sites/janady13__heroic-duckanoo-27bf42__e4da36ba-c133-41bf-b947-fc4df2339c31/originals/deploy_696945e0b902ed493da89961/files/synthesizer.js |
| Original SHA-256 | 952d7453ae07fa5c61952067f50b21c7c505e8a8969d3c043eb8bbbefb0d5b5f |
| Reading-copy SHA-256 | 952d7453ae07fa5c61952067f50b21c7c505e8a8969d3c043eb8bbbefb0d5b5f |
| Archive source | {"sourceId": "libsrc-1c3f88063121", "member": "NETLIFY_AI_RECOVERY_2026-10-14_21-10-49/sites/janady13__heroic-duckanoo-27bf42__e4da36ba-c133-41bf-b947-fc4df2339c31/originals/deploy_696945e0b902ed493da89961/files/synthesizer.js"} |
Every recorded occurrence
These 2 records contain identical original source bytes. Their paths and revisions remain part of the archive.
| Original record | Branch or archive | Revision / identifier | Original location |
|---|---|---|---|
| synthesizer.js | {"sourceId": "libsrc-1c3f88063121", "member": "NETLIFY_AI_RECOVERY_2026-10-14_21-10-49/sites/janady13__heroic-duckanoo-27bf42__e4da36ba-c133-41bf-b947-fc4df2339c31/originals/deploy_696945e0b902ed493da89961/files/synthesizer.js"} | libsrc-2c7cb4f90383 | archives/libsrc-1c3f88063121/NETLIFY_AI_RECOVERY_2026-10-14_21-10-49/sites/janady13__heroic-duckanoo-27bf42__e4da36ba-c133-41bf-b947-fc4df2339c31/originals/deploy_696945e0b902ed493da89961/files/synthesizer.js |
| e79fc25b69b9cf194bcb4800e6c0d2d63e8b19ea | {"sourceId": "libsrc-1c3f88063121", "member": "NETLIFY_AI_RECOVERY_2026-10-14_21-10-49/sites/janady13__heroic-duckanoo-27bf42__e4da36ba-c133-41bf-b947-fc4df2339c31/originals/.content_store/e7/e79fc25b69b9cf194bcb4800e6c0d2d63e8b19ea"} | libsrc-8e9044d7354c | archives/libsrc-1c3f88063121/NETLIFY_AI_RECOVERY_2026-10-14_21-10-49/sites/janady13__heroic-duckanoo-27bf42__e4da36ba-c133-41bf-b947-fc4df2339c31/originals/.content_store/e7/e79fc25b69b9cf194bcb4800e6c0d2d63e8b19ea |
Recovered source transcript
Download complete source text ↓437 lines · 25.6 KB
/**
* AI Music Interaction - Synthesizer Engine
* Full Instrument Library with Real-Time Web Audio Synthesis
*/
// Complete Instrument Library with proper synthesis parameters
const INSTRUMENTS = [
// === KEYS ===
{ id: 'grand_piano', name: 'Grand Piano', category: 'keys', waveform: 'triangle', attack: 0.005, decay: 0.3, sustain: 0.4, release: 0.8, detune: 2, harmonics: [1, 0.5, 0.25] },
{ id: 'bright_piano', name: 'Bright Piano', category: 'keys', waveform: 'sawtooth', attack: 0.002, decay: 0.2, sustain: 0.3, release: 0.6, detune: 3, harmonics: [1, 0.6, 0.3], filter: 4000 },
{ id: 'electric_piano', name: 'Electric Piano', category: 'keys', waveform: 'sine', attack: 0.01, decay: 0.4, sustain: 0.5, release: 0.5, detune: 0, harmonics: [1, 0.3, 0.1], tremolo: 4 },
{ id: 'rhodes', name: 'Rhodes', category: 'keys', waveform: 'sine', attack: 0.008, decay: 0.5, sustain: 0.3, release: 0.7, detune: 1, harmonics: [1, 0.4, 0.15, 0.05], tremolo: 2 },
{ id: 'wurlitzer', name: 'Wurlitzer', category: 'keys', waveform: 'triangle', attack: 0.005, decay: 0.3, sustain: 0.4, release: 0.4, detune: 2, harmonics: [1, 0.5, 0.2] },
{ id: 'clavinet', name: 'Clavinet', category: 'keys', waveform: 'square', attack: 0.001, decay: 0.1, sustain: 0.2, release: 0.3, detune: 1, harmonics: [1, 0.8, 0.4], filter: 3000 },
{ id: 'harpsichord', name: 'Harpsichord', category: 'keys', waveform: 'sawtooth', attack: 0.001, decay: 0.15, sustain: 0.1, release: 0.4, detune: 3, harmonics: [1, 0.7, 0.5, 0.3], filter: 5000 },
{ id: 'celesta', name: 'Celesta', category: 'keys', waveform: 'sine', attack: 0.001, decay: 0.8, sustain: 0.0, release: 1.2, detune: 0, harmonics: [1, 0.2, 0.05] },
{ id: 'organ_rock', name: 'Rock Organ', category: 'keys', waveform: 'sine', attack: 0.02, decay: 0.1, sustain: 0.9, release: 0.1, detune: 0, harmonics: [1, 1, 0.5, 0.5, 0.25] },
{ id: 'organ_church', name: 'Church Organ', category: 'keys', waveform: 'sine', attack: 0.1, decay: 0.2, sustain: 0.95, release: 0.3, detune: 0, harmonics: [1, 0.8, 0.6, 0.4, 0.3, 0.2] },
{ id: 'accordion', name: 'Accordion', category: 'keys', waveform: 'sawtooth', attack: 0.05, decay: 0.1, sustain: 0.8, release: 0.15, detune: 5, harmonics: [1, 0.6, 0.3], filter: 2500 },
{ id: 'harmonica', name: 'Harmonica', category: 'keys', waveform: 'sawtooth', attack: 0.03, decay: 0.1, sustain: 0.7, release: 0.2, detune: 3, harmonics: [1, 0.5, 0.25], filter: 2000 },
// === STRINGS ===
{ id: 'violin', name: 'Violin', category: 'strings', waveform: 'sawtooth', attack: 0.1, decay: 0.2, sustain: 0.7, release: 0.3, detune: 4, harmonics: [1, 0.5, 0.3, 0.2], filter: 4000, vibrato: 5 },
{ id: 'viola', name: 'Viola', category: 'strings', waveform: 'sawtooth', attack: 0.12, decay: 0.2, sustain: 0.7, release: 0.35, detune: 4, harmonics: [1, 0.6, 0.35, 0.2], filter: 3000, vibrato: 4.5 },
{ id: 'cello', name: 'Cello', category: 'strings', waveform: 'sawtooth', attack: 0.15, decay: 0.25, sustain: 0.65, release: 0.4, detune: 3, harmonics: [1, 0.6, 0.4, 0.25], filter: 2500, vibrato: 4 },
{ id: 'double_bass', name: 'Double Bass', category: 'strings', waveform: 'sawtooth', attack: 0.08, decay: 0.3, sustain: 0.5, release: 0.5, detune: 2, harmonics: [1, 0.7, 0.4], filter: 1500 },
{ id: 'string_ensemble', name: 'String Ensemble', category: 'strings', waveform: 'sawtooth', attack: 0.2, decay: 0.3, sustain: 0.8, release: 0.5, detune: 8, harmonics: [1, 0.5, 0.3, 0.2], filter: 3500, vibrato: 3 },
{ id: 'acoustic_guitar', name: 'Acoustic Guitar', category: 'strings', waveform: 'triangle', attack: 0.002, decay: 0.4, sustain: 0.2, release: 0.6, detune: 2, harmonics: [1, 0.5, 0.3, 0.15] },
{ id: 'electric_guitar_clean', name: 'Electric Guitar (Clean)', category: 'strings', waveform: 'triangle', attack: 0.001, decay: 0.3, sustain: 0.4, release: 0.5, detune: 1, harmonics: [1, 0.4, 0.2] },
{ id: 'electric_guitar_dist', name: 'Electric Guitar (Distorted)', category: 'strings', waveform: 'sawtooth', attack: 0.001, decay: 0.2, sustain: 0.7, release: 0.4, detune: 5, harmonics: [1, 0.9, 0.8, 0.6, 0.4], distortion: true },
{ id: 'nylon_guitar', name: 'Nylon Guitar', category: 'strings', waveform: 'sine', attack: 0.005, decay: 0.5, sustain: 0.15, release: 0.7, detune: 1, harmonics: [1, 0.4, 0.15] },
{ id: 'harp', name: 'Harp', category: 'strings', waveform: 'triangle', attack: 0.001, decay: 0.8, sustain: 0.1, release: 1.5, detune: 0, harmonics: [1, 0.3, 0.1] },
{ id: 'banjo', name: 'Banjo', category: 'strings', waveform: 'triangle', attack: 0.001, decay: 0.2, sustain: 0.1, release: 0.3, detune: 3, harmonics: [1, 0.6, 0.4], filter: 4500 },
{ id: 'mandolin', name: 'Mandolin', category: 'strings', waveform: 'triangle', attack: 0.001, decay: 0.15, sustain: 0.15, release: 0.3, detune: 4, harmonics: [1, 0.5, 0.3], tremolo: 8 },
// === BRASS ===
{ id: 'trumpet', name: 'Trumpet', category: 'brass', waveform: 'sawtooth', attack: 0.02, decay: 0.1, sustain: 0.7, release: 0.2, detune: 2, harmonics: [1, 0.6, 0.4, 0.3], filter: 5000, vibrato: 4 },
{ id: 'trombone', name: 'Trombone', category: 'brass', waveform: 'sawtooth', attack: 0.03, decay: 0.15, sustain: 0.65, release: 0.25, detune: 2, harmonics: [1, 0.7, 0.5, 0.3], filter: 3000, vibrato: 3 },
{ id: 'french_horn', name: 'French Horn', category: 'brass', waveform: 'sawtooth', attack: 0.05, decay: 0.2, sustain: 0.6, release: 0.3, detune: 3, harmonics: [1, 0.5, 0.35, 0.2], filter: 2500, vibrato: 3.5 },
{ id: 'tuba', name: 'Tuba', category: 'brass', waveform: 'sawtooth', attack: 0.06, decay: 0.2, sustain: 0.6, release: 0.35, detune: 2, harmonics: [1, 0.8, 0.5, 0.3], filter: 1500 },
{ id: 'brass_section', name: 'Brass Section', category: 'brass', waveform: 'sawtooth', attack: 0.04, decay: 0.15, sustain: 0.7, release: 0.25, detune: 6, harmonics: [1, 0.6, 0.4, 0.25], filter: 4000 },
{ id: 'muted_trumpet', name: 'Muted Trumpet', category: 'brass', waveform: 'square', attack: 0.02, decay: 0.1, sustain: 0.5, release: 0.2, detune: 2, harmonics: [1, 0.4, 0.2], filter: 2000 },
// === WOODWIND ===
{ id: 'flute', name: 'Flute', category: 'woodwind', waveform: 'sine', attack: 0.05, decay: 0.1, sustain: 0.7, release: 0.2, detune: 0, harmonics: [1, 0.2, 0.05], vibrato: 5 },
{ id: 'clarinet', name: 'Clarinet', category: 'woodwind', waveform: 'square', attack: 0.03, decay: 0.1, sustain: 0.7, release: 0.2, detune: 1, harmonics: [1, 0.0, 0.5, 0.0, 0.25], filter: 3000, vibrato: 4 },
{ id: 'oboe', name: 'Oboe', category: 'woodwind', waveform: 'sawtooth', attack: 0.02, decay: 0.1, sustain: 0.65, release: 0.15, detune: 2, harmonics: [1, 0.5, 0.3, 0.2], filter: 3500, vibrato: 5 },
{ id: 'bassoon', name: 'Bassoon', category: 'woodwind', waveform: 'sawtooth', attack: 0.04, decay: 0.15, sustain: 0.6, release: 0.25, detune: 2, harmonics: [1, 0.6, 0.4, 0.25], filter: 2000, vibrato: 3 },
{ id: 'alto_sax', name: 'Alto Saxophone', category: 'woodwind', waveform: 'sawtooth', attack: 0.02, decay: 0.1, sustain: 0.7, release: 0.2, detune: 3, harmonics: [1, 0.6, 0.35, 0.2], filter: 4000, vibrato: 4.5 },
{ id: 'tenor_sax', name: 'Tenor Saxophone', category: 'woodwind', waveform: 'sawtooth', attack: 0.025, decay: 0.12, sustain: 0.68, release: 0.22, detune: 3, harmonics: [1, 0.65, 0.4, 0.25], filter: 3000, vibrato: 4 },
{ id: 'soprano_sax', name: 'Soprano Saxophone', category: 'woodwind', waveform: 'sawtooth', attack: 0.015, decay: 0.08, sustain: 0.7, release: 0.18, detune: 3, harmonics: [1, 0.55, 0.3, 0.15], filter: 5000, vibrato: 5 },
{ id: 'recorder', name: 'Recorder', category: 'woodwind', waveform: 'sine', attack: 0.04, decay: 0.1, sustain: 0.6, release: 0.15, detune: 0, harmonics: [1, 0.15, 0.05] },
{ id: 'pan_flute', name: 'Pan Flute', category: 'woodwind', waveform: 'sine', attack: 0.08, decay: 0.15, sustain: 0.5, release: 0.3, detune: 0, harmonics: [1, 0.1] },
// === PERCUSSION ===
{ id: 'kick', name: 'Kick Drum', category: 'percussion', waveform: 'sine', attack: 0.001, decay: 0.15, sustain: 0.0, release: 0.2, detune: 0, harmonics: [1], pitchEnv: -50 },
{ id: 'snare', name: 'Snare Drum', category: 'percussion', waveform: 'triangle', attack: 0.001, decay: 0.1, sustain: 0.0, release: 0.15, detune: 0, harmonics: [1, 0.5], noise: 0.8 },
{ id: 'hihat_closed', name: 'Hi-Hat (Closed)', category: 'percussion', waveform: 'square', attack: 0.001, decay: 0.05, sustain: 0.0, release: 0.05, detune: 0, harmonics: [1], noise: 1, filter: 8000 },
{ id: 'hihat_open', name: 'Hi-Hat (Open)', category: 'percussion', waveform: 'square', attack: 0.001, decay: 0.3, sustain: 0.1, release: 0.3, detune: 0, harmonics: [1], noise: 1, filter: 7000 },
{ id: 'tom_high', name: 'Tom (High)', category: 'percussion', waveform: 'sine', attack: 0.001, decay: 0.2, sustain: 0.0, release: 0.25, detune: 0, harmonics: [1, 0.3], pitchEnv: -20 },
{ id: 'tom_low', name: 'Tom (Low)', category: 'percussion', waveform: 'sine', attack: 0.001, decay: 0.3, sustain: 0.0, release: 0.35, detune: 0, harmonics: [1, 0.4], pitchEnv: -30 },
{ id: 'crash', name: 'Crash Cymbal', category: 'percussion', waveform: 'sawtooth', attack: 0.001, decay: 1.0, sustain: 0.2, release: 1.5, detune: 10, harmonics: [1, 0.8, 0.6], noise: 0.9, filter: 6000 },
{ id: 'ride', name: 'Ride Cymbal', category: 'percussion', waveform: 'triangle', attack: 0.001, decay: 0.5, sustain: 0.3, release: 0.8, detune: 5, harmonics: [1, 0.6], noise: 0.5, filter: 7000 },
{ id: 'clap', name: 'Hand Clap', category: 'percussion', waveform: 'triangle', attack: 0.001, decay: 0.08, sustain: 0.0, release: 0.1, detune: 0, harmonics: [1], noise: 0.9, filter: 2000 },
{ id: 'cowbell', name: 'Cowbell', category: 'percussion', waveform: 'square', attack: 0.001, decay: 0.3, sustain: 0.0, release: 0.3, detune: 0, harmonics: [1, 0.8], filter: 3500 },
// === SYNTH ===
{ id: 'synth_lead', name: 'Synth Lead', category: 'synth', waveform: 'sawtooth', attack: 0.01, decay: 0.2, sustain: 0.6, release: 0.3, detune: 3, harmonics: [1, 0.5], filter: 5000 },
{ id: 'synth_pad', name: 'Synth Pad', category: 'synth', waveform: 'sawtooth', attack: 0.5, decay: 0.5, sustain: 0.8, release: 1.0, detune: 10, harmonics: [1, 0.4, 0.2], filter: 3000 },
{ id: 'synth_brass', name: 'Synth Brass', category: 'synth', waveform: 'sawtooth', attack: 0.03, decay: 0.15, sustain: 0.7, release: 0.2, detune: 5, harmonics: [1, 0.6, 0.3], filter: 4500 },
{ id: 'synth_strings', name: 'Synth Strings', category: 'synth', waveform: 'sawtooth', attack: 0.3, decay: 0.3, sustain: 0.7, release: 0.6, detune: 12, harmonics: [1, 0.5, 0.25], filter: 4000 },
{ id: 'square_lead', name: 'Square Lead', category: 'synth', waveform: 'square', attack: 0.01, decay: 0.1, sustain: 0.7, release: 0.2, detune: 0, harmonics: [1] },
{ id: 'pulse_lead', name: 'Pulse Lead', category: 'synth', waveform: 'square', attack: 0.005, decay: 0.15, sustain: 0.5, release: 0.25, detune: 2, harmonics: [1, 0.3], pwm: true },
{ id: 'fm_bell', name: 'FM Bell', category: 'synth', waveform: 'sine', attack: 0.001, decay: 1.0, sustain: 0.0, release: 1.5, detune: 0, harmonics: [1, 0.3, 0.1], fm: true },
{ id: 'chiptune', name: 'Chiptune', category: 'synth', waveform: 'square', attack: 0.001, decay: 0.05, sustain: 0.8, release: 0.05, detune: 0, harmonics: [1], filter: 8000 },
{ id: 'supersaw', name: 'Supersaw', category: 'synth', waveform: 'sawtooth', attack: 0.01, decay: 0.2, sustain: 0.7, release: 0.4, detune: 15, harmonics: [1, 1, 1, 1, 1], filter: 6000 },
{ id: 'ambient_pad', name: 'Ambient Pad', category: 'synth', waveform: 'sine', attack: 1.0, decay: 1.0, sustain: 0.9, release: 2.0, detune: 8, harmonics: [1, 0.3, 0.1], filter: 2000 },
// === BASS ===
{ id: 'acoustic_bass', name: 'Acoustic Bass', category: 'bass', waveform: 'triangle', attack: 0.01, decay: 0.3, sustain: 0.4, release: 0.4, detune: 1, harmonics: [1, 0.5, 0.2] },
{ id: 'electric_bass', name: 'Electric Bass', category: 'bass', waveform: 'sawtooth', attack: 0.005, decay: 0.2, sustain: 0.5, release: 0.3, detune: 2, harmonics: [1, 0.6, 0.3], filter: 2500 },
{ id: 'synth_bass', name: 'Synth Bass', category: 'bass', waveform: 'sawtooth', attack: 0.005, decay: 0.15, sustain: 0.6, release: 0.2, detune: 3, harmonics: [1, 0.8, 0.4], filter: 3000 },
{ id: 'sub_bass', name: 'Sub Bass', category: 'bass', waveform: 'sine', attack: 0.02, decay: 0.2, sustain: 0.8, release: 0.3, detune: 0, harmonics: [1] },
{ id: 'slap_bass', name: 'Slap Bass', category: 'bass', waveform: 'triangle', attack: 0.001, decay: 0.1, sustain: 0.3, release: 0.2, detune: 2, harmonics: [1, 0.7, 0.5, 0.3], filter: 4000 },
{ id: 'fretless_bass', name: 'Fretless Bass', category: 'bass', waveform: 'triangle', attack: 0.02, decay: 0.25, sustain: 0.5, release: 0.35, detune: 1, harmonics: [1, 0.4, 0.2], vibrato: 3 },
// === WORLD ===
{ id: 'sitar', name: 'Sitar', category: 'world', waveform: 'sawtooth', attack: 0.005, decay: 0.5, sustain: 0.3, release: 0.8, detune: 6, harmonics: [1, 0.7, 0.5, 0.4, 0.3], vibrato: 6, filter: 4000 },
{ id: 'koto', name: 'Koto', category: 'world', waveform: 'triangle', attack: 0.001, decay: 0.6, sustain: 0.1, release: 0.8, detune: 2, harmonics: [1, 0.4, 0.2] },
{ id: 'shamisen', name: 'Shamisen', category: 'world', waveform: 'sawtooth', attack: 0.001, decay: 0.2, sustain: 0.15, release: 0.3, detune: 3, harmonics: [1, 0.6, 0.4], filter: 4500 },
{ id: 'kalimba', name: 'Kalimba', category: 'world', waveform: 'sine', attack: 0.001, decay: 0.6, sustain: 0.0, release: 0.8, detune: 0, harmonics: [1, 0.3, 0.1] },
{ id: 'steel_drum', name: 'Steel Drum', category: 'world', waveform: 'sine', attack: 0.001, decay: 0.4, sustain: 0.1, release: 0.6, detune: 0, harmonics: [1, 0.5, 0.25], filter: 5000 },
{ id: 'bagpipe', name: 'Bagpipe', category: 'world', waveform: 'sawtooth', attack: 0.1, decay: 0.1, sustain: 0.95, release: 0.2, detune: 5, harmonics: [1, 0.8, 0.6, 0.4], filter: 2500, drone: true },
{ id: 'didgeridoo', name: 'Didgeridoo', category: 'world', waveform: 'sawtooth', attack: 0.1, decay: 0.2, sustain: 0.8, release: 0.3, detune: 3, harmonics: [1, 0.9, 0.7, 0.5], filter: 800 },
{ id: 'erhu', name: 'Erhu', category: 'world', waveform: 'sawtooth', attack: 0.08, decay: 0.2, sustain: 0.6, release: 0.3, detune: 4, harmonics: [1, 0.5, 0.3], filter: 3500, vibrato: 5.5 }
];
/**
* Synthesizer Engine Class
*/
class SynthesizerEngine {
constructor() {
this.audioContext = null;
this.masterGain = null;
this.reverbNode = null;
this.analyserWaveform = null;
this.analyserSpectrum = null;
this.activeVoices = new Map();
this.selectedInstrument = INSTRUMENTS[0];
this.currentOctave = 4;
this.masterVolume = 0.5;
this.reverbMix = 0.2;
this.consciousnessMod = 0.5;
this.sustainPedal = false;
this.sustainedNotes = new Set();
this.init();
}
async init() {
// Create audio context on first user interaction
if (!this.audioContext) {
this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
}
// Master gain
this.masterGain = this.audioContext.createGain();
this.masterGain.gain.value = this.masterVolume;
// Analyzers
this.analyserWaveform = this.audioContext.createAnalyser();
this.analyserWaveform.fftSize = 2048;
this.analyserSpectrum = this.audioContext.createAnalyser();
this.analyserSpectrum.fftSize = 512;
// Create reverb
this.reverbNode = await this.createReverb();
// Dry path
this.dryGain = this.audioContext.createGain();
this.dryGain.gain.value = 1 - this.reverbMix;
// Wet path
this.wetGain = this.audioContext.createGain();
this.wetGain.gain.value = this.reverbMix;
// Routing
this.masterGain.connect(this.dryGain);
this.masterGain.connect(this.reverbNode);
this.reverbNode.connect(this.wetGain);
this.dryGain.connect(this.analyserWaveform);
this.wetGain.connect(this.analyserWaveform);
this.analyserWaveform.connect(this.analyserSpectrum);
this.analyserSpectrum.connect(this.audioContext.destination);
}
async createReverb() {
const convolver = this.audioContext.createConvolver();
const rate = this.audioContext.sampleRate;
const length = rate * 2;
const impulse = this.audioContext.createBuffer(2, length, rate);
for (let channel = 0; channel < 2; channel++) {
const data = impulse.getChannelData(channel);
for (let i = 0; i < length; i++) {
data[i] = (Math.random() * 2 - 1) * Math.pow(1 - i / length, 2);
}
}
convolver.buffer = impulse;
return convolver;
}
setMasterVolume(value) {
this.masterVolume = value;
if (this.masterGain) {
this.masterGain.gain.value = value;
}
}
setReverbMix(value) {
this.reverbMix = value;
if (this.dryGain && this.wetGain) {
this.dryGain.gain.value = 1 - value;
this.wetGain.gain.value = value;
}
}
setConsciousnessMod(value) {
this.consciousnessMod = value;
}
selectInstrument(instrumentId) {
const instrument = INSTRUMENTS.find(i => i.id === instrumentId);
if (instrument) {
this.selectedInstrument = instrument;
return instrument;
}
return null;
}
setOctave(octave) {
this.currentOctave = Math.max(1, Math.min(8, octave));
}
noteToFrequency(note, octave = this.currentOctave) {
const noteMap = { 'C': 0, 'C#': 1, 'D': 2, 'D#': 3, 'E': 4, 'F': 5, 'F#': 6, 'G': 7, 'G#': 8, 'A': 9, 'A#': 10, 'B': 11 };
const semitone = noteMap[note];
if (semitone === undefined) return 440;
return 440 * Math.pow(2, (semitone - 9 + (octave - 4) * 12) / 12);
}
playNote(note, octave = this.currentOctave, velocity = 0.8) {
if (!this.audioContext) this.init();
if (this.audioContext.state === 'suspended') {
this.audioContext.resume();
}
const freq = typeof note === 'number' ? note : this.noteToFrequency(note, octave);
const noteId = `${note}-${octave}`;
// Stop existing note
this.stopNote(note, octave, true);
const inst = this.selectedInstrument;
const voices = [];
const time = this.audioContext.currentTime;
// Apply consciousness modulation
const conscMod = 1 + (this.consciousnessMod - 0.5) * 0.2;
// Create oscillators for harmonics
const harmonics = inst.harmonics || [1];
harmonics.forEach((harmAmp, idx) => {
if (harmAmp <= 0) return;
const osc = this.audioContext.createOscillator();
osc.type = inst.waveform;
// Frequency with detuning
const harmFreq = freq * (idx + 1);
const detuneCents = (inst.detune || 0) * (Math.random() - 0.5) * 2;
osc.frequency.value = harmFreq * conscMod;
osc.detune.value = detuneCents;
// Gain for this harmonic
const harmGain = this.audioContext.createGain();
harmGain.gain.value = 0;
// Optional filter
let filterNode = null;
if (inst.filter) {
filterNode = this.audioContext.createBiquadFilter();
filterNode.type = 'lowpass';
filterNode.frequency.value = inst.filter * conscMod;
filterNode.Q.value = 1;
osc.connect(filterNode);
filterNode.connect(harmGain);
} else {
osc.connect(harmGain);
}
harmGain.connect(this.masterGain);
// ADSR Envelope
const attack = inst.attack || 0.01;
const decay = inst.decay || 0.1;
const sustain = (inst.sustain || 0.5) * harmAmp * velocity;
const release = inst.release || 0.3;
// Attack
harmGain.gain.setValueAtTime(0, time);
harmGain.gain.linearRampToValueAtTime(harmAmp * velocity, time + attack);
// Decay to sustain
harmGain.gain.linearRampToValueAtTime(sustain, time + attack + decay);
// Pitch envelope for percussion
if (inst.pitchEnv) {
osc.frequency.setValueAtTime(harmFreq * 2, time);
osc.frequency.exponentialRampToValueAtTime(harmFreq, time + 0.05);
}
// Vibrato
if (inst.vibrato) {
const lfo = this.audioContext.createOscillator();
const lfoGain = this.audioContext.createGain();
lfo.frequency.value = inst.vibrato;
lfoGain.gain.value = freq * 0.01;
lfo.connect(lfoGain);
lfoGain.connect(osc.frequency);
lfo.start(time);
voices.push({ node: lfo, type: 'lfo' });
}
osc.start(time);
voices.push({ node: osc, gain: harmGain, filter: filterNode, type: 'osc', sustain, release });
});
// Add noise for percussion
if (inst.noise && inst.noise > 0) {
const bufferSize = this.audioContext.sampleRate * 0.5;
const noiseBuffer = this.audioContext.createBuffer(1, bufferSize, this.audioContext.sampleRate);
const output = noiseBuffer.getChannelData(0);
for (let i = 0; i < bufferSize; i++) {
output[i] = Math.random() * 2 - 1;
}
const noiseSource = this.audioContext.createBufferSource();
noiseSource.buffer = noiseBuffer;
const noiseGain = this.audioContext.createGain();
noiseGain.gain.value = 0;
const noiseFilter = this.audioContext.createBiquadFilter();
noiseFilter.type = 'highpass';
noiseFilter.frequency.value = inst.filter || 4000;
noiseSource.connect(noiseFilter);
noiseFilter.connect(noiseGain);
noiseGain.connect(this.masterGain);
const attack = inst.attack || 0.001;
const decay = inst.decay || 0.1;
const sustain = inst.noise * velocity * 0.3;
const release = inst.release || 0.1;
noiseGain.gain.setValueAtTime(0, time);
noiseGain.gain.linearRampToValueAtTime(inst.noise * velocity * 0.5, time + attack);
noiseGain.gain.linearRampToValueAtTime(sustain, time + attack + decay);
noiseSource.start(time);
voices.push({ node: noiseSource, gain: noiseGain, type: 'noise', sustain, release });
}
this.activeVoices.set(noteId, voices);
return noteId;
}
stopNote(note, octave = this.currentOctave, immediate = false) {
const noteId = `${note}-${octave}`;
const voices = this.activeVoices.get(noteId);
if (!voices) return;
// If sustain pedal is on and not immediate, add to sustained notes
if (this.sustainPedal && !immediate) {
this.sustainedNotes.add(noteId);
return;
}
const time = this.audioContext.currentTime;
voices.forEach(voice => {
if (voice.gain) {
const release = voice.release || 0.3;
voice.gain.gain.cancelScheduledValues(time);
voice.gain.gain.setValueAtTime(voice.gain.gain.value, time);
voice.gain.gain.linearRampToValueAtTime(0, time + release);
if (voice.node) {
voice.node.stop(time + release + 0.1);
}
} else if (voice.type === 'lfo' && voice.node) {
voice.node.stop(time + 0.5);
}
});
setTimeout(() => {
this.activeVoices.delete(noteId);
}, 1000);
}
setSustainPedal(on) {
this.sustainPedal = on;
if (!on) {
// Release all sustained notes
this.sustainedNotes.forEach(noteId => {
const [note, octave] = noteId.split('-');
this.stopNote(note, parseInt(octave), false);
});
this.sustainedNotes.clear();
}
}
playTestNote(instrumentId) {
const prevInst = this.selectedInstrument;
this.selectInstrument(instrumentId);
const inst = INSTRUMENTS.find(i => i.id === instrumentId);
let testNote = 'C';
let testOctave = 4;
// Adjust for bass instruments
if (inst.category === 'bass') {
testOctave = 2;
} else if (inst.category === 'percussion') {
testOctave = 3;
}
this.playNote(testNote, testOctave, 0.7);
setTimeout(() => {
this.stopNote(testNote, testOctave);
this.selectedInstrument = prevInst;
}, 500);
}
getWaveformData() {
if (!this.analyserWaveform) return new Uint8Array(128);
const data = new Uint8Array(this.analyserWaveform.frequencyBinCount);
this.analyserWaveform.getByteTimeDomainData(data);
return data;
}
getSpectrumData() {
if (!this.analyserSpectrum) return new Uint8Array(64);
const data = new Uint8Array(this.analyserSpectrum.frequencyBinCount);
this.analyserSpectrum.getByteFrequencyData(data);
return data;
}
stopAll() {
this.activeVoices.forEach((voices, noteId) => {
const [note, octave] = noteId.split('-');
this.stopNote(note, parseInt(octave), true);
});
this.sustainedNotes.clear();
}
}
// Export
window.INSTRUMENTS = INSTRUMENTS;
window.SynthesizerEngine = SynthesizerEngine;