@al8b/audio 0.1.12 → 0.1.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -24,7 +24,6 @@ __export(audio_core_exports, {
24
24
  AudioCore: () => AudioCore
25
25
  });
26
26
  module.exports = __toCommonJS(audio_core_exports);
27
- var import_diagnostics = require("@al8b/diagnostics");
28
27
 
29
28
  // src/constants.ts
30
29
  var A4_FREQUENCY = 440;
@@ -474,8 +473,12 @@ var AudioCore = class {
474
473
  const soundName = sound.replace(/\//g, "-");
475
474
  const s = this.runtime.sounds[soundName];
476
475
  if (!s) {
477
- (0, import_diagnostics.reportRuntimeError)(this.runtime?.listener, import_diagnostics.APIErrorCode.E7013, {
478
- soundName
476
+ this.runtime?.listener?.reportError?.({
477
+ code: "E7013",
478
+ message: "Sound not found",
479
+ data: {
480
+ soundName
481
+ }
479
482
  });
480
483
  return 0;
481
484
  }
@@ -491,8 +494,12 @@ var AudioCore = class {
491
494
  const musicName = music.replace(/\//g, "-");
492
495
  const m = this.runtime.music[musicName];
493
496
  if (!m) {
494
- (0, import_diagnostics.reportRuntimeError)(this.runtime?.listener, import_diagnostics.APIErrorCode.E7014, {
495
- musicName
497
+ this.runtime?.listener?.reportError?.({
498
+ code: "E7014",
499
+ message: "Music not found",
500
+ data: {
501
+ musicName
502
+ }
496
503
  });
497
504
  return 0;
498
505
  }
@@ -559,8 +566,12 @@ var AudioCore = class {
559
566
  this.workletNode.connect(this.context.destination);
560
567
  this.flushBuffer();
561
568
  } catch (e) {
562
- (0, import_diagnostics.reportRuntimeError)(this.runtime?.listener, import_diagnostics.APIErrorCode.E7012, {
563
- error: String(e)
569
+ this.runtime?.listener?.reportError?.({
570
+ code: "E7012",
571
+ message: "Audio worklet error",
572
+ data: {
573
+ error: String(e)
574
+ }
564
575
  });
565
576
  }
566
577
  }
@@ -649,8 +660,12 @@ var AudioCore = class {
649
660
  try {
650
661
  p.stop();
651
662
  } catch (err) {
652
- (0, import_diagnostics.reportRuntimeError)(this.runtime?.listener, import_diagnostics.APIErrorCode.E7016, {
653
- error: String(err)
663
+ this.runtime?.listener?.reportError?.({
664
+ code: "E7016",
665
+ message: "Audio error",
666
+ data: {
667
+ error: String(err)
668
+ }
654
669
  });
655
670
  }
656
671
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/core/audio-core.ts","../../src/constants.ts","../../src/devices/beeper.ts","../../src/core/audio-worklet.ts"],"sourcesContent":["/**\n * AudioCore - Web Audio API wrapper\n * Manages audio context, beeper, and sound/music playback\n */\n\nimport { APIErrorCode, reportRuntimeError } from \"@al8b/diagnostics\";\nimport { Beeper } from \"../devices/beeper\";\nimport { AUDIO_WORKLET_CODE } from \"./audio-worklet\";\n\n/** An actively playing sound/music that can be stopped */\ninterface PlayingHandle {\n\tstop: () => void;\n}\n\n/** Item that can be woken up on audio context activation */\ninterface WakeUpItem {\n\twakeUp: () => void;\n}\n\nexport class AudioCore {\n\tpublic context!: AudioContext;\n\tprivate buffer: string[] = [];\n\tprivate playing: PlayingHandle[] = [];\n\tprivate wakeupList: WakeUpItem[] = [];\n\tprivate workletNode?: AudioWorkletNode;\n\tprivate beeper?: Beeper;\n\tprivate runtime: any;\n\tprivate masterVolume: number = 1;\n\n\tconstructor(runtime: any) {\n\t\tthis.runtime = runtime;\n\t\tthis.getContext();\n\t}\n\n\t/**\n\t * Check if audio context is running\n\t */\n\tpublic isStarted(): boolean {\n\t\treturn this.context.state === \"running\";\n\t}\n\n\t/**\n\t * Add item to wakeup list (for mobile audio activation)\n\t */\n\tpublic addToWakeUpList(item: WakeUpItem): void {\n\t\tthis.wakeupList.push(item);\n\t}\n\n\tprivate interfaceCache: Record<string, any> | null = null;\n\n\t/**\n\t * Set master volume (0-1). Applied as a multiplier to all sound/music playback.\n\t */\n\tpublic setVolume(volume: number): void {\n\t\tthis.masterVolume = Math.max(0, Math.min(1, volume));\n\t}\n\n\t/**\n\t * Get current master volume (0-1)\n\t */\n\tpublic getVolume(): number {\n\t\treturn this.masterVolume;\n\t}\n\n\t/**\n\t * Get interface for game code\n\t */\n\tpublic getInterface() {\n\t\tif (this.interfaceCache) {\n\t\t\treturn this.interfaceCache;\n\t\t}\n\t\tthis.interfaceCache = {\n\t\t\tbeep: (sequence: string) => this.beep(sequence),\n\t\t\tcancelBeeps: () => this.cancelBeeps(),\n\t\t\tplaySound: (sound: any, volume?: number, pitch?: number, pan?: number, loopit?: boolean) =>\n\t\t\t\tthis.playSound(sound, volume, pitch, pan, loopit),\n\t\t\tplayMusic: (music: any, volume?: number, loopit?: boolean) => this.playMusic(music, volume, loopit),\n\t\t\tsetVolume: (volume: number) => this.setVolume(volume),\n\t\t\tgetVolume: () => this.getVolume(),\n\t\t\tstopAll: () => this.stopAll(),\n\t\t};\n\t\treturn this.interfaceCache;\n\t}\n\n\t/**\n\t * Play sound effect\n\t */\n\tpublic playSound(sound: any, volume: number = 1, pitch: number = 1, pan: number = 0, loopit: boolean = false): number {\n\t\tif (typeof sound === \"string\") {\n\t\t\tconst soundName = sound.replace(/\\//g, \"-\");\n\t\t\tconst s = this.runtime.sounds[soundName];\n\t\t\tif (!s) {\n\t\t\t\treportRuntimeError(this.runtime?.listener, APIErrorCode.E7013, { soundName });\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\treturn s.play(volume * this.masterVolume, pitch, pan, loopit);\n\t\t}\n\t\treturn 0;\n\t}\n\n\t/**\n\t * Play music\n\t */\n\tpublic playMusic(music: any, volume: number = 1, loopit: boolean = false): number {\n\t\tif (typeof music === \"string\") {\n\t\t\tconst musicName = music.replace(/\\//g, \"-\");\n\t\t\tconst m = this.runtime.music[musicName];\n\t\t\tif (!m) {\n\t\t\t\treportRuntimeError(this.runtime?.listener, APIErrorCode.E7014, { musicName });\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\treturn m.play(volume * this.masterVolume, loopit);\n\t\t}\n\t\treturn 0;\n\t}\n\n\t/**\n\t * Get or create audio context (lazy initialization - created on first use)\n\t * Note: Browser may suspend context until user interaction, which is handled automatically\n\t */\n\tpublic getContext(): AudioContext {\n\t\tif (!this.context) {\n\t\t\tconst AudioContextClass = (window as any).AudioContext || (window as any).webkitAudioContext;\n\t\t\t// Create context - browser may suspend until user interaction\n\t\t\tthis.context = new AudioContextClass();\n\n\t\t\t// If context is suspended, set up activation listeners\n\t\t\tif (this.context.state !== \"running\") {\n\t\t\t\tconst activate = () => {\n\t\t\t\t\tif (this.context && this.context.state !== \"running\") {\n\t\t\t\t\t\tthis.context.resume();\n\t\t\t\t\t\tif (this.beeper) {\n\t\t\t\t\t\t\tthis.start();\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfor (const item of this.wakeupList) {\n\t\t\t\t\t\t\titem.wakeUp();\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Clean up listeners\n\t\t\t\t\t\tdocument.body.removeEventListener(\"touchend\", activate);\n\t\t\t\t\t\tdocument.body.removeEventListener(\"mouseup\", activate);\n\t\t\t\t\t\tdocument.body.removeEventListener(\"click\", activate);\n\t\t\t\t\t\tdocument.body.removeEventListener(\"keydown\", activate);\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\t// Add multiple event listeners for better compatibility\n\t\t\t\tdocument.body.addEventListener(\"touchend\", activate, {\n\t\t\t\t\tonce: true,\n\t\t\t\t});\n\t\t\t\tdocument.body.addEventListener(\"mouseup\", activate, {\n\t\t\t\t\tonce: true,\n\t\t\t\t});\n\t\t\t\tdocument.body.addEventListener(\"click\", activate, {\n\t\t\t\t\tonce: true,\n\t\t\t\t});\n\t\t\t\tdocument.body.addEventListener(\"keydown\", activate, {\n\t\t\t\t\tonce: true,\n\t\t\t\t});\n\t\t\t} else if (this.beeper) {\n\t\t\t\tthis.start();\n\t\t\t}\n\t\t}\n\n\t\treturn this.context;\n\t}\n\n\t/**\n\t * Start audio processor\n\t */\n\tpublic async start(): Promise<void> {\n\t\tif (this.workletNode) return;\n\n\t\ttry {\n\t\t\tconst blob = new Blob([AUDIO_WORKLET_CODE], {\n\t\t\t\ttype: \"application/javascript\",\n\t\t\t});\n\t\t\tconst url = URL.createObjectURL(blob);\n\n\t\t\tawait this.context.audioWorklet.addModule(url);\n\n\t\t\tthis.workletNode = new AudioWorkletNode(this.context, \"l8b-audio-processor\");\n\t\t\tthis.workletNode.connect(this.context.destination);\n\n\t\t\tthis.flushBuffer();\n\t\t} catch (e) {\n\t\t\treportRuntimeError(this.runtime?.listener, APIErrorCode.E7012, { error: String(e) });\n\t\t}\n\t}\n\n\t/**\n\t * Flush buffered messages\n\t */\n\tprivate flushBuffer(): void {\n\t\tif (!this.workletNode) return;\n\n\t\twhile (this.buffer.length > 0) {\n\t\t\tthis.workletNode.port.postMessage(this.buffer.splice(0, 1)[0]);\n\t\t}\n\t}\n\n\t/**\n\t * Get or create beeper\n\t */\n\tpublic getBeeper(): Beeper {\n\t\tif (!this.beeper) {\n\t\t\t// Create Beeper instance\n\t\t\tthis.beeper = new Beeper(this);\n\n\t\t\tif (this.context.state === \"running\") {\n\t\t\t\tthis.start();\n\t\t\t}\n\t\t}\n\t\treturn this.beeper;\n\t}\n\n\t/**\n\t * Play beep sequence\n\t */\n\tpublic beep(sequence: string): void {\n\t\tthis.getBeeper().beep(sequence);\n\t}\n\n\t/**\n\t * Add beeps to audio processor\n\t */\n\tpublic addBeeps(beeps: any[]): void {\n\t\tfor (const b of beeps) {\n\t\t\tb.duration *= this.context.sampleRate;\n\t\t\tb.increment = b.frequency / this.context.sampleRate;\n\t\t}\n\n\t\tif (this.workletNode) {\n\t\t\tthis.workletNode.port.postMessage(\n\t\t\t\tJSON.stringify({\n\t\t\t\t\tname: \"beep\",\n\t\t\t\t\tsequence: beeps,\n\t\t\t\t}),\n\t\t\t);\n\t\t} else {\n\t\t\tthis.buffer.push(\n\t\t\t\tJSON.stringify({\n\t\t\t\t\tname: \"beep\",\n\t\t\t\t\tsequence: beeps,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Cancel all beeps\n\t */\n\tpublic cancelBeeps(): void {\n\t\tif (this.workletNode) {\n\t\t\tthis.workletNode.port.postMessage(\n\t\t\t\tJSON.stringify({\n\t\t\t\t\tname: \"cancel_beeps\",\n\t\t\t\t}),\n\t\t\t);\n\t\t} else {\n\t\t\tthis.buffer.push(\n\t\t\t\tJSON.stringify({\n\t\t\t\t\tname: \"cancel_beeps\",\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\tthis.stopAll();\n\t}\n\n\t/**\n\t * Add playing sound/music to list\n\t */\n\tpublic addPlaying(item: PlayingHandle): void {\n\t\tthis.playing.push(item);\n\t}\n\n\t/**\n\t * Remove playing sound/music from list\n\t */\n\tpublic removePlaying(item: PlayingHandle): void {\n\t\tconst index = this.playing.indexOf(item);\n\t\tif (index >= 0) {\n\t\t\tthis.playing.splice(index, 1);\n\t\t}\n\t}\n\n\t/**\n\t * Stop all playing sounds/music\n\t */\n\tpublic stopAll(): void {\n\t\tfor (const p of this.playing) {\n\t\t\ttry {\n\t\t\t\tp.stop();\n\t\t\t} catch (err) {\n\t\t\t\treportRuntimeError(this.runtime?.listener, APIErrorCode.E7016, { error: String(err) });\n\t\t\t}\n\t\t}\n\t\tthis.playing = [];\n\t}\n}\n","/** A4 reference frequency in Hz (concert pitch) */\nexport const A4_FREQUENCY = 440;\n\n/** Ratio between adjacent semitones in equal temperament */\nexport const SEMITONE_RATIO = 2 ** (1 / 12);\n\n/** MIDI note number for A4 */\nexport const A4_MIDI_NOTE = 69;\n","/**\n * Beeper - Procedural sound generation from text sequences\n * Converts music notation strings into beep sequences\n * Example: \"square tempo 120 C4 D4 E4 F4\"\n */\nimport { A4_FREQUENCY, A4_MIDI_NOTE, SEMITONE_RATIO } from \"../constants\";\nexport class Beeper {\n\tprivate audio: any;\n\tprivate notes: Record<string | number, number> = {};\n\tprivate plainNotes: Record<string, number> = {};\n\tprivate currentOctave = 5;\n\tprivate currentDuration = 0.5;\n\tprivate currentVolume = 0.5;\n\tprivate currentSpan = 1;\n\tprivate currentWaveform = \"square\";\n\n\tconstructor(audio: any) {\n\t\tthis.audio = audio;\n\t\tthis.initializeNotes();\n\t}\n\n\t/**\n\t * Initialize note mappings\n\t */\n\tprivate initializeNotes(): void {\n\t\tconst noteNames = [\n\t\t\t[\"C\", \"DO\"],\n\t\t\t[\"C#\", \"DO#\", \"Db\", \"REb\"],\n\t\t\t[\"D\", \"RE\"],\n\t\t\t[\"D#\", \"RE#\", \"Eb\", \"MIb\"],\n\t\t\t[\"E\", \"MI\"],\n\t\t\t[\"F\", \"FA\"],\n\t\t\t[\"F#\", \"FA#\", \"Gb\", \"SOLb\"],\n\t\t\t[\"G\", \"SOL\"],\n\t\t\t[\"G#\", \"SOL#\", \"Ab\", \"LAb\"],\n\t\t\t[\"A\", \"LA\"],\n\t\t\t[\"A#\", \"LA#\", \"Bb\", \"SIb\"],\n\t\t\t[\"B\", \"SI\"],\n\t\t];\n\n\t\tfor (let i = 0; i <= 127; i++) {\n\t\t\tthis.notes[i] = i;\n\t\t\tconst oct = Math.floor(i / 12) - 1;\n\n\t\t\tfor (const n of noteNames[i % 12]) {\n\t\t\t\tthis.notes[n + oct] = i;\n\t\t\t}\n\n\t\t\tif (oct === -1) {\n\t\t\t\tfor (const n of noteNames[i % 12]) {\n\t\t\t\t\tthis.plainNotes[n] = i;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Parse and play beep sequence\n\t */\n\tpublic beep(input: string): void {\n\t\tlet status: string = \"normal\";\n\t\tconst sequence: any[] = [];\n\t\tconst loops: any[] = [];\n\t\tlet note: number | undefined;\n\n\t\tconst parsed = input.split(\" \");\n\n\t\tfor (const t of parsed) {\n\t\t\tif (t === \"\") continue;\n\n\t\t\tswitch (status) {\n\t\t\t\tcase \"normal\":\n\t\t\t\t\tif (this.notes[t] !== undefined) {\n\t\t\t\t\t\t// Full note with octave (e.g., \"C4\")\n\t\t\t\t\t\tnote = this.notes[t];\n\t\t\t\t\t\tthis.currentOctave = Math.floor(note / 12);\n\t\t\t\t\t\tsequence.push({\n\t\t\t\t\t\t\tfrequency: A4_FREQUENCY * SEMITONE_RATIO ** (note - A4_MIDI_NOTE),\n\t\t\t\t\t\t\tvolume: this.currentVolume,\n\t\t\t\t\t\t\tspan: this.currentSpan,\n\t\t\t\t\t\t\tduration: this.currentDuration,\n\t\t\t\t\t\t\twaveform: this.currentWaveform,\n\t\t\t\t\t\t});\n\t\t\t\t\t} else if (this.plainNotes[t] !== undefined) {\n\t\t\t\t\t\t// Note without octave (e.g., \"C\")\n\t\t\t\t\t\tnote = this.plainNotes[t] + this.currentOctave * 12;\n\t\t\t\t\t\tsequence.push({\n\t\t\t\t\t\t\tfrequency: A4_FREQUENCY * SEMITONE_RATIO ** (note - A4_MIDI_NOTE),\n\t\t\t\t\t\t\tvolume: this.currentVolume,\n\t\t\t\t\t\t\tspan: this.currentSpan,\n\t\t\t\t\t\t\tduration: this.currentDuration,\n\t\t\t\t\t\t\twaveform: this.currentWaveform,\n\t\t\t\t\t\t});\n\t\t\t\t\t} else if ([\"square\", \"sine\", \"saw\", \"noise\"].includes(t)) {\n\t\t\t\t\t\t// Waveform\n\t\t\t\t\t\tthis.currentWaveform = t;\n\t\t\t\t\t} else if ([\"tempo\", \"duration\", \"volume\", \"span\", \"loop\", \"to\"].includes(t)) {\n\t\t\t\t\t\t// Commands\n\t\t\t\t\t\tstatus = t;\n\t\t\t\t\t} else if (t === \"-\") {\n\t\t\t\t\t\t// Rest/silence\n\t\t\t\t\t\tsequence.push({\n\t\t\t\t\t\t\tfrequency: A4_FREQUENCY,\n\t\t\t\t\t\t\tvolume: 0,\n\t\t\t\t\t\t\tspan: this.currentSpan,\n\t\t\t\t\t\t\tduration: this.currentDuration,\n\t\t\t\t\t\t\twaveform: this.currentWaveform,\n\t\t\t\t\t\t});\n\t\t\t\t\t} else if (t === \"end\") {\n\t\t\t\t\t\t// End loop\n\t\t\t\t\t\tif (loops.length > 0 && sequence.length > 0) {\n\t\t\t\t\t\t\tsequence.push({\n\t\t\t\t\t\t\t\tfrequency: A4_FREQUENCY,\n\t\t\t\t\t\t\t\tvolume: 0,\n\t\t\t\t\t\t\t\tspan: this.currentSpan,\n\t\t\t\t\t\t\t\tduration: 0,\n\t\t\t\t\t\t\t\twaveform: this.currentWaveform,\n\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\tconst lop = loops.splice(loops.length - 1, 1)[0];\n\t\t\t\t\t\t\tsequence[sequence.length - 1].loopto = lop.start;\n\t\t\t\t\t\t\tsequence[sequence.length - 1].repeats = lop.repeats;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase \"tempo\": {\n\t\t\t\t\tstatus = \"normal\";\n\t\t\t\t\tconst tempo = Number.parseFloat(t);\n\t\t\t\t\tif (!Number.isNaN(tempo) && tempo > 0) {\n\t\t\t\t\t\tthis.currentDuration = 60 / tempo;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase \"duration\": {\n\t\t\t\t\tstatus = \"normal\";\n\t\t\t\t\tconst duration = Number.parseFloat(t);\n\t\t\t\t\tif (!Number.isNaN(duration) && duration > 0) {\n\t\t\t\t\t\tthis.currentDuration = duration / 1000;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase \"volume\": {\n\t\t\t\t\tstatus = \"normal\";\n\t\t\t\t\tconst volume = Number.parseFloat(t);\n\t\t\t\t\tif (!Number.isNaN(volume)) {\n\t\t\t\t\t\tthis.currentVolume = volume / 100;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase \"span\": {\n\t\t\t\t\tstatus = \"normal\";\n\t\t\t\t\tconst span = Number.parseFloat(t);\n\t\t\t\t\tif (!Number.isNaN(span)) {\n\t\t\t\t\t\tthis.currentSpan = span / 100;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase \"loop\": {\n\t\t\t\t\tstatus = \"normal\";\n\t\t\t\t\tloops.push({\n\t\t\t\t\t\tstart: sequence.length,\n\t\t\t\t\t});\n\t\t\t\t\tconst repeats = Number.parseFloat(t);\n\t\t\t\t\tif (!Number.isNaN(repeats)) {\n\t\t\t\t\t\tloops[loops.length - 1].repeats = repeats;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase \"to\":\n\t\t\t\t\tstatus = \"normal\";\n\t\t\t\t\tif (note !== undefined) {\n\t\t\t\t\t\tlet n: number | undefined;\n\n\t\t\t\t\t\tif (this.notes[t] !== undefined) {\n\t\t\t\t\t\t\tn = this.notes[t];\n\t\t\t\t\t\t} else if (this.plainNotes[t] !== undefined) {\n\t\t\t\t\t\t\tn = this.plainNotes[t] + this.currentOctave * 12;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (n !== undefined && n !== note) {\n\t\t\t\t\t\t\t// Generate slide from note to n\n\t\t\t\t\t\t\tconst step = n > note ? 1 : -1;\n\t\t\t\t\t\t\tfor (let i = note + step; step > 0 ? i <= n : i >= n; i += step) {\n\t\t\t\t\t\t\t\tsequence.push({\n\t\t\t\t\t\t\t\t\tfrequency: A4_FREQUENCY * SEMITONE_RATIO ** (i - A4_MIDI_NOTE),\n\t\t\t\t\t\t\t\t\tvolume: this.currentVolume,\n\t\t\t\t\t\t\t\t\tspan: this.currentSpan,\n\t\t\t\t\t\t\t\t\tduration: this.currentDuration,\n\t\t\t\t\t\t\t\t\twaveform: this.currentWaveform,\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tnote = n;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t// Close any remaining loops\n\t\tif (loops.length > 0 && sequence.length > 0) {\n\t\t\tconst lop = loops.splice(loops.length - 1, 1)[0];\n\t\t\tsequence.push({\n\t\t\t\tfrequency: A4_FREQUENCY,\n\t\t\t\tvolume: 0,\n\t\t\t\tspan: this.currentSpan,\n\t\t\t\tduration: 0,\n\t\t\t\twaveform: this.currentWaveform,\n\t\t\t});\n\n\t\t\tsequence[sequence.length - 1].loopto = lop.start;\n\t\t\tsequence[sequence.length - 1].repeats = lop.repeats;\n\t\t}\n\n\t\tthis.audio.addBeeps(sequence);\n\t}\n}\n","export const AUDIO_WORKLET_CODE = `\nclass L8bAudioProcessor extends AudioWorkletProcessor {\n constructor() {\n super();\n this.beeps = [];\n this.last = 0;\n this.port.onmessage = (event) => {\n const data = JSON.parse(event.data);\n if (data.name === \"cancel_beeps\") {\n this.beeps = [];\n } else if (data.name === \"beep\") {\n const seq = data.sequence;\n // Link sequence notes together\n for (let i = 0; i < seq.length; i++) {\n const note = seq[i];\n if (i > 0) {\n seq[i - 1].next = note;\n }\n // Resolve loopto index to actual note reference\n if (note.loopto != null) {\n note.loopto = seq[note.loopto];\n }\n // Initialize phase and time\n note.phase = 0;\n note.time = 0;\n }\n // Add first note to beeps queue\n if (seq.length > 0) {\n this.beeps.push(seq[0]);\n }\n }\n };\n }\n\n process(inputs, outputs, parameters) {\n const output = outputs[0];\n \n for (let i = 0; i < output.length; i++) {\n const channel = output[i];\n \n if (i > 0) {\n // Copy first channel to other channels\n for (let j = 0; j < channel.length; j++) {\n channel[j] = output[0][j];\n }\n } else {\n // Generate audio for first channel\n for (let j = 0; j < channel.length; j++) {\n let sig = 0;\n \n for (let k = this.beeps.length - 1; k >= 0; k--) {\n const b = this.beeps[k];\n let volume = b.volume;\n \n if (b.time / b.duration > b.span) {\n volume = 0;\n }\n \n // Generate waveform\n switch (b.waveform) {\n case \"square\":\n sig += b.phase > 0.5 ? volume : -volume;\n break;\n case \"saw\":\n sig += (b.phase * 2 - 1) * volume;\n break;\n case \"noise\":\n sig += (Math.random() * 2 - 1) * volume;\n break;\n default: // sine\n sig += Math.sin(b.phase * Math.PI * 2) * volume;\n }\n \n b.phase = (b.phase + b.increment) % 1;\n b.time += 1;\n \n if (b.time >= b.duration) {\n b.time = 0;\n \n if (b.loopto != null) {\n if (b.repeats != null && b.repeats > 0) {\n if (b.loopcount == null) {\n b.loopcount = 0;\n }\n b.loopcount++;\n \n if (b.loopcount >= b.repeats) {\n b.loopcount = 0;\n if (b.next != null) {\n b.next.phase = b.phase;\n this.beeps[k] = b.next;\n } else {\n this.beeps.splice(k, 1);\n }\n } else {\n b.loopto.phase = b.phase;\n this.beeps[k] = b.loopto;\n }\n } else {\n b.loopto.phase = b.phase;\n this.beeps[k] = b.loopto;\n }\n } else if (b.next != null) {\n b.next.phase = b.phase;\n this.beeps[k] = b.next;\n } else {\n this.beeps.splice(k, 1);\n }\n }\n }\n \n this.last = this.last * 0.9 + sig * 0.1;\n channel[j] = this.last;\n }\n }\n }\n \n return true;\n }\n}\n\nregisterProcessor(\"l8b-audio-processor\", L8bAudioProcessor);\n`;\n"],"mappings":"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","names":["A4_FREQUENCY","SEMITONE_RATIO","A4_MIDI_NOTE","Beeper","audio","notes","plainNotes","currentOctave","currentDuration","currentVolume","currentSpan","currentWaveform","initializeNotes","noteNames","i","oct","Math","floor","n","beep","input","status","sequence","loops","note","parsed","split","t","undefined","push","frequency","A4_FREQUENCY","SEMITONE_RATIO","A4_MIDI_NOTE","volume","span","duration","waveform","includes","length","lop","splice","loopto","start","repeats","tempo","Number","parseFloat","isNaN","step","addBeeps","AUDIO_WORKLET_CODE","AudioCore","context","buffer","playing","wakeupList","workletNode","beeper","runtime","masterVolume","getContext","isStarted","state","addToWakeUpList","item","push","interfaceCache","setVolume","volume","Math","max","min","getVolume","getInterface","beep","sequence","cancelBeeps","playSound","sound","pitch","pan","loopit","playMusic","music","stopAll","soundName","replace","s","sounds","reportRuntimeError","listener","APIErrorCode","E7013","play","musicName","m","E7014","AudioContextClass","window","AudioContext","webkitAudioContext","activate","resume","start","wakeUp","document","body","removeEventListener","addEventListener","once","blob","Blob","AUDIO_WORKLET_CODE","type","url","URL","createObjectURL","audioWorklet","addModule","AudioWorkletNode","connect","destination","flushBuffer","e","E7012","error","String","length","port","postMessage","splice","getBeeper","Beeper","addBeeps","beeps","b","duration","sampleRate","increment","frequency","JSON","stringify","name","addPlaying","removePlaying","index","indexOf","p","stop","err","E7016"]}
1
+ {"version":3,"sources":["../../src/core/audio-core.ts","../../src/constants.ts","../../src/devices/beeper.ts","../../src/core/audio-worklet.ts"],"sourcesContent":["/**\n * AudioCore - Web Audio API wrapper\n * Manages audio context, beeper, and sound/music playback\n */\n\nimport { Beeper } from \"../devices/beeper\";\nimport { AUDIO_WORKLET_CODE } from \"./audio-worklet\";\n\n/** An actively playing sound/music that can be stopped */\ninterface PlayingHandle {\n\tstop: () => void;\n}\n\n/** Item that can be woken up on audio context activation */\ninterface WakeUpItem {\n\twakeUp: () => void;\n}\n\nexport class AudioCore {\n\tpublic context!: AudioContext;\n\tprivate buffer: string[] = [];\n\tprivate playing: PlayingHandle[] = [];\n\tprivate wakeupList: WakeUpItem[] = [];\n\tprivate workletNode?: AudioWorkletNode;\n\tprivate beeper?: Beeper;\n\tprivate runtime: any;\n\tprivate masterVolume: number = 1;\n\n\tconstructor(runtime: any) {\n\t\tthis.runtime = runtime;\n\t\tthis.getContext();\n\t}\n\n\t/**\n\t * Check if audio context is running\n\t */\n\tpublic isStarted(): boolean {\n\t\treturn this.context.state === \"running\";\n\t}\n\n\t/**\n\t * Add item to wakeup list (for mobile audio activation)\n\t */\n\tpublic addToWakeUpList(item: WakeUpItem): void {\n\t\tthis.wakeupList.push(item);\n\t}\n\n\tprivate interfaceCache: Record<string, any> | null = null;\n\n\t/**\n\t * Set master volume (0-1). Applied as a multiplier to all sound/music playback.\n\t */\n\tpublic setVolume(volume: number): void {\n\t\tthis.masterVolume = Math.max(0, Math.min(1, volume));\n\t}\n\n\t/**\n\t * Get current master volume (0-1)\n\t */\n\tpublic getVolume(): number {\n\t\treturn this.masterVolume;\n\t}\n\n\t/**\n\t * Get interface for game code\n\t */\n\tpublic getInterface() {\n\t\tif (this.interfaceCache) {\n\t\t\treturn this.interfaceCache;\n\t\t}\n\t\tthis.interfaceCache = {\n\t\t\tbeep: (sequence: string) => this.beep(sequence),\n\t\t\tcancelBeeps: () => this.cancelBeeps(),\n\t\t\tplaySound: (sound: any, volume?: number, pitch?: number, pan?: number, loopit?: boolean) =>\n\t\t\t\tthis.playSound(sound, volume, pitch, pan, loopit),\n\t\t\tplayMusic: (music: any, volume?: number, loopit?: boolean) => this.playMusic(music, volume, loopit),\n\t\t\tsetVolume: (volume: number) => this.setVolume(volume),\n\t\t\tgetVolume: () => this.getVolume(),\n\t\t\tstopAll: () => this.stopAll(),\n\t\t};\n\t\treturn this.interfaceCache;\n\t}\n\n\t/**\n\t * Play sound effect\n\t */\n\tpublic playSound(sound: any, volume: number = 1, pitch: number = 1, pan: number = 0, loopit: boolean = false): number {\n\t\tif (typeof sound === \"string\") {\n\t\t\tconst soundName = sound.replace(/\\//g, \"-\");\n\t\t\tconst s = this.runtime.sounds[soundName];\n\t\t\tif (!s) {\n\t\t\t\tthis.runtime?.listener?.reportError?.({ code: \"E7013\", message: \"Sound not found\", data: { soundName } });\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\treturn s.play(volume * this.masterVolume, pitch, pan, loopit);\n\t\t}\n\t\treturn 0;\n\t}\n\n\t/**\n\t * Play music\n\t */\n\tpublic playMusic(music: any, volume: number = 1, loopit: boolean = false): number {\n\t\tif (typeof music === \"string\") {\n\t\t\tconst musicName = music.replace(/\\//g, \"-\");\n\t\t\tconst m = this.runtime.music[musicName];\n\t\t\tif (!m) {\n\t\t\t\tthis.runtime?.listener?.reportError?.({ code: \"E7014\", message: \"Music not found\", data: { musicName } });\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\treturn m.play(volume * this.masterVolume, loopit);\n\t\t}\n\t\treturn 0;\n\t}\n\n\t/**\n\t * Get or create audio context (lazy initialization - created on first use)\n\t * Note: Browser may suspend context until user interaction, which is handled automatically\n\t */\n\tpublic getContext(): AudioContext {\n\t\tif (!this.context) {\n\t\t\tconst AudioContextClass = (window as any).AudioContext || (window as any).webkitAudioContext;\n\t\t\t// Create context - browser may suspend until user interaction\n\t\t\tthis.context = new AudioContextClass();\n\n\t\t\t// If context is suspended, set up activation listeners\n\t\t\tif (this.context.state !== \"running\") {\n\t\t\t\tconst activate = () => {\n\t\t\t\t\tif (this.context && this.context.state !== \"running\") {\n\t\t\t\t\t\tthis.context.resume();\n\t\t\t\t\t\tif (this.beeper) {\n\t\t\t\t\t\t\tthis.start();\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfor (const item of this.wakeupList) {\n\t\t\t\t\t\t\titem.wakeUp();\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Clean up listeners\n\t\t\t\t\t\tdocument.body.removeEventListener(\"touchend\", activate);\n\t\t\t\t\t\tdocument.body.removeEventListener(\"mouseup\", activate);\n\t\t\t\t\t\tdocument.body.removeEventListener(\"click\", activate);\n\t\t\t\t\t\tdocument.body.removeEventListener(\"keydown\", activate);\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\t// Add multiple event listeners for better compatibility\n\t\t\t\tdocument.body.addEventListener(\"touchend\", activate, {\n\t\t\t\t\tonce: true,\n\t\t\t\t});\n\t\t\t\tdocument.body.addEventListener(\"mouseup\", activate, {\n\t\t\t\t\tonce: true,\n\t\t\t\t});\n\t\t\t\tdocument.body.addEventListener(\"click\", activate, {\n\t\t\t\t\tonce: true,\n\t\t\t\t});\n\t\t\t\tdocument.body.addEventListener(\"keydown\", activate, {\n\t\t\t\t\tonce: true,\n\t\t\t\t});\n\t\t\t} else if (this.beeper) {\n\t\t\t\tthis.start();\n\t\t\t}\n\t\t}\n\n\t\treturn this.context;\n\t}\n\n\t/**\n\t * Start audio processor\n\t */\n\tpublic async start(): Promise<void> {\n\t\tif (this.workletNode) return;\n\n\t\ttry {\n\t\t\tconst blob = new Blob([AUDIO_WORKLET_CODE], {\n\t\t\t\ttype: \"application/javascript\",\n\t\t\t});\n\t\t\tconst url = URL.createObjectURL(blob);\n\n\t\t\tawait this.context.audioWorklet.addModule(url);\n\n\t\t\tthis.workletNode = new AudioWorkletNode(this.context, \"l8b-audio-processor\");\n\t\t\tthis.workletNode.connect(this.context.destination);\n\n\t\t\tthis.flushBuffer();\n\t\t} catch (e) {\n\t\t\tthis.runtime?.listener?.reportError?.({ code: \"E7012\", message: \"Audio worklet error\", data: { error: String(e) } });\n\t\t}\n\t}\n\n\t/**\n\t * Flush buffered messages\n\t */\n\tprivate flushBuffer(): void {\n\t\tif (!this.workletNode) return;\n\n\t\twhile (this.buffer.length > 0) {\n\t\t\tthis.workletNode.port.postMessage(this.buffer.splice(0, 1)[0]);\n\t\t}\n\t}\n\n\t/**\n\t * Get or create beeper\n\t */\n\tpublic getBeeper(): Beeper {\n\t\tif (!this.beeper) {\n\t\t\t// Create Beeper instance\n\t\t\tthis.beeper = new Beeper(this);\n\n\t\t\tif (this.context.state === \"running\") {\n\t\t\t\tthis.start();\n\t\t\t}\n\t\t}\n\t\treturn this.beeper;\n\t}\n\n\t/**\n\t * Play beep sequence\n\t */\n\tpublic beep(sequence: string): void {\n\t\tthis.getBeeper().beep(sequence);\n\t}\n\n\t/**\n\t * Add beeps to audio processor\n\t */\n\tpublic addBeeps(beeps: any[]): void {\n\t\tfor (const b of beeps) {\n\t\t\tb.duration *= this.context.sampleRate;\n\t\t\tb.increment = b.frequency / this.context.sampleRate;\n\t\t}\n\n\t\tif (this.workletNode) {\n\t\t\tthis.workletNode.port.postMessage(\n\t\t\t\tJSON.stringify({\n\t\t\t\t\tname: \"beep\",\n\t\t\t\t\tsequence: beeps,\n\t\t\t\t}),\n\t\t\t);\n\t\t} else {\n\t\t\tthis.buffer.push(\n\t\t\t\tJSON.stringify({\n\t\t\t\t\tname: \"beep\",\n\t\t\t\t\tsequence: beeps,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Cancel all beeps\n\t */\n\tpublic cancelBeeps(): void {\n\t\tif (this.workletNode) {\n\t\t\tthis.workletNode.port.postMessage(\n\t\t\t\tJSON.stringify({\n\t\t\t\t\tname: \"cancel_beeps\",\n\t\t\t\t}),\n\t\t\t);\n\t\t} else {\n\t\t\tthis.buffer.push(\n\t\t\t\tJSON.stringify({\n\t\t\t\t\tname: \"cancel_beeps\",\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\tthis.stopAll();\n\t}\n\n\t/**\n\t * Add playing sound/music to list\n\t */\n\tpublic addPlaying(item: PlayingHandle): void {\n\t\tthis.playing.push(item);\n\t}\n\n\t/**\n\t * Remove playing sound/music from list\n\t */\n\tpublic removePlaying(item: PlayingHandle): void {\n\t\tconst index = this.playing.indexOf(item);\n\t\tif (index >= 0) {\n\t\t\tthis.playing.splice(index, 1);\n\t\t}\n\t}\n\n\t/**\n\t * Stop all playing sounds/music\n\t */\n\tpublic stopAll(): void {\n\t\tfor (const p of this.playing) {\n\t\t\ttry {\n\t\t\t\tp.stop();\n\t\t\t} catch (err) {\n\t\t\t\tthis.runtime?.listener?.reportError?.({ code: \"E7016\", message: \"Audio error\", data: { error: String(err) } });\n\t\t\t}\n\t\t}\n\t\tthis.playing = [];\n\t}\n}\n","/** A4 reference frequency in Hz (concert pitch) */\nexport const A4_FREQUENCY = 440;\n\n/** Ratio between adjacent semitones in equal temperament */\nexport const SEMITONE_RATIO = 2 ** (1 / 12);\n\n/** MIDI note number for A4 */\nexport const A4_MIDI_NOTE = 69;\n","/**\n * Beeper - Procedural sound generation from text sequences\n * Converts music notation strings into beep sequences\n * Example: \"square tempo 120 C4 D4 E4 F4\"\n */\nimport { A4_FREQUENCY, A4_MIDI_NOTE, SEMITONE_RATIO } from \"../constants\";\nexport class Beeper {\n\tprivate audio: any;\n\tprivate notes: Record<string | number, number> = {};\n\tprivate plainNotes: Record<string, number> = {};\n\tprivate currentOctave = 5;\n\tprivate currentDuration = 0.5;\n\tprivate currentVolume = 0.5;\n\tprivate currentSpan = 1;\n\tprivate currentWaveform = \"square\";\n\n\tconstructor(audio: any) {\n\t\tthis.audio = audio;\n\t\tthis.initializeNotes();\n\t}\n\n\t/**\n\t * Initialize note mappings\n\t */\n\tprivate initializeNotes(): void {\n\t\tconst noteNames = [\n\t\t\t[\"C\", \"DO\"],\n\t\t\t[\"C#\", \"DO#\", \"Db\", \"REb\"],\n\t\t\t[\"D\", \"RE\"],\n\t\t\t[\"D#\", \"RE#\", \"Eb\", \"MIb\"],\n\t\t\t[\"E\", \"MI\"],\n\t\t\t[\"F\", \"FA\"],\n\t\t\t[\"F#\", \"FA#\", \"Gb\", \"SOLb\"],\n\t\t\t[\"G\", \"SOL\"],\n\t\t\t[\"G#\", \"SOL#\", \"Ab\", \"LAb\"],\n\t\t\t[\"A\", \"LA\"],\n\t\t\t[\"A#\", \"LA#\", \"Bb\", \"SIb\"],\n\t\t\t[\"B\", \"SI\"],\n\t\t];\n\n\t\tfor (let i = 0; i <= 127; i++) {\n\t\t\tthis.notes[i] = i;\n\t\t\tconst oct = Math.floor(i / 12) - 1;\n\n\t\t\tfor (const n of noteNames[i % 12]) {\n\t\t\t\tthis.notes[n + oct] = i;\n\t\t\t}\n\n\t\t\tif (oct === -1) {\n\t\t\t\tfor (const n of noteNames[i % 12]) {\n\t\t\t\t\tthis.plainNotes[n] = i;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Parse and play beep sequence\n\t */\n\tpublic beep(input: string): void {\n\t\tlet status: string = \"normal\";\n\t\tconst sequence: any[] = [];\n\t\tconst loops: any[] = [];\n\t\tlet note: number | undefined;\n\n\t\tconst parsed = input.split(\" \");\n\n\t\tfor (const t of parsed) {\n\t\t\tif (t === \"\") continue;\n\n\t\t\tswitch (status) {\n\t\t\t\tcase \"normal\":\n\t\t\t\t\tif (this.notes[t] !== undefined) {\n\t\t\t\t\t\t// Full note with octave (e.g., \"C4\")\n\t\t\t\t\t\tnote = this.notes[t];\n\t\t\t\t\t\tthis.currentOctave = Math.floor(note / 12);\n\t\t\t\t\t\tsequence.push({\n\t\t\t\t\t\t\tfrequency: A4_FREQUENCY * SEMITONE_RATIO ** (note - A4_MIDI_NOTE),\n\t\t\t\t\t\t\tvolume: this.currentVolume,\n\t\t\t\t\t\t\tspan: this.currentSpan,\n\t\t\t\t\t\t\tduration: this.currentDuration,\n\t\t\t\t\t\t\twaveform: this.currentWaveform,\n\t\t\t\t\t\t});\n\t\t\t\t\t} else if (this.plainNotes[t] !== undefined) {\n\t\t\t\t\t\t// Note without octave (e.g., \"C\")\n\t\t\t\t\t\tnote = this.plainNotes[t] + this.currentOctave * 12;\n\t\t\t\t\t\tsequence.push({\n\t\t\t\t\t\t\tfrequency: A4_FREQUENCY * SEMITONE_RATIO ** (note - A4_MIDI_NOTE),\n\t\t\t\t\t\t\tvolume: this.currentVolume,\n\t\t\t\t\t\t\tspan: this.currentSpan,\n\t\t\t\t\t\t\tduration: this.currentDuration,\n\t\t\t\t\t\t\twaveform: this.currentWaveform,\n\t\t\t\t\t\t});\n\t\t\t\t\t} else if ([\"square\", \"sine\", \"saw\", \"noise\"].includes(t)) {\n\t\t\t\t\t\t// Waveform\n\t\t\t\t\t\tthis.currentWaveform = t;\n\t\t\t\t\t} else if ([\"tempo\", \"duration\", \"volume\", \"span\", \"loop\", \"to\"].includes(t)) {\n\t\t\t\t\t\t// Commands\n\t\t\t\t\t\tstatus = t;\n\t\t\t\t\t} else if (t === \"-\") {\n\t\t\t\t\t\t// Rest/silence\n\t\t\t\t\t\tsequence.push({\n\t\t\t\t\t\t\tfrequency: A4_FREQUENCY,\n\t\t\t\t\t\t\tvolume: 0,\n\t\t\t\t\t\t\tspan: this.currentSpan,\n\t\t\t\t\t\t\tduration: this.currentDuration,\n\t\t\t\t\t\t\twaveform: this.currentWaveform,\n\t\t\t\t\t\t});\n\t\t\t\t\t} else if (t === \"end\") {\n\t\t\t\t\t\t// End loop\n\t\t\t\t\t\tif (loops.length > 0 && sequence.length > 0) {\n\t\t\t\t\t\t\tsequence.push({\n\t\t\t\t\t\t\t\tfrequency: A4_FREQUENCY,\n\t\t\t\t\t\t\t\tvolume: 0,\n\t\t\t\t\t\t\t\tspan: this.currentSpan,\n\t\t\t\t\t\t\t\tduration: 0,\n\t\t\t\t\t\t\t\twaveform: this.currentWaveform,\n\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\tconst lop = loops.splice(loops.length - 1, 1)[0];\n\t\t\t\t\t\t\tsequence[sequence.length - 1].loopto = lop.start;\n\t\t\t\t\t\t\tsequence[sequence.length - 1].repeats = lop.repeats;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase \"tempo\": {\n\t\t\t\t\tstatus = \"normal\";\n\t\t\t\t\tconst tempo = Number.parseFloat(t);\n\t\t\t\t\tif (!Number.isNaN(tempo) && tempo > 0) {\n\t\t\t\t\t\tthis.currentDuration = 60 / tempo;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase \"duration\": {\n\t\t\t\t\tstatus = \"normal\";\n\t\t\t\t\tconst duration = Number.parseFloat(t);\n\t\t\t\t\tif (!Number.isNaN(duration) && duration > 0) {\n\t\t\t\t\t\tthis.currentDuration = duration / 1000;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase \"volume\": {\n\t\t\t\t\tstatus = \"normal\";\n\t\t\t\t\tconst volume = Number.parseFloat(t);\n\t\t\t\t\tif (!Number.isNaN(volume)) {\n\t\t\t\t\t\tthis.currentVolume = volume / 100;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase \"span\": {\n\t\t\t\t\tstatus = \"normal\";\n\t\t\t\t\tconst span = Number.parseFloat(t);\n\t\t\t\t\tif (!Number.isNaN(span)) {\n\t\t\t\t\t\tthis.currentSpan = span / 100;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase \"loop\": {\n\t\t\t\t\tstatus = \"normal\";\n\t\t\t\t\tloops.push({\n\t\t\t\t\t\tstart: sequence.length,\n\t\t\t\t\t});\n\t\t\t\t\tconst repeats = Number.parseFloat(t);\n\t\t\t\t\tif (!Number.isNaN(repeats)) {\n\t\t\t\t\t\tloops[loops.length - 1].repeats = repeats;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase \"to\":\n\t\t\t\t\tstatus = \"normal\";\n\t\t\t\t\tif (note !== undefined) {\n\t\t\t\t\t\tlet n: number | undefined;\n\n\t\t\t\t\t\tif (this.notes[t] !== undefined) {\n\t\t\t\t\t\t\tn = this.notes[t];\n\t\t\t\t\t\t} else if (this.plainNotes[t] !== undefined) {\n\t\t\t\t\t\t\tn = this.plainNotes[t] + this.currentOctave * 12;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (n !== undefined && n !== note) {\n\t\t\t\t\t\t\t// Generate slide from note to n\n\t\t\t\t\t\t\tconst step = n > note ? 1 : -1;\n\t\t\t\t\t\t\tfor (let i = note + step; step > 0 ? i <= n : i >= n; i += step) {\n\t\t\t\t\t\t\t\tsequence.push({\n\t\t\t\t\t\t\t\t\tfrequency: A4_FREQUENCY * SEMITONE_RATIO ** (i - A4_MIDI_NOTE),\n\t\t\t\t\t\t\t\t\tvolume: this.currentVolume,\n\t\t\t\t\t\t\t\t\tspan: this.currentSpan,\n\t\t\t\t\t\t\t\t\tduration: this.currentDuration,\n\t\t\t\t\t\t\t\t\twaveform: this.currentWaveform,\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tnote = n;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t// Close any remaining loops\n\t\tif (loops.length > 0 && sequence.length > 0) {\n\t\t\tconst lop = loops.splice(loops.length - 1, 1)[0];\n\t\t\tsequence.push({\n\t\t\t\tfrequency: A4_FREQUENCY,\n\t\t\t\tvolume: 0,\n\t\t\t\tspan: this.currentSpan,\n\t\t\t\tduration: 0,\n\t\t\t\twaveform: this.currentWaveform,\n\t\t\t});\n\n\t\t\tsequence[sequence.length - 1].loopto = lop.start;\n\t\t\tsequence[sequence.length - 1].repeats = lop.repeats;\n\t\t}\n\n\t\tthis.audio.addBeeps(sequence);\n\t}\n}\n","export const AUDIO_WORKLET_CODE = `\nclass L8bAudioProcessor extends AudioWorkletProcessor {\n constructor() {\n super();\n this.beeps = [];\n this.last = 0;\n this.port.onmessage = (event) => {\n const data = JSON.parse(event.data);\n if (data.name === \"cancel_beeps\") {\n this.beeps = [];\n } else if (data.name === \"beep\") {\n const seq = data.sequence;\n // Link sequence notes together\n for (let i = 0; i < seq.length; i++) {\n const note = seq[i];\n if (i > 0) {\n seq[i - 1].next = note;\n }\n // Resolve loopto index to actual note reference\n if (note.loopto != null) {\n note.loopto = seq[note.loopto];\n }\n // Initialize phase and time\n note.phase = 0;\n note.time = 0;\n }\n // Add first note to beeps queue\n if (seq.length > 0) {\n this.beeps.push(seq[0]);\n }\n }\n };\n }\n\n process(inputs, outputs, parameters) {\n const output = outputs[0];\n \n for (let i = 0; i < output.length; i++) {\n const channel = output[i];\n \n if (i > 0) {\n // Copy first channel to other channels\n for (let j = 0; j < channel.length; j++) {\n channel[j] = output[0][j];\n }\n } else {\n // Generate audio for first channel\n for (let j = 0; j < channel.length; j++) {\n let sig = 0;\n \n for (let k = this.beeps.length - 1; k >= 0; k--) {\n const b = this.beeps[k];\n let volume = b.volume;\n \n if (b.time / b.duration > b.span) {\n volume = 0;\n }\n \n // Generate waveform\n switch (b.waveform) {\n case \"square\":\n sig += b.phase > 0.5 ? volume : -volume;\n break;\n case \"saw\":\n sig += (b.phase * 2 - 1) * volume;\n break;\n case \"noise\":\n sig += (Math.random() * 2 - 1) * volume;\n break;\n default: // sine\n sig += Math.sin(b.phase * Math.PI * 2) * volume;\n }\n \n b.phase = (b.phase + b.increment) % 1;\n b.time += 1;\n \n if (b.time >= b.duration) {\n b.time = 0;\n \n if (b.loopto != null) {\n if (b.repeats != null && b.repeats > 0) {\n if (b.loopcount == null) {\n b.loopcount = 0;\n }\n b.loopcount++;\n \n if (b.loopcount >= b.repeats) {\n b.loopcount = 0;\n if (b.next != null) {\n b.next.phase = b.phase;\n this.beeps[k] = b.next;\n } else {\n this.beeps.splice(k, 1);\n }\n } else {\n b.loopto.phase = b.phase;\n this.beeps[k] = b.loopto;\n }\n } else {\n b.loopto.phase = b.phase;\n this.beeps[k] = b.loopto;\n }\n } else if (b.next != null) {\n b.next.phase = b.phase;\n this.beeps[k] = b.next;\n } else {\n this.beeps.splice(k, 1);\n }\n }\n }\n \n this.last = this.last * 0.9 + sig * 0.1;\n channel[j] = this.last;\n }\n }\n }\n \n return true;\n }\n}\n\nregisterProcessor(\"l8b-audio-processor\", L8bAudioProcessor);\n`;\n"],"mappings":"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","names":["A4_FREQUENCY","SEMITONE_RATIO","A4_MIDI_NOTE","Beeper","audio","notes","plainNotes","currentOctave","currentDuration","currentVolume","currentSpan","currentWaveform","initializeNotes","noteNames","i","oct","Math","floor","n","beep","input","status","sequence","loops","note","parsed","split","t","undefined","push","frequency","A4_FREQUENCY","SEMITONE_RATIO","A4_MIDI_NOTE","volume","span","duration","waveform","includes","length","lop","splice","loopto","start","repeats","tempo","Number","parseFloat","isNaN","step","addBeeps","AUDIO_WORKLET_CODE","AudioCore","context","buffer","playing","wakeupList","workletNode","beeper","runtime","masterVolume","getContext","isStarted","state","addToWakeUpList","item","push","interfaceCache","setVolume","volume","Math","max","min","getVolume","getInterface","beep","sequence","cancelBeeps","playSound","sound","pitch","pan","loopit","playMusic","music","stopAll","soundName","replace","s","sounds","listener","reportError","code","message","data","play","musicName","m","AudioContextClass","window","AudioContext","webkitAudioContext","activate","resume","start","wakeUp","document","body","removeEventListener","addEventListener","once","blob","Blob","AUDIO_WORKLET_CODE","type","url","URL","createObjectURL","audioWorklet","addModule","AudioWorkletNode","connect","destination","flushBuffer","e","error","String","length","port","postMessage","splice","getBeeper","Beeper","addBeeps","beeps","b","duration","sampleRate","increment","frequency","JSON","stringify","name","addPlaying","removePlaying","index","indexOf","p","stop","err"]}
@@ -1,9 +1,6 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
3
 
4
- // src/core/audio-core.ts
5
- import { APIErrorCode, reportRuntimeError } from "@al8b/diagnostics";
6
-
7
4
  // src/constants.ts
8
5
  var A4_FREQUENCY = 440;
9
6
  var SEMITONE_RATIO = 2 ** (1 / 12);
@@ -452,8 +449,12 @@ var AudioCore = class {
452
449
  const soundName = sound.replace(/\//g, "-");
453
450
  const s = this.runtime.sounds[soundName];
454
451
  if (!s) {
455
- reportRuntimeError(this.runtime?.listener, APIErrorCode.E7013, {
456
- soundName
452
+ this.runtime?.listener?.reportError?.({
453
+ code: "E7013",
454
+ message: "Sound not found",
455
+ data: {
456
+ soundName
457
+ }
457
458
  });
458
459
  return 0;
459
460
  }
@@ -469,8 +470,12 @@ var AudioCore = class {
469
470
  const musicName = music.replace(/\//g, "-");
470
471
  const m = this.runtime.music[musicName];
471
472
  if (!m) {
472
- reportRuntimeError(this.runtime?.listener, APIErrorCode.E7014, {
473
- musicName
473
+ this.runtime?.listener?.reportError?.({
474
+ code: "E7014",
475
+ message: "Music not found",
476
+ data: {
477
+ musicName
478
+ }
474
479
  });
475
480
  return 0;
476
481
  }
@@ -537,8 +542,12 @@ var AudioCore = class {
537
542
  this.workletNode.connect(this.context.destination);
538
543
  this.flushBuffer();
539
544
  } catch (e) {
540
- reportRuntimeError(this.runtime?.listener, APIErrorCode.E7012, {
541
- error: String(e)
545
+ this.runtime?.listener?.reportError?.({
546
+ code: "E7012",
547
+ message: "Audio worklet error",
548
+ data: {
549
+ error: String(e)
550
+ }
542
551
  });
543
552
  }
544
553
  }
@@ -627,8 +636,12 @@ var AudioCore = class {
627
636
  try {
628
637
  p.stop();
629
638
  } catch (err) {
630
- reportRuntimeError(this.runtime?.listener, APIErrorCode.E7016, {
631
- error: String(err)
639
+ this.runtime?.listener?.reportError?.({
640
+ code: "E7016",
641
+ message: "Audio error",
642
+ data: {
643
+ error: String(err)
644
+ }
632
645
  });
633
646
  }
634
647
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/core/audio-core.ts","../../src/constants.ts","../../src/devices/beeper.ts","../../src/core/audio-worklet.ts"],"sourcesContent":["/**\n * AudioCore - Web Audio API wrapper\n * Manages audio context, beeper, and sound/music playback\n */\n\nimport { APIErrorCode, reportRuntimeError } from \"@al8b/diagnostics\";\nimport { Beeper } from \"../devices/beeper\";\nimport { AUDIO_WORKLET_CODE } from \"./audio-worklet\";\n\n/** An actively playing sound/music that can be stopped */\ninterface PlayingHandle {\n\tstop: () => void;\n}\n\n/** Item that can be woken up on audio context activation */\ninterface WakeUpItem {\n\twakeUp: () => void;\n}\n\nexport class AudioCore {\n\tpublic context!: AudioContext;\n\tprivate buffer: string[] = [];\n\tprivate playing: PlayingHandle[] = [];\n\tprivate wakeupList: WakeUpItem[] = [];\n\tprivate workletNode?: AudioWorkletNode;\n\tprivate beeper?: Beeper;\n\tprivate runtime: any;\n\tprivate masterVolume: number = 1;\n\n\tconstructor(runtime: any) {\n\t\tthis.runtime = runtime;\n\t\tthis.getContext();\n\t}\n\n\t/**\n\t * Check if audio context is running\n\t */\n\tpublic isStarted(): boolean {\n\t\treturn this.context.state === \"running\";\n\t}\n\n\t/**\n\t * Add item to wakeup list (for mobile audio activation)\n\t */\n\tpublic addToWakeUpList(item: WakeUpItem): void {\n\t\tthis.wakeupList.push(item);\n\t}\n\n\tprivate interfaceCache: Record<string, any> | null = null;\n\n\t/**\n\t * Set master volume (0-1). Applied as a multiplier to all sound/music playback.\n\t */\n\tpublic setVolume(volume: number): void {\n\t\tthis.masterVolume = Math.max(0, Math.min(1, volume));\n\t}\n\n\t/**\n\t * Get current master volume (0-1)\n\t */\n\tpublic getVolume(): number {\n\t\treturn this.masterVolume;\n\t}\n\n\t/**\n\t * Get interface for game code\n\t */\n\tpublic getInterface() {\n\t\tif (this.interfaceCache) {\n\t\t\treturn this.interfaceCache;\n\t\t}\n\t\tthis.interfaceCache = {\n\t\t\tbeep: (sequence: string) => this.beep(sequence),\n\t\t\tcancelBeeps: () => this.cancelBeeps(),\n\t\t\tplaySound: (sound: any, volume?: number, pitch?: number, pan?: number, loopit?: boolean) =>\n\t\t\t\tthis.playSound(sound, volume, pitch, pan, loopit),\n\t\t\tplayMusic: (music: any, volume?: number, loopit?: boolean) => this.playMusic(music, volume, loopit),\n\t\t\tsetVolume: (volume: number) => this.setVolume(volume),\n\t\t\tgetVolume: () => this.getVolume(),\n\t\t\tstopAll: () => this.stopAll(),\n\t\t};\n\t\treturn this.interfaceCache;\n\t}\n\n\t/**\n\t * Play sound effect\n\t */\n\tpublic playSound(sound: any, volume: number = 1, pitch: number = 1, pan: number = 0, loopit: boolean = false): number {\n\t\tif (typeof sound === \"string\") {\n\t\t\tconst soundName = sound.replace(/\\//g, \"-\");\n\t\t\tconst s = this.runtime.sounds[soundName];\n\t\t\tif (!s) {\n\t\t\t\treportRuntimeError(this.runtime?.listener, APIErrorCode.E7013, { soundName });\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\treturn s.play(volume * this.masterVolume, pitch, pan, loopit);\n\t\t}\n\t\treturn 0;\n\t}\n\n\t/**\n\t * Play music\n\t */\n\tpublic playMusic(music: any, volume: number = 1, loopit: boolean = false): number {\n\t\tif (typeof music === \"string\") {\n\t\t\tconst musicName = music.replace(/\\//g, \"-\");\n\t\t\tconst m = this.runtime.music[musicName];\n\t\t\tif (!m) {\n\t\t\t\treportRuntimeError(this.runtime?.listener, APIErrorCode.E7014, { musicName });\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\treturn m.play(volume * this.masterVolume, loopit);\n\t\t}\n\t\treturn 0;\n\t}\n\n\t/**\n\t * Get or create audio context (lazy initialization - created on first use)\n\t * Note: Browser may suspend context until user interaction, which is handled automatically\n\t */\n\tpublic getContext(): AudioContext {\n\t\tif (!this.context) {\n\t\t\tconst AudioContextClass = (window as any).AudioContext || (window as any).webkitAudioContext;\n\t\t\t// Create context - browser may suspend until user interaction\n\t\t\tthis.context = new AudioContextClass();\n\n\t\t\t// If context is suspended, set up activation listeners\n\t\t\tif (this.context.state !== \"running\") {\n\t\t\t\tconst activate = () => {\n\t\t\t\t\tif (this.context && this.context.state !== \"running\") {\n\t\t\t\t\t\tthis.context.resume();\n\t\t\t\t\t\tif (this.beeper) {\n\t\t\t\t\t\t\tthis.start();\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfor (const item of this.wakeupList) {\n\t\t\t\t\t\t\titem.wakeUp();\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Clean up listeners\n\t\t\t\t\t\tdocument.body.removeEventListener(\"touchend\", activate);\n\t\t\t\t\t\tdocument.body.removeEventListener(\"mouseup\", activate);\n\t\t\t\t\t\tdocument.body.removeEventListener(\"click\", activate);\n\t\t\t\t\t\tdocument.body.removeEventListener(\"keydown\", activate);\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\t// Add multiple event listeners for better compatibility\n\t\t\t\tdocument.body.addEventListener(\"touchend\", activate, {\n\t\t\t\t\tonce: true,\n\t\t\t\t});\n\t\t\t\tdocument.body.addEventListener(\"mouseup\", activate, {\n\t\t\t\t\tonce: true,\n\t\t\t\t});\n\t\t\t\tdocument.body.addEventListener(\"click\", activate, {\n\t\t\t\t\tonce: true,\n\t\t\t\t});\n\t\t\t\tdocument.body.addEventListener(\"keydown\", activate, {\n\t\t\t\t\tonce: true,\n\t\t\t\t});\n\t\t\t} else if (this.beeper) {\n\t\t\t\tthis.start();\n\t\t\t}\n\t\t}\n\n\t\treturn this.context;\n\t}\n\n\t/**\n\t * Start audio processor\n\t */\n\tpublic async start(): Promise<void> {\n\t\tif (this.workletNode) return;\n\n\t\ttry {\n\t\t\tconst blob = new Blob([AUDIO_WORKLET_CODE], {\n\t\t\t\ttype: \"application/javascript\",\n\t\t\t});\n\t\t\tconst url = URL.createObjectURL(blob);\n\n\t\t\tawait this.context.audioWorklet.addModule(url);\n\n\t\t\tthis.workletNode = new AudioWorkletNode(this.context, \"l8b-audio-processor\");\n\t\t\tthis.workletNode.connect(this.context.destination);\n\n\t\t\tthis.flushBuffer();\n\t\t} catch (e) {\n\t\t\treportRuntimeError(this.runtime?.listener, APIErrorCode.E7012, { error: String(e) });\n\t\t}\n\t}\n\n\t/**\n\t * Flush buffered messages\n\t */\n\tprivate flushBuffer(): void {\n\t\tif (!this.workletNode) return;\n\n\t\twhile (this.buffer.length > 0) {\n\t\t\tthis.workletNode.port.postMessage(this.buffer.splice(0, 1)[0]);\n\t\t}\n\t}\n\n\t/**\n\t * Get or create beeper\n\t */\n\tpublic getBeeper(): Beeper {\n\t\tif (!this.beeper) {\n\t\t\t// Create Beeper instance\n\t\t\tthis.beeper = new Beeper(this);\n\n\t\t\tif (this.context.state === \"running\") {\n\t\t\t\tthis.start();\n\t\t\t}\n\t\t}\n\t\treturn this.beeper;\n\t}\n\n\t/**\n\t * Play beep sequence\n\t */\n\tpublic beep(sequence: string): void {\n\t\tthis.getBeeper().beep(sequence);\n\t}\n\n\t/**\n\t * Add beeps to audio processor\n\t */\n\tpublic addBeeps(beeps: any[]): void {\n\t\tfor (const b of beeps) {\n\t\t\tb.duration *= this.context.sampleRate;\n\t\t\tb.increment = b.frequency / this.context.sampleRate;\n\t\t}\n\n\t\tif (this.workletNode) {\n\t\t\tthis.workletNode.port.postMessage(\n\t\t\t\tJSON.stringify({\n\t\t\t\t\tname: \"beep\",\n\t\t\t\t\tsequence: beeps,\n\t\t\t\t}),\n\t\t\t);\n\t\t} else {\n\t\t\tthis.buffer.push(\n\t\t\t\tJSON.stringify({\n\t\t\t\t\tname: \"beep\",\n\t\t\t\t\tsequence: beeps,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Cancel all beeps\n\t */\n\tpublic cancelBeeps(): void {\n\t\tif (this.workletNode) {\n\t\t\tthis.workletNode.port.postMessage(\n\t\t\t\tJSON.stringify({\n\t\t\t\t\tname: \"cancel_beeps\",\n\t\t\t\t}),\n\t\t\t);\n\t\t} else {\n\t\t\tthis.buffer.push(\n\t\t\t\tJSON.stringify({\n\t\t\t\t\tname: \"cancel_beeps\",\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\tthis.stopAll();\n\t}\n\n\t/**\n\t * Add playing sound/music to list\n\t */\n\tpublic addPlaying(item: PlayingHandle): void {\n\t\tthis.playing.push(item);\n\t}\n\n\t/**\n\t * Remove playing sound/music from list\n\t */\n\tpublic removePlaying(item: PlayingHandle): void {\n\t\tconst index = this.playing.indexOf(item);\n\t\tif (index >= 0) {\n\t\t\tthis.playing.splice(index, 1);\n\t\t}\n\t}\n\n\t/**\n\t * Stop all playing sounds/music\n\t */\n\tpublic stopAll(): void {\n\t\tfor (const p of this.playing) {\n\t\t\ttry {\n\t\t\t\tp.stop();\n\t\t\t} catch (err) {\n\t\t\t\treportRuntimeError(this.runtime?.listener, APIErrorCode.E7016, { error: String(err) });\n\t\t\t}\n\t\t}\n\t\tthis.playing = [];\n\t}\n}\n","/** A4 reference frequency in Hz (concert pitch) */\nexport const A4_FREQUENCY = 440;\n\n/** Ratio between adjacent semitones in equal temperament */\nexport const SEMITONE_RATIO = 2 ** (1 / 12);\n\n/** MIDI note number for A4 */\nexport const A4_MIDI_NOTE = 69;\n","/**\n * Beeper - Procedural sound generation from text sequences\n * Converts music notation strings into beep sequences\n * Example: \"square tempo 120 C4 D4 E4 F4\"\n */\nimport { A4_FREQUENCY, A4_MIDI_NOTE, SEMITONE_RATIO } from \"../constants\";\nexport class Beeper {\n\tprivate audio: any;\n\tprivate notes: Record<string | number, number> = {};\n\tprivate plainNotes: Record<string, number> = {};\n\tprivate currentOctave = 5;\n\tprivate currentDuration = 0.5;\n\tprivate currentVolume = 0.5;\n\tprivate currentSpan = 1;\n\tprivate currentWaveform = \"square\";\n\n\tconstructor(audio: any) {\n\t\tthis.audio = audio;\n\t\tthis.initializeNotes();\n\t}\n\n\t/**\n\t * Initialize note mappings\n\t */\n\tprivate initializeNotes(): void {\n\t\tconst noteNames = [\n\t\t\t[\"C\", \"DO\"],\n\t\t\t[\"C#\", \"DO#\", \"Db\", \"REb\"],\n\t\t\t[\"D\", \"RE\"],\n\t\t\t[\"D#\", \"RE#\", \"Eb\", \"MIb\"],\n\t\t\t[\"E\", \"MI\"],\n\t\t\t[\"F\", \"FA\"],\n\t\t\t[\"F#\", \"FA#\", \"Gb\", \"SOLb\"],\n\t\t\t[\"G\", \"SOL\"],\n\t\t\t[\"G#\", \"SOL#\", \"Ab\", \"LAb\"],\n\t\t\t[\"A\", \"LA\"],\n\t\t\t[\"A#\", \"LA#\", \"Bb\", \"SIb\"],\n\t\t\t[\"B\", \"SI\"],\n\t\t];\n\n\t\tfor (let i = 0; i <= 127; i++) {\n\t\t\tthis.notes[i] = i;\n\t\t\tconst oct = Math.floor(i / 12) - 1;\n\n\t\t\tfor (const n of noteNames[i % 12]) {\n\t\t\t\tthis.notes[n + oct] = i;\n\t\t\t}\n\n\t\t\tif (oct === -1) {\n\t\t\t\tfor (const n of noteNames[i % 12]) {\n\t\t\t\t\tthis.plainNotes[n] = i;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Parse and play beep sequence\n\t */\n\tpublic beep(input: string): void {\n\t\tlet status: string = \"normal\";\n\t\tconst sequence: any[] = [];\n\t\tconst loops: any[] = [];\n\t\tlet note: number | undefined;\n\n\t\tconst parsed = input.split(\" \");\n\n\t\tfor (const t of parsed) {\n\t\t\tif (t === \"\") continue;\n\n\t\t\tswitch (status) {\n\t\t\t\tcase \"normal\":\n\t\t\t\t\tif (this.notes[t] !== undefined) {\n\t\t\t\t\t\t// Full note with octave (e.g., \"C4\")\n\t\t\t\t\t\tnote = this.notes[t];\n\t\t\t\t\t\tthis.currentOctave = Math.floor(note / 12);\n\t\t\t\t\t\tsequence.push({\n\t\t\t\t\t\t\tfrequency: A4_FREQUENCY * SEMITONE_RATIO ** (note - A4_MIDI_NOTE),\n\t\t\t\t\t\t\tvolume: this.currentVolume,\n\t\t\t\t\t\t\tspan: this.currentSpan,\n\t\t\t\t\t\t\tduration: this.currentDuration,\n\t\t\t\t\t\t\twaveform: this.currentWaveform,\n\t\t\t\t\t\t});\n\t\t\t\t\t} else if (this.plainNotes[t] !== undefined) {\n\t\t\t\t\t\t// Note without octave (e.g., \"C\")\n\t\t\t\t\t\tnote = this.plainNotes[t] + this.currentOctave * 12;\n\t\t\t\t\t\tsequence.push({\n\t\t\t\t\t\t\tfrequency: A4_FREQUENCY * SEMITONE_RATIO ** (note - A4_MIDI_NOTE),\n\t\t\t\t\t\t\tvolume: this.currentVolume,\n\t\t\t\t\t\t\tspan: this.currentSpan,\n\t\t\t\t\t\t\tduration: this.currentDuration,\n\t\t\t\t\t\t\twaveform: this.currentWaveform,\n\t\t\t\t\t\t});\n\t\t\t\t\t} else if ([\"square\", \"sine\", \"saw\", \"noise\"].includes(t)) {\n\t\t\t\t\t\t// Waveform\n\t\t\t\t\t\tthis.currentWaveform = t;\n\t\t\t\t\t} else if ([\"tempo\", \"duration\", \"volume\", \"span\", \"loop\", \"to\"].includes(t)) {\n\t\t\t\t\t\t// Commands\n\t\t\t\t\t\tstatus = t;\n\t\t\t\t\t} else if (t === \"-\") {\n\t\t\t\t\t\t// Rest/silence\n\t\t\t\t\t\tsequence.push({\n\t\t\t\t\t\t\tfrequency: A4_FREQUENCY,\n\t\t\t\t\t\t\tvolume: 0,\n\t\t\t\t\t\t\tspan: this.currentSpan,\n\t\t\t\t\t\t\tduration: this.currentDuration,\n\t\t\t\t\t\t\twaveform: this.currentWaveform,\n\t\t\t\t\t\t});\n\t\t\t\t\t} else if (t === \"end\") {\n\t\t\t\t\t\t// End loop\n\t\t\t\t\t\tif (loops.length > 0 && sequence.length > 0) {\n\t\t\t\t\t\t\tsequence.push({\n\t\t\t\t\t\t\t\tfrequency: A4_FREQUENCY,\n\t\t\t\t\t\t\t\tvolume: 0,\n\t\t\t\t\t\t\t\tspan: this.currentSpan,\n\t\t\t\t\t\t\t\tduration: 0,\n\t\t\t\t\t\t\t\twaveform: this.currentWaveform,\n\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\tconst lop = loops.splice(loops.length - 1, 1)[0];\n\t\t\t\t\t\t\tsequence[sequence.length - 1].loopto = lop.start;\n\t\t\t\t\t\t\tsequence[sequence.length - 1].repeats = lop.repeats;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase \"tempo\": {\n\t\t\t\t\tstatus = \"normal\";\n\t\t\t\t\tconst tempo = Number.parseFloat(t);\n\t\t\t\t\tif (!Number.isNaN(tempo) && tempo > 0) {\n\t\t\t\t\t\tthis.currentDuration = 60 / tempo;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase \"duration\": {\n\t\t\t\t\tstatus = \"normal\";\n\t\t\t\t\tconst duration = Number.parseFloat(t);\n\t\t\t\t\tif (!Number.isNaN(duration) && duration > 0) {\n\t\t\t\t\t\tthis.currentDuration = duration / 1000;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase \"volume\": {\n\t\t\t\t\tstatus = \"normal\";\n\t\t\t\t\tconst volume = Number.parseFloat(t);\n\t\t\t\t\tif (!Number.isNaN(volume)) {\n\t\t\t\t\t\tthis.currentVolume = volume / 100;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase \"span\": {\n\t\t\t\t\tstatus = \"normal\";\n\t\t\t\t\tconst span = Number.parseFloat(t);\n\t\t\t\t\tif (!Number.isNaN(span)) {\n\t\t\t\t\t\tthis.currentSpan = span / 100;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase \"loop\": {\n\t\t\t\t\tstatus = \"normal\";\n\t\t\t\t\tloops.push({\n\t\t\t\t\t\tstart: sequence.length,\n\t\t\t\t\t});\n\t\t\t\t\tconst repeats = Number.parseFloat(t);\n\t\t\t\t\tif (!Number.isNaN(repeats)) {\n\t\t\t\t\t\tloops[loops.length - 1].repeats = repeats;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase \"to\":\n\t\t\t\t\tstatus = \"normal\";\n\t\t\t\t\tif (note !== undefined) {\n\t\t\t\t\t\tlet n: number | undefined;\n\n\t\t\t\t\t\tif (this.notes[t] !== undefined) {\n\t\t\t\t\t\t\tn = this.notes[t];\n\t\t\t\t\t\t} else if (this.plainNotes[t] !== undefined) {\n\t\t\t\t\t\t\tn = this.plainNotes[t] + this.currentOctave * 12;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (n !== undefined && n !== note) {\n\t\t\t\t\t\t\t// Generate slide from note to n\n\t\t\t\t\t\t\tconst step = n > note ? 1 : -1;\n\t\t\t\t\t\t\tfor (let i = note + step; step > 0 ? i <= n : i >= n; i += step) {\n\t\t\t\t\t\t\t\tsequence.push({\n\t\t\t\t\t\t\t\t\tfrequency: A4_FREQUENCY * SEMITONE_RATIO ** (i - A4_MIDI_NOTE),\n\t\t\t\t\t\t\t\t\tvolume: this.currentVolume,\n\t\t\t\t\t\t\t\t\tspan: this.currentSpan,\n\t\t\t\t\t\t\t\t\tduration: this.currentDuration,\n\t\t\t\t\t\t\t\t\twaveform: this.currentWaveform,\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tnote = n;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t// Close any remaining loops\n\t\tif (loops.length > 0 && sequence.length > 0) {\n\t\t\tconst lop = loops.splice(loops.length - 1, 1)[0];\n\t\t\tsequence.push({\n\t\t\t\tfrequency: A4_FREQUENCY,\n\t\t\t\tvolume: 0,\n\t\t\t\tspan: this.currentSpan,\n\t\t\t\tduration: 0,\n\t\t\t\twaveform: this.currentWaveform,\n\t\t\t});\n\n\t\t\tsequence[sequence.length - 1].loopto = lop.start;\n\t\t\tsequence[sequence.length - 1].repeats = lop.repeats;\n\t\t}\n\n\t\tthis.audio.addBeeps(sequence);\n\t}\n}\n","export const AUDIO_WORKLET_CODE = `\nclass L8bAudioProcessor extends AudioWorkletProcessor {\n constructor() {\n super();\n this.beeps = [];\n this.last = 0;\n this.port.onmessage = (event) => {\n const data = JSON.parse(event.data);\n if (data.name === \"cancel_beeps\") {\n this.beeps = [];\n } else if (data.name === \"beep\") {\n const seq = data.sequence;\n // Link sequence notes together\n for (let i = 0; i < seq.length; i++) {\n const note = seq[i];\n if (i > 0) {\n seq[i - 1].next = note;\n }\n // Resolve loopto index to actual note reference\n if (note.loopto != null) {\n note.loopto = seq[note.loopto];\n }\n // Initialize phase and time\n note.phase = 0;\n note.time = 0;\n }\n // Add first note to beeps queue\n if (seq.length > 0) {\n this.beeps.push(seq[0]);\n }\n }\n };\n }\n\n process(inputs, outputs, parameters) {\n const output = outputs[0];\n \n for (let i = 0; i < output.length; i++) {\n const channel = output[i];\n \n if (i > 0) {\n // Copy first channel to other channels\n for (let j = 0; j < channel.length; j++) {\n channel[j] = output[0][j];\n }\n } else {\n // Generate audio for first channel\n for (let j = 0; j < channel.length; j++) {\n let sig = 0;\n \n for (let k = this.beeps.length - 1; k >= 0; k--) {\n const b = this.beeps[k];\n let volume = b.volume;\n \n if (b.time / b.duration > b.span) {\n volume = 0;\n }\n \n // Generate waveform\n switch (b.waveform) {\n case \"square\":\n sig += b.phase > 0.5 ? volume : -volume;\n break;\n case \"saw\":\n sig += (b.phase * 2 - 1) * volume;\n break;\n case \"noise\":\n sig += (Math.random() * 2 - 1) * volume;\n break;\n default: // sine\n sig += Math.sin(b.phase * Math.PI * 2) * volume;\n }\n \n b.phase = (b.phase + b.increment) % 1;\n b.time += 1;\n \n if (b.time >= b.duration) {\n b.time = 0;\n \n if (b.loopto != null) {\n if (b.repeats != null && b.repeats > 0) {\n if (b.loopcount == null) {\n b.loopcount = 0;\n }\n b.loopcount++;\n \n if (b.loopcount >= b.repeats) {\n b.loopcount = 0;\n if (b.next != null) {\n b.next.phase = b.phase;\n this.beeps[k] = b.next;\n } else {\n this.beeps.splice(k, 1);\n }\n } else {\n b.loopto.phase = b.phase;\n this.beeps[k] = b.loopto;\n }\n } else {\n b.loopto.phase = b.phase;\n this.beeps[k] = b.loopto;\n }\n } else if (b.next != null) {\n b.next.phase = b.phase;\n this.beeps[k] = b.next;\n } else {\n this.beeps.splice(k, 1);\n }\n }\n }\n \n this.last = this.last * 0.9 + sig * 0.1;\n channel[j] = this.last;\n }\n }\n }\n \n return true;\n }\n}\n\nregisterProcessor(\"l8b-audio-processor\", L8bAudioProcessor);\n`;\n"],"mappings":"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","names":["APIErrorCode","reportRuntimeError","A4_FREQUENCY","SEMITONE_RATIO","A4_MIDI_NOTE","Beeper","audio","notes","plainNotes","currentOctave","currentDuration","currentVolume","currentSpan","currentWaveform","initializeNotes","noteNames","i","oct","Math","floor","n","beep","input","status","sequence","loops","note","parsed","split","t","undefined","push","frequency","A4_FREQUENCY","SEMITONE_RATIO","A4_MIDI_NOTE","volume","span","duration","waveform","includes","length","lop","splice","loopto","start","repeats","tempo","Number","parseFloat","isNaN","step","addBeeps","AUDIO_WORKLET_CODE","AudioCore","context","buffer","playing","wakeupList","workletNode","beeper","runtime","masterVolume","getContext","isStarted","state","addToWakeUpList","item","push","interfaceCache","setVolume","volume","Math","max","min","getVolume","getInterface","beep","sequence","cancelBeeps","playSound","sound","pitch","pan","loopit","playMusic","music","stopAll","soundName","replace","s","sounds","reportRuntimeError","listener","APIErrorCode","E7013","play","musicName","m","E7014","AudioContextClass","window","AudioContext","webkitAudioContext","activate","resume","start","wakeUp","document","body","removeEventListener","addEventListener","once","blob","Blob","AUDIO_WORKLET_CODE","type","url","URL","createObjectURL","audioWorklet","addModule","AudioWorkletNode","connect","destination","flushBuffer","e","E7012","error","String","length","port","postMessage","splice","getBeeper","Beeper","addBeeps","beeps","b","duration","sampleRate","increment","frequency","JSON","stringify","name","addPlaying","removePlaying","index","indexOf","p","stop","err","E7016"]}
1
+ {"version":3,"sources":["../../src/constants.ts","../../src/devices/beeper.ts","../../src/core/audio-worklet.ts","../../src/core/audio-core.ts"],"sourcesContent":["/** A4 reference frequency in Hz (concert pitch) */\nexport const A4_FREQUENCY = 440;\n\n/** Ratio between adjacent semitones in equal temperament */\nexport const SEMITONE_RATIO = 2 ** (1 / 12);\n\n/** MIDI note number for A4 */\nexport const A4_MIDI_NOTE = 69;\n","/**\n * Beeper - Procedural sound generation from text sequences\n * Converts music notation strings into beep sequences\n * Example: \"square tempo 120 C4 D4 E4 F4\"\n */\nimport { A4_FREQUENCY, A4_MIDI_NOTE, SEMITONE_RATIO } from \"../constants\";\nexport class Beeper {\n\tprivate audio: any;\n\tprivate notes: Record<string | number, number> = {};\n\tprivate plainNotes: Record<string, number> = {};\n\tprivate currentOctave = 5;\n\tprivate currentDuration = 0.5;\n\tprivate currentVolume = 0.5;\n\tprivate currentSpan = 1;\n\tprivate currentWaveform = \"square\";\n\n\tconstructor(audio: any) {\n\t\tthis.audio = audio;\n\t\tthis.initializeNotes();\n\t}\n\n\t/**\n\t * Initialize note mappings\n\t */\n\tprivate initializeNotes(): void {\n\t\tconst noteNames = [\n\t\t\t[\"C\", \"DO\"],\n\t\t\t[\"C#\", \"DO#\", \"Db\", \"REb\"],\n\t\t\t[\"D\", \"RE\"],\n\t\t\t[\"D#\", \"RE#\", \"Eb\", \"MIb\"],\n\t\t\t[\"E\", \"MI\"],\n\t\t\t[\"F\", \"FA\"],\n\t\t\t[\"F#\", \"FA#\", \"Gb\", \"SOLb\"],\n\t\t\t[\"G\", \"SOL\"],\n\t\t\t[\"G#\", \"SOL#\", \"Ab\", \"LAb\"],\n\t\t\t[\"A\", \"LA\"],\n\t\t\t[\"A#\", \"LA#\", \"Bb\", \"SIb\"],\n\t\t\t[\"B\", \"SI\"],\n\t\t];\n\n\t\tfor (let i = 0; i <= 127; i++) {\n\t\t\tthis.notes[i] = i;\n\t\t\tconst oct = Math.floor(i / 12) - 1;\n\n\t\t\tfor (const n of noteNames[i % 12]) {\n\t\t\t\tthis.notes[n + oct] = i;\n\t\t\t}\n\n\t\t\tif (oct === -1) {\n\t\t\t\tfor (const n of noteNames[i % 12]) {\n\t\t\t\t\tthis.plainNotes[n] = i;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Parse and play beep sequence\n\t */\n\tpublic beep(input: string): void {\n\t\tlet status: string = \"normal\";\n\t\tconst sequence: any[] = [];\n\t\tconst loops: any[] = [];\n\t\tlet note: number | undefined;\n\n\t\tconst parsed = input.split(\" \");\n\n\t\tfor (const t of parsed) {\n\t\t\tif (t === \"\") continue;\n\n\t\t\tswitch (status) {\n\t\t\t\tcase \"normal\":\n\t\t\t\t\tif (this.notes[t] !== undefined) {\n\t\t\t\t\t\t// Full note with octave (e.g., \"C4\")\n\t\t\t\t\t\tnote = this.notes[t];\n\t\t\t\t\t\tthis.currentOctave = Math.floor(note / 12);\n\t\t\t\t\t\tsequence.push({\n\t\t\t\t\t\t\tfrequency: A4_FREQUENCY * SEMITONE_RATIO ** (note - A4_MIDI_NOTE),\n\t\t\t\t\t\t\tvolume: this.currentVolume,\n\t\t\t\t\t\t\tspan: this.currentSpan,\n\t\t\t\t\t\t\tduration: this.currentDuration,\n\t\t\t\t\t\t\twaveform: this.currentWaveform,\n\t\t\t\t\t\t});\n\t\t\t\t\t} else if (this.plainNotes[t] !== undefined) {\n\t\t\t\t\t\t// Note without octave (e.g., \"C\")\n\t\t\t\t\t\tnote = this.plainNotes[t] + this.currentOctave * 12;\n\t\t\t\t\t\tsequence.push({\n\t\t\t\t\t\t\tfrequency: A4_FREQUENCY * SEMITONE_RATIO ** (note - A4_MIDI_NOTE),\n\t\t\t\t\t\t\tvolume: this.currentVolume,\n\t\t\t\t\t\t\tspan: this.currentSpan,\n\t\t\t\t\t\t\tduration: this.currentDuration,\n\t\t\t\t\t\t\twaveform: this.currentWaveform,\n\t\t\t\t\t\t});\n\t\t\t\t\t} else if ([\"square\", \"sine\", \"saw\", \"noise\"].includes(t)) {\n\t\t\t\t\t\t// Waveform\n\t\t\t\t\t\tthis.currentWaveform = t;\n\t\t\t\t\t} else if ([\"tempo\", \"duration\", \"volume\", \"span\", \"loop\", \"to\"].includes(t)) {\n\t\t\t\t\t\t// Commands\n\t\t\t\t\t\tstatus = t;\n\t\t\t\t\t} else if (t === \"-\") {\n\t\t\t\t\t\t// Rest/silence\n\t\t\t\t\t\tsequence.push({\n\t\t\t\t\t\t\tfrequency: A4_FREQUENCY,\n\t\t\t\t\t\t\tvolume: 0,\n\t\t\t\t\t\t\tspan: this.currentSpan,\n\t\t\t\t\t\t\tduration: this.currentDuration,\n\t\t\t\t\t\t\twaveform: this.currentWaveform,\n\t\t\t\t\t\t});\n\t\t\t\t\t} else if (t === \"end\") {\n\t\t\t\t\t\t// End loop\n\t\t\t\t\t\tif (loops.length > 0 && sequence.length > 0) {\n\t\t\t\t\t\t\tsequence.push({\n\t\t\t\t\t\t\t\tfrequency: A4_FREQUENCY,\n\t\t\t\t\t\t\t\tvolume: 0,\n\t\t\t\t\t\t\t\tspan: this.currentSpan,\n\t\t\t\t\t\t\t\tduration: 0,\n\t\t\t\t\t\t\t\twaveform: this.currentWaveform,\n\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\tconst lop = loops.splice(loops.length - 1, 1)[0];\n\t\t\t\t\t\t\tsequence[sequence.length - 1].loopto = lop.start;\n\t\t\t\t\t\t\tsequence[sequence.length - 1].repeats = lop.repeats;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase \"tempo\": {\n\t\t\t\t\tstatus = \"normal\";\n\t\t\t\t\tconst tempo = Number.parseFloat(t);\n\t\t\t\t\tif (!Number.isNaN(tempo) && tempo > 0) {\n\t\t\t\t\t\tthis.currentDuration = 60 / tempo;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase \"duration\": {\n\t\t\t\t\tstatus = \"normal\";\n\t\t\t\t\tconst duration = Number.parseFloat(t);\n\t\t\t\t\tif (!Number.isNaN(duration) && duration > 0) {\n\t\t\t\t\t\tthis.currentDuration = duration / 1000;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase \"volume\": {\n\t\t\t\t\tstatus = \"normal\";\n\t\t\t\t\tconst volume = Number.parseFloat(t);\n\t\t\t\t\tif (!Number.isNaN(volume)) {\n\t\t\t\t\t\tthis.currentVolume = volume / 100;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase \"span\": {\n\t\t\t\t\tstatus = \"normal\";\n\t\t\t\t\tconst span = Number.parseFloat(t);\n\t\t\t\t\tif (!Number.isNaN(span)) {\n\t\t\t\t\t\tthis.currentSpan = span / 100;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase \"loop\": {\n\t\t\t\t\tstatus = \"normal\";\n\t\t\t\t\tloops.push({\n\t\t\t\t\t\tstart: sequence.length,\n\t\t\t\t\t});\n\t\t\t\t\tconst repeats = Number.parseFloat(t);\n\t\t\t\t\tif (!Number.isNaN(repeats)) {\n\t\t\t\t\t\tloops[loops.length - 1].repeats = repeats;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase \"to\":\n\t\t\t\t\tstatus = \"normal\";\n\t\t\t\t\tif (note !== undefined) {\n\t\t\t\t\t\tlet n: number | undefined;\n\n\t\t\t\t\t\tif (this.notes[t] !== undefined) {\n\t\t\t\t\t\t\tn = this.notes[t];\n\t\t\t\t\t\t} else if (this.plainNotes[t] !== undefined) {\n\t\t\t\t\t\t\tn = this.plainNotes[t] + this.currentOctave * 12;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (n !== undefined && n !== note) {\n\t\t\t\t\t\t\t// Generate slide from note to n\n\t\t\t\t\t\t\tconst step = n > note ? 1 : -1;\n\t\t\t\t\t\t\tfor (let i = note + step; step > 0 ? i <= n : i >= n; i += step) {\n\t\t\t\t\t\t\t\tsequence.push({\n\t\t\t\t\t\t\t\t\tfrequency: A4_FREQUENCY * SEMITONE_RATIO ** (i - A4_MIDI_NOTE),\n\t\t\t\t\t\t\t\t\tvolume: this.currentVolume,\n\t\t\t\t\t\t\t\t\tspan: this.currentSpan,\n\t\t\t\t\t\t\t\t\tduration: this.currentDuration,\n\t\t\t\t\t\t\t\t\twaveform: this.currentWaveform,\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tnote = n;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t// Close any remaining loops\n\t\tif (loops.length > 0 && sequence.length > 0) {\n\t\t\tconst lop = loops.splice(loops.length - 1, 1)[0];\n\t\t\tsequence.push({\n\t\t\t\tfrequency: A4_FREQUENCY,\n\t\t\t\tvolume: 0,\n\t\t\t\tspan: this.currentSpan,\n\t\t\t\tduration: 0,\n\t\t\t\twaveform: this.currentWaveform,\n\t\t\t});\n\n\t\t\tsequence[sequence.length - 1].loopto = lop.start;\n\t\t\tsequence[sequence.length - 1].repeats = lop.repeats;\n\t\t}\n\n\t\tthis.audio.addBeeps(sequence);\n\t}\n}\n","export const AUDIO_WORKLET_CODE = `\nclass L8bAudioProcessor extends AudioWorkletProcessor {\n constructor() {\n super();\n this.beeps = [];\n this.last = 0;\n this.port.onmessage = (event) => {\n const data = JSON.parse(event.data);\n if (data.name === \"cancel_beeps\") {\n this.beeps = [];\n } else if (data.name === \"beep\") {\n const seq = data.sequence;\n // Link sequence notes together\n for (let i = 0; i < seq.length; i++) {\n const note = seq[i];\n if (i > 0) {\n seq[i - 1].next = note;\n }\n // Resolve loopto index to actual note reference\n if (note.loopto != null) {\n note.loopto = seq[note.loopto];\n }\n // Initialize phase and time\n note.phase = 0;\n note.time = 0;\n }\n // Add first note to beeps queue\n if (seq.length > 0) {\n this.beeps.push(seq[0]);\n }\n }\n };\n }\n\n process(inputs, outputs, parameters) {\n const output = outputs[0];\n \n for (let i = 0; i < output.length; i++) {\n const channel = output[i];\n \n if (i > 0) {\n // Copy first channel to other channels\n for (let j = 0; j < channel.length; j++) {\n channel[j] = output[0][j];\n }\n } else {\n // Generate audio for first channel\n for (let j = 0; j < channel.length; j++) {\n let sig = 0;\n \n for (let k = this.beeps.length - 1; k >= 0; k--) {\n const b = this.beeps[k];\n let volume = b.volume;\n \n if (b.time / b.duration > b.span) {\n volume = 0;\n }\n \n // Generate waveform\n switch (b.waveform) {\n case \"square\":\n sig += b.phase > 0.5 ? volume : -volume;\n break;\n case \"saw\":\n sig += (b.phase * 2 - 1) * volume;\n break;\n case \"noise\":\n sig += (Math.random() * 2 - 1) * volume;\n break;\n default: // sine\n sig += Math.sin(b.phase * Math.PI * 2) * volume;\n }\n \n b.phase = (b.phase + b.increment) % 1;\n b.time += 1;\n \n if (b.time >= b.duration) {\n b.time = 0;\n \n if (b.loopto != null) {\n if (b.repeats != null && b.repeats > 0) {\n if (b.loopcount == null) {\n b.loopcount = 0;\n }\n b.loopcount++;\n \n if (b.loopcount >= b.repeats) {\n b.loopcount = 0;\n if (b.next != null) {\n b.next.phase = b.phase;\n this.beeps[k] = b.next;\n } else {\n this.beeps.splice(k, 1);\n }\n } else {\n b.loopto.phase = b.phase;\n this.beeps[k] = b.loopto;\n }\n } else {\n b.loopto.phase = b.phase;\n this.beeps[k] = b.loopto;\n }\n } else if (b.next != null) {\n b.next.phase = b.phase;\n this.beeps[k] = b.next;\n } else {\n this.beeps.splice(k, 1);\n }\n }\n }\n \n this.last = this.last * 0.9 + sig * 0.1;\n channel[j] = this.last;\n }\n }\n }\n \n return true;\n }\n}\n\nregisterProcessor(\"l8b-audio-processor\", L8bAudioProcessor);\n`;\n","/**\n * AudioCore - Web Audio API wrapper\n * Manages audio context, beeper, and sound/music playback\n */\n\nimport { Beeper } from \"../devices/beeper\";\nimport { AUDIO_WORKLET_CODE } from \"./audio-worklet\";\n\n/** An actively playing sound/music that can be stopped */\ninterface PlayingHandle {\n\tstop: () => void;\n}\n\n/** Item that can be woken up on audio context activation */\ninterface WakeUpItem {\n\twakeUp: () => void;\n}\n\nexport class AudioCore {\n\tpublic context!: AudioContext;\n\tprivate buffer: string[] = [];\n\tprivate playing: PlayingHandle[] = [];\n\tprivate wakeupList: WakeUpItem[] = [];\n\tprivate workletNode?: AudioWorkletNode;\n\tprivate beeper?: Beeper;\n\tprivate runtime: any;\n\tprivate masterVolume: number = 1;\n\n\tconstructor(runtime: any) {\n\t\tthis.runtime = runtime;\n\t\tthis.getContext();\n\t}\n\n\t/**\n\t * Check if audio context is running\n\t */\n\tpublic isStarted(): boolean {\n\t\treturn this.context.state === \"running\";\n\t}\n\n\t/**\n\t * Add item to wakeup list (for mobile audio activation)\n\t */\n\tpublic addToWakeUpList(item: WakeUpItem): void {\n\t\tthis.wakeupList.push(item);\n\t}\n\n\tprivate interfaceCache: Record<string, any> | null = null;\n\n\t/**\n\t * Set master volume (0-1). Applied as a multiplier to all sound/music playback.\n\t */\n\tpublic setVolume(volume: number): void {\n\t\tthis.masterVolume = Math.max(0, Math.min(1, volume));\n\t}\n\n\t/**\n\t * Get current master volume (0-1)\n\t */\n\tpublic getVolume(): number {\n\t\treturn this.masterVolume;\n\t}\n\n\t/**\n\t * Get interface for game code\n\t */\n\tpublic getInterface() {\n\t\tif (this.interfaceCache) {\n\t\t\treturn this.interfaceCache;\n\t\t}\n\t\tthis.interfaceCache = {\n\t\t\tbeep: (sequence: string) => this.beep(sequence),\n\t\t\tcancelBeeps: () => this.cancelBeeps(),\n\t\t\tplaySound: (sound: any, volume?: number, pitch?: number, pan?: number, loopit?: boolean) =>\n\t\t\t\tthis.playSound(sound, volume, pitch, pan, loopit),\n\t\t\tplayMusic: (music: any, volume?: number, loopit?: boolean) => this.playMusic(music, volume, loopit),\n\t\t\tsetVolume: (volume: number) => this.setVolume(volume),\n\t\t\tgetVolume: () => this.getVolume(),\n\t\t\tstopAll: () => this.stopAll(),\n\t\t};\n\t\treturn this.interfaceCache;\n\t}\n\n\t/**\n\t * Play sound effect\n\t */\n\tpublic playSound(sound: any, volume: number = 1, pitch: number = 1, pan: number = 0, loopit: boolean = false): number {\n\t\tif (typeof sound === \"string\") {\n\t\t\tconst soundName = sound.replace(/\\//g, \"-\");\n\t\t\tconst s = this.runtime.sounds[soundName];\n\t\t\tif (!s) {\n\t\t\t\tthis.runtime?.listener?.reportError?.({ code: \"E7013\", message: \"Sound not found\", data: { soundName } });\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\treturn s.play(volume * this.masterVolume, pitch, pan, loopit);\n\t\t}\n\t\treturn 0;\n\t}\n\n\t/**\n\t * Play music\n\t */\n\tpublic playMusic(music: any, volume: number = 1, loopit: boolean = false): number {\n\t\tif (typeof music === \"string\") {\n\t\t\tconst musicName = music.replace(/\\//g, \"-\");\n\t\t\tconst m = this.runtime.music[musicName];\n\t\t\tif (!m) {\n\t\t\t\tthis.runtime?.listener?.reportError?.({ code: \"E7014\", message: \"Music not found\", data: { musicName } });\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\treturn m.play(volume * this.masterVolume, loopit);\n\t\t}\n\t\treturn 0;\n\t}\n\n\t/**\n\t * Get or create audio context (lazy initialization - created on first use)\n\t * Note: Browser may suspend context until user interaction, which is handled automatically\n\t */\n\tpublic getContext(): AudioContext {\n\t\tif (!this.context) {\n\t\t\tconst AudioContextClass = (window as any).AudioContext || (window as any).webkitAudioContext;\n\t\t\t// Create context - browser may suspend until user interaction\n\t\t\tthis.context = new AudioContextClass();\n\n\t\t\t// If context is suspended, set up activation listeners\n\t\t\tif (this.context.state !== \"running\") {\n\t\t\t\tconst activate = () => {\n\t\t\t\t\tif (this.context && this.context.state !== \"running\") {\n\t\t\t\t\t\tthis.context.resume();\n\t\t\t\t\t\tif (this.beeper) {\n\t\t\t\t\t\t\tthis.start();\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfor (const item of this.wakeupList) {\n\t\t\t\t\t\t\titem.wakeUp();\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Clean up listeners\n\t\t\t\t\t\tdocument.body.removeEventListener(\"touchend\", activate);\n\t\t\t\t\t\tdocument.body.removeEventListener(\"mouseup\", activate);\n\t\t\t\t\t\tdocument.body.removeEventListener(\"click\", activate);\n\t\t\t\t\t\tdocument.body.removeEventListener(\"keydown\", activate);\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\t// Add multiple event listeners for better compatibility\n\t\t\t\tdocument.body.addEventListener(\"touchend\", activate, {\n\t\t\t\t\tonce: true,\n\t\t\t\t});\n\t\t\t\tdocument.body.addEventListener(\"mouseup\", activate, {\n\t\t\t\t\tonce: true,\n\t\t\t\t});\n\t\t\t\tdocument.body.addEventListener(\"click\", activate, {\n\t\t\t\t\tonce: true,\n\t\t\t\t});\n\t\t\t\tdocument.body.addEventListener(\"keydown\", activate, {\n\t\t\t\t\tonce: true,\n\t\t\t\t});\n\t\t\t} else if (this.beeper) {\n\t\t\t\tthis.start();\n\t\t\t}\n\t\t}\n\n\t\treturn this.context;\n\t}\n\n\t/**\n\t * Start audio processor\n\t */\n\tpublic async start(): Promise<void> {\n\t\tif (this.workletNode) return;\n\n\t\ttry {\n\t\t\tconst blob = new Blob([AUDIO_WORKLET_CODE], {\n\t\t\t\ttype: \"application/javascript\",\n\t\t\t});\n\t\t\tconst url = URL.createObjectURL(blob);\n\n\t\t\tawait this.context.audioWorklet.addModule(url);\n\n\t\t\tthis.workletNode = new AudioWorkletNode(this.context, \"l8b-audio-processor\");\n\t\t\tthis.workletNode.connect(this.context.destination);\n\n\t\t\tthis.flushBuffer();\n\t\t} catch (e) {\n\t\t\tthis.runtime?.listener?.reportError?.({ code: \"E7012\", message: \"Audio worklet error\", data: { error: String(e) } });\n\t\t}\n\t}\n\n\t/**\n\t * Flush buffered messages\n\t */\n\tprivate flushBuffer(): void {\n\t\tif (!this.workletNode) return;\n\n\t\twhile (this.buffer.length > 0) {\n\t\t\tthis.workletNode.port.postMessage(this.buffer.splice(0, 1)[0]);\n\t\t}\n\t}\n\n\t/**\n\t * Get or create beeper\n\t */\n\tpublic getBeeper(): Beeper {\n\t\tif (!this.beeper) {\n\t\t\t// Create Beeper instance\n\t\t\tthis.beeper = new Beeper(this);\n\n\t\t\tif (this.context.state === \"running\") {\n\t\t\t\tthis.start();\n\t\t\t}\n\t\t}\n\t\treturn this.beeper;\n\t}\n\n\t/**\n\t * Play beep sequence\n\t */\n\tpublic beep(sequence: string): void {\n\t\tthis.getBeeper().beep(sequence);\n\t}\n\n\t/**\n\t * Add beeps to audio processor\n\t */\n\tpublic addBeeps(beeps: any[]): void {\n\t\tfor (const b of beeps) {\n\t\t\tb.duration *= this.context.sampleRate;\n\t\t\tb.increment = b.frequency / this.context.sampleRate;\n\t\t}\n\n\t\tif (this.workletNode) {\n\t\t\tthis.workletNode.port.postMessage(\n\t\t\t\tJSON.stringify({\n\t\t\t\t\tname: \"beep\",\n\t\t\t\t\tsequence: beeps,\n\t\t\t\t}),\n\t\t\t);\n\t\t} else {\n\t\t\tthis.buffer.push(\n\t\t\t\tJSON.stringify({\n\t\t\t\t\tname: \"beep\",\n\t\t\t\t\tsequence: beeps,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Cancel all beeps\n\t */\n\tpublic cancelBeeps(): void {\n\t\tif (this.workletNode) {\n\t\t\tthis.workletNode.port.postMessage(\n\t\t\t\tJSON.stringify({\n\t\t\t\t\tname: \"cancel_beeps\",\n\t\t\t\t}),\n\t\t\t);\n\t\t} else {\n\t\t\tthis.buffer.push(\n\t\t\t\tJSON.stringify({\n\t\t\t\t\tname: \"cancel_beeps\",\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\tthis.stopAll();\n\t}\n\n\t/**\n\t * Add playing sound/music to list\n\t */\n\tpublic addPlaying(item: PlayingHandle): void {\n\t\tthis.playing.push(item);\n\t}\n\n\t/**\n\t * Remove playing sound/music from list\n\t */\n\tpublic removePlaying(item: PlayingHandle): void {\n\t\tconst index = this.playing.indexOf(item);\n\t\tif (index >= 0) {\n\t\t\tthis.playing.splice(index, 1);\n\t\t}\n\t}\n\n\t/**\n\t * Stop all playing sounds/music\n\t */\n\tpublic stopAll(): void {\n\t\tfor (const p of this.playing) {\n\t\t\ttry {\n\t\t\t\tp.stop();\n\t\t\t} catch (err) {\n\t\t\t\tthis.runtime?.listener?.reportError?.({ code: \"E7016\", message: \"Audio error\", data: { error: String(err) } });\n\t\t\t}\n\t\t}\n\t\tthis.playing = [];\n\t}\n}\n"],"mappings":"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","names":["A4_FREQUENCY","SEMITONE_RATIO","A4_MIDI_NOTE","Beeper","audio","notes","plainNotes","currentOctave","currentDuration","currentVolume","currentSpan","currentWaveform","initializeNotes","noteNames","i","oct","Math","floor","n","beep","input","status","sequence","loops","note","parsed","split","t","undefined","push","frequency","A4_FREQUENCY","SEMITONE_RATIO","A4_MIDI_NOTE","volume","span","duration","waveform","includes","length","lop","splice","loopto","start","repeats","tempo","Number","parseFloat","isNaN","step","addBeeps","AUDIO_WORKLET_CODE","AudioCore","context","buffer","playing","wakeupList","workletNode","beeper","runtime","masterVolume","getContext","isStarted","state","addToWakeUpList","item","push","interfaceCache","setVolume","volume","Math","max","min","getVolume","getInterface","beep","sequence","cancelBeeps","playSound","sound","pitch","pan","loopit","playMusic","music","stopAll","soundName","replace","s","sounds","listener","reportError","code","message","data","play","musicName","m","AudioContextClass","window","AudioContext","webkitAudioContext","activate","resume","start","wakeUp","document","body","removeEventListener","addEventListener","once","blob","Blob","AUDIO_WORKLET_CODE","type","url","URL","createObjectURL","audioWorklet","addModule","AudioWorkletNode","connect","destination","flushBuffer","e","error","String","length","port","postMessage","splice","getBeeper","Beeper","addBeeps","beeps","b","duration","sampleRate","increment","frequency","JSON","stringify","name","addPlaying","removePlaying","index","indexOf","p","stop","err"]}
@@ -25,9 +25,6 @@ __export(core_exports, {
25
25
  });
26
26
  module.exports = __toCommonJS(core_exports);
27
27
 
28
- // src/core/audio-core.ts
29
- var import_diagnostics = require("@al8b/diagnostics");
30
-
31
28
  // src/constants.ts
32
29
  var A4_FREQUENCY = 440;
33
30
  var SEMITONE_RATIO = 2 ** (1 / 12);
@@ -476,8 +473,12 @@ var AudioCore = class {
476
473
  const soundName = sound.replace(/\//g, "-");
477
474
  const s = this.runtime.sounds[soundName];
478
475
  if (!s) {
479
- (0, import_diagnostics.reportRuntimeError)(this.runtime?.listener, import_diagnostics.APIErrorCode.E7013, {
480
- soundName
476
+ this.runtime?.listener?.reportError?.({
477
+ code: "E7013",
478
+ message: "Sound not found",
479
+ data: {
480
+ soundName
481
+ }
481
482
  });
482
483
  return 0;
483
484
  }
@@ -493,8 +494,12 @@ var AudioCore = class {
493
494
  const musicName = music.replace(/\//g, "-");
494
495
  const m = this.runtime.music[musicName];
495
496
  if (!m) {
496
- (0, import_diagnostics.reportRuntimeError)(this.runtime?.listener, import_diagnostics.APIErrorCode.E7014, {
497
- musicName
497
+ this.runtime?.listener?.reportError?.({
498
+ code: "E7014",
499
+ message: "Music not found",
500
+ data: {
501
+ musicName
502
+ }
498
503
  });
499
504
  return 0;
500
505
  }
@@ -561,8 +566,12 @@ var AudioCore = class {
561
566
  this.workletNode.connect(this.context.destination);
562
567
  this.flushBuffer();
563
568
  } catch (e) {
564
- (0, import_diagnostics.reportRuntimeError)(this.runtime?.listener, import_diagnostics.APIErrorCode.E7012, {
565
- error: String(e)
569
+ this.runtime?.listener?.reportError?.({
570
+ code: "E7012",
571
+ message: "Audio worklet error",
572
+ data: {
573
+ error: String(e)
574
+ }
566
575
  });
567
576
  }
568
577
  }
@@ -651,8 +660,12 @@ var AudioCore = class {
651
660
  try {
652
661
  p.stop();
653
662
  } catch (err) {
654
- (0, import_diagnostics.reportRuntimeError)(this.runtime?.listener, import_diagnostics.APIErrorCode.E7016, {
655
- error: String(err)
663
+ this.runtime?.listener?.reportError?.({
664
+ code: "E7016",
665
+ message: "Audio error",
666
+ data: {
667
+ error: String(err)
668
+ }
656
669
  });
657
670
  }
658
671
  }