diff --git a/audio/context/context.ts b/audio/context/context.ts new file mode 100644 index 00000000000..48a79244ba8 --- /dev/null +++ b/audio/context/context.ts @@ -0,0 +1,9 @@ +namespace $ { + export class $mol_audio_context extends $mol_object2 { + @ $mol_memo.method + static context() { + const AudioContext = this.$.$mol_dom_context.AudioContext || this.$.$node['web-audio-api'].AudioContext + return new AudioContext() + } + } +} diff --git a/audio/demo/demo.web.view.tree b/audio/demo/demo.view.tree similarity index 93% rename from audio/demo/demo.web.view.tree rename to audio/demo/demo.view.tree index 8b59d4f17ce..47f86c0f1c0 100644 --- a/audio/demo/demo.web.view.tree +++ b/audio/demo/demo.view.tree @@ -2,13 +2,13 @@ $mol_audio_demo $mol_example_small title \WebAudio API example Beep $mol_audio_room play => beep_play - duration 100 + duration 0.1 input / <= Beep_vibe $mol_audio_vibe freq 440 Noise $mol_audio_room play => noise_play - duration 1000 + duration 1 input / <= Noise_vibe $mol_audio_vibe freq <= noise_freq 440 diff --git a/audio/demo/demo.web.view.ts b/audio/demo/demo.view.ts similarity index 100% rename from audio/demo/demo.web.view.ts rename to audio/demo/demo.view.ts diff --git a/audio/demo/vibe/vibe.web.view.tree b/audio/demo/vibe/vibe.view.tree similarity index 89% rename from audio/demo/vibe/vibe.web.view.tree rename to audio/demo/vibe/vibe.view.tree index 2676dbc9c1f..2e0232df7db 100644 --- a/audio/demo/vibe/vibe.web.view.tree +++ b/audio/demo/vibe/vibe.view.tree @@ -10,11 +10,11 @@ $mol_audio_demo_vibe $mol_example_small sub / <= List $mol_list rows / <= Duration $mol_labeler - title <= duration_label \Duration, ms + title <= duration_label \Duration, s content / <= Duration_num $mol_number - precision_change 50 - value? <=> duration? 500 + precision_change 0.05 + value? <=> duration? 0.5 <= Frequency $mol_labeler title <= frequency_label \Frequency, Hz content / @@ -28,7 +28,7 @@ $mol_audio_demo_vibe $mol_example_small Filter null value? <=> shape? null options /$mol_audio_vibe_shape - \sine + \sine \square \sawtooth \triangle diff --git a/audio/demo/vibe/vibe.web.view.ts b/audio/demo/vibe/vibe.view.ts similarity index 100% rename from audio/demo/vibe/vibe.web.view.ts rename to audio/demo/vibe/vibe.view.ts diff --git a/audio/gain/gain.ts b/audio/gain/gain.ts new file mode 100644 index 00000000000..63c74ffa860 --- /dev/null +++ b/audio/gain/gain.ts @@ -0,0 +1,20 @@ +namespace $ { + + export class $mol_audio_gain extends $mol_audio_node { + + @ $mol_mem + override node_raw() { return this.context().createGain() } + + @ $mol_mem + override node() { + const node = super.node() + node.gain.setValueAtTime( this.gain(), this.time() ) + return node + } + + @ $mol_mem + gain( next = 1 ) { return next } + + } + +} diff --git a/audio/gain/gain.web.ts b/audio/gain/gain.web.ts deleted file mode 100644 index f304a31bfc0..00000000000 --- a/audio/gain/gain.web.ts +++ /dev/null @@ -1,19 +0,0 @@ -namespace $ { - - export class $mol_audio_gain extends $mol_audio_node { - - @ $mol_mem - node() { return $mol_audio_node.context().createGain() } - - @ $mol_mem - gain( next = 1 ) { return next } - - @ $mol_mem - output() { - this.node().gain.setValueAtTime( this.gain(), this.time() ) - return super.output() - } - - } - -} diff --git a/audio/instrument/instrument.ts b/audio/instrument/instrument.ts new file mode 100644 index 00000000000..621fd7c6160 --- /dev/null +++ b/audio/instrument/instrument.ts @@ -0,0 +1,59 @@ +namespace $ { + export class $mol_audio_instrument extends $mol_audio_node { + override node_raw(): AudioScheduledSourceNode { + throw new Error('implement') + } + + @ $mol_mem + override node() { + const node = super.node() + node.onended = $mol_wire_async((e: Event) => this.end(e)) + + return node + } + + protected promise = $mol_promise() + + @ $mol_mem + wait() { + return this.promise + } + + end(e: Event) { + this.active( false ) + } + + @ $mol_mem + active( next?: boolean ): boolean { + + $mol_wire_solid() + + const node = next === false ? this.node_raw() : this.node() + + const prev = $mol_wire_probe( ()=> this.active() ) + if( prev === next ) return next ?? false + + if( next === true ) { + node.start() + } else if( prev === true ) { + node.stop() + this.promise.done() + this.promise = $mol_promise() + } + + return next ?? false + } + + override destructor() { + this.active( false ) + super.destructor() + } + + @ $mol_mem + override output() { + this.active( true ) + return super.output() + } + + } +} diff --git a/audio/node/node.web.ts b/audio/node/node.ts similarity index 58% rename from audio/node/node.web.ts rename to audio/node/node.ts index 71953d99772..9ad6e3de567 100644 --- a/audio/node/node.web.ts +++ b/audio/node/node.ts @@ -1,21 +1,30 @@ namespace $ { export class $mol_audio_node extends $mol_object2 { + context() { return this.$.$mol_audio_context.context() } - @ $mol_memo.method - static context() { - return new AudioContext + @ $mol_mem + node_raw() { return this.context().destination as AudioNode } + + node() { + return this.node_raw() as ReturnType } - + @ $mol_mem - node() { return $mol_audio_node.context().destination as AudioNode } + duration() { + let duration = 0 + for (const input of this.input_connected()) duration = Math.max(duration, input.duration()) + + return duration + } + @ $mol_mem input( next = [] as readonly $mol_audio_node[] ) { return next } @ $mol_mem input_connected() { - const node = this.node() + const node = this.node_raw() const prev = $mol_mem_cached( ()=> this.input_connected() ) ?? [] const next = this.input() @@ -35,14 +44,14 @@ namespace $ { @ $mol_mem output() { this.input_connected() - return this.node() + return this.node_raw() } - time() { return $mol_audio_node.context().currentTime } + time() { return this.context().currentTime } destructor() { - const node = this.node() + const node = this.node_raw() for( const src of this.input() ) { src.output().disconnect( node ) diff --git a/audio/room/room.web.ts b/audio/room/room.ts similarity index 70% rename from audio/room/room.web.ts rename to audio/room/room.ts index 6f958854117..4684f6ece70 100644 --- a/audio/room/room.web.ts +++ b/audio/room/room.ts @@ -5,14 +5,10 @@ namespace $ { */ export class $mol_audio_room extends $mol_audio_node { - duration() { - return 1000 - } - @ $mol_action play() { this.output() - this.$.$mol_wait_timeout( this.duration() ) + this.$.$mol_wait_timeout( this.duration() * 1000 ) } } diff --git a/audio/sample/sample.ts b/audio/sample/sample.ts new file mode 100644 index 00000000000..738d12d686b --- /dev/null +++ b/audio/sample/sample.ts @@ -0,0 +1,28 @@ +namespace $ { + export class $mol_audio_sample extends $mol_audio_instrument { + @ $mol_mem + override node_raw() { return this.context().createBufferSource() } + + override duration() { + return this.audio_buffer().duration + } + + buffer() { + return new ArrayBuffer(0) + } + + @ $mol_mem + audio_buffer() { + return $mol_wire_sync(this.context()).decodeAudioData(this.buffer()) + } + + @ $mol_mem + override node() { + const node = super.node() + node.buffer = this.audio_buffer() + + return node + } + + } +} diff --git a/audio/vibe/vibe.ts b/audio/vibe/vibe.ts new file mode 100644 index 00000000000..e7ac418568f --- /dev/null +++ b/audio/vibe/vibe.ts @@ -0,0 +1,38 @@ +namespace $ { + + export type $mol_audio_vibe_shape = + | 'sine' + | 'square' + | 'sawtooth' + | 'triangle' + | 'custom' + + /** + * @see https://mol.hyoo.ru/#!section=demos/demo=mol_audio_demo_vibe + */ + export class $mol_audio_vibe extends $mol_audio_instrument { + + @ $mol_mem + override node_raw() { return this.context().createOscillator() } + + @ $mol_mem + freq( next = 440 ) { return next } + + @ $mol_mem + shape( next: $mol_audio_vibe_shape = 'sine' ) { return next } + + override duration() { + return 0.5 + } + + @ $mol_mem + override node() { + const node = super.node() + node.frequency.setValueAtTime( this.freq(), this.time() ) + node.type = this.shape() + + return node + } + + } +} diff --git a/audio/vibe/vibe.web.ts b/audio/vibe/vibe.web.ts deleted file mode 100644 index 10c561a4112..00000000000 --- a/audio/vibe/vibe.web.ts +++ /dev/null @@ -1,55 +0,0 @@ -namespace $ { - - export type $mol_audio_vibe_shape = - | 'sine' - | 'square' - | 'sawtooth' - | 'triangle' - | 'custom' - - /** - * @see https://mol.hyoo.ru/#!section=demos/demo=mol_audio_demo_vibe - */ - export class $mol_audio_vibe extends $mol_audio_node { - - @ $mol_mem - node() { return $mol_audio_node.context().createOscillator() } - - @ $mol_mem - freq( next = 440 ) { return next } - - @ $mol_mem - shape( next: $mol_audio_vibe_shape = 'sine' ) { return next } - - @ $mol_mem - active( next?: boolean ): boolean { - - $mol_wire_solid() - - const prev = $mol_wire_probe( ()=> this.active() ) - if( prev === next ) return next ?? false - - if( next === true ) this.node().start() - else if( prev === true ) this.node().stop() - - return next ?? false - } - - @ $mol_mem - output() { - const node = this.node() - - node.frequency.setValueAtTime( this.freq(), this.time() ) - node.type = this.shape() - - this.active( true ) - return super.output() - } - - destructor() { - this.active( false ) - super.destructor() - } - - } -}