diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 4555ed00..8d93b497 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -35,7 +35,7 @@ jobs: #- name: Spago test # run: npx spago -x test.dhall test - name: Build qc - run: npx spago -x examples.dhall bundle-app --main WAGS.Example.Docs --to examples/docs/index.js --minify + run: npx spago -x examples.dhall bundle-module --main WAGS.Example.Docs --to examples/docs/index.js --minify - name: Deploy if: ${{ github.ref == 'refs/heads/main' }} uses: peaceiris/actions-gh-pages@v3 diff --git a/CHANGELOG.md b/CHANGELOG.md index f3189ce1..262d8e0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.1.2] - 2022-05-03 + +- Allows for Audio Units to control Audio units. + ## [1.1.1] - 2022-04-30 - Updates to 0.15.0. diff --git a/examples/atari-speaks/AtariSpeaks.purs b/examples/atari-speaks/AtariSpeaks.purs index a2017658..cb016a2a 100644 --- a/examples/atari-speaks/AtariSpeaks.purs +++ b/examples/atari-speaks/AtariSpeaks.purs @@ -31,7 +31,7 @@ import WAGS.Control (analyser, gain, loopBuf, speaker2) import WAGS.Core (Node) import WAGS.Example.Utils (RaiseCancellation) import WAGS.Interpret (close, context, decodeAudioDataFromUri, effectfulAudioInterpret, getByteFrequencyData, makeFFIAudioSnapshot) -import WAGS.Parameter (opticN, bangOn) +import WAGS.Core (opticN, bangOn) import WAGS.Properties (loopEnd, loopStart, playbackRate) import WAGS.WebAPI (AnalyserNodeCb(..), AudioContext, BrowserAudioBuffer) import Web.HTML (window) diff --git a/examples/docs/AudioUnits/Allpass.purs b/examples/docs/AudioUnits/Allpass.purs index c7873ff6..239eb788 100644 --- a/examples/docs/AudioUnits/Allpass.purs +++ b/examples/docs/AudioUnits/Allpass.purs @@ -12,7 +12,7 @@ import WAGS.Core (mix) import WAGS.Example.Docs.Types (CancelCurrentAudio, Page, SingleSubgraphEvent) import WAGS.Example.Docs.Util (audioWrapper) import WAGS.Interpret (decodeAudioDataFromUri) -import WAGS.Parameter (bangOn) +import WAGS.Core (bangOn) import WAGS.Run (run2) px = diff --git a/examples/docs/AudioUnits/Analyser.purs b/examples/docs/AudioUnits/Analyser.purs index a2f3f1fc..96dae1eb 100644 --- a/examples/docs/AudioUnits/Analyser.purs +++ b/examples/docs/AudioUnits/Analyser.purs @@ -33,7 +33,7 @@ import WAGS.Core (Po2(..)) import WAGS.Example.Docs.Types (CancelCurrentAudio, Page, SingleSubgraphEvent) import WAGS.Example.Docs.Util (WrapperStates(..), clickCb, mkWrapperEvent) import WAGS.Interpret (close, context, contextState, bracketCtx, decodeAudioDataFromUri, effectfulAudioInterpret, getByteFrequencyData, makeFFIAudioSnapshot) -import WAGS.Parameter (bangOn) +import WAGS.Core (bangOn) import WAGS.WebAPI (AnalyserNodeCb(..), BrowserAudioBuffer) px = diff --git a/examples/docs/AudioUnits/Bandpass.purs b/examples/docs/AudioUnits/Bandpass.purs index a6ec88dd..5593fc3c 100644 --- a/examples/docs/AudioUnits/Bandpass.purs +++ b/examples/docs/AudioUnits/Bandpass.purs @@ -12,7 +12,7 @@ import WAGS.Core (mix) import WAGS.Example.Docs.Types (CancelCurrentAudio, Page, SingleSubgraphEvent) import WAGS.Example.Docs.Util (audioWrapper) import WAGS.Interpret (decodeAudioDataFromUri) -import WAGS.Parameter (bangOn) +import WAGS.Core (bangOn) import WAGS.Run (run2) px = diff --git a/examples/docs/AudioUnits/Compression.purs b/examples/docs/AudioUnits/Compression.purs index 2814cc56..dafc9526 100644 --- a/examples/docs/AudioUnits/Compression.purs +++ b/examples/docs/AudioUnits/Compression.purs @@ -11,7 +11,7 @@ import WAGS.Control (dynamicsCompressor_, loopBuf) import WAGS.Example.Docs.Types (CancelCurrentAudio, Page, SingleSubgraphEvent) import WAGS.Example.Docs.Util (audioWrapper) import WAGS.Interpret (decodeAudioDataFromUri) -import WAGS.Parameter (bangOn) +import WAGS.Core (bangOn) import WAGS.Run (run2) px = Proxy :: Proxy """
diff --git a/examples/docs/AudioUnits/Constant.purs b/examples/docs/AudioUnits/Constant.purs index 338a8797..ae27d9a5 100644 --- a/examples/docs/AudioUnits/Constant.purs +++ b/examples/docs/AudioUnits/Constant.purs @@ -15,7 +15,7 @@ import Type.Proxy (Proxy(..)) import WAGS.Control (gain_, constant) import WAGS.Example.Docs.Types (CancelCurrentAudio, Page, SingleSubgraphEvent) import WAGS.Example.Docs.Util (audioWrapper) -import WAGS.Parameter (AudioEnvelope(..), bangOn) +import WAGS.Core (AudioEnvelope(..), bangOn) import WAGS.Properties (offset) import WAGS.Run (run2) diff --git a/examples/docs/AudioUnits/Convolution.purs b/examples/docs/AudioUnits/Convolution.purs index 29bffc39..e544119f 100644 --- a/examples/docs/AudioUnits/Convolution.purs +++ b/examples/docs/AudioUnits/Convolution.purs @@ -11,7 +11,7 @@ import WAGS.Control (convolver, loopBuf) import WAGS.Example.Docs.Types (CancelCurrentAudio, Page, SingleSubgraphEvent) import WAGS.Example.Docs.Util (audioWrapper) import WAGS.Interpret (decodeAudioDataFromUri) -import WAGS.Parameter (bangOn) +import WAGS.Core (bangOn) import WAGS.Run (run2) px = Proxy :: Proxy """
diff --git a/examples/docs/AudioUnits/Delay.purs b/examples/docs/AudioUnits/Delay.purs index f1a82e05..586d702e 100644 --- a/examples/docs/AudioUnits/Delay.purs +++ b/examples/docs/AudioUnits/Delay.purs @@ -12,7 +12,7 @@ import WAGS.Core (mix) import WAGS.Example.Docs.Types (CancelCurrentAudio, Page, SingleSubgraphEvent) import WAGS.Example.Docs.Util (audioWrapper) import WAGS.Interpret (decodeAudioDataFromUri) -import WAGS.Parameter (bangOn) +import WAGS.Core (bangOn) import WAGS.Run (run2) px = diff --git a/examples/docs/AudioUnits/Gain.purs b/examples/docs/AudioUnits/Gain.purs index e2705e90..dcb0851c 100644 --- a/examples/docs/AudioUnits/Gain.purs +++ b/examples/docs/AudioUnits/Gain.purs @@ -11,7 +11,7 @@ import WAGS.Control (gain_, loopBuf) import WAGS.Example.Docs.Types (CancelCurrentAudio, Page, SingleSubgraphEvent) import WAGS.Example.Docs.Util (audioWrapper) import WAGS.Interpret (decodeAudioDataFromUri) -import WAGS.Parameter (bangOn) +import WAGS.Core (bangOn) import WAGS.Run (run2) px = Proxy :: Proxy """
diff --git a/examples/docs/AudioUnits/Highpass.purs b/examples/docs/AudioUnits/Highpass.purs index e1f7c2a1..0313f319 100644 --- a/examples/docs/AudioUnits/Highpass.purs +++ b/examples/docs/AudioUnits/Highpass.purs @@ -11,7 +11,7 @@ import WAGS.Control (highpass_, loopBuf) import WAGS.Example.Docs.Types (CancelCurrentAudio, Page, SingleSubgraphEvent) import WAGS.Example.Docs.Util (audioWrapper) import WAGS.Interpret (decodeAudioDataFromUri) -import WAGS.Parameter (bangOn) +import WAGS.Core (bangOn) import WAGS.Run (run2) px = Proxy :: Proxy """
diff --git a/examples/docs/AudioUnits/Highshelf.purs b/examples/docs/AudioUnits/Highshelf.purs index fcd8bbab..75400da4 100644 --- a/examples/docs/AudioUnits/Highshelf.purs +++ b/examples/docs/AudioUnits/Highshelf.purs @@ -11,7 +11,7 @@ import WAGS.Control (highshelf_, loopBuf) import WAGS.Example.Docs.Types (CancelCurrentAudio, Page, SingleSubgraphEvent) import WAGS.Example.Docs.Util (audioWrapper) import WAGS.Interpret (decodeAudioDataFromUri) -import WAGS.Parameter (bangOn) +import WAGS.Core (bangOn) import WAGS.Run (run2) px = Proxy :: Proxy """
diff --git a/examples/docs/AudioUnits/IIRFilter.purs b/examples/docs/AudioUnits/IIRFilter.purs index 16123123..9361f56e 100644 --- a/examples/docs/AudioUnits/IIRFilter.purs +++ b/examples/docs/AudioUnits/IIRFilter.purs @@ -13,7 +13,7 @@ import WAGS.Control (iirFilter, loopBuf) import WAGS.Example.Docs.Types (CancelCurrentAudio, Page, SingleSubgraphEvent) import WAGS.Example.Docs.Util (audioWrapper) import WAGS.Interpret (decodeAudioDataFromUri) -import WAGS.Parameter (bangOn) +import WAGS.Core (bangOn) import WAGS.Run (run2) px = diff --git a/examples/docs/AudioUnits/LoopBuf.purs b/examples/docs/AudioUnits/LoopBuf.purs index 5447ac98..1e42fc69 100644 --- a/examples/docs/AudioUnits/LoopBuf.purs +++ b/examples/docs/AudioUnits/LoopBuf.purs @@ -11,7 +11,7 @@ import WAGS.Control (loopBuf) import WAGS.Example.Docs.Types (CancelCurrentAudio, Page, SingleSubgraphEvent) import WAGS.Example.Docs.Util (audioWrapper) import WAGS.Interpret (decodeAudioDataFromUri) -import WAGS.Parameter (bangOn) +import WAGS.Core (bangOn) import WAGS.Run (run2) px = diff --git a/examples/docs/AudioUnits/Lowpass.purs b/examples/docs/AudioUnits/Lowpass.purs index 866af88a..a92e00d2 100644 --- a/examples/docs/AudioUnits/Lowpass.purs +++ b/examples/docs/AudioUnits/Lowpass.purs @@ -11,7 +11,7 @@ import WAGS.Control (loopBuf, lowpass_) import WAGS.Example.Docs.Types (CancelCurrentAudio, Page, SingleSubgraphEvent) import WAGS.Example.Docs.Util (audioWrapper) import WAGS.Interpret (decodeAudioDataFromUri) -import WAGS.Parameter (bangOn) +import WAGS.Core (bangOn) import WAGS.Run (run2) px = Proxy :: Proxy """
diff --git a/examples/docs/AudioUnits/Lowshelf.purs b/examples/docs/AudioUnits/Lowshelf.purs index 2262cf97..542eab57 100644 --- a/examples/docs/AudioUnits/Lowshelf.purs +++ b/examples/docs/AudioUnits/Lowshelf.purs @@ -11,7 +11,7 @@ import WAGS.Control (loopBuf, lowshelf_) import WAGS.Example.Docs.Types (CancelCurrentAudio, Page, SingleSubgraphEvent) import WAGS.Example.Docs.Util (audioWrapper) import WAGS.Interpret (decodeAudioDataFromUri) -import WAGS.Parameter (bangOn) +import WAGS.Core (bangOn) import WAGS.Run (run2) px = Proxy :: Proxy """
diff --git a/examples/docs/AudioUnits/Notch.purs b/examples/docs/AudioUnits/Notch.purs index 37265858..eebc3f5a 100644 --- a/examples/docs/AudioUnits/Notch.purs +++ b/examples/docs/AudioUnits/Notch.purs @@ -11,7 +11,7 @@ import WAGS.Control (loopBuf, notch_) import WAGS.Example.Docs.Types (CancelCurrentAudio, Page, SingleSubgraphEvent) import WAGS.Example.Docs.Util (audioWrapper) import WAGS.Interpret (decodeAudioDataFromUri) -import WAGS.Parameter (bangOn) +import WAGS.Core (bangOn) import WAGS.Run (run2) px = diff --git a/examples/docs/AudioUnits/Peaking.purs b/examples/docs/AudioUnits/Peaking.purs index 3c311f82..3b641227 100644 --- a/examples/docs/AudioUnits/Peaking.purs +++ b/examples/docs/AudioUnits/Peaking.purs @@ -11,7 +11,7 @@ import WAGS.Control (loopBuf, peaking_) import WAGS.Example.Docs.Types (CancelCurrentAudio, Page, SingleSubgraphEvent) import WAGS.Example.Docs.Util (audioWrapper) import WAGS.Interpret (decodeAudioDataFromUri) -import WAGS.Parameter (bangOn) +import WAGS.Core (bangOn) import WAGS.Run (run2) px = diff --git a/examples/docs/AudioUnits/PeriodicOsc.purs b/examples/docs/AudioUnits/PeriodicOsc.purs index f6eccaff..eadc0055 100644 --- a/examples/docs/AudioUnits/PeriodicOsc.purs +++ b/examples/docs/AudioUnits/PeriodicOsc.purs @@ -12,7 +12,7 @@ import Type.Proxy (Proxy(..)) import WAGS.Control (gain_, periodicOsc) import WAGS.Example.Docs.Types (CancelCurrentAudio, Page, SingleSubgraphEvent) import WAGS.Example.Docs.Util (audioWrapper) -import WAGS.Parameter (bangOn) +import WAGS.Core (bangOn) import WAGS.Run (run2) px = diff --git a/examples/docs/AudioUnits/PlayBuf.purs b/examples/docs/AudioUnits/PlayBuf.purs index 8befba07..d2c1215d 100644 --- a/examples/docs/AudioUnits/PlayBuf.purs +++ b/examples/docs/AudioUnits/PlayBuf.purs @@ -11,7 +11,7 @@ import WAGS.Control (playBuf) import WAGS.Example.Docs.Types (CancelCurrentAudio, Page, SingleSubgraphEvent) import WAGS.Example.Docs.Util (audioWrapper) import WAGS.Interpret (decodeAudioDataFromUri) -import WAGS.Parameter (bangOn) +import WAGS.Core (bangOn) import WAGS.Run (run2) px = Proxy :: Proxy """
diff --git a/examples/docs/AudioUnits/Sawtooth.purs b/examples/docs/AudioUnits/Sawtooth.purs index b9aacce4..da081460 100644 --- a/examples/docs/AudioUnits/Sawtooth.purs +++ b/examples/docs/AudioUnits/Sawtooth.purs @@ -10,7 +10,7 @@ import Type.Proxy (Proxy(..)) import WAGS.Control (gain_, sawtoothOsc) import WAGS.Example.Docs.Types (CancelCurrentAudio, Page, SingleSubgraphEvent) import WAGS.Example.Docs.Util (audioWrapper) -import WAGS.Parameter (bangOn) +import WAGS.Core (bangOn) import WAGS.Run (run2) px = diff --git a/examples/docs/AudioUnits/SineOsc.purs b/examples/docs/AudioUnits/SineOsc.purs index 1136aa97..06265bc7 100644 --- a/examples/docs/AudioUnits/SineOsc.purs +++ b/examples/docs/AudioUnits/SineOsc.purs @@ -10,7 +10,7 @@ import Type.Proxy (Proxy(..)) import WAGS.Control (gain_, sinOsc) import WAGS.Example.Docs.Types (CancelCurrentAudio, Page, SingleSubgraphEvent) import WAGS.Example.Docs.Util (audioWrapper) -import WAGS.Parameter (bangOn) +import WAGS.Core (bangOn) import WAGS.Run (run2) px = Proxy :: Proxy """
diff --git a/examples/docs/AudioUnits/SquareOsc.purs b/examples/docs/AudioUnits/SquareOsc.purs index f0acca96..29ce0d66 100644 --- a/examples/docs/AudioUnits/SquareOsc.purs +++ b/examples/docs/AudioUnits/SquareOsc.purs @@ -10,7 +10,7 @@ import Type.Proxy (Proxy(..)) import WAGS.Control (gain_, squareOsc) import WAGS.Example.Docs.Types (CancelCurrentAudio, Page, SingleSubgraphEvent) import WAGS.Example.Docs.Util (audioWrapper) -import WAGS.Parameter (bangOn) +import WAGS.Core (bangOn) import WAGS.Run (run2) px = diff --git a/examples/docs/AudioUnits/StereoPanner.purs b/examples/docs/AudioUnits/StereoPanner.purs index a416784d..6e569647 100644 --- a/examples/docs/AudioUnits/StereoPanner.purs +++ b/examples/docs/AudioUnits/StereoPanner.purs @@ -11,7 +11,7 @@ import WAGS.Control (loopBuf, pan_) import WAGS.Example.Docs.Types (CancelCurrentAudio, Page, SingleSubgraphEvent) import WAGS.Example.Docs.Util (audioWrapper) import WAGS.Interpret (decodeAudioDataFromUri) -import WAGS.Parameter (bangOn) +import WAGS.Core (bangOn) import WAGS.Run (run2) px = diff --git a/examples/docs/AudioUnits/TriangleOsc.purs b/examples/docs/AudioUnits/TriangleOsc.purs index e73555fc..2d002ddb 100644 --- a/examples/docs/AudioUnits/TriangleOsc.purs +++ b/examples/docs/AudioUnits/TriangleOsc.purs @@ -10,7 +10,7 @@ import Type.Proxy (Proxy(..)) import WAGS.Control (gain_, triangleOsc) import WAGS.Example.Docs.Types (CancelCurrentAudio, Page, SingleSubgraphEvent) import WAGS.Example.Docs.Util (audioWrapper) -import WAGS.Parameter (bangOn) +import WAGS.Core (bangOn) import WAGS.Run (run2) px = diff --git a/examples/docs/AudioUnits/WaveShaper.purs b/examples/docs/AudioUnits/WaveShaper.purs index c66bbd5b..9f918995 100644 --- a/examples/docs/AudioUnits/WaveShaper.purs +++ b/examples/docs/AudioUnits/WaveShaper.purs @@ -16,7 +16,7 @@ import WAGS.Control (loopBuf, waveShaper) import WAGS.Example.Docs.Types (CancelCurrentAudio, Page, SingleSubgraphEvent) import WAGS.Example.Docs.Util (audioWrapper) import WAGS.Interpret (decodeAudioDataFromUri, makeFloatArray) -import WAGS.Parameter (bangOn) +import WAGS.Core (bangOn) import WAGS.Run (run2) px = diff --git a/examples/docs/Component.purs b/examples/docs/Component.purs index cabbd40d..ad98c8c1 100644 --- a/examples/docs/Component.purs +++ b/examples/docs/Component.purs @@ -39,7 +39,7 @@ import WAGS.Example.Docs.AudioUnits.WaveShaper as WaveShaper import WAGS.Example.Docs.Types (CancelCurrentAudio, Page(..), SingleSubgraphEvent, SingleSubgraphPusher) import WAGS.Example.Docs.Util (audioWrapperSpan, ccassp, mkNext, scrollToTop) import WAGS.Interpret (bracketCtx, decodeAudioDataFromUri) -import WAGS.Parameter (bangOn) +import WAGS.Core (bangOn) import WAGS.Run (run2, run2_) diff --git a/examples/docs/Docs.purs b/examples/docs/Docs.purs index c74dd09e..2a37e71e 100644 --- a/examples/docs/Docs.purs +++ b/examples/docs/Docs.purs @@ -27,6 +27,7 @@ import WAGS.Example.Docs.MultiChannel as Multichannel import WAGS.Example.Docs.Portals as Portals import WAGS.Example.Docs.Pursx1 as Pursx1 import WAGS.Example.Docs.Pursx2 as Pursx2 +import WAGS.Example.Docs.Params as Params import WAGS.Example.Docs.Subgraphs as Subgraph import WAGS.Example.Docs.Types (Page(..), ToplevelEvent(..)) import Web.HTML (window) @@ -95,9 +96,9 @@ scene push event' = , Events /\ "Events" /\ true - -- , MultiChannel - -- /\ "Merging and splitting" - -- /\ true + , Params + /\ "Parameters" + /\ true -- , AudioWorklets -- /\ "Audio worklets" -- /\ true @@ -145,6 +146,7 @@ scene push event' = go AudioUnits = D.div_ $ map plant $ bus (Component.components setCancellation setPage) go AudioWorklets = D.div_ $ map plant $ bus (Pursx1.pursx1 setCancellation setPage) go Events = D.div_ $ map plant $ bus (Events.events setCancellation setPage) + go Params = D.div_ $ map plant $ bus (Params.params setCancellation setPage) go State = D.div_ $ map plant $ bus (Effects.effects setCancellation setPage) go Imperative = D.div_ $ map plant $ bus (Pursx2.pursx2 setCancellation setPage) go MultiChannel = D.div_ $ map plant $ bus (Multichannel.multiChannel setCancellation setPage) diff --git a/examples/docs/Effects.purs b/examples/docs/Effects.purs index e94ad41c..4728287c 100644 --- a/examples/docs/Effects.purs +++ b/examples/docs/Effects.purs @@ -32,7 +32,7 @@ import WAGS.Example.Docs.Types (CancelCurrentAudio, Page(..), SingleSubgraphEven import WAGS.Example.Docs.Util (ccassp, mkNext, scrollToTop) import WAGS.Interpret (close, context) import WAGS.Math (calcSlope) -import WAGS.Parameter (AudioNumeric(..), _linear, bangOn) +import WAGS.Core (AudioNumeric(..), _linear, bangOn) import WAGS.Properties as P import WAGS.Run (run2) diff --git a/examples/docs/Events.purs b/examples/docs/Events.purs index 429aface..f1a90c54 100644 --- a/examples/docs/Events.purs +++ b/examples/docs/Events.purs @@ -35,12 +35,12 @@ px = Proxy :: Proxy @ex2@

Next steps

-

In this section, saw how to build rich audio applications using the Event and Behavior types. We also covered the three most common patterns you'll see when working with events: events that need to happen now, events that come from user interaction, and timed events. In the next section, we'll look at how to make events stateful.

+

In this section, saw how to build rich audio applications using the Event and Behavior types. We also covered the three most common patterns you'll see when working with events: events that need to happen now, events that come from user interaction, and timed events. In the next section, we'll look at different ways to specify the numeric parameters being sent as events.

""" events :: forall lock payload. CancelCurrentAudio -> (Page -> Effect Unit) -> SingleSubgraphPusher -> Event SingleSubgraphEvent -> Element lock payload events cca' dpage ssp ev = makePursx' (Proxy :: _ "@") px - { next: mnx State + { next: mnx Params , primer: nut $ Primer.primer , inWags: nut $ InWags.inWags , flavors: nut $ Flavors.flavors diff --git a/examples/docs/Events/Ex0.purs b/examples/docs/Events/Ex0.purs index d9ff3104..d4834cca 100644 --- a/examples/docs/Events/Ex0.purs +++ b/examples/docs/Events/Ex0.purs @@ -23,7 +23,7 @@ import Type.Proxy (Proxy(..)) import WAGS.Control (gain, gain_, microphone, recorder, sinOsc) import WAGS.Core (Node) import WAGS.Example.Docs.Types (CancelCurrentAudio, Page, SingleSubgraphEvent(..)) -import WAGS.Parameter (AudioEnvelope(..), AudioOnOff(..), _off, _on) +import WAGS.Core (AudioEnvelope(..), AudioOnOff(..), _off, _on) import WAGS.Properties (onOff) import WAGS.Properties as P import WAGS.Run (run2_) @@ -103,7 +103,7 @@ import FRP.Event (bus) import FRP.Event.Class (bang) import Math (pow) import WAGS.Control (gain_, gain, sinOsc) -import WAGS.Parameter (AudioEnvelope(..), AudioOnOff(..), _on, _off) +import WAGS.Core (AudioEnvelope(..), AudioOnOff(..), _on, _off) import WAGS.Properties (onOff) import WAGS.Properties as P import WAGS.Run (run2_) diff --git a/examples/docs/Events/Ex0TL.purs b/examples/docs/Events/Ex0TL.purs index 048feb8e..a63bea34 100644 --- a/examples/docs/Events/Ex0TL.purs +++ b/examples/docs/Events/Ex0TL.purs @@ -15,7 +15,7 @@ import FRP.Event (bus) import FRP.Event.Class (bang) import Data.Number (pow) import WAGS.Control (gain_, gain, sinOsc) -import WAGS.Parameter (AudioEnvelope(..), AudioOnOff(..), _on, _off) +import WAGS.Core (AudioEnvelope(..), AudioOnOff(..), _on, _off) import WAGS.Properties (onOff) import WAGS.Properties as P import WAGS.Run (run2_) diff --git a/examples/docs/Events/Ex1.purs b/examples/docs/Events/Ex1.purs index 751b80d6..e4f0a671 100644 --- a/examples/docs/Events/Ex1.purs +++ b/examples/docs/Events/Ex1.purs @@ -22,7 +22,7 @@ import WAGS.Example.Docs.Types (CancelCurrentAudio, Page, SingleSubgraphEvent(.. import WAGS.Example.Docs.Util (raceSelf) import WAGS.Interpret (close, constant0Hack, context, decodeAudioDataFromUri) import WAGS.Math (calcSlope) -import WAGS.Parameter (bangOn) +import WAGS.Core (bangOn) import WAGS.Properties (loopEnd, loopStart, playbackRate) import WAGS.Run (run2) import Web.Event.Event (target) @@ -75,7 +75,7 @@ import Type.Proxy (Proxy(..)) import WAGS.Control (loopBuf) import WAGS.Interpret (bracketCtx, decodeAudioDataFromUri) import WAGS.Math (calcSlope) -import WAGS.Parameter (bangOn) +import WAGS.Core (bangOn) import WAGS.Properties (loopEnd, loopStart, playbackRate) import WAGS.Run (run2_) import WAGS.WebAPI (BrowserAudioBuffer) diff --git a/examples/docs/Events/Ex1TL.purs b/examples/docs/Events/Ex1TL.purs index d4ea9cf0..5632b892 100644 --- a/examples/docs/Events/Ex1TL.purs +++ b/examples/docs/Events/Ex1TL.purs @@ -20,7 +20,7 @@ import Type.Proxy (Proxy(..)) import WAGS.Control (loopBuf) import WAGS.Interpret (bracketCtx, decodeAudioDataFromUri) import WAGS.Math (calcSlope) -import WAGS.Parameter (bangOn) +import WAGS.Core (bangOn) import WAGS.Properties (loopEnd, loopStart, playbackRate) import WAGS.Run (run2_) import WAGS.WebAPI (BrowserAudioBuffer) diff --git a/examples/docs/Events/Ex2.purs b/examples/docs/Events/Ex2.purs index 1493fae7..74973576 100644 --- a/examples/docs/Events/Ex2.purs +++ b/examples/docs/Events/Ex2.purs @@ -23,7 +23,7 @@ import WAGS.Core (Node, mix) import WAGS.Example.Docs.Types (CancelCurrentAudio, Page, SingleSubgraphEvent(..)) import WAGS.Interpret (close, context) import WAGS.Math (calcSlope) -import WAGS.Parameter (AudioEnvelope(..), bangOn) +import WAGS.Core (AudioEnvelope(..), bangOn) import WAGS.Properties (frequency) import WAGS.Properties as P import WAGS.Run (run2e) @@ -82,7 +82,7 @@ import WAGS.Control (bandpass_, fan1, gain, gain_, highpass_, triangleOsc) import WAGS.Core (Node, mix) import WAGS.Interpret (close, context) import WAGS.Math (calcSlope) -import WAGS.Parameter (AudioEnvelope(..), bangOn) +import WAGS.Core (AudioEnvelope(..), bangOn) import WAGS.Properties (frequency) import WAGS.Properties as P import WAGS.Run (run2e) diff --git a/examples/docs/Events/Ex2TL.purs b/examples/docs/Events/Ex2TL.purs index 80abb337..8a128659 100644 --- a/examples/docs/Events/Ex2TL.purs +++ b/examples/docs/Events/Ex2TL.purs @@ -21,7 +21,7 @@ import WAGS.Control (bandpass_, fan1, gain, gain_, highpass_, triangleOsc) import WAGS.Core (Node, mix) import WAGS.Interpret (close, context) import WAGS.Math (calcSlope) -import WAGS.Parameter (AudioEnvelope(..), bangOn) +import WAGS.Core (AudioEnvelope(..), bangOn) import WAGS.Properties (frequency) import WAGS.Properties as P import WAGS.Run (run2e) diff --git a/examples/docs/FixFan/AI0.purs b/examples/docs/FixFan/AI0.purs index 50786d57..0688613b 100644 --- a/examples/docs/FixFan/AI0.purs +++ b/examples/docs/FixFan/AI0.purs @@ -14,7 +14,7 @@ import WAGS.Control (gain_, playBuf) import WAGS.Example.Docs.Types (CancelCurrentAudio, Page, SingleSubgraphEvent) import WAGS.Example.Docs.Util (audioWrapper) import WAGS.Interpret (bracketCtx, decodeAudioDataFromUri) -import WAGS.Parameter (apOn, dt) +import WAGS.Core (apOn, dt) import WAGS.Properties (onOff) import WAGS.Run (run2, run2_) diff --git a/examples/docs/FixFan/AI1.purs b/examples/docs/FixFan/AI1.purs index 9b236b2a..a7786545 100644 --- a/examples/docs/FixFan/AI1.purs +++ b/examples/docs/FixFan/AI1.purs @@ -19,7 +19,7 @@ import WAGS.Control (gain_, playBuf) import WAGS.Example.Docs.Types (CancelCurrentAudio, Page, SingleSubgraphEvent) import WAGS.Example.Docs.Util (audioWrapper) import WAGS.Interpret (bracketCtx, decodeAudioDataFromUri) -import WAGS.Parameter (apOn, dt) +import WAGS.Core (apOn, dt) import WAGS.Properties (onOff) import WAGS.Run (run2, run2_) diff --git a/examples/docs/FixFan/Fan0.purs b/examples/docs/FixFan/Fan0.purs index 7112e4c6..c5232c9e 100644 --- a/examples/docs/FixFan/Fan0.purs +++ b/examples/docs/FixFan/Fan0.purs @@ -12,7 +12,7 @@ import WAGS.Core (mix) import WAGS.Example.Docs.Types (CancelCurrentAudio, Page, SingleSubgraphEvent) import WAGS.Example.Docs.Util (audioWrapper) import WAGS.Interpret (decodeAudioDataFromUri) -import WAGS.Parameter (bangOn) +import WAGS.Core (bangOn) import WAGS.Run (run2) px = diff --git a/examples/docs/FixFan/Fan1.purs b/examples/docs/FixFan/Fan1.purs index 2e2b9bff..3aec9024 100644 --- a/examples/docs/FixFan/Fan1.purs +++ b/examples/docs/FixFan/Fan1.purs @@ -16,7 +16,7 @@ import WAGS.Core (mix) import WAGS.Example.Docs.Types (CancelCurrentAudio, Page, SingleSubgraphEvent) import WAGS.Example.Docs.Util (audioWrapper) import WAGS.Interpret (decodeAudioDataFromUri) -import WAGS.Parameter (bangOn) +import WAGS.Core (bangOn) import WAGS.Run (run2) px = diff --git a/examples/docs/FixFan/Fix0.purs b/examples/docs/FixFan/Fix0.purs index 8ba61fbd..d8b74868 100644 --- a/examples/docs/FixFan/Fix0.purs +++ b/examples/docs/FixFan/Fix0.purs @@ -11,7 +11,7 @@ import WAGS.Control (delay_, gain_, playBuf, fix) import WAGS.Example.Docs.Types (CancelCurrentAudio, Page, SingleSubgraphEvent) import WAGS.Example.Docs.Util (audioWrapper) import WAGS.Interpret (bracketCtx, decodeAudioDataFromUri) -import WAGS.Parameter (bangOn) +import WAGS.Core (bangOn) import WAGS.Run (run2, run2_) px = diff --git a/examples/docs/FixFan/Fix1.purs b/examples/docs/FixFan/Fix1.purs index d2a17ced..5e5d379f 100644 --- a/examples/docs/FixFan/Fix1.purs +++ b/examples/docs/FixFan/Fix1.purs @@ -14,7 +14,7 @@ import WAGS.Core (mix) import WAGS.Example.Docs.Types (CancelCurrentAudio, Page, SingleSubgraphEvent) import WAGS.Example.Docs.Util (audioWrapper) import WAGS.Interpret (decodeAudioDataFromUri) -import WAGS.Parameter (AudioEnvelope(..), bangOn) +import WAGS.Core (AudioEnvelope(..), bangOn) import WAGS.Properties as P import WAGS.Run (run2) diff --git a/examples/docs/HelloWorld.purs b/examples/docs/HelloWorld.purs index 76df3b31..8bb2743a 100644 --- a/examples/docs/HelloWorld.purs +++ b/examples/docs/HelloWorld.purs @@ -16,7 +16,7 @@ import Type.Proxy (Proxy(..)) import WAGS.Control (gain_, sinOsc) import WAGS.Example.Docs.Types (CancelCurrentAudio, Page(..), SingleSubgraphEvent(..), SingleSubgraphPusher) import WAGS.Example.Docs.Util (audioWrapper, ccassp, mkNext, scrollToTop) -import WAGS.Parameter (bangOn) +import WAGS.Core (bangOn) import WAGS.Run (run2, run2_) px = diff --git a/examples/docs/Intro/IntroEx.purs b/examples/docs/Intro/IntroEx.purs index e1f1d065..622a0004 100644 --- a/examples/docs/Intro/IntroEx.purs +++ b/examples/docs/Intro/IntroEx.purs @@ -45,7 +45,7 @@ import WAGS.Example.Docs.Types (CancelCurrentAudio, Page, SingleSubgraphEvent(.. import WAGS.Example.Docs.Util (raceSelf) import WAGS.Interpret (close, constant0Hack, context, decodeAudioDataFromUri, getByteFrequencyData) import WAGS.Math (calcSlope) -import WAGS.Parameter (AudioEnvelope(..), AudioNumeric(..), _linear, bangOn) +import WAGS.Core (AudioEnvelope(..), AudioNumeric(..), _linear, bangOn) import WAGS.Properties as P import WAGS.Run (run2) import WAGS.WebAPI (AnalyserNodeCb(..)) diff --git a/examples/docs/Params.purs b/examples/docs/Params.purs new file mode 100644 index 00000000..d07e2ca3 --- /dev/null +++ b/examples/docs/Params.purs @@ -0,0 +1,47 @@ +module WAGS.Example.Docs.Params where + +import Prelude + +import Deku.Core (Element) +import Deku.Pursx (nut, (~~)) +import Effect (Effect) +import FRP.Event (Event) +import Type.Proxy (Proxy(..)) +import WAGS.Example.Docs.Params.Numeric as Numeric +import WAGS.Example.Docs.Params.Unit as Unit +import WAGS.Example.Docs.Params.Sudden as Sudden +import WAGS.Example.Docs.Params.Envelope as Envelope +import WAGS.Example.Docs.Params.Cancel as Cancel +import WAGS.Example.Docs.Types (CancelCurrentAudio, Page(..), SingleSubgraphEvent, SingleSubgraphPusher) +import WAGS.Example.Docs.Util (ccassp, mkNext, scrollToTop) + +px = Proxy :: Proxy """
+

Parameters

+ +

Controlling our units

+

+ In the previous section, we saw how we can use browser events to control audio units. The Web Audio API provides a rich set of tools to control both the audio-rate and control-rate parameters of audio units. This section goes over how wags exposes those parameters. +

+ + ~sudden~ + ~numeric~ + ~envelope~ + ~cancel~ + ~unit~ + +

Next steps

+

In this section, we saw how to specify parameters for audio units, including using audio-rate audio units as parameters. In the next section, we'll look at how to make events stateful.

+
""" + +params :: forall lock payload. CancelCurrentAudio -> (Page -> Effect Unit) -> SingleSubgraphPusher -> Event SingleSubgraphEvent -> Element lock payload +params cca' dpage ssp ev = px ~~ + { sudden: nut $ Sudden.suddenEx ccb dpage ev + , numeric: nut $ Numeric.numericEx ccb dpage ev + , envelope: nut $ Envelope.envelopeEx ccb dpage ev + , cancel: nut $ Cancel.cancelEx ccb dpage ev + , unit: nut $ Unit.unitEx ccb dpage ev + , next: mkNext ev cpage + } + where + cpage = dpage State *> scrollToTop + ccb = ccassp cca' ssp \ No newline at end of file diff --git a/examples/docs/Params/Cancel.purs b/examples/docs/Params/Cancel.purs new file mode 100644 index 00000000..73e323ed --- /dev/null +++ b/examples/docs/Params/Cancel.purs @@ -0,0 +1,80 @@ +module WAGS.Example.Docs.Params.Cancel where + +import Prelude + +import Data.Array ((..)) +import Data.Foldable (oneOf) +import Deku.Control (text_) +import Deku.Core (Element) +import Deku.Pursx (nut, (~~)) +import Effect (Effect) +import FRP.Event (Event, bang) +import FRP.Event.Time (delay) +import Type.Proxy (Proxy(..)) +import WAGS.Control (gain_, loopBuf) +import WAGS.Core (AudioCancel(..), AudioEnvelope(..), bangOn) +import WAGS.Example.Docs.Types (CancelCurrentAudio, Page, SingleSubgraphEvent) +import WAGS.Example.Docs.Util (audioWrapper) +import WAGS.Interpret (decodeAudioDataFromUri) +import WAGS.Properties (playbackRate) +import WAGS.Run (run2) + +px = + Proxy :: Proxy """
+

Cancel

+

The AudioCancel parameter corresponds to the Web Audio API's cancelScheduledValues function and cancels whatever effects you programmed in the future. In the example below, we execute the following sequence:

+
    +
  1. Play an audio file
  2. +
  3. Send an event at 1.0 seconds to schedule an evenlope to modulate the audio rate starting at 1.5 seconds.
  4. +
  5. Cancel the envelope at 3.0 seconds, but schedule the cancelation to take effect at 4.0 seconds.
  6. +
+
~txt~
+ ~cancel~ +
+""" + +cancelEx :: forall lock payload. CancelCurrentAudio -> (Page -> Effect Unit) -> Event SingleSubgraphEvent -> Element lock payload +cancelEx ccb _ ev = px ~~ + { txt: nut + ( text_ + """\ctx buf -> run2 ctx + [ gain_ 1.0 + [ loopBuf buf + ( oneOf + [ bangOn + , delay 1000 + $ bang + $ playbackRate + $ AudioEnvelope + { p: join (0 .. 60 $> [ 1.0, 1.2, 1.0, 0.8 ]) + , o: 1.5 + , d: 30.0 + } + , delay 3000 (bang (playbackRate (AudioCancel { o: 3.5 }))) + ] + ) + ] + ]""" + ) + , cancel: nut + ( audioWrapper ev ccb (\ctx -> decodeAudioDataFromUri ctx "https://freesound.org/data/previews/320/320873_527080-hq.mp3") + \ctx buf -> run2 ctx + [ gain_ 1.0 + [ loopBuf buf + ( oneOf + [ bangOn + , delay 1000 + $ bang + $ playbackRate + $ AudioEnvelope + { p: join (0 .. 60 $> [ 1.0, 1.2, 1.0, 0.8 ]) + , o: 1.5 + , d: 30.0 + } + , delay 3000 (bang (playbackRate (AudioCancel { o: 3.5 }))) + ] + ) + ] + ] + ) + } \ No newline at end of file diff --git a/examples/docs/Params/Envelope.purs b/examples/docs/Params/Envelope.purs new file mode 100644 index 00000000..4e8bd904 --- /dev/null +++ b/examples/docs/Params/Envelope.purs @@ -0,0 +1,73 @@ +module WAGS.Example.Docs.Params.Envelope where + +import Prelude + +import Data.Array ((..)) +import Data.Foldable (oneOf) +import Deku.Control (text_) +import Deku.Core (Element) +import Deku.Pursx (nut, (~~)) +import Effect (Effect) +import FRP.Event (Event, bang) +import FRP.Event.Time (delay) +import Type.Proxy (Proxy(..)) +import WAGS.Control (gain_, loopBuf) +import WAGS.Core (AudioCancel(..), AudioEnvelope(..), bangOn) +import WAGS.Example.Docs.Types (CancelCurrentAudio, Page, SingleSubgraphEvent) +import WAGS.Example.Docs.Util (audioWrapper) +import WAGS.Interpret (decodeAudioDataFromUri) +import WAGS.Properties (playbackRate) +import WAGS.Run (run2) + +px = + Proxy :: Proxy """
+

Envelope

+

The AudioEnvelope parameter corresponds to the Web Audio API's setValueCurveAtTime function and sets an envelope p over the duration d starting at time o.

+
~txt~
+ ~envelope~ +
+""" + +envelopeEx :: forall lock payload. CancelCurrentAudio -> (Page -> Effect Unit) -> Event SingleSubgraphEvent -> Element lock payload +envelopeEx ccb _ ev = px ~~ + { txt: nut + ( text_ + """\ctx buf -> run2 ctx + [ gain_ 1.0 + [ loopBuf buf + ( oneOf + [ bangOn + , delay 1000 + $ bang + $ playbackRate + $ AudioEnvelope + { p: join (0 .. 60 $> [ 1.0, 1.2, 1.0, 0.8 ]) + , o: 1.5 + , d: 30.0 + } + ] + ) + ] + ]""" + ) + , envelope: nut + ( audioWrapper ev ccb (\ctx -> decodeAudioDataFromUri ctx "https://freesound.org/data/previews/320/320873_527080-hq.mp3") + \ctx buf -> run2 ctx + [ gain_ 1.0 + [ loopBuf buf + ( oneOf + [ bangOn + , delay 1000 + $ bang + $ playbackRate + $ AudioEnvelope + { p: join (0 .. 60 $> [ 1.0, 1.2, 1.0, 0.8 ]) + , o: 1.5 + , d: 30.0 + } + ] + ) + ] + ] + ) + } \ No newline at end of file diff --git a/examples/docs/Params/Numeric.purs b/examples/docs/Params/Numeric.purs new file mode 100644 index 00000000..6e391ac3 --- /dev/null +++ b/examples/docs/Params/Numeric.purs @@ -0,0 +1,94 @@ +module WAGS.Example.Docs.Params.Numeric where + +import Prelude + +import Data.Foldable (oneOf) +import Deku.Core (Element) +import Deku.Pursx (makePursx', nut) +import Effect (Effect) +import FRP.Event (Event, bang) +import FRP.Event.Time (delay) +import Type.Proxy (Proxy(..)) +import WAGS.Control (gain_, loopBuf) +import WAGS.Core (AudioNumeric(..), AudioSudden(..), _exponential, _linear, _step, bangOn) +import WAGS.Example.Docs.Types (CancelCurrentAudio, Page, SingleSubgraphEvent) +import WAGS.Example.Docs.Util (audioWrapper) +import WAGS.Interpret (decodeAudioDataFromUri) +import WAGS.Properties (playbackRate) +import WAGS.Run (run2) + +px = + Proxy :: Proxy """
+

AudioNumeric

+

AudioNumeric encompasses the following three functions from the Web Audio API:

+ +

Let's explore all of them in the example below.

+ +
Pro tip: When using AudioNumeric, consider starting with a _step transition. Otherwise, the transition may be abrupt and unpleasant!
+ +
\ctx buf -> run2 ctx
+  [ gain_ 1.0
+      [ loopBuf buf
+          ( oneOf
+              [ bangOn
+              , delay 1000
+                  $ oneOf
+                      [ bang
+                          $ playbackRate
+                          $ AudioNumeric { n: 1.0, o: 1.0, t: _step }
+                      , bang
+                          $ playbackRate
+                          $ AudioNumeric { n: 1.3, o: 2.0, t: _linear }
+                      ]
+              , delay 2500
+                  $ oneOf
+                      [ bang
+                          $ playbackRate
+                          $ AudioNumeric { n: 1.0, o: 2.5, t: _step }
+                      , bang
+                          $ playbackRate
+                          $ AudioNumeric { n: 0.7, o: 3.5, t: _exponential }
+                      ]
+              ]
+          )
+      ]
+  ]
+ + @numericEx@ +
+""" + +numericEx + :: forall lock payload. CancelCurrentAudio -> (Page -> Effect Unit) -> Event SingleSubgraphEvent -> Element lock payload +numericEx ccb _ ev = makePursx' (Proxy :: _ "@") px + { numericEx: nut + ( audioWrapper ev ccb (\ctx -> decodeAudioDataFromUri ctx "https://freesound.org/data/previews/320/320873_527080-hq.mp3") + \ctx buf -> run2 ctx + [ gain_ 1.0 + [ loopBuf buf + ( oneOf + [ bangOn + , delay 1000 + $ oneOf + [ bang + $ playbackRate + $ AudioNumeric { n: 1.0, o: 1.0, t: _step } + , bang + $ playbackRate + $ AudioNumeric { n: 1.3, o: 2.0, t: _linear } + ] + , delay 2500 + $ oneOf + [ bang + $ playbackRate + $ AudioNumeric { n: 1.0, o: 2.5, t: _step } + , bang + $ playbackRate + $ AudioNumeric { n: 0.7, o: 3.5, t: _exponential } + ] + ] + ) + ] + ] + ) + } \ No newline at end of file diff --git a/examples/docs/Params/Sudden.purs b/examples/docs/Params/Sudden.purs new file mode 100644 index 00000000..576cbd34 --- /dev/null +++ b/examples/docs/Params/Sudden.purs @@ -0,0 +1,64 @@ +module WAGS.Example.Docs.Params.Sudden where + +import Prelude + +import Data.Foldable (oneOf) +import Deku.Core (Element) +import Deku.Pursx (makePursx', nut) +import Effect (Effect) +import FRP.Event (Event, bang) +import FRP.Event.Time (delay) +import Type.Proxy (Proxy(..)) +import WAGS.Control (gain_, loopBuf) +import WAGS.Core (AudioSudden(..), bangOn) +import WAGS.Example.Docs.Types (CancelCurrentAudio, Page, SingleSubgraphEvent) +import WAGS.Example.Docs.Util (audioWrapper) +import WAGS.Interpret (decodeAudioDataFromUri) +import WAGS.Properties (playbackRate) +import WAGS.Run (run2) + +px = + Proxy :: Proxy """
+

AudioSudden

+

The simplest change you can make is scheduling a value to change now. This is done with AudioSudden, which is a wrapper around the setter for an audio parameter's `value` field in the Web Audio API.

+ +

In the example below, we change a value after it has run for 1.5 seconds.

+ +
\ctx buf -> run2 ctx
+  [ gain_ 1.0
+      [ loopBuf buf
+          ( oneOf
+              [ bangOn
+              , delay 1500
+                  $ bang
+                  $ playbackRate
+                  $ AudioSudden { n: 1.4 }
+              ]
+          )
+      ]
+  ]
+ + @suddenEx@ +
+""" + +suddenEx + :: forall lock payload. CancelCurrentAudio -> (Page -> Effect Unit) -> Event SingleSubgraphEvent -> Element lock payload +suddenEx ccb _ ev = makePursx' (Proxy :: _ "@") px + { suddenEx: nut + ( audioWrapper ev ccb (\ctx -> decodeAudioDataFromUri ctx "https://freesound.org/data/previews/320/320873_527080-hq.mp3") + \ctx buf -> run2 ctx + [ gain_ 1.0 + [ loopBuf buf + ( oneOf + [ bangOn + , delay 1500 + $ bang + $ playbackRate + $ AudioSudden { n: 1.4 } + ] + ) + ] + ] + ) + } \ No newline at end of file diff --git a/examples/docs/Params/Unit.purs b/examples/docs/Params/Unit.purs new file mode 100644 index 00000000..290018cb --- /dev/null +++ b/examples/docs/Params/Unit.purs @@ -0,0 +1,67 @@ +module WAGS.Example.Docs.Params.Unit where + +import Prelude + +import Data.Foldable (oneOf) +import Deku.Core (Element) +import Deku.Pursx (nut, (~~)) +import Effect (Effect) +import FRP.Event (Event, bang) +import Type.Proxy (Proxy(..)) +import WAGS.Control (constant, gain_, loopBuf, lowpass_, squareOsc) +import WAGS.Core (AudioUnit(..), bangOn, c1) +import WAGS.Example.Docs.Types (CancelCurrentAudio, Page, SingleSubgraphEvent) +import WAGS.Example.Docs.Util (audioWrapper) +import WAGS.Interpret (decodeAudioDataFromUri) +import WAGS.Properties (playbackRate) +import WAGS.Run (run2) + +px = + Proxy :: Proxy """
+

Audio Units

+

In my humble opinion, the summit of Web Audio programming is when audio units control the audio parameters of other audio units. This allows for a form of radical experimentation that is difficult in many other frameworks.

+ +

To control an audio parameter with an audio unit, use the AudioUnit constructor. You can also use a Node D1 l p. If your node is for an arbitrary number of channels, make sure to coerce it to mono using the c1 function, as in the example below.

+ +
\ctx buf -> run2 ctx
+  [ loopBuf buf
+      ( oneOf
+          [ bangOn
+          , bang
+              $ playbackRate
+              $ c1
+                  ( gain_ 1.0
+                      [ constant 1.0 bangOn
+                      , gain_ 0.2 (lowpass_ 100.0 (squareOsc 50.0 bangOn))
+                      ]
+                  )
+          ]
+      )
+  ]
+
+ + ~unitEx~ +
+""" + +unitEx :: forall lock payload. CancelCurrentAudio -> (Page -> Effect Unit) -> Event SingleSubgraphEvent -> Element lock payload +unitEx ccb _ ev = px ~~ + { unitEx: nut + ( audioWrapper ev ccb (\ctx -> decodeAudioDataFromUri ctx "https://freesound.org/data/previews/320/320873_527080-hq.mp3") + \ctx buf -> run2 ctx + [ loopBuf buf + ( oneOf + [ bangOn + , bang + $ playbackRate + $ c1 + ( gain_ 1.0 + [ constant 1.0 bangOn + , gain_ 0.2 (lowpass_ 100.0 (squareOsc 50.0 bangOn)) + ] + ) + ] + ) + ] + ) + } \ No newline at end of file diff --git a/examples/docs/State/FixEx.purs b/examples/docs/State/FixEx.purs index fb68f4f1..8a1ea3d9 100644 --- a/examples/docs/State/FixEx.purs +++ b/examples/docs/State/FixEx.purs @@ -33,7 +33,7 @@ import WAGS.Clock (withACTime) import WAGS.Control (bandpass_, gain, lowpass_, periodicOsc, squareOsc_) import WAGS.Example.Docs.Types (CancelCurrentAudio, Page, SingleSubgraphEvent(..), SingleSubgraphPusher) import WAGS.Interpret (close, constant0Hack, context) -import WAGS.Parameter (AudioNumeric(..), _linear, bangOn) +import WAGS.Core (AudioNumeric(..), _linear, bangOn) import WAGS.Properties as P import WAGS.Run (run2e) @@ -174,7 +174,7 @@ import Type.Proxy (Proxy(..)) import WAGS.Clock (withACTime) import WAGS.Control (bandpass_, gain, lowpass_, periodicOsc, squareOsc_) import WAGS.Interpret (close, constant0Hack, context) -import WAGS.Parameter (AudioNumeric(..), _linear, bangOn) +import WAGS.Core (AudioNumeric(..), _linear, bangOn) import WAGS.Properties as P import WAGS.Run (run2e) diff --git a/examples/docs/State/Fold.purs b/examples/docs/State/Fold.purs index 825b2e0d..313ac754 100644 --- a/examples/docs/State/Fold.purs +++ b/examples/docs/State/Fold.purs @@ -23,7 +23,7 @@ import WAGS.Clock (withACTime) import WAGS.Control (gain, periodicOsc) import WAGS.Interpret (close, constant0Hack, context) import WAGS.Math (calcSlope) -import WAGS.Parameter (AudioNumeric(..), _linear, bangOn) +import WAGS.Core (AudioNumeric(..), _linear, bangOn) import WAGS.Properties as P import WAGS.Run (run2e) diff --git a/examples/docs/State/FoldEx.purs b/examples/docs/State/FoldEx.purs index 09baa833..f88bf22d 100644 --- a/examples/docs/State/FoldEx.purs +++ b/examples/docs/State/FoldEx.purs @@ -25,7 +25,7 @@ import WAGS.Control (gain, periodicOsc) import WAGS.Example.Docs.Types (CancelCurrentAudio, Page, SingleSubgraphEvent(..), SingleSubgraphPusher) import WAGS.Interpret (close, constant0Hack, context) import WAGS.Math (calcSlope) -import WAGS.Parameter (AudioNumeric(..), _linear, bangOn) +import WAGS.Core (AudioNumeric(..), _linear, bangOn) import WAGS.Properties as P import WAGS.Run (run2e) @@ -92,7 +92,7 @@ import WAGS.Clock (withACTime) import WAGS.Control (gain, periodicOsc) import WAGS.Interpret (close, constant0Hack, context) import WAGS.Math (calcSlope) -import WAGS.Parameter (AudioNumeric(..), _linear, bangOn) +import WAGS.Core (AudioNumeric(..), _linear, bangOn) import WAGS.Properties as P import WAGS.Run (run2e) diff --git a/examples/docs/State/Swell.purs b/examples/docs/State/Swell.purs index 42458f2b..ed134997 100644 --- a/examples/docs/State/Swell.purs +++ b/examples/docs/State/Swell.purs @@ -31,7 +31,7 @@ import Type.Proxy (Proxy(..)) import WAGS.Clock (withACTime) import WAGS.Control (bandpass_, gain, lowpass_, periodicOsc, squareOsc_) import WAGS.Interpret (close, constant0Hack, context) -import WAGS.Parameter (AudioNumeric(..), _linear, bangOn) +import WAGS.Core (AudioNumeric(..), _linear, bangOn) import WAGS.Properties as P import WAGS.Run (run2e) diff --git a/examples/docs/Subgraph/Slider.purs b/examples/docs/Subgraph/Slider.purs index 859fde89..7280e045 100644 --- a/examples/docs/Subgraph/Slider.purs +++ b/examples/docs/Subgraph/Slider.purs @@ -25,7 +25,7 @@ import Type.Proxy (Proxy(..)) import WAGS.Control (gain_, playBuf) import WAGS.Core (Channel(..)) import WAGS.Interpret (bracketCtx, decodeAudioDataFromUri) -import WAGS.Parameter (bangOn) +import WAGS.Core (bangOn) import WAGS.Run (run2_) import WAGS.WebAPI (BrowserAudioBuffer) diff --git a/examples/docs/Subgraph/SliderEx.purs b/examples/docs/Subgraph/SliderEx.purs index a937bbac..01ac1774 100644 --- a/examples/docs/Subgraph/SliderEx.purs +++ b/examples/docs/Subgraph/SliderEx.purs @@ -26,7 +26,7 @@ import WAGS.Core (Node, Channel(..)) import WAGS.Example.Docs.Types (CancelCurrentAudio, Page, SingleSubgraphEvent(..)) import WAGS.Example.Docs.Util (raceSelf) import WAGS.Interpret (close, constant0Hack, context, decodeAudioDataFromUri) -import WAGS.Parameter (bangOn) +import WAGS.Core (bangOn) import WAGS.Run (run2_) px = @@ -72,7 +72,7 @@ import Type.Proxy (Proxy(..)) import WAGS.Control (gain_, playBuf) import WAGS.Core (Channel(..)) import WAGS.Interpret (bracketCtx, decodeAudioDataFromUri) -import WAGS.Parameter (bangOn) +import WAGS.Core (bangOn) import WAGS.Run (run2_) import WAGS.WebAPI (BrowserAudioBuffer) diff --git a/examples/docs/Subgraphs.purs b/examples/docs/Subgraphs.purs index 59e79e5b..3cf0eed8 100644 --- a/examples/docs/Subgraphs.purs +++ b/examples/docs/Subgraphs.purs @@ -16,7 +16,7 @@ import WAGS.Example.Docs.Subgraph.SliderEx as SliderEx import WAGS.Example.Docs.Types (CancelCurrentAudio, Page(..), SingleSubgraphEvent, SingleSubgraphPusher) import WAGS.Example.Docs.Util (audioWrapperSpan, ccassp, scrollToTop) import WAGS.Interpret (bracketCtx, decodeAudioDataFromUri) -import WAGS.Parameter (bangOn) +import WAGS.Core (bangOn) import WAGS.Run (run2, run2_) px = Proxy :: Proxy """
diff --git a/examples/docs/Types.purs b/examples/docs/Types.purs index 8f1983fb..cbcee58a 100644 --- a/examples/docs/Types.purs +++ b/examples/docs/Types.purs @@ -12,6 +12,7 @@ data Page | AudioUnits | Events | FixFan + | Params | MultiChannel | AudioWorklets | Imperative diff --git a/examples/docs/index.html b/examples/docs/index.html index 8edd3cb0..5f8c91ad 100644 --- a/examples/docs/index.html +++ b/examples/docs/index.html @@ -1,12 +1,15 @@ - - Wags documentation - - - - - - - - \ No newline at end of file + + Wags documentation + + + + + + + + diff --git a/examples/hello-world/HelloWorld.purs b/examples/hello-world/HelloWorld.purs index 286aa45e..4c603094 100644 --- a/examples/hello-world/HelloWorld.purs +++ b/examples/hello-world/HelloWorld.purs @@ -32,7 +32,7 @@ import WAGS.Example.Utils (RaiseCancellation) import WAGS.Imperative (InitialGraphBuilder, runGraphBuilder) import WAGS.Imperative as I import WAGS.Interpret (close, context, effectfulAudioInterpret, makeFFIAudioSnapshot) -import WAGS.Parameter (opticN, bangOn) +import WAGS.Core (opticN, bangOn) import WAGS.Properties (frequency) import WAGS.WebAPI (AudioContext) import Web.HTML (window) diff --git a/examples/stress-test/StressTest.purs b/examples/stress-test/StressTest.purs index 00bdd750..505e847e 100644 --- a/examples/stress-test/StressTest.purs +++ b/examples/stress-test/StressTest.purs @@ -29,7 +29,7 @@ import WAGS.Core (Audible, mix) import WAGS.Example.Utils (RaiseCancellation) import WAGS.Interpret (FFIAudioSnapshot, close, context, effectfulAudioInterpret, makeFFIAudioSnapshot) import WAGS.Math (calcSlope) -import WAGS.Parameter (AudioNumeric(..), AudioOnOff(..), _off, _on, _step, opticN) +import WAGS.Core (AudioNumeric(..), AudioOnOff(..), _off, _on, _step, opticN) import WAGS.Properties as Common import WAGS.WebAPI (AudioContext) import Web.HTML (window) diff --git a/examples/wac2022/WAC.purs b/examples/wac2022/WAC.purs index 8cc9f51e..2d7ca75d 100644 --- a/examples/wac2022/WAC.purs +++ b/examples/wac2022/WAC.purs @@ -20,7 +20,7 @@ import WAGS.Clock (interval) import WAGS.Control (gain, gain_, sinOsc) import WAGS.Core (Node) import WAGS.Interpret (close, context) -import WAGS.Parameter (AudioEnvelope(..), AudioOnOff(..), _off, _on) +import WAGS.Core (AudioEnvelope(..), AudioOnOff(..), _off, _on) import WAGS.Properties (onOff) import WAGS.Properties as P import WAGS.Run (run2e) diff --git a/package.json b/package.json index 172d0fb6..d1163606 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "purescript-wags", - "version": "1.1.1", + "version": "1.1.2", "description": "Web Audio Graphs as a Stream", "scripts": { "build": "spago build", diff --git a/packages.dhall b/packages.dhall index 426b9701..2b68e902 100644 --- a/packages.dhall +++ b/packages.dhall @@ -2,7 +2,13 @@ let upstream = https://github.com/purescript/package-sets/releases/download/psc-0.15.0-20220429/packages.dhall sha256:03c682bff56fc8f9d8c495ffcc6f524cbd3c89fe04778f965265c08757de8c9d -let overrides = {=} +let overrides = {js-timers = + { dependencies = + [ "refs" + ] + , repo = "https://github.com/mikesol/purescript-js-timers.git" + , version = "rename-functions" + }} let additions = { event = diff --git a/spago.dhall b/spago.dhall index c105a420..50ecdf14 100644 --- a/spago.dhall +++ b/spago.dhall @@ -20,9 +20,9 @@ , "integers" , "js-timers" , "lists" - , "numbers" , "maybe" , "newtype" + , "numbers" , "ordered-collections" , "prelude" , "profunctor" diff --git a/src/WAGS/Clock.purs b/src/WAGS/Clock.purs index 8737c476..fc382eb4 100644 --- a/src/WAGS/Clock.purs +++ b/src/WAGS/Clock.purs @@ -10,7 +10,7 @@ import Effect.Timer (clearTimeout, setTimeout) import FRP.Behavior (Behavior, behavior) import FRP.Event (Event, makeEvent, subscribe) import WAGS.Interpret (getAudioClockTime) -import WAGS.Parameter (AudioNumeric(..), AudioOnOff(..), _linear) +import WAGS.Core (AudioNumeric(..), AudioOnOff(..), _linear) import WAGS.WebAPI (AudioContext) import WAGS.WebAPI as WebAPI diff --git a/src/WAGS/Common.purs b/src/WAGS/Common.purs index ecc956ba..1826e7f5 100644 --- a/src/WAGS/Common.purs +++ b/src/WAGS/Common.purs @@ -2,18 +2,23 @@ module WAGS.Common where import Prelude +import Control.Alt ((<|>)) import ConvertableOptions (class ConvertOption, class ConvertOptionsWithDefaults, convertOptionsWithDefaults) +import Data.Either (Either(..)) import Data.Tuple.Nested (type (/\), (/\)) import Data.Typelevel.Num (class Pos) -import Data.Variant (inj) +import Data.Variant (inj, match) import Data.Variant.Maybe (Maybe, just, nothing) import Data.Vec (Vec, toArray) +import Effect.AVar as AVar +import Effect.Exception (throwException) +import FRP.Event (Event, bang, makeEvent, subscribe) import Safe.Coerce (coerce) import Type.Equality (class TypeEquals, proof) import Type.Proxy (Proxy(..)) import WAGS.Core (Oversample, PeriodicOscSpec(..), RealImg(..), _twoX) +import WAGS.Core as C import WAGS.Core as Core -import WAGS.Parameter (InitialAudioParameter) import WAGS.WebAPI (BrowserAudioBuffer, BrowserFloatArray, BrowserMicrophone, BrowserPeriodicWave, MediaRecorderCb) -- Allpass @@ -23,23 +28,23 @@ data AllpassOptions = AllpassOptions instance ConvertOption AllpassOptions "frequency" - InitialAudioParameter - InitialAudioParameter where + Core.InitialAudioParameter + Core.InitialAudioParameter where convertOption _ _ = identity instance ConvertOption AllpassOptions "q" - InitialAudioParameter - InitialAudioParameter where + Core.InitialAudioParameter + Core.InitialAudioParameter where convertOption _ _ = identity type AllpassOptional = - ( q :: InitialAudioParameter + ( q :: Core.InitialAudioParameter ) type AllpassAll = - ( frequency :: InitialAudioParameter + ( frequency :: Core.InitialAudioParameter | AllpassOptional ) @@ -53,7 +58,7 @@ class InitialAllpass i where instance InitialAllpass Core.InitializeAllpass where toInitializeAllpass = identity -instance InitialAllpass InitialAudioParameter where +instance InitialAllpass Core.InitialAudioParameter where toInitializeAllpass = toInitializeAllpass <<< { frequency: _ } instance @@ -70,23 +75,23 @@ data BandpassOptions = BandpassOptions instance ConvertOption BandpassOptions "frequency" - InitialAudioParameter - InitialAudioParameter where + Core.InitialAudioParameter + Core.InitialAudioParameter where convertOption _ _ = identity instance ConvertOption BandpassOptions "q" - InitialAudioParameter - InitialAudioParameter where + Core.InitialAudioParameter + Core.InitialAudioParameter where convertOption _ _ = identity type BandpassOptional = - ( q :: InitialAudioParameter + ( q :: Core.InitialAudioParameter ) type BandpassAll = - ( frequency :: InitialAudioParameter + ( frequency :: Core.InitialAudioParameter | BandpassOptional ) @@ -100,7 +105,7 @@ class InitialBandpass i where instance InitialBandpass Core.InitializeBandpass where toInitializeBandpass = identity -instance InitialBandpass InitialAudioParameter where +instance InitialBandpass Core.InitialAudioParameter where toInitializeBandpass = toInitializeBandpass <<< { frequency: _ } instance @@ -157,8 +162,8 @@ data DelayOptions = DelayOptions instance ConvertOption DelayOptions "delayTime" - InitialAudioParameter - InitialAudioParameter where + Core.InitialAudioParameter + Core.InitialAudioParameter where convertOption _ _ = identity instance @@ -173,7 +178,7 @@ type DelayOptional = ) type DelayAll = - ( delayTime :: InitialAudioParameter + ( delayTime :: Core.InitialAudioParameter | DelayOptional ) @@ -187,7 +192,7 @@ class InitialDelay i where instance InitialDelay Core.InitializeDelay where toInitializeDelay = identity -instance InitialDelay InitialAudioParameter where +instance InitialDelay Core.InitialAudioParameter where toInitializeDelay = toInitializeDelay <<< { delayTime: _ } instance @@ -205,44 +210,44 @@ data DynamicsCompressorOptions = DynamicsCompressorOptions instance ConvertOption DynamicsCompressorOptions "threshold" - InitialAudioParameter - InitialAudioParameter where + Core.InitialAudioParameter + Core.InitialAudioParameter where convertOption _ _ = identity instance ConvertOption DynamicsCompressorOptions "ratio" - InitialAudioParameter - InitialAudioParameter where + Core.InitialAudioParameter + Core.InitialAudioParameter where convertOption _ _ = identity instance ConvertOption DynamicsCompressorOptions "knee" - InitialAudioParameter - InitialAudioParameter where + Core.InitialAudioParameter + Core.InitialAudioParameter where convertOption _ _ = identity instance ConvertOption DynamicsCompressorOptions "attack" - InitialAudioParameter - InitialAudioParameter where + Core.InitialAudioParameter + Core.InitialAudioParameter where convertOption _ _ = identity instance ConvertOption DynamicsCompressorOptions "release" - InitialAudioParameter - InitialAudioParameter where + Core.InitialAudioParameter + Core.InitialAudioParameter where convertOption _ _ = identity type DynamicsCompressorOptional = - ( ratio :: InitialAudioParameter - , threshold :: InitialAudioParameter - , attack :: InitialAudioParameter - , release :: InitialAudioParameter - , knee :: InitialAudioParameter + ( ratio :: Core.InitialAudioParameter + , threshold :: Core.InitialAudioParameter + , attack :: Core.InitialAudioParameter + , release :: Core.InitialAudioParameter + , knee :: Core.InitialAudioParameter ) type DynamicsCompressorAll = @@ -288,23 +293,23 @@ data HighpassOptions = HighpassOptions instance ConvertOption HighpassOptions "frequency" - InitialAudioParameter - InitialAudioParameter where + Core.InitialAudioParameter + Core.InitialAudioParameter where convertOption _ _ = identity instance ConvertOption HighpassOptions "q" - InitialAudioParameter - InitialAudioParameter where + Core.InitialAudioParameter + Core.InitialAudioParameter where convertOption _ _ = identity type HighpassOptional = - ( q :: InitialAudioParameter + ( q :: Core.InitialAudioParameter ) type HighpassAll = - ( frequency :: InitialAudioParameter + ( frequency :: Core.InitialAudioParameter | HighpassOptional ) @@ -318,7 +323,7 @@ class InitialHighpass i where instance InitialHighpass Core.InitializeHighpass where toInitializeHighpass = identity -instance InitialHighpass InitialAudioParameter where +instance InitialHighpass Core.InitialAudioParameter where toInitializeHighpass = toInitializeHighpass <<< { frequency: _ } instance @@ -336,23 +341,23 @@ data HighshelfOptions = HighshelfOptions instance ConvertOption HighshelfOptions "frequency" - InitialAudioParameter - InitialAudioParameter where + Core.InitialAudioParameter + Core.InitialAudioParameter where convertOption _ _ = identity instance ConvertOption HighshelfOptions "gain" - InitialAudioParameter - InitialAudioParameter where + Core.InitialAudioParameter + Core.InitialAudioParameter where convertOption _ _ = identity type HighshelfOptional = - ( gain :: InitialAudioParameter + ( gain :: Core.InitialAudioParameter ) type HighshelfAll = - ( frequency :: InitialAudioParameter + ( frequency :: Core.InitialAudioParameter | HighshelfOptional ) @@ -366,7 +371,7 @@ class InitialHighshelf i where instance InitialHighshelf Core.InitializeHighshelf where toInitializeHighshelf = identity -instance InitialHighshelf InitialAudioParameter where +instance InitialHighshelf Core.InitialAudioParameter where toInitializeHighshelf = toInitializeHighshelf <<< { frequency: _ } instance @@ -383,8 +388,8 @@ data LoopBufOptions = LoopBufOptions instance ConvertOption LoopBufOptions "playbackRate" - InitialAudioParameter - InitialAudioParameter where + Core.InitialAudioParameter + Core.InitialAudioParameter where convertOption _ _ = identity instance ConvertOption LoopBufOptions "duration" Number (Maybe Number) where @@ -403,7 +408,7 @@ instance type LoopBufOptional = ( loopStart :: Number , loopEnd :: Number - , playbackRate :: InitialAudioParameter + , playbackRate :: Core.InitialAudioParameter , duration :: Maybe Number ) @@ -442,23 +447,23 @@ data LowpassOptions = LowpassOptions instance ConvertOption LowpassOptions "frequency" - InitialAudioParameter - InitialAudioParameter where + Core.InitialAudioParameter + Core.InitialAudioParameter where convertOption _ _ = identity instance ConvertOption LowpassOptions "q" - InitialAudioParameter - InitialAudioParameter where + Core.InitialAudioParameter + Core.InitialAudioParameter where convertOption _ _ = identity type LowpassOptional = - ( q :: InitialAudioParameter + ( q :: Core.InitialAudioParameter ) type LowpassAll = - ( frequency :: InitialAudioParameter + ( frequency :: Core.InitialAudioParameter | LowpassOptional ) @@ -472,7 +477,7 @@ class InitialLowpass i where instance InitialLowpass Core.InitializeLowpass where toInitializeLowpass = identity -instance InitialLowpass InitialAudioParameter where +instance InitialLowpass Core.InitialAudioParameter where toInitializeLowpass = toInitializeLowpass <<< { frequency: _ } instance @@ -489,23 +494,23 @@ data LowshelfOptions = LowshelfOptions instance ConvertOption LowshelfOptions "frequency" - InitialAudioParameter - InitialAudioParameter where + Core.InitialAudioParameter + Core.InitialAudioParameter where convertOption _ _ = identity instance ConvertOption LowshelfOptions "gain" - InitialAudioParameter - InitialAudioParameter where + Core.InitialAudioParameter + Core.InitialAudioParameter where convertOption _ _ = identity type LowshelfOptional = - ( gain :: InitialAudioParameter + ( gain :: Core.InitialAudioParameter ) type LowshelfAll = - ( frequency :: InitialAudioParameter + ( frequency :: Core.InitialAudioParameter | LowshelfOptional ) @@ -519,7 +524,7 @@ class InitialLowshelf i where instance InitialLowshelf Core.InitializeLowshelf where toInitializeLowshelf = identity -instance InitialLowshelf InitialAudioParameter where +instance InitialLowshelf Core.InitialAudioParameter where toInitializeLowshelf = toInitializeLowshelf <<< { frequency: _ } instance @@ -549,23 +554,23 @@ data NotchOptions = NotchOptions instance ConvertOption NotchOptions "frequency" - InitialAudioParameter - InitialAudioParameter where + Core.InitialAudioParameter + Core.InitialAudioParameter where convertOption _ _ = identity instance ConvertOption NotchOptions "q" - InitialAudioParameter - InitialAudioParameter where + Core.InitialAudioParameter + Core.InitialAudioParameter where convertOption _ _ = identity type NotchOptional = - ( q :: InitialAudioParameter + ( q :: Core.InitialAudioParameter ) type NotchAll = - ( frequency :: InitialAudioParameter + ( frequency :: Core.InitialAudioParameter | NotchOptional ) @@ -579,7 +584,7 @@ class InitialNotch i where instance InitialNotch Core.InitializeNotch where toInitializeNotch = identity -instance InitialNotch InitialAudioParameter where +instance InitialNotch Core.InitialAudioParameter where toInitializeNotch = toInitializeNotch <<< { frequency: _ } instance @@ -606,31 +611,31 @@ data PeakingOptions = PeakingOptions instance ConvertOption PeakingOptions "frequency" - InitialAudioParameter - InitialAudioParameter where + Core.InitialAudioParameter + Core.InitialAudioParameter where convertOption _ _ = identity instance ConvertOption PeakingOptions "gain" - InitialAudioParameter - InitialAudioParameter where + Core.InitialAudioParameter + Core.InitialAudioParameter where convertOption _ _ = identity instance ConvertOption PeakingOptions "q" - InitialAudioParameter - InitialAudioParameter where + Core.InitialAudioParameter + Core.InitialAudioParameter where convertOption _ _ = identity type PeakingOptional = - ( q :: InitialAudioParameter - , gain :: InitialAudioParameter + ( q :: Core.InitialAudioParameter + , gain :: Core.InitialAudioParameter ) type PeakingAll = - ( frequency :: InitialAudioParameter + ( frequency :: Core.InitialAudioParameter | PeakingOptional ) @@ -644,7 +649,7 @@ class InitialPeaking i where instance InitialPeaking Core.InitializePeaking where toInitializePeaking = identity -instance InitialPeaking InitialAudioParameter where +instance InitialPeaking Core.InitialAudioParameter where toInitializePeaking = toInitializePeaking <<< { frequency: _ } instance @@ -660,8 +665,8 @@ data PlayBufOptions = PlayBufOptions instance ConvertOption PlayBufOptions "playbackRate" - InitialAudioParameter - InitialAudioParameter where + Core.InitialAudioParameter + Core.InitialAudioParameter where convertOption _ _ = identity instance ConvertOption PlayBufOptions "duration" Number (Maybe Number) where @@ -676,7 +681,7 @@ instance type PlayBufOptional = ( bufferOffset :: Number - , playbackRate :: InitialAudioParameter + , playbackRate :: Core.InitialAudioParameter , duration :: Maybe Number ) @@ -716,8 +721,8 @@ data PeriodicOscOptions = PeriodicOscOptions instance ConvertOption PeriodicOscOptions "frequency" - InitialAudioParameter - InitialAudioParameter where + Core.InitialAudioParameter + Core.InitialAudioParameter where convertOption _ _ = identity class PeriodicOscSpecable i where @@ -729,21 +734,21 @@ instance PeriodicOscSpecable BrowserPeriodicWave where instance Pos n => PeriodicOscSpecable (Vec n Number /\ Vec n Number) where toPeriodicOscSpec (real /\ img) = PeriodicOscSpec $ inj (Proxy :: _ "realImg") $ RealImg { real: toArray real, img: toArray img } -instance PeriodicOscSpecable i => +instance + PeriodicOscSpecable i => ConvertOption PeriodicOscOptions "spec" i PeriodicOscSpec where convertOption _ _ = toPeriodicOscSpec - type PeriodicOscAll = - ( frequency :: InitialAudioParameter + ( frequency :: Core.InitialAudioParameter , spec :: PeriodicOscSpec ) -defaultPeriodicOsc :: { } -defaultPeriodicOsc = { } +defaultPeriodicOsc :: {} +defaultPeriodicOsc = {} class InitialPeriodicOsc i where toInitializePeriodicOsc :: i -> Core.InitializePeriodicOsc @@ -752,13 +757,12 @@ instance InitialPeriodicOsc Core.InitializePeriodicOsc where toInitializePeriodicOsc = identity instance - ConvertOptionsWithDefaults PeriodicOscOptions { } { | provided } + ConvertOptionsWithDefaults PeriodicOscOptions {} { | provided } { | PeriodicOscAll } => InitialPeriodicOsc { | provided } where toInitializePeriodicOsc provided = Core.InitializePeriodicOsc (convertOptionsWithDefaults PeriodicOscOptions defaultPeriodicOsc provided) - -- SawtoothOsc class InitialSawtoothOsc i where toInitializeSawtoothOsc :: i -> Core.InitializeSawtoothOsc @@ -855,3 +859,33 @@ instance InitialWaveShaper { | provided } where toInitializeWaveShaper provided = Core.InitializeWaveShaper (convertOptionsWithDefaults WaveShaperOptions defaultWaveShaper provided) + +-- resolveAU + +resolveAU :: forall lock payload. C.AudioInterpret payload -> (C.FFIAudioParameter -> payload) -> C.AudioParameter lock payload -> Event payload +resolveAU = go + where + cncl = C.FFIAudioParameter <<< inj (Proxy :: _ "cancel") + ev = C.FFIAudioParameter <<< inj (Proxy :: _ "envelope") + nmc = C.FFIAudioParameter <<< inj (Proxy :: _ "numeric") + sdn = C.FFIAudioParameter <<< inj (Proxy :: _ "sudden") + ut = C.FFIAudioParameter <<< inj (Proxy :: _ "unit") + go di@(C.AudioInterpret { ids }) f (C.AudioParameter a) = match + { numeric: bang <<< f <<< nmc + , envelope: bang <<< f <<< ev + , cancel: bang <<< f <<< cncl + , sudden: bang <<< f <<< sdn + , unit: \(C.AudioUnit { u }) -> let C.Node n = u in makeEvent \k -> do + newScope <- ids + av <- AVar.empty + subscribe + ( n { parent: nothing, scope: newScope, raiseId: \x -> void $ AVar.tryPut x av } di <|> makeEvent \k2 -> do + void $ AVar.take av case _ of + Left e -> throwException e + -- only do the connection if not silence + Right i -> k2 (f (ut (C.FFIAudioUnit { i }))) + pure (pure unit) + ) + k + } + a diff --git a/src/WAGS/Control.purs b/src/WAGS/Control.purs index 2ff2b3c5..a1779860 100644 --- a/src/WAGS/Control.purs +++ b/src/WAGS/Control.purs @@ -7,7 +7,7 @@ import Control.Comonad (extract) import Control.Plus (empty) import ConvertableOptions (class ConvertOption, class ConvertOptionsWithDefaults, convertOptionsWithDefaults) import Data.Either (Either(..)) -import Data.Foldable (oneOf) +import Data.Foldable (for_, oneOf) import Data.FunctorWithIndex (mapWithIndex) import Data.Homogeneous (class HomogeneousRowLabels) import Data.Homogeneous.Variant (homogeneous) @@ -16,15 +16,14 @@ import Data.Profunctor (lcmap) import Data.Symbol (class IsSymbol, reflectSymbol) import Data.Tuple.Nested (type (/\), (/\)) import Data.Typelevel.Num (class Lt, class Nat, class Pos, class Pred, D1, D2, d0, pred, toInt) -import Data.Variant (Unvariant(..), match, unvariant) +import Data.Variant (Unvariant(..), inj, match, unvariant) import Data.Variant.Maybe (Maybe, just, nothing) import Data.Vec (Vec, index, singleton, toArray) import Effect (Effect, foreachE) import Effect.AVar (tryPut) import Effect.AVar as AVar import Effect.Exception (throwException) -import FRP.Event (Event, makeEvent, subscribe) -import FRP.Event.Class (bang) +import FRP.Event (Event, bang, keepLatest, makeEvent, subscribe) import Foreign.Object (fromHomogeneous) import Simple.JSON as JSON import Type.Proxy (Proxy(..)) @@ -33,7 +32,6 @@ import Unsafe.Coerce (unsafeCoerce) import WAGS.Common as Common import WAGS.Core (ChannelCountMode(..), ChannelInterpretation(..), Po2(..), __internalWagsFlatten, mix) import WAGS.Core as C -import WAGS.Parameter (AudioParameter, InitialAudioParameter) import WAGS.WebAPI (AnalyserNodeCb(..), BrowserAudioBuffer) -- allpass @@ -43,7 +41,7 @@ allpass . Common.InitialAllpass i => C.Mix aud (C.Audible outputChannels lock payload) => i - -> Event C.Allpass + -> Event (C.Allpass lock payload) -> aud -- Array (C.Node outputChannels lock payload) -> C.Node outputChannels lock payload allpass i' atts elts = C.Node go @@ -52,21 +50,22 @@ allpass i' atts elts = C.Node go go parent di@(C.AudioInterpret { ids, deleteFromCache, makeAllpass, setFrequency, setQ }) = makeEvent \k -> do me <- ids parent.raiseId me - map ((*>) (k (deleteFromCache { id: me }))) $ flip subscribe k $ - + map (k (deleteFromCache { id: me }) *> _) $ flip subscribe k $ bang ( makeAllpass - { id: me, parent: just parent.parent, scope: parent.scope, frequency: i.frequency, q: i.q } + { id: me, parent: parent.parent, scope: parent.scope, frequency: i.frequency, q: i.q } ) - <|> map - ( \(C.Allpass e) -> match - { frequency: \g -> setFrequency { id: me, frequency: g } - , q: \g -> setQ { id: me, q: g } - } - e + <|> + ( keepLatest $ map + ( \(C.Allpass e) -> match + { frequency: tmpResolveAU parent.scope di (setFrequency <<< { id: me, frequency: _ }) + , q: tmpResolveAU parent.scope di (setQ <<< { id: me, q: _ }) + } + e + ) + atts ) - atts - <|> __internalWagsFlatten me di (mix elts) + <|> __internalWagsFlatten (just me) parent.scope di (mix elts) allpass_ :: forall i aud (outputChannels :: Type) lock payload @@ -84,8 +83,8 @@ data AnalyserOptions = AnalyserOptions instance ConvertOption AnalyserOptions "playbackRate" - InitialAudioParameter - InitialAudioParameter where + C.InitialAudioParameter + C.InitialAudioParameter where convertOption _ _ = identity instance @@ -180,11 +179,11 @@ analyser i' atts elts = C.Node go makeEvent \k -> do me <- ids parent.raiseId me - map ((*>) (k (deleteFromCache { id: me }))) $ flip subscribe k $ + map (k (deleteFromCache { id: me }) *> _) $ flip subscribe k $ bang ( makeAnalyser { id: me - , parent: just parent.parent + , parent: parent.parent , scope: parent.scope , cb: i.cb , fftSize: 2 `pow` @@ -217,7 +216,7 @@ analyser i' atts elts = C.Node go e ) atts - <|> __internalWagsFlatten me di (mix elts) + <|> __internalWagsFlatten (just me) parent.scope di (mix elts) analyser_ :: forall i aud outputChannels lock payload @@ -261,8 +260,8 @@ __audioWorklet => Nat numberOfInputs => Pos numberOfOutputs => ValidateOutputChannelCount numberOfOutputs outputChannelCount - => Homogeneous parameterData InitialAudioParameter - => HomogeneousRowLabels parameterData AudioParameter parameterDataRL + => Homogeneous parameterData C.InitialAudioParameter + => HomogeneousRowLabels parameterData (C.AudioParameter lock payload) parameterDataRL => JSON.WriteForeign { | processorOptions } => C.InitializeAudioWorkletNode name numberOfInputs numberOfOutputs outputChannelCount @@ -282,11 +281,11 @@ __audioWorklet (C.InitializeAudioWorkletNode i) atts elt = C.Node go makeEvent \k -> do me <- ids parent.raiseId me - map ((*>) (k (deleteFromCache { id: me }))) $ flip subscribe k $ + map (k (deleteFromCache { id: me }) *> _) $ flip subscribe k $ bang ( makeAudioWorkletNode { id: me - , parent: just parent.parent + , parent: parent.parent , scope: parent.scope , options: C.AudioWorkletNodeOptions_ @@ -301,16 +300,21 @@ __audioWorklet (C.InitializeAudioWorkletNode i) atts elt = C.Node go } } ) - <|> map - ( \(C.AudioWorkletNode e) -> setAudioWorkletParameter - { id: me - , paramName: (let Unvariant e' = unvariant e in e') - (\sym _ -> reflectSymbol sym) - , paramValue: extract (homogeneous e) - } + <|> + ( keepLatest $ map + ( \(C.AudioWorkletNode e) -> tmpResolveAU parent.scope di + ( \paramValue -> setAudioWorkletParameter + { id: me + , paramName: (let Unvariant e' = unvariant e in e') + (\sym _ -> reflectSymbol sym) + , paramValue + } + ) + (extract (homogeneous e)) + ) + atts ) - atts - <|> __internalWagsFlatten me di (mix elt) + <|> __internalWagsFlatten (just me) parent.scope di (mix elt) audioWorklet :: forall name numberOfInputs numberOfOutputs outputChannelCount parameterData @@ -320,8 +324,8 @@ audioWorklet => Nat numberOfInputs => Pos numberOfOutputs => ValidateOutputChannelCount numberOfOutputs outputChannelCount - => Homogeneous parameterData InitialAudioParameter - => HomogeneousRowLabels parameterData AudioParameter parameterDataRL + => Homogeneous parameterData C.InitialAudioParameter + => HomogeneousRowLabels parameterData (C.AudioParameter lock payload) parameterDataRL => JSON.WriteForeign { | processorOptions } => C.InitializeAudioWorkletNode name numberOfInputs numberOfOutputs outputChannelCount @@ -338,7 +342,7 @@ bandpass . Common.InitialBandpass i => C.Mix aud (C.Audible outputChannels lock payload) => i - -> Event C.Bandpass + -> Event (C.Bandpass lock payload) -> aud -> C.Node outputChannels lock payload bandpass i' atts elts = C.Node go @@ -347,21 +351,22 @@ bandpass i' atts elts = C.Node go go parent di@(C.AudioInterpret { ids, deleteFromCache, makeBandpass, setFrequency, setQ }) = makeEvent \k -> do me <- ids parent.raiseId me - map ((*>) (k (deleteFromCache { id: me }))) $ flip subscribe k $ - + map (k (deleteFromCache { id: me }) *> _) $ flip subscribe k $ bang ( makeBandpass - { id: me, parent: just parent.parent, scope: parent.scope, frequency: i.frequency, q: i.q } + { id: me, parent: parent.parent, scope: parent.scope, frequency: i.frequency, q: i.q } ) - <|> map - ( \(C.Bandpass e) -> match - { frequency: \g -> setFrequency { id: me, frequency: g } - , q: \g -> setQ { id: me, q: g } - } - e + <|> + ( keepLatest $ map + ( \(C.Bandpass e) -> match + { frequency: tmpResolveAU parent.scope di (setFrequency <<< { id: me, frequency: _ }) + , q: tmpResolveAU parent.scope di (setQ <<< { id: me, q: _ }) + } + e + ) + atts ) - atts - <|> __internalWagsFlatten me di (mix elts) + <|> __internalWagsFlatten (just me) parent.scope di (mix elts) bandpass_ :: forall i aud (outputChannels :: Type) lock payload @@ -378,39 +383,40 @@ __constant :: forall i outputChannels lock payload . Common.InitialConstant i => i - -> Event C.Constant + -> Event (C.Constant lock payload) -> C.Node outputChannels lock payload __constant i' atts = C.Node go where C.InitializeConstant i = Common.toInitializeConstant i' - go parent (C.AudioInterpret { ids, deleteFromCache, makeConstant, setOffset, setOnOff }) = + go parent di@(C.AudioInterpret { ids, deleteFromCache, makeConstant, setOffset, setOnOff }) = makeEvent \k -> do me <- ids parent.raiseId me - map ((*>) (k (deleteFromCache { id: me }))) $ flip subscribe k $ + map (k (deleteFromCache { id: me }) *> _) $ flip subscribe k $ bang ( makeConstant { id: me - , parent: just parent.parent + , parent: parent.parent , scope: parent.scope , offset: i.offset } ) - <|> map - ( \(C.Constant e) -> match - { offset: \offset -> setOffset - { id: me, offset } - , onOff: \onOff -> setOnOff { id: me, onOff } - } - e + <|> + ( keepLatest $ map + ( \(C.Constant e) -> match + { offset: tmpResolveAU parent.scope di (setOffset <<< { id: me, offset: _ }) + , onOff: \onOff -> bang $ setOnOff { id: me, onOff } + } + e + ) + atts ) - atts constant :: forall i outputChannels lock payload . Common.InitialConstant i => i - -> Event C.Constant + -> Event (C.Constant lock payload) -> C.Node outputChannels lock payload constant = __constant @@ -437,16 +443,16 @@ convolver i' elts = C.Node go makeEvent \k -> do me <- ids parent.raiseId me - map ((*>) (k (deleteFromCache { id: me }))) $ flip subscribe k $ + map (k (deleteFromCache { id: me }) *> _) $ flip subscribe k $ bang ( makeConvolver { id: me - , parent: just parent.parent + , parent: parent.parent , scope: parent.scope , buffer: i.buffer } ) - <|> __internalWagsFlatten me di (mix elts) + <|> __internalWagsFlatten (just me) parent.scope di (mix elts) -- delay delay @@ -454,7 +460,7 @@ delay . Common.InitialDelay i => C.Mix aud (C.Audible outputChannels lock payload) => i - -> Event C.Delay + -> Event (C.Delay lock payload) -> aud -> C.Node outputChannels lock payload delay i' atts elts = C.Node go @@ -463,20 +469,21 @@ delay i' atts elts = C.Node go go parent di@(C.AudioInterpret { ids, deleteFromCache, makeDelay, setDelay }) = makeEvent \k -> do me <- ids parent.raiseId me - map ((*>) (k (deleteFromCache { id: me }))) $ flip subscribe k $ - + map (k (deleteFromCache { id: me }) *> _) $ flip subscribe k $ bang ( makeDelay - { id: me, parent: just parent.parent, scope: parent.scope, delayTime: i.delayTime, maxDelayTime: i.maxDelayTime } + { id: me, parent: parent.parent, scope: parent.scope, delayTime: i.delayTime, maxDelayTime: i.maxDelayTime } ) - <|> map - ( \(C.Delay e) -> match - { delayTime: \g -> setDelay { id: me, delayTime: g } - } - e + <|> + ( keepLatest $ map + ( \(C.Delay e) -> match + { delayTime: tmpResolveAU parent.scope di (setDelay <<< { id: me, delayTime: _ }) + } + e + ) + atts ) - atts - <|> __internalWagsFlatten me di (mix elts) + <|> __internalWagsFlatten (just me) parent.scope di (mix elts) delay_ :: forall i aud (outputChannels :: Type) lock payload @@ -493,7 +500,7 @@ dynamicsCompressor . Common.InitialDynamicsCompressor i => C.Mix aud (C.Audible outputChannels lock payload) => i - -> Event C.DynamicsCompressor + -> Event (C.DynamicsCompressor lock payload) -> aud -> C.Node outputChannels lock payload dynamicsCompressor i' atts elts = C.Node go @@ -516,11 +523,11 @@ dynamicsCompressor i' atts elts = C.Node go makeEvent \k -> do me <- ids parent.raiseId me - map ((*>) (k (deleteFromCache { id: me }))) $ flip subscribe k $ + map (k (deleteFromCache { id: me }) *> _) $ flip subscribe k $ bang ( makeDynamicsCompressor { id: me - , parent: just parent.parent + , parent: parent.parent , scope: parent.scope , threshold: i.threshold , ratio: i.ratio @@ -529,23 +536,35 @@ dynamicsCompressor i' atts elts = C.Node go , release: i.release } ) - <|> map - ( \(C.DynamicsCompressor e) -> match - { threshold: \threshold -> setThreshold - { id: me, threshold } - , ratio: \ratio -> setRatio - { id: me, ratio } - , knee: \knee -> setKnee - { id: me, knee } - , attack: \attack -> setAttack - { id: me, attack } - , release: \release -> setRelease - { id: me, release } - } - e + <|> + ( keepLatest $ map + ( \(C.DynamicsCompressor e) -> match + { threshold: tmpResolveAU parent.scope di + ( setThreshold <<< + { id: me, threshold: _ } + ) + , ratio: tmpResolveAU parent.scope di + ( setRatio <<< + { id: me, ratio: _ } + ) + , knee: tmpResolveAU parent.scope di + ( setKnee <<< + { id: me, knee: _ } + ) + , attack: tmpResolveAU parent.scope di + ( setAttack <<< + { id: me, attack: _ } + ) + , release: tmpResolveAU parent.scope di + ( setRelease <<< + { id: me, release: _ } + ) + } + e + ) + atts ) - atts - <|> __internalWagsFlatten me di (mix elts) + <|> __internalWagsFlatten (just me) parent.scope di (mix elts) dynamicsCompressor_ :: forall i aud (outputChannels :: Type) lock payload @@ -562,7 +581,7 @@ gain . Common.InitialGain i => C.Mix aud (C.Audible outputChannels lock payload) => i - -> Event C.Gain + -> Event (C.Gain lock payload) -> aud -> C.Node outputChannels lock payload gain i' atts elts = C.Node go @@ -571,20 +590,21 @@ gain i' atts elts = C.Node go go parent di@(C.AudioInterpret { ids, deleteFromCache, makeGain, setGain }) = makeEvent \k -> do me <- ids parent.raiseId me - map ((*>) (k (deleteFromCache { id: me }))) $ flip subscribe k $ - + map (k (deleteFromCache { id: me }) *> _) $ flip subscribe k $ bang ( makeGain - { id: me, parent: just parent.parent, scope: parent.scope, gain: i.gain } + { id: me, parent: parent.parent, scope: parent.scope, gain: i.gain } ) - <|> map - ( \(C.Gain e) -> match - { gain: \g -> setGain { id: me, gain: g } - } - e + <|> + ( keepLatest $ map + ( \(C.Gain e) -> match + { gain: tmpResolveAU parent.scope di (setGain <<< { id: me, gain: _ }) + } + e + ) + atts ) - atts - <|> __internalWagsFlatten me di (mix elts) + <|> __internalWagsFlatten (just me) parent.scope di (mix elts) gain_ :: forall i aud (outputChannels :: Type) lock payload @@ -601,7 +621,7 @@ highpass . Common.InitialHighpass i => C.Mix aud (C.Audible outputChannels lock payload) => i - -> Event C.Highpass + -> Event (C.Highpass lock payload) -> aud -> C.Node outputChannels lock payload highpass i' atts elts = C.Node go @@ -610,21 +630,22 @@ highpass i' atts elts = C.Node go go parent di@(C.AudioInterpret { ids, deleteFromCache, makeHighpass, setFrequency, setQ }) = makeEvent \k -> do me <- ids parent.raiseId me - map ((*>) (k (deleteFromCache { id: me }))) $ flip subscribe k $ - + map (k (deleteFromCache { id: me }) *> _) $ flip subscribe k $ bang ( makeHighpass - { id: me, parent: just parent.parent, scope: parent.scope, frequency: i.frequency, q: i.q } + { id: me, parent: parent.parent, scope: parent.scope, frequency: i.frequency, q: i.q } ) - <|> map - ( \(C.Highpass e) -> match - { frequency: \g -> setFrequency { id: me, frequency: g } - , q: \g -> setQ { id: me, q: g } - } - e + <|> + ( keepLatest $ map + ( \(C.Highpass e) -> match + { frequency: tmpResolveAU parent.scope di (setFrequency <<< { id: me, frequency: _ }) + , q: tmpResolveAU parent.scope di (setQ <<< { id: me, q: _ }) + } + e + ) + atts ) - atts - <|> __internalWagsFlatten me di (mix elts) + <|> __internalWagsFlatten (just me) parent.scope di (mix elts) highpass_ :: forall i aud (outputChannels :: Type) lock payload @@ -641,7 +662,7 @@ highshelf . Common.InitialHighshelf i => C.Mix aud (C.Audible outputChannels lock payload) => i - -> Event C.Highshelf + -> Event (C.Highshelf lock payload) -> aud -> C.Node outputChannels lock payload highshelf i' atts elts = C.Node go @@ -650,21 +671,22 @@ highshelf i' atts elts = C.Node go go parent di@(C.AudioInterpret { ids, deleteFromCache, makeHighshelf, setFrequency, setGain }) = makeEvent \k -> do me <- ids parent.raiseId me - map ((*>) (k (deleteFromCache { id: me }))) $ flip subscribe k $ - + map (k (deleteFromCache { id: me }) *> _) $ flip subscribe k $ bang ( makeHighshelf - { id: me, parent: just parent.parent, scope: parent.scope, frequency: i.frequency, gain: i.gain } + { id: me, parent: parent.parent, scope: parent.scope, frequency: i.frequency, gain: i.gain } ) - <|> map - ( \(C.Highshelf e) -> match - { frequency: \g -> setFrequency { id: me, frequency: g } - , gain: \g -> setGain { id: me, gain: g } - } - e + <|> + ( keepLatest $ map + ( \(C.Highshelf e) -> match + { frequency: tmpResolveAU parent.scope di (setFrequency <<< { id: me, frequency: _ }) + , gain: tmpResolveAU parent.scope di (setGain <<< { id: me, gain: _ }) + } + e + ) + atts ) - atts - <|> __internalWagsFlatten me di (mix elts) + <|> __internalWagsFlatten (just me) parent.scope di (mix elts) highshelf_ :: forall i aud (outputChannels :: Type) lock payload @@ -717,17 +739,17 @@ iirFilter' fwd bk i' elts = C.Node go makeEvent \k -> do me <- ids parent.raiseId me - map ((*>) (k (deleteFromCache { id: me }))) $ flip subscribe k $ + map (k (deleteFromCache { id: me }) *> _) $ flip subscribe k $ bang ( makeIIRFilter { id: me - , parent: just parent.parent + , parent: parent.parent , scope: parent.scope , feedforward: toArray i.feedforward , feedback: toArray i.feedback } ) - <|> __internalWagsFlatten me di (mix elts) + <|> __internalWagsFlatten (just me) parent.scope di (mix elts) -- lowpass lowpass @@ -735,7 +757,7 @@ lowpass . Common.InitialLowpass i => C.Mix aud (C.Audible outputChannels lock payload) => i - -> Event C.Lowpass + -> Event (C.Lowpass lock payload) -> aud -> C.Node outputChannels lock payload lowpass i' atts elts = C.Node go @@ -744,21 +766,22 @@ lowpass i' atts elts = C.Node go go parent di@(C.AudioInterpret { ids, deleteFromCache, makeLowpass, setFrequency, setQ }) = makeEvent \k -> do me <- ids parent.raiseId me - map ((*>) (k (deleteFromCache { id: me }))) $ flip subscribe k $ - + map (k (deleteFromCache { id: me }) *> _) $ flip subscribe k $ bang ( makeLowpass - { id: me, parent: just parent.parent, scope: parent.scope, frequency: i.frequency, q: i.q } + { id: me, parent: parent.parent, scope: parent.scope, frequency: i.frequency, q: i.q } ) - <|> map - ( \(C.Lowpass e) -> match - { frequency: \g -> setFrequency { id: me, frequency: g } - , q: \g -> setQ { id: me, q: g } - } - e + <|> + ( keepLatest $ map + ( \(C.Lowpass e) -> match + { frequency: tmpResolveAU parent.scope di (setFrequency <<< { id: me, frequency: _ }) + , q: tmpResolveAU parent.scope di (setQ <<< { id: me, q: _ }) + } + e + ) + atts ) - atts - <|> __internalWagsFlatten me di (mix elts) + <|> __internalWagsFlatten (just me) parent.scope di (mix elts) lowpass_ :: forall i aud (outputChannels :: Type) lock payload @@ -775,7 +798,7 @@ lowshelf . Common.InitialLowshelf i => C.Mix aud (C.Audible outputChannels lock payload) => i - -> Event C.Lowshelf + -> Event (C.Lowshelf lock payload) -> aud -> C.Node outputChannels lock payload lowshelf i' atts elts = C.Node go @@ -784,21 +807,22 @@ lowshelf i' atts elts = C.Node go go parent di@(C.AudioInterpret { ids, deleteFromCache, makeLowshelf, setFrequency, setGain }) = makeEvent \k -> do me <- ids parent.raiseId me - map ((*>) (k (deleteFromCache { id: me }))) $ flip subscribe k $ - + map (k (deleteFromCache { id: me }) *> _) $ flip subscribe k $ bang ( makeLowshelf - { id: me, parent: just parent.parent, scope: parent.scope, frequency: i.frequency, gain: i.gain } + { id: me, parent: parent.parent, scope: parent.scope, frequency: i.frequency, gain: i.gain } ) - <|> map - ( \(C.Lowshelf e) -> match - { frequency: \g -> setFrequency { id: me, frequency: g } - , gain: \g -> setGain { id: me, gain: g } - } - e + <|> + ( keepLatest $ map + ( \(C.Lowshelf e) -> match + { frequency: tmpResolveAU parent.scope di (setFrequency <<< { id: me, frequency: _ }) + , gain: tmpResolveAU parent.scope di (setGain <<< { id: me, gain: _ }) + } + e + ) + atts ) - atts - <|> __internalWagsFlatten me di (mix elts) + <|> __internalWagsFlatten (just me) parent.scope di (mix elts) lowshelf_ :: forall i aud (outputChannels :: Type) lock payload @@ -815,32 +839,33 @@ __loopBuf :: forall i outputChannels lock payload . Common.InitialLoopBuf i => i - -> Event C.LoopBuf + -> Event (C.LoopBuf lock payload) -> C.Node outputChannels lock payload __loopBuf i' atts = C.Node go where C.InitializeLoopBuf i = Common.toInitializeLoopBuf i' go parent - ( C.AudioInterpret - { ids - , deleteFromCache - , makeLoopBuf - , setBuffer - , setOnOff - , setPlaybackRate - , setLoopStart - , setLoopEnd - } - ) = + di@ + ( C.AudioInterpret + { ids + , deleteFromCache + , makeLoopBuf + , setBuffer + , setOnOff + , setPlaybackRate + , setLoopStart + , setLoopEnd + } + ) = makeEvent \k -> do me <- ids parent.raiseId me - map ((*>) (k (deleteFromCache { id: me }))) $ flip subscribe k $ + map (k (deleteFromCache { id: me }) *> _) $ flip subscribe k $ bang ( makeLoopBuf { id: me - , parent: just parent.parent + , parent: parent.parent , scope: parent.scope , buffer: i.buffer , playbackRate: i.playbackRate @@ -849,24 +874,25 @@ __loopBuf i' atts = C.Node go , duration: i.duration } ) - <|> map - ( \(C.LoopBuf e) -> match - { buffer: \buffer -> setBuffer { id: me, buffer } - , playbackRate: \playbackRate -> setPlaybackRate - { id: me, playbackRate } - , loopStart: \loopStart -> setLoopStart { id: me, loopStart } - , loopEnd: \loopEnd -> setLoopEnd { id: me, loopEnd } - , onOff: \onOff -> setOnOff { id: me, onOff } - } - e + <|> + ( keepLatest $ map + ( \(C.LoopBuf e) -> match + { buffer: \buffer -> bang $ setBuffer { id: me, buffer } + , playbackRate: tmpResolveAU parent.scope di (setPlaybackRate <<< { id: me, playbackRate: _ }) + , loopStart: \loopStart -> bang $ setLoopStart { id: me, loopStart } + , loopEnd: \loopEnd -> bang $ setLoopEnd { id: me, loopEnd } + , onOff: \onOff -> bang $ setOnOff { id: me, onOff } + } + e + ) + atts ) - atts loopBuf :: forall i outputChannels lock payload . Common.InitialLoopBuf i => i - -> Event C.LoopBuf + -> Event (C.LoopBuf lock payload) -> C.Node outputChannels lock payload loopBuf = __loopBuf @@ -889,11 +915,11 @@ __mediaElement (C.InitializeMediaElement i) = C.Node go makeEvent \k -> do me <- ids parent.raiseId me - map ((*>) (k (deleteFromCache { id: me }))) $ flip subscribe k $ + map (k (deleteFromCache { id: me }) *> _) $ flip subscribe k $ bang ( makeMediaElement { id: me - , parent: just parent.parent + , parent: parent.parent , scope: parent.scope , element: i.element } @@ -919,11 +945,11 @@ __microphone i' = C.Node go makeEvent \k -> do me <- ids parent.raiseId me - map ((*>) (k (deleteFromCache { id: me }))) $ flip subscribe k $ + map (k (deleteFromCache { id: me }) *> _) $ flip subscribe k $ bang ( makeMicrophone { id: me - , parent: just parent.parent + , parent: parent.parent , scope: parent.scope , microphone: i.microphone } @@ -942,7 +968,7 @@ notch . Common.InitialNotch i => C.Mix aud (C.Audible outputChannels lock payload) => i - -> Event C.Notch + -> Event (C.Notch lock payload) -> aud -> C.Node outputChannels lock payload notch i' atts elts = C.Node go @@ -951,21 +977,22 @@ notch i' atts elts = C.Node go go parent di@(C.AudioInterpret { ids, deleteFromCache, makeNotch, setFrequency, setQ }) = makeEvent \k -> do me <- ids parent.raiseId me - map ((*>) (k (deleteFromCache { id: me }))) $ flip subscribe k $ - + map (k (deleteFromCache { id: me }) *> _) $ flip subscribe k $ bang ( makeNotch - { id: me, parent: just parent.parent, scope: parent.scope, frequency: i.frequency, q: i.q } + { id: me, parent: parent.parent, scope: parent.scope, frequency: i.frequency, q: i.q } ) - <|> map - ( \(C.Notch e) -> match - { frequency: \g -> setFrequency { id: me, frequency: g } - , q: \g -> setQ { id: me, q: g } - } - e + <|> + ( keepLatest $ map + ( \(C.Notch e) -> match + { frequency: tmpResolveAU parent.scope di (setFrequency <<< { id: me, frequency: _ }) + , q: tmpResolveAU parent.scope di (setQ <<< { id: me, q: _ }) + } + e + ) + atts ) - atts - <|> __internalWagsFlatten me di (mix elts) + <|> __internalWagsFlatten (just me) parent.scope di (mix elts) notch_ :: forall i aud (outputChannels :: Type) lock payload @@ -982,7 +1009,7 @@ peaking . Common.InitialPeaking i => C.Mix aud (C.Audible outputChannels lock payload) => i - -> Event C.Peaking + -> Event (C.Peaking lock payload) -> aud -> C.Node outputChannels lock payload peaking i' atts elts = C.Node go @@ -991,22 +1018,23 @@ peaking i' atts elts = C.Node go go parent di@(C.AudioInterpret { ids, deleteFromCache, makePeaking, setFrequency, setQ, setGain }) = makeEvent \k -> do me <- ids parent.raiseId me - map ((*>) (k (deleteFromCache { id: me }))) $ flip subscribe k $ - + map (k (deleteFromCache { id: me }) *> _) $ flip subscribe k $ bang ( makePeaking - { id: me, parent: just parent.parent, scope: parent.scope, frequency: i.frequency, q: i.q, gain: i.gain } + { id: me, parent: parent.parent, scope: parent.scope, frequency: i.frequency, q: i.q, gain: i.gain } ) - <|> map - ( \(C.Peaking e) -> match - { frequency: \g -> setFrequency { id: me, frequency: g } - , q: \g -> setQ { id: me, q: g } - , gain: \g -> setGain { id: me, gain: g } - } - e + <|> + ( keepLatest $ map + ( \(C.Peaking e) -> match + { frequency: tmpResolveAU parent.scope di (setFrequency <<< { id: me, frequency: _ }) + , q: tmpResolveAU parent.scope di (setQ <<< { id: me, q: _ }) + , gain: tmpResolveAU parent.scope di (setGain <<< { id: me, gain: _ }) + } + e + ) + atts ) - atts - <|> __internalWagsFlatten me di (mix elts) + <|> __internalWagsFlatten (just me) parent.scope di (mix elts) peaking_ :: forall i aud (outputChannels :: Type) lock payload @@ -1023,45 +1051,47 @@ __periodicOsc :: forall i outputChannels lock payload . Common.InitialPeriodicOsc i => i - -> Event C.PeriodicOsc + -> Event (C.PeriodicOsc lock payload) -> C.Node outputChannels lock payload __periodicOsc i' atts = C.Node go where C.InitializePeriodicOsc i = Common.toInitializePeriodicOsc i' go parent - ( C.AudioInterpret - { ids, deleteFromCache, makePeriodicOsc, setFrequency, setOnOff, setPeriodicOsc } - ) = + di@ + ( C.AudioInterpret + { ids, deleteFromCache, makePeriodicOsc, setFrequency, setOnOff, setPeriodicOsc } + ) = makeEvent \k -> do me <- ids parent.raiseId me - map ((*>) (k (deleteFromCache { id: me }))) $ flip subscribe k $ + map (k (deleteFromCache { id: me }) *> _) $ flip subscribe k $ bang ( makePeriodicOsc { id: me - , parent: just parent.parent + , parent: parent.parent , scope: parent.scope , frequency: i.frequency , spec: i.spec } ) - <|> map - ( \(C.PeriodicOsc e) -> match - { frequency: \frequency -> setFrequency - { id: me, frequency } - , onOff: \onOff -> setOnOff { id: me, onOff } - , spec: \spec -> setPeriodicOsc { id: me, spec } - } - e + <|> + ( keepLatest $ map + ( \(C.PeriodicOsc e) -> match + { frequency: tmpResolveAU parent.scope di (setFrequency <<< { id: me, frequency: _ }) + , onOff: \onOff -> bang $ setOnOff { id: me, onOff } + , spec: \spec -> bang $ setPeriodicOsc { id: me, spec } + } + e + ) + atts ) - atts periodicOsc :: forall i outputChannels lock payload . Common.InitialPeriodicOsc i => i - -> Event C.PeriodicOsc + -> Event (C.PeriodicOsc lock payload) -> C.Node outputChannels lock payload periodicOsc = __periodicOsc @@ -1079,8 +1109,8 @@ data PlayBufOptions = PlayBufOptions instance ConvertOption PlayBufOptions "playbackRate" - InitialAudioParameter - InitialAudioParameter where + C.InitialAudioParameter + C.InitialAudioParameter where convertOption _ _ = identity instance ConvertOption PlayBufOptions "duration" Number (Maybe Number) where @@ -1095,7 +1125,7 @@ instance type PlayBufOptional = ( bufferOffset :: Number - , playbackRate :: InitialAudioParameter + , playbackRate :: C.InitialAudioParameter , duration :: Maybe Number ) @@ -1131,32 +1161,33 @@ __playBuf :: forall i outputChannels lock payload . Common.InitialPlayBuf i => i - -> Event C.PlayBuf + -> Event (C.PlayBuf lock payload) -> C.Node outputChannels lock payload __playBuf i' atts = C.Node go where C.InitializePlayBuf i = Common.toInitializePlayBuf i' go parent - ( C.AudioInterpret - { ids - , deleteFromCache - , makePlayBuf - , setBuffer - , setOnOff - , setDuration - , setPlaybackRate - , setBufferOffset - } - ) = + di@ + ( C.AudioInterpret + { ids + , deleteFromCache + , makePlayBuf + , setBuffer + , setOnOff + , setDuration + , setPlaybackRate + , setBufferOffset + } + ) = makeEvent \k -> do me <- ids parent.raiseId me - map ((*>) (k (deleteFromCache { id: me }))) $ flip subscribe k $ + map (k (deleteFromCache { id: me }) *> _) $ flip subscribe k $ bang ( makePlayBuf { id: me - , parent: just parent.parent + , parent: parent.parent , scope: parent.scope , buffer: i.buffer , playbackRate: i.playbackRate @@ -1164,25 +1195,29 @@ __playBuf i' atts = C.Node go , duration: i.duration } ) - <|> map - ( \(C.PlayBuf e) -> match - { buffer: \buffer -> setBuffer { id: me, buffer } - , playbackRate: \playbackRate -> setPlaybackRate - { id: me, playbackRate } - , bufferOffset: \bufferOffset -> setBufferOffset - { id: me, bufferOffset } - , onOff: \onOff -> setOnOff { id: me, onOff } - , duration: \duration -> setDuration { id: me, duration } - } - e + <|> + ( keepLatest $ map + ( \(C.PlayBuf e) -> match + { buffer: \buffer -> bang $ setBuffer { id: me, buffer } + , playbackRate: tmpResolveAU parent.scope di + ( setPlaybackRate <<< + { id: me, playbackRate: _ } + ) + , bufferOffset: \bufferOffset -> bang $ setBufferOffset + { id: me, bufferOffset } + , onOff: \onOff -> bang $ setOnOff { id: me, onOff } + , duration: \duration -> bang $ setDuration { id: me, duration } + } + e + ) + atts ) - atts playBuf :: forall i outputChannels lock payload . Common.InitialPlayBuf i => i - -> Event C.PlayBuf + -> Event (C.PlayBuf lock payload) -> C.Node outputChannels lock payload playBuf = __playBuf @@ -1207,12 +1242,12 @@ recorder i' elt = C.Node go makeEvent \k -> do me <- ids parent.raiseId me - map ((*>) (k (deleteFromCache { id: me }))) $ flip subscribe k $ + map (k (deleteFromCache { id: me }) *> _) $ flip subscribe k $ bang ( makeRecorder - { id: me, parent: just parent.parent, scope: parent.scope, cb: i.cb } + { id: me, parent: parent.parent, scope: parent.scope, cb: i.cb } ) - <|> __internalWagsFlatten me di (mix elt) + <|> __internalWagsFlatten (just me) parent.scope di (mix elt) -- sawtoothOsc @@ -1220,41 +1255,42 @@ __sawtoothOsc :: forall i outputChannels lock payload . Common.InitialSawtoothOsc i => i - -> Event C.SawtoothOsc + -> Event (C.SawtoothOsc lock payload) -> C.Node outputChannels lock payload __sawtoothOsc i' atts = C.Node go where C.InitializeSawtoothOsc i = Common.toInitializeSawtoothOsc i' go parent - (C.AudioInterpret { ids, deleteFromCache, makeSawtoothOsc, setFrequency, setOnOff }) = + di@(C.AudioInterpret { ids, deleteFromCache, makeSawtoothOsc, setFrequency, setOnOff }) = makeEvent \k -> do me <- ids parent.raiseId me - map ((*>) (k (deleteFromCache { id: me }))) $ flip subscribe k $ + map (k (deleteFromCache { id: me }) *> _) $ flip subscribe k $ bang ( makeSawtoothOsc { id: me - , parent: just parent.parent + , parent: parent.parent , scope: parent.scope , frequency: i.frequency } ) - <|> map - ( \(C.SawtoothOsc e) -> match - { frequency: \frequency -> setFrequency - { id: me, frequency } - , onOff: \onOff -> setOnOff { id: me, onOff } - } - e + <|> + ( keepLatest $ map + ( \(C.SawtoothOsc e) -> match + { frequency: tmpResolveAU parent.scope di (setFrequency <<< { id: me, frequency: _ }) + , onOff: \onOff -> bang $ setOnOff { id: me, onOff } + } + e + ) + atts ) - atts sawtoothOsc :: forall i outputChannels lock payload . Common.InitialSawtoothOsc i => i - -> Event C.SawtoothOsc + -> Event (C.SawtoothOsc lock payload) -> C.Node outputChannels lock payload sawtoothOsc = __sawtoothOsc @@ -1271,41 +1307,42 @@ __sinOsc :: forall i outputChannels lock payload . Common.InitialSinOsc i => i - -> Event C.SinOsc + -> Event (C.SinOsc lock payload) -> C.Node outputChannels lock payload __sinOsc i' atts = C.Node go where C.InitializeSinOsc i = Common.toInitializeSinOsc i' go parent - (C.AudioInterpret { ids, deleteFromCache, makeSinOsc, setFrequency, setOnOff }) = + di@(C.AudioInterpret { ids, deleteFromCache, makeSinOsc, setFrequency, setOnOff }) = makeEvent \k -> do me <- ids parent.raiseId me - map ((*>) (k (deleteFromCache { id: me }))) $ flip subscribe k $ + map (k (deleteFromCache { id: me }) *> _) $ flip subscribe k $ bang ( makeSinOsc { id: me - , parent: just parent.parent + , parent: parent.parent , scope: parent.scope , frequency: i.frequency } ) - <|> map - ( \(C.SinOsc e) -> match - { frequency: \frequency -> setFrequency - { id: me, frequency } - , onOff: \onOff -> setOnOff { id: me, onOff } - } - e + <|> + ( keepLatest $ map + ( \(C.SinOsc e) -> match + { frequency: tmpResolveAU parent.scope di (setFrequency <<< { id: me, frequency: _ }) + , onOff: \onOff -> bang $ setOnOff { id: me, onOff } + } + e + ) + atts ) - atts sinOsc :: forall i outputChannels lock payload . Common.InitialSinOsc i => i - -> Event C.SinOsc + -> Event (C.SinOsc lock payload) -> C.Node outputChannels lock payload sinOsc = __sinOsc @@ -1322,41 +1359,42 @@ __squareOsc :: forall i outputChannels lock payload . Common.InitialSquareOsc i => i - -> Event C.SquareOsc + -> Event (C.SquareOsc lock payload) -> C.Node outputChannels lock payload __squareOsc i' atts = C.Node go where C.InitializeSquareOsc i = Common.toInitializeSquareOsc i' go parent - (C.AudioInterpret { ids, deleteFromCache, makeSquareOsc, setFrequency, setOnOff }) = + di@(C.AudioInterpret { ids, deleteFromCache, makeSquareOsc, setFrequency, setOnOff }) = makeEvent \k -> do me <- ids parent.raiseId me - map ((*>) (k (deleteFromCache { id: me }))) $ flip subscribe k $ + map (k (deleteFromCache { id: me }) *> _) $ flip subscribe k $ bang ( makeSquareOsc { id: me - , parent: just parent.parent + , parent: parent.parent , scope: parent.scope , frequency: i.frequency } ) - <|> map - ( \(C.SquareOsc e) -> match - { frequency: \frequency -> setFrequency - { id: me, frequency } - , onOff: \onOff -> setOnOff { id: me, onOff } - } - e + <|> + ( keepLatest $ map + ( \(C.SquareOsc e) -> match + { frequency: tmpResolveAU parent.scope di (setFrequency <<< { id: me, frequency: _ }) + , onOff: \onOff -> bang $ setOnOff { id: me, onOff } + } + e + ) + atts ) - atts squareOsc :: forall i outputChannels lock payload . Common.InitialSquareOsc i => i - -> Event C.SquareOsc + -> Event (C.SquareOsc lock payload) -> C.Node outputChannels lock payload squareOsc = __squareOsc @@ -1377,7 +1415,7 @@ speaker speaker elts di@(C.AudioInterpret { ids, makeSpeaker }) = makeEvent \k -> do id <- ids k (makeSpeaker { id }) - subscribe (__internalWagsFlatten id di (mix elts)) k + subscribe (__internalWagsFlatten (just id) "toplevel" di (mix elts)) k speaker2 :: forall aud lock payload @@ -1393,7 +1431,7 @@ pan . Common.InitialStereoPanner i => C.Mix aud (C.Audible outputChannels lock payload) => i - -> Event C.StereoPanner + -> Event (C.StereoPanner lock payload) -> aud -> C.Node outputChannels lock payload pan i' atts elts = C.Node go @@ -1402,20 +1440,21 @@ pan i' atts elts = C.Node go go parent di@(C.AudioInterpret { ids, deleteFromCache, makeStereoPanner, setPan }) = makeEvent \k -> do me <- ids parent.raiseId me - map ((*>) (k (deleteFromCache { id: me }))) $ flip subscribe k $ - + map (k (deleteFromCache { id: me }) *> _) $ flip subscribe k $ bang ( makeStereoPanner - { id: me, parent: just parent.parent, scope: parent.scope, pan: i.pan } + { id: me, parent: parent.parent, scope: parent.scope, pan: i.pan } ) - <|> map - ( \(C.StereoPanner e) -> match - { pan: \g -> setPan { id: me, pan: g } - } - e + <|> + ( keepLatest $ map + ( \(C.StereoPanner e) -> match + { pan: tmpResolveAU parent.scope di (setPan <<< { id: me, pan: _ }) + } + e + ) + atts ) - atts - <|> __internalWagsFlatten me di (mix elts) + <|> __internalWagsFlatten (just me) parent.scope di (mix elts) pan_ :: forall i aud (outputChannels :: Type) lock payload @@ -1432,41 +1471,42 @@ __triangleOsc :: forall i outputChannels lock payload . Common.InitialTriangleOsc i => i - -> Event C.TriangleOsc + -> Event (C.TriangleOsc lock payload) -> C.Node outputChannels lock payload __triangleOsc i' atts = C.Node go where C.InitializeTriangleOsc i = Common.toInitializeTriangleOsc i' go parent - (C.AudioInterpret { ids, deleteFromCache, makeTriangleOsc, setFrequency, setOnOff }) = + di@(C.AudioInterpret { ids, deleteFromCache, makeTriangleOsc, setFrequency, setOnOff }) = makeEvent \k -> do me <- ids parent.raiseId me - map ((*>) (k (deleteFromCache { id: me }))) $ flip subscribe k $ + map (k (deleteFromCache { id: me }) *> _) $ flip subscribe k $ bang ( makeTriangleOsc { id: me - , parent: just parent.parent + , parent: parent.parent , scope: parent.scope , frequency: i.frequency } ) - <|> map - ( \(C.TriangleOsc e) -> match - { frequency: \frequency -> setFrequency - { id: me, frequency } - , onOff: \onOff -> setOnOff { id: me, onOff } - } - e + <|> + ( keepLatest $ map + ( \(C.TriangleOsc e) -> match + { frequency: tmpResolveAU parent.scope di (setFrequency <<< { id: me, frequency: _ }) + , onOff: \onOff -> bang $ setOnOff { id: me, onOff } + } + e + ) + atts ) - atts triangleOsc :: forall i outputChannels lock payload . Common.InitialTriangleOsc i => i - -> Event C.TriangleOsc + -> Event (C.TriangleOsc lock payload) -> C.Node outputChannels lock payload triangleOsc = __triangleOsc @@ -1493,16 +1533,16 @@ waveShaper i' elts = C.Node go makeEvent \k -> do me <- ids parent.raiseId me - map ((*>) (k (deleteFromCache { id: me }))) $ flip subscribe k $ + map (k (deleteFromCache { id: me }) *> _) $ flip subscribe k $ bang ( makeWaveShaper { id: me - , parent: just parent.parent + , parent: parent.parent , scope: parent.scope , curve: i.curve , oversample: i.oversample } - ) <|> __internalWagsFlatten me di (mix elts) + ) <|> __internalWagsFlatten (just me) parent.scope di (mix elts) ---------- @@ -1535,7 +1575,7 @@ internalFan isGlobal scopeF gaga closure = C.Node go actualized = oneOf $ mapWithIndex ( \ix (C.Node gogo) -> gogo - { parent: "@fan@" + { parent: just "@fan@" , scope: scopeF psr.scope , raiseId: \id -> unsafeUpdateMutAr ix id av } @@ -1557,11 +1597,12 @@ internalFan isGlobal scopeF gaga closure = C.Node go \{ parent, raiseId } (C.AudioInterpret { connectXToY }) -> makeEvent \k2 -> do raiseId id - k2 (connectXToY { from: id, to: parent }) + for_ parent \p' -> + k2 (connectXToY { from: id, to: p' }) pure (pure unit) ) idz - realized = __internalWagsFlatten psr.parent di + realized = __internalWagsFlatten psr.parent psr.scope di ((unsafeCoerce :: C.Audible _ _ _ -> C.Audible _ _ _) (closure injectable (\(C.Node q) -> C.Node q))) u <- subscribe realized k void $ tryPut u av2 @@ -1628,7 +1669,8 @@ fix f = C.Node go void $ AVar.read av case _ of Left e -> throwException e -- only do the connection if not silence - Right r -> when (r /= ii.parent) (ii.raiseId r *> k (connectXToY { from: r, to: ii.parent })) + Right r -> for_ ii.parent \p' -> + when (r /= p') (ii.raiseId r *> k (connectXToY { from: r, to: p' })) pure (pure unit) subscribe ( nn @@ -1671,7 +1713,7 @@ silence = fix identity -- bang -- ( makeMerger -- { id: me --- , parent: just parent.parent +-- , parent: parent.parent -- , scope: parent.scope -- } -- ) @@ -1683,4 +1725,45 @@ silence = fix identity -- elts -- ) -- ) --- ) \ No newline at end of file +-- ) + +-- TODO +-- this function is copied between two files +-- with the sole difference that this version wraps its argument in a gain node +-- the reason for this is that, otherwise, we'd have to write additional machinery +-- for all generators (ie sine wave oscillators) to listen to when they turn on and off and reconnect fresh generators whenever something turns on again +-- by doing it this way, all generators go to a gain node, so we can use code we've already written +-- the downside is that we have an extra gain node for every audio parameter +-- which can add up +-- so we definitely want to delete this and use Common.resolveAU +-- as soon as we can correctly attach and detach generators +tmpResolveAU :: forall lock payload. String -> C.AudioInterpret payload -> (C.FFIAudioParameter -> payload) -> C.AudioParameter lock payload -> Event payload +tmpResolveAU = go + where + cncl = C.FFIAudioParameter <<< inj (Proxy :: _ "cancel") + ev = C.FFIAudioParameter <<< inj (Proxy :: _ "envelope") + nmc = C.FFIAudioParameter <<< inj (Proxy :: _ "numeric") + sdn = C.FFIAudioParameter <<< inj (Proxy :: _ "sudden") + ut = C.FFIAudioParameter <<< inj (Proxy :: _ "unit") + go scope di f (C.AudioParameter a) = match + { numeric: bang <<< f <<< nmc + , envelope: bang <<< f <<< ev + , cancel: bang <<< f <<< cncl + , sudden: bang <<< f <<< sdn + , unit: \(C.AudioUnit { u }) -> + let + C.Node n = gain_ 1.0 u + in + makeEvent \k -> do + av <- AVar.empty + subscribe + ( n { parent: nothing, scope: scope, raiseId: \x -> void $ AVar.tryPut x av } di <|> makeEvent \k2 -> do + void $ AVar.take av case _ of + Left e -> throwException e + -- only do the connection if not silence + Right i -> k2 (f (ut (C.FFIAudioUnit { i }))) + pure (pure unit) + ) + k + } + a \ No newline at end of file diff --git a/src/WAGS/Core.purs b/src/WAGS/Core.purs index 811353bb..c91409ed 100644 --- a/src/WAGS/Core.purs +++ b/src/WAGS/Core.purs @@ -3,12 +3,17 @@ module WAGS.Core where import Prelude import Data.Either (Either(..)) -import Data.Foldable (fold, oneOfMap, traverse_) +import Data.Foldable (fold, for_, oneOfMap, traverse_) import Data.Function (on) import Data.Generic.Rep (class Generic) +import Data.Lens (Optic', over) +import Data.Lens.Iso.Newtype (_Newtype, unto) +import Data.Lens.Record (prop) import Data.Maybe as DM -import Data.Newtype (class Newtype, unwrap) +import Data.Newtype (class Newtype, unwrap, wrap) +import Data.Profunctor.Strong (class Strong) import Data.Show.Generic (genericShow) +import Data.Typelevel.Num (D1) import Data.Variant (Variant, inj, match) import Data.Variant.Maybe (Maybe) import Data.Vec (Vec) @@ -18,16 +23,225 @@ import Effect.AVar as AVar import Effect.Exception (throwException) import Effect.Ref as Ref import FRP.Event (Event, keepLatest, makeEvent, subscribe) +import FRP.Event.Class (bang) import Foreign (Foreign) import Foreign.Object (Object) import Foreign.Object as Object +import Prim.Row (class Cons) import Simple.JSON as JSON import Type.Equality (class TypeEquals) import Type.Proxy (Proxy(..)) import Unsafe.Coerce (unsafeCoerce) -import WAGS.Parameter (AudioOnOff, AudioParameter, InitialAudioParameter) import WAGS.WebAPI (AnalyserNodeCb, BrowserAudioBuffer, BrowserFloatArray, BrowserMediaElement, BrowserMicrophone, BrowserPeriodicWave, MediaRecorderCb) +-- start param +newtype Transition = Transition + (Variant (linear :: Unit, exponential :: Unit, step :: Unit)) + +_linear :: Transition +_linear = Transition $ inj (Proxy :: _ "linear") unit + +_exponential :: Transition +_exponential = Transition $ inj (Proxy :: _ "exponential") unit + +_step :: Transition +_step = Transition $ inj (Proxy :: _ "step") unit + +_numeric' :: forall lock payload. AudioNumeric' -> AudioParameter lock payload +_numeric' = _numeric <<< AudioNumeric + +_numeric :: forall lock payload. AudioNumeric -> AudioParameter lock payload +_numeric = AudioParameter <<< inj (Proxy :: _ "numeric") + +_unit' :: forall lock payload. AudioUnit' lock payload -> AudioParameter lock payload +_unit' = _unit <<< AudioUnit + +_unit :: forall lock payload. AudioUnit lock payload -> AudioParameter lock payload +_unit = AudioParameter <<< inj (Proxy :: _ "unit") + +_envelope :: forall lock payload. AudioEnvelope -> AudioParameter lock payload +_envelope = AudioParameter <<< inj (Proxy :: _ "envelope") + +_envelope' :: forall lock payload. AudioEnvelope' -> AudioParameter lock payload +_envelope' = _envelope <<< AudioEnvelope + +_cancel :: forall lock payload. AudioCancel -> AudioParameter lock payload +_cancel = AudioParameter <<< inj (Proxy :: _ "cancel") + +_cancel' :: forall lock payload. AudioCancel' -> AudioParameter lock payload +_cancel' = _cancel <<< AudioCancel + +_sudden :: forall lock payload. AudioSudden -> AudioParameter lock payload +_sudden = AudioParameter <<< inj (Proxy :: _ "sudden") + +_sudden' :: forall lock payload. AudioSudden' -> AudioParameter lock payload +_sudden' = _sudden <<< AudioSudden + +type AudioNumeric' = { n :: Number, o :: Number, t :: Transition } +newtype AudioNumeric = AudioNumeric AudioNumeric' +derive instance Newtype AudioNumeric _ + +type AudioEnvelope' = { p :: Array Number, o :: Number, d :: Number } +newtype AudioEnvelope = AudioEnvelope AudioEnvelope' +derive instance Newtype AudioEnvelope _ + +type AudioCancel' = { o :: Number } +newtype AudioCancel = AudioCancel AudioCancel' +derive instance Newtype AudioCancel _ + +type AudioUnit' lock payload = { u :: Node D1 lock payload } +newtype AudioUnit lock payload = AudioUnit (AudioUnit' lock payload) + +type AudioSudden' = { n :: Number } +newtype AudioSudden = AudioSudden AudioSudden' +derive instance Newtype AudioSudden _ + +type InitialAudioParameter = Number + +newtype AudioParameter lock payload = AudioParameter + ( Variant + ( numeric :: AudioNumeric + , envelope :: AudioEnvelope + , cancel :: AudioCancel + , sudden :: AudioSudden + , unit :: AudioUnit lock payload + ) + ) + +type FFIAudioUnit' = { i :: String } +newtype FFIAudioUnit = FFIAudioUnit FFIAudioUnit' +derive instance Newtype FFIAudioUnit _ + +newtype FFIAudioParameter = FFIAudioParameter + ( Variant + ( numeric :: AudioNumeric + , envelope :: AudioEnvelope + , cancel :: AudioCancel + , sudden :: AudioSudden + , unit :: FFIAudioUnit + ) + ) + +-- | Term-level constructor for a generator being on or off +newtype OnOff = OnOff + ( Variant + ( on :: Unit + , off :: Unit + ) + ) + +_on :: OnOff +_on = OnOff $ inj (Proxy :: _ "on") unit + +_off :: OnOff +_off = OnOff $ inj (Proxy :: _ "off") unit + +derive instance eqOnOff :: Eq OnOff +derive instance ordOnOff :: Ord OnOff +derive instance newtypeOnOff :: Newtype OnOff _ +derive instance genericOnOff :: Generic OnOff _ + +instance showOnOff :: Show OnOff where + show = unwrap >>> match + { on: const "on", off: const "off" } + +newtype AudioOnOff = AudioOnOff + { x :: OnOff + , o :: Number + } + +apOn :: AudioOnOff +apOn = AudioOnOff { x: _on, o: 0.0 } + +bangOn + :: forall nt r + . Newtype nt (Variant (onOff :: AudioOnOff | r)) + => Event nt +bangOn = bang (wrap $ inj (Proxy :: _ "onOff") apOn) + +apOff :: AudioOnOff +apOff = AudioOnOff { x: _off, o: 0.0 } + +dt + :: forall nt r + . Newtype nt { o :: Number | r } + => (Number -> Number) + -> nt + -> nt +dt = over (_Newtype <<< prop (Proxy :: _ "o")) + +derive instance eqAudioOnOff :: Eq AudioOnOff +derive instance ordAudioOnOff :: Ord AudioOnOff +derive instance newtypeAudioOnOff :: Newtype AudioOnOff _ +derive instance genericAudioOnOff :: Generic AudioOnOff _ + +class ToAudioOnOff i where + toAudioOnOff :: i -> AudioOnOff + +instance ToAudioOnOff Number where + toAudioOnOff = AudioOnOff <<< { o: _, x: _on } + +instance ToAudioOnOff OnOff where + toAudioOnOff = AudioOnOff <<< { o: 0.0, x: _ } + +instance ToAudioOnOff AudioOnOff where + toAudioOnOff = identity + +class ToAudioParameter i l p where + toAudioParameter :: i -> AudioParameter l p + +instance ToAudioParameter Number l p where + toAudioParameter n = _sudden (AudioSudden { n }) + +instance (TypeEquals l0 l1, TypeEquals p0 p1) => ToAudioParameter (AudioParameter l0 p0) l1 p1 where + toAudioParameter = (unsafeCoerce :: AudioParameter l0 p0 -> AudioParameter l1 p1) + +instance ToAudioParameter AudioNumeric l p where + toAudioParameter = _numeric + +instance ToAudioParameter AudioSudden l p where + toAudioParameter = _sudden + +instance ToAudioParameter AudioCancel l p where + toAudioParameter = _cancel + +instance ToAudioParameter AudioEnvelope l p where + toAudioParameter = _envelope + +instance (TypeEquals l0 l1, TypeEquals p0 p1) => ToAudioParameter (AudioUnit l0 p0) l1 p1 where + toAudioParameter = _unit <<< (unsafeCoerce :: AudioUnit l0 p0 -> AudioUnit l1 p1) + +instance (TypeEquals l0 l1, TypeEquals p0 p1) => ToAudioParameter (Node D1 l0 p0) l1 p1 where + toAudioParameter = toAudioParameter <<< AudioUnit <<< { u: _ } + +c1 :: forall l p. (forall o. Node o l p) -> Node D1 l p +c1 (Node o) = Node o + +class OpticN s where + opticN :: forall p. Strong p => Optic' p s Number + +instance (Cons "n" Number r' r) => OpticN AudioNumeric where + opticN = unto AudioNumeric <<< prop (Proxy :: _ "n") + +instance (Cons "n" Number r' r) => OpticN AudioSudden where + opticN = unto AudioSudden <<< prop (Proxy :: _ "n") + +class OpticO s where + opticO :: forall p. Strong p => Optic' p s Number + +instance (Cons "n" Number r' r) => OpticO AudioOnOff where + opticO = unto AudioOnOff <<< prop (Proxy :: _ "o") + +instance (Cons "n" Number r' r) => OpticO AudioNumeric where + opticO = unto AudioNumeric <<< prop (Proxy :: _ "o") + +instance (Cons "n" Number r' r) => OpticO AudioEnvelope where + opticO = unto AudioEnvelope <<< prop (Proxy :: _ "o") + +instance (Cons "n" Number r' r) => OpticO AudioCancel where + opticO = unto AudioCancel <<< prop (Proxy :: _ "o") +----------- +-- end param -- type AudioWorkletNodeOptions_' param = { name :: String @@ -113,8 +327,9 @@ newtype FixedChannels outputChannels lock payload = FixedChannels newtype EventfulNode outputChannels lock payload = EventfulNode (Event (Audible outputChannels lock payload)) -type Node' payload = - { parent :: String +type Node' :: forall k. k -> Type -> Type +type Node' lock payload = + { parent :: Maybe String , scope :: String , raiseId :: String -> Effect Unit } @@ -122,7 +337,7 @@ type Node' payload = -> Event payload newtype Node :: Type -> Type -> Type -> Type -newtype Node outputChannels lock payload = Node (Node' payload) +newtype Node outputChannels lock payload = Node (Node' lock payload) data Channel outputChannels lock payload = Sound (Node outputChannels lock payload) @@ -147,7 +362,7 @@ instance , TypeEquals payloadi payloado ) => Mix (Audible outputChannelsi locki payloadi) - (Audible outputChannelso locko paylaodo) where + (Audible outputChannelso locko payloado) where mix = unsafeCoerce :: Audible _ _ _ -> Audible _ _ _ instance @@ -156,7 +371,7 @@ instance , TypeEquals payloadi payloado ) => Mix (Event (Event (Channel outputChannelsi locki payloadi))) - (Audible outputChannelso locko paylaodo) where + (Audible outputChannelso locko payloado) where mix i = Audible $ inj (Proxy :: _ "dynamicChannels") (DynamicChannels ((unsafeCoerce :: (Event (Event (Channel _ _ _))) -> (Event (Event (Channel _ _ _)))) i)) @@ -166,7 +381,7 @@ instance , TypeEquals payloadi payloado ) => Mix (Event (Audible outputChannelsi locki payloadi)) - (Audible outputChannelso locko paylaodo) where + (Audible outputChannelso locko payloado) where mix i = Audible $ inj (Proxy :: _ "eventfulNode") (EventfulNode ((unsafeCoerce :: Event (Audible _ _ _) -> Event (Audible _ _ _)) i)) instance @@ -174,7 +389,7 @@ instance , TypeEquals locki locko , TypeEquals payloadi payloado ) => - Mix (Node outputChannelsi locki payloadi) (Audible outputChannelso locko paylaodo) where + Mix (Node outputChannelsi locki payloadi) (Audible outputChannelso locko payloado) where mix i = Audible $ inj (Proxy :: _ "node") ((unsafeCoerce :: Node _ _ _ -> Node _ _ _) i) instance @@ -183,7 +398,7 @@ instance , TypeEquals payloadi payloado ) => Mix (Array (Node outputChannelsi locki payloadi)) - (Audible outputChannelso locko paylaodo) where + (Audible outputChannelso locko payloado) where mix i = Audible $ inj (Proxy :: _ "fixedChannels") (FixedChannels ((unsafeCoerce :: Array (Node _ _ _) -> Array (Node _ _ _)) i)) -- @@ -249,11 +464,11 @@ type DisconnectXFromY' = { fromUnit :: String, toUnit :: String | DisconnectXFromY_ } type DeleteFromCache = { id :: String } -derive instance newtypeAllpass :: Newtype Allpass _ -newtype Allpass = Allpass +derive instance newtypeAllpass :: Newtype (Allpass lock parameter) _ +newtype Allpass lock parameter = Allpass ( Variant - ( frequency :: AudioParameter - , q :: AudioParameter + ( frequency :: AudioParameter lock parameter + , q :: AudioParameter lock parameter ) ) @@ -272,7 +487,7 @@ type MakeAllpass_ param = } type MakeAllpass = MakeAllpass_ InitialAudioParameter -type MakeAllpass' = MakeAllpass_ AudioParameter +type MakeAllpass' lock payload = MakeAllpass_ (AudioParameter lock payload) derive instance newtypeAnalyser :: Newtype Analyser _ newtype Analyser = Analyser (Variant (cb :: AnalyserNodeCb)) @@ -345,13 +560,13 @@ type MakeAudioWorkletNode_ param = } type MakeAudioWorkletNode = MakeAudioWorkletNode_ InitialAudioParameter -type MakeAudioWorkletNode' = MakeAudioWorkletNode_ AudioParameter +type MakeAudioWorkletNode' lock payload = MakeAudioWorkletNode_ (AudioParameter lock payload) -derive instance newtypeBandpass :: Newtype Bandpass _ -newtype Bandpass = Bandpass +derive instance newtypeBandpass :: Newtype (Bandpass lock payload) _ +newtype Bandpass lock payload = Bandpass ( Variant - ( frequency :: AudioParameter - , q :: AudioParameter + ( frequency :: AudioParameter lock payload + , q :: AudioParameter lock payload ) ) @@ -370,11 +585,11 @@ type MakeBandpass_ param = } type MakeBandpass = MakeBandpass_ InitialAudioParameter -type MakeBandpass' = MakeBandpass_ AudioParameter +type MakeBandpass' lock payload = MakeBandpass_ (AudioParameter lock payload) -derive instance newtypeConstant :: Newtype Constant _ -newtype Constant = Constant - (Variant (offset :: AudioParameter, onOff :: AudioOnOff)) +derive instance newtypeConstant :: Newtype (Constant lock payload) _ +newtype Constant lock payload = Constant + (Variant (offset :: AudioParameter lock payload, onOff :: AudioOnOff)) derive instance newtypeInitializeConstant :: Newtype InitializeConstant _ newtype InitializeConstant = InitializeConstant @@ -388,7 +603,7 @@ type MakeConstant_ param = ) type MakeConstant = { | MakeConstant_ InitialAudioParameter } -type MakeConstant' = { onOff :: AudioOnOff | MakeConstant_ AudioParameter } +type MakeConstant' lock payload = { onOff :: AudioOnOff | MakeConstant_ (AudioParameter lock payload) } derive instance newtypeInitializeConvolver :: Newtype InitializeConvolver _ newtype InitializeConvolver = InitializeConvolver @@ -401,8 +616,8 @@ type MakeConvolver = , buffer :: BrowserAudioBuffer } -derive instance newtypeDelay :: Newtype Delay _ -newtype Delay = Delay (Variant (delayTime :: AudioParameter)) +derive instance newtypeDelay :: Newtype (Delay lock payload) _ +newtype Delay lock payload = Delay (Variant (delayTime :: AudioParameter lock payload)) derive instance newtypeInitializeDelay :: Newtype InitializeDelay _ newtype InitializeDelay = InitializeDelay { delayTime :: InitialAudioParameter, maxDelayTime :: InitialAudioParameter } @@ -415,16 +630,16 @@ type MakeDelay_ param = } type MakeDelay = MakeDelay_ InitialAudioParameter -type MakeDelay' = MakeDelay_ AudioParameter +type MakeDelay' lock payload = MakeDelay_ (AudioParameter lock payload) -derive instance newtypeDynamicsCompressor :: Newtype DynamicsCompressor _ -newtype DynamicsCompressor = DynamicsCompressor +derive instance newtypeDynamicsCompressor :: Newtype (DynamicsCompressor lock payload) _ +newtype DynamicsCompressor lock payload = DynamicsCompressor ( Variant - ( threshold :: AudioParameter - , knee :: AudioParameter - , ratio :: AudioParameter - , attack :: AudioParameter - , release :: AudioParameter + ( threshold :: AudioParameter lock payload + , knee :: AudioParameter lock payload + , ratio :: AudioParameter lock payload + , attack :: AudioParameter lock payload + , release :: AudioParameter lock payload ) ) @@ -451,10 +666,10 @@ type MakeDynamicsCompressor_ param = } type MakeDynamicsCompressor = MakeDynamicsCompressor_ InitialAudioParameter -type MakeDynamicsCompressor' = MakeDynamicsCompressor_ AudioParameter +type MakeDynamicsCompressor' lock payload = MakeDynamicsCompressor_ (AudioParameter lock payload) -derive instance newtypeGain :: Newtype Gain _ -newtype Gain = Gain (Variant (gain :: AudioParameter)) +derive instance newtypeGain :: Newtype (Gain lock payload) _ +newtype Gain lock payload = Gain (Variant (gain :: AudioParameter lock payload)) derive instance newtypeInitializeGain :: Newtype InitializeGain _ newtype InitializeGain = InitializeGain { gain :: InitialAudioParameter } @@ -462,13 +677,13 @@ type MakeGain_ param = { id :: String, parent :: Maybe String, scope :: String, gain :: param } type MakeGain = MakeGain_ InitialAudioParameter -type MakeGain' = MakeGain_ AudioParameter +type MakeGain' lock payload = MakeGain_ (AudioParameter lock payload) -derive instance newtypeHighpass :: Newtype Highpass _ -newtype Highpass = Highpass +derive instance newtypeHighpass :: Newtype (Highpass lock payload) _ +newtype Highpass lock payload = Highpass ( Variant - ( frequency :: AudioParameter - , q :: AudioParameter + ( frequency :: AudioParameter lock payload + , q :: AudioParameter lock payload ) ) @@ -487,13 +702,13 @@ type MakeHighpass_ param = } type MakeHighpass = MakeHighpass_ InitialAudioParameter -type MakeHighpass' = MakeHighpass_ AudioParameter +type MakeHighpass' lock payload = MakeHighpass_ (AudioParameter lock payload) -derive instance newtypeHighshelf :: Newtype Highshelf _ -newtype Highshelf = Highshelf +derive instance newtypeHighshelf :: Newtype (Highshelf lock payload) _ +newtype Highshelf lock payload = Highshelf ( Variant - ( frequency :: AudioParameter - , gain :: AudioParameter + ( frequency :: AudioParameter lock payload + , gain :: AudioParameter lock payload ) ) @@ -512,7 +727,7 @@ type MakeHighshelf_ param = } type MakeHighshelf = MakeHighshelf_ InitialAudioParameter -type MakeHighshelf' = MakeHighshelf_ AudioParameter +type MakeHighshelf' lock payload = MakeHighshelf_ (AudioParameter lock payload) derive instance newtypeInitializeIIRFilter :: Newtype (InitializeIIRFilter feedforward feedback) _ newtype InitializeIIRFilter (feedforward :: Type) (feedback :: Type) = InitializeIIRFilter @@ -526,12 +741,12 @@ type MakeIIRFilter = , feedback :: Array Number } -derive instance newtypeLoopBuf :: Newtype LoopBuf _ -newtype LoopBuf = LoopBuf +derive instance newtypeLoopBuf :: Newtype (LoopBuf lock payload) _ +newtype LoopBuf lock payload = LoopBuf ( Variant ( buffer :: BrowserAudioBuffer , onOff :: AudioOnOff - , playbackRate :: AudioParameter + , playbackRate :: AudioParameter lock payload , loopStart :: Number , loopEnd :: Number ) @@ -559,13 +774,13 @@ type MakeLoopBuf_ param = ) type MakeLoopBuf = { | MakeLoopBuf_ InitialAudioParameter } -type MakeLoopBuf' = { onOff :: AudioOnOff | MakeLoopBuf_ AudioParameter } +type MakeLoopBuf' lock payload = { onOff :: AudioOnOff | MakeLoopBuf_ (AudioParameter lock payload) } -derive instance newtypeLowpass :: Newtype Lowpass _ -newtype Lowpass = Lowpass +derive instance newtypeLowpass :: Newtype (Lowpass lock payload) _ +newtype Lowpass lock payload = Lowpass ( Variant - ( frequency :: AudioParameter - , q :: AudioParameter + ( frequency :: AudioParameter lock payload + , q :: AudioParameter lock payload ) ) @@ -584,13 +799,13 @@ type MakeLowpass_ param = } type MakeLowpass = MakeLowpass_ InitialAudioParameter -type MakeLowpass' = MakeLowpass_ AudioParameter +type MakeLowpass' lock payload = MakeLowpass_ (AudioParameter lock payload) -derive instance newtypeLowshelf :: Newtype Lowshelf _ -newtype Lowshelf = Lowshelf +derive instance newtypeLowshelf :: Newtype (Lowshelf lock payload) _ +newtype Lowshelf lock payload = Lowshelf ( Variant - ( frequency :: AudioParameter - , gain :: AudioParameter + ( frequency :: AudioParameter lock payload + , gain :: AudioParameter lock payload ) ) @@ -609,7 +824,7 @@ type MakeLowshelf_ param = } type MakeLowshelf = MakeLowshelf_ InitialAudioParameter -type MakeLowshelf' = MakeLowshelf_ AudioParameter +type MakeLowshelf' lock payload = MakeLowshelf_ (AudioParameter lock payload) derive instance newtypeInitializeMediaElement :: Newtype InitializeMediaElement _ @@ -637,11 +852,11 @@ type MakeMicrophone = , scope :: String } -derive instance newtypeNotch :: Newtype Notch _ -newtype Notch = Notch +derive instance newtypeNotch :: Newtype (Notch lock payload) _ +newtype Notch lock payload = Notch ( Variant - ( frequency :: AudioParameter - , q :: AudioParameter + ( frequency :: AudioParameter lock payload + , q :: AudioParameter lock payload ) ) @@ -660,14 +875,14 @@ type MakeNotch_ param = } type MakeNotch = MakeNotch_ InitialAudioParameter -type MakeNotch' = MakeNotch_ AudioParameter +type MakeNotch' lock payload = MakeNotch_ (AudioParameter lock payload) -derive instance newtypePeaking :: Newtype Peaking _ -newtype Peaking = Peaking +derive instance newtypePeaking :: Newtype (Peaking lock payload) _ +newtype Peaking lock payload = Peaking ( Variant - ( frequency :: AudioParameter - , q :: AudioParameter - , gain :: AudioParameter + ( frequency :: AudioParameter lock payload + , q :: AudioParameter lock payload + , gain :: AudioParameter lock payload ) ) @@ -688,15 +903,15 @@ type MakePeaking_ param = } type MakePeaking = MakePeaking_ InitialAudioParameter -type MakePeaking' = MakePeaking_ AudioParameter +type MakePeaking' lock payload = MakePeaking_ (AudioParameter lock payload) -derive instance newtypePeriodicOsc :: Newtype PeriodicOsc _ -newtype PeriodicOsc = +derive instance newtypePeriodicOsc :: Newtype (PeriodicOsc lock payload) _ +newtype PeriodicOsc lock payload = PeriodicOsc ( Variant ( spec :: PeriodicOscSpec , onOff :: AudioOnOff - , frequency :: AudioParameter + , frequency :: AudioParameter lock payload ) ) @@ -715,17 +930,17 @@ type MakePeriodicOsc_ param = ) type MakePeriodicOsc = { | MakePeriodicOsc_ InitialAudioParameter } -type MakePeriodicOsc' = - { onOff :: AudioOnOff | MakePeriodicOsc_ AudioParameter } +type MakePeriodicOsc' lock payload = + { onOff :: AudioOnOff | MakePeriodicOsc_ (AudioParameter lock payload) } -derive instance newtypePlayBuf :: Newtype PlayBuf _ -newtype PlayBuf = +derive instance newtypePlayBuf :: Newtype (PlayBuf lock payload) _ +newtype PlayBuf lock payload = PlayBuf ( Variant ( buffer :: BrowserAudioBuffer , bufferOffset :: Number , onOff :: AudioOnOff - , playbackRate :: AudioParameter + , playbackRate :: AudioParameter lock payload , duration :: Maybe Number ) ) @@ -749,7 +964,7 @@ type MakePlayBuf_ param = ) type MakePlayBuf = { | MakePlayBuf_ InitialAudioParameter } -type MakePlayBuf' = { onOff :: AudioOnOff | MakePlayBuf_ AudioParameter } +type MakePlayBuf' lock payload = { onOff :: AudioOnOff | MakePlayBuf_ (AudioParameter lock payload) } derive instance newtypeInitializeRecorder :: Newtype InitializeRecorder _ newtype InitializeRecorder = InitializeRecorder { cb :: MediaRecorderCb } @@ -762,12 +977,12 @@ type MakeRecorder = type MakeSpeaker = { id :: String } -derive instance newtypeSawtoothOsc :: Newtype SawtoothOsc _ -newtype SawtoothOsc = +derive instance newtypeSawtoothOsc :: Newtype (SawtoothOsc lock payload) _ +newtype SawtoothOsc lock payload = SawtoothOsc ( Variant ( onOff :: AudioOnOff - , frequency :: AudioParameter + , frequency :: AudioParameter lock payload ) ) @@ -784,12 +999,12 @@ type MakeSawtoothOsc_ param = ) type MakeSawtoothOsc = { | MakeSawtoothOsc_ InitialAudioParameter } -type MakeSawtoothOsc' = - { onOff :: AudioOnOff | MakeSawtoothOsc_ AudioParameter } +type MakeSawtoothOsc' lock payload = + { onOff :: AudioOnOff | MakeSawtoothOsc_ (AudioParameter lock payload) } -derive instance newtypeSinOsc :: Newtype SinOsc _ -newtype SinOsc = SinOsc - (Variant (frequency :: AudioParameter, onOff :: AudioOnOff)) +derive instance newtypeSinOsc :: Newtype (SinOsc lock payload) _ +newtype SinOsc lock payload = SinOsc + (Variant (frequency :: AudioParameter lock payload, onOff :: AudioOnOff)) derive instance newtypeInitializeSinOsc :: Newtype InitializeSinOsc _ newtype InitializeSinOsc = InitializeSinOsc @@ -804,14 +1019,14 @@ type MakeSinOsc_ param = ) type MakeSinOsc = { | MakeSinOsc_ InitialAudioParameter } -type MakeSinOsc' = { onOff :: AudioOnOff | MakeSinOsc_ AudioParameter } +type MakeSinOsc' lock payload = { onOff :: AudioOnOff | MakeSinOsc_ (AudioParameter lock payload) } -derive instance newtypeSquareOsc :: Newtype SquareOsc _ -newtype SquareOsc = +derive instance newtypeSquareOsc :: Newtype (SquareOsc lock payload) _ +newtype SquareOsc lock payload = SquareOsc ( Variant ( onOff :: AudioOnOff - , frequency :: AudioParameter + , frequency :: AudioParameter lock payload ) ) @@ -828,11 +1043,11 @@ type MakeSquareOsc_ param = ) type MakeSquareOsc = { | MakeSquareOsc_ InitialAudioParameter } -type MakeSquareOsc' = { onOff :: AudioOnOff | MakeSquareOsc_ AudioParameter } +type MakeSquareOsc' lock payload = { onOff :: AudioOnOff | MakeSquareOsc_ (AudioParameter lock payload) } -derive instance newtypeStereoPanner :: Newtype StereoPanner _ -newtype StereoPanner = - StereoPanner (Variant (pan :: AudioParameter)) +derive instance newtypeStereoPanner :: Newtype (StereoPanner lock payload) _ +newtype StereoPanner lock payload = + StereoPanner (Variant (pan :: AudioParameter lock payload)) derive instance newtypeInitializeStereoPanner :: Newtype InitializeStereoPanner _ @@ -844,14 +1059,14 @@ type MakeStereoPanner_ param = { id :: String, parent :: Maybe String, scope :: String, pan :: param } type MakeStereoPanner = MakeStereoPanner_ InitialAudioParameter -type MakeStereoPanner' = MakeStereoPanner_ AudioParameter +type MakeStereoPanner' lock payload = MakeStereoPanner_ (AudioParameter lock payload) -derive instance newtypeTriangleOsc :: Newtype TriangleOsc _ -newtype TriangleOsc = +derive instance newtypeTriangleOsc :: Newtype (TriangleOsc lock parameter) _ +newtype TriangleOsc lock parameter = TriangleOsc ( Variant ( onOff :: AudioOnOff - , frequency :: AudioParameter + , frequency :: AudioParameter lock parameter ) ) @@ -868,8 +1083,8 @@ type MakeTriangleOsc_ param = ) type MakeTriangleOsc = { | MakeTriangleOsc_ InitialAudioParameter } -type MakeTriangleOsc' = - { onOff :: AudioOnOff | MakeTriangleOsc_ AudioParameter } +type MakeTriangleOsc' lock payload = + { onOff :: AudioOnOff | MakeTriangleOsc_ (AudioParameter lock payload) } derive instance newtypeInitializeWaveShaper :: Newtype InitializeWaveShaper _ newtype InitializeWaveShaper = InitializeWaveShaper @@ -888,7 +1103,7 @@ type MakeWaveShaper = type SetAnalyserNodeCb = { id :: String, cb :: AnalyserNodeCb } type SetMediaRecorderCb = { id :: String, cb :: MediaRecorderCb } type SetAudioWorkletParameter = - { id :: String, paramName :: String, paramValue :: AudioParameter } + { id :: String, paramName :: String, paramValue :: FFIAudioParameter } type SetBuffer = { id :: String, buffer :: BrowserAudioBuffer } type SetConvolverBuffer = { id :: String, buffer :: BrowserAudioBuffer } @@ -898,18 +1113,18 @@ type SetBufferOffset = { id :: String, bufferOffset :: Number } type SetDuration = { id :: String, duration :: Maybe Number } type SetLoopStart = { id :: String, loopStart :: Number } type SetLoopEnd = { id :: String, loopEnd :: Number } -type SetRatio = { id :: String, ratio :: AudioParameter } -type SetOffset = { id :: String, offset :: AudioParameter } -type SetAttack = { id :: String, attack :: AudioParameter } -type SetGain = { id :: String, gain :: AudioParameter } -type SetQ = { id :: String, q :: AudioParameter } -type SetPan = { id :: String, pan :: AudioParameter } -type SetThreshold = { id :: String, threshold :: AudioParameter } -type SetRelease = { id :: String, release :: AudioParameter } -type SetKnee = { id :: String, knee :: AudioParameter } -type SetDelay = { id :: String, delayTime :: AudioParameter } -type SetPlaybackRate = { id :: String, playbackRate :: AudioParameter } -type SetFrequency = { id :: String, frequency :: AudioParameter } +type SetRatio = { id :: String, ratio :: FFIAudioParameter } +type SetOffset = { id :: String, offset :: FFIAudioParameter } +type SetAttack = { id :: String, attack :: FFIAudioParameter } +type SetGain = { id :: String, gain :: FFIAudioParameter } +type SetQ = { id :: String, q :: FFIAudioParameter } +type SetPan = { id :: String, pan :: FFIAudioParameter } +type SetThreshold = { id :: String, threshold :: FFIAudioParameter } +type SetRelease = { id :: String, release :: FFIAudioParameter } +type SetKnee = { id :: String, knee :: FFIAudioParameter } +type SetDelay = { id :: String, delayTime :: FFIAudioParameter } +type SetPlaybackRate = { id :: String, playbackRate :: FFIAudioParameter } +type SetFrequency = { id :: String, frequency :: FFIAudioParameter } type SetWaveShaperCurve = { id :: String, curve :: BrowserFloatArray } newtype AudioInterpret payload = AudioInterpret @@ -982,16 +1197,18 @@ data Stage = Begin | Middle | End __internalWagsFlatten :: forall outputChannels lock payload - . String + . Maybe String + -> String -> AudioInterpret payload -> Audible outputChannels lock payload -> Event payload __internalWagsFlatten parent + pScope di@(AudioInterpret { ids, disconnectXFromY }) (Audible children') = children' # match { fixedChannels: \(FixedChannels f) -> oneOfMap node f - , eventfulNode: \(EventfulNode e) -> keepLatest (map (__internalWagsFlatten parent di) e) + , eventfulNode: \(EventfulNode e) -> keepLatest (map (__internalWagsFlatten parent pScope di) e) , node , dynamicChannels: \(DynamicChannels children) -> makeEvent \k -> do cancelInner <- Ref.new Object.empty @@ -1016,9 +1233,9 @@ __internalWagsFlatten let mic = ( Ref.read myId >>= traverse_ \old -> - k + for_ parent \p' -> k ( disconnectXFromY - { from: old, to: parent } + { from: old, to: p' } ) ) *> join (Ref.read myUnsub) *> join (Ref.read eltsUnsub) @@ -1065,7 +1282,7 @@ __internalWagsFlatten where node (Node e) = e { parent - , scope: "trivial" + , scope: pScope , raiseId: mempty } di \ No newline at end of file diff --git a/src/WAGS/Imperative/Create.purs b/src/WAGS/Imperative/Create.purs index b4d103fb..dd5c8bf2 100644 --- a/src/WAGS/Imperative/Create.purs +++ b/src/WAGS/Imperative/Create.purs @@ -12,7 +12,7 @@ import Control.Alternative ((<|>)) import Data.Newtype (unwrap) import Data.Variant (match) import Data.Variant.Maybe (nothing) -import FRP.Event (Event) +import FRP.Event (Event, keepLatest) import FRP.Event.Class (bang) import Prim.Boolean (True, False) import Prim.Row as Row @@ -84,18 +84,18 @@ speaker _ = GraphBuilder go -- | gain <- Create.gain (Proxy :: _ "gain") 1.0 empty -- | ``` gain - :: forall p i o id initialGain + :: forall l p i o id initialGain . IsSymbol id => Common.InitialGain initialGain => CreateNode i id False o => Proxy id -> initialGain - -> Event Core.Gain + -> Event (Core.Gain l p) -> GraphBuilder p i o (T.GraphUnit id T.Gain) gain _ initialGain attributes = GraphBuilder go where initializeGain = unwrap $ Common.toInitializeGain initialGain - go (Core.AudioInterpret { makeGain, setGain }) = + go di@(Core.AudioInterpret { makeGain, setGain }) = { event: let id = reflectSymbol (Proxy :: _ id) @@ -106,9 +106,9 @@ gain _ initialGain attributes = GraphBuilder go , gain: initializeGain.gain , scope: "imperative" } - eventN = attributes <#> unwrap >>> match - { gain: setGain <<< { id, gain: _ } - } + eventN = keepLatest (attributes <#> unwrap >>> match + { gain: Common.resolveAU di (setGain <<< { id, gain: _ }) + }) in event0 <|> eventN , result: T.GraphUnit @@ -120,27 +120,27 @@ gain _ initialGain attributes = GraphBuilder go -- | sinOsc <- Create.sinOsc (Proxy :: _ "sinOsc") 440.0 bangOn -- | ``` sinOsc - :: forall p i o id initialSinOsc + :: forall l p i o id initialSinOsc . IsSymbol id => Common.InitialSinOsc initialSinOsc => CreateNode i id False o => Proxy id -> initialSinOsc - -> Event Core.SinOsc + -> Event (Core.SinOsc l p) -> GraphBuilder p i o (T.GraphUnit id T.SinOsc) sinOsc _ initialSinOsc attributes = GraphBuilder go where { frequency } = unwrap $ Common.toInitializeSinOsc initialSinOsc - go (Core.AudioInterpret { makeSinOsc, setFrequency, setOnOff }) = + go di@(Core.AudioInterpret { makeSinOsc, setFrequency, setOnOff }) = { event: let id = reflectSymbol (Proxy :: _ id) event0 = bang $ makeSinOsc { id, parent: nothing, frequency, scope: "imperative" } - eventN = attributes <#> unwrap >>> match - { frequency: setFrequency <<< { id, frequency: _ } - , onOff: setOnOff <<< { id, onOff: _ } - } + eventN = keepLatest (attributes <#> unwrap >>> match + { frequency: Common.resolveAU di (setFrequency <<< { id, frequency: _ }) + , onOff: bang <<< setOnOff <<< { id, onOff: _ } + }) in event0 <|> eventN , result: T.GraphUnit @@ -152,20 +152,20 @@ sinOsc _ initialSinOsc attributes = GraphBuilder go -- | playBuf <- Create.playBuf (Proxy :: _ "playBuf") audioBuffer bangOn -- | ``` playBuf - :: forall p i o id initialPlayBuf + :: forall l p i o id initialPlayBuf . IsSymbol id => Common.InitialPlayBuf initialPlayBuf => CreateNode i id False o => Proxy id -> initialPlayBuf - -> Event Core.PlayBuf + -> Event (Core.PlayBuf l p) -> GraphBuilder p i o (T.GraphUnit id T.PlayBuf) playBuf _ initialPlayBuf attributes = GraphBuilder go where { buffer, playbackRate, bufferOffset, duration } = unwrap $ Common.toInitializePlayBuf initialPlayBuf go - ( Core.AudioInterpret + di@( Core.AudioInterpret { makePlayBuf , setBuffer , setOnOff @@ -186,13 +186,13 @@ playBuf _ initialPlayBuf attributes = GraphBuilder go , duration , scope: "imperative" } - eventN = attributes <#> unwrap >>> match - { buffer: setBuffer <<< { id, buffer: _ } - , playbackRate: setPlaybackRate <<< { id, playbackRate: _ } - , bufferOffset: setBufferOffset <<< { id, bufferOffset: _ } - , duration: setDuration <<< { id, duration: _ } - , onOff: setOnOff <<< { id, onOff: _ } - } + eventN = keepLatest (attributes <#> unwrap >>> match + { buffer: bang <<< setBuffer <<< { id, buffer: _ } + , playbackRate: Common.resolveAU di (setPlaybackRate <<< { id, playbackRate: _ }) + , bufferOffset: bang <<< setBufferOffset <<< { id, bufferOffset: _ } + , duration: bang <<< setDuration <<< { id, duration: _ } + , onOff: bang <<< setOnOff <<< { id, onOff: _ } + }) in event0 <|> eventN , result: T.GraphUnit diff --git a/src/WAGS/Interpret.js b/src/WAGS/Interpret.js index fb80a72f..79bbce98 100644 --- a/src/WAGS/Interpret.js +++ b/src/WAGS/Interpret.js @@ -10,17 +10,21 @@ var makeid = function (length) { }; var NUMERIC = "numeric"; var SUDDEN = "sudden"; -var CANCELLATION = "cancellation"; +var UNIT = "unit"; +var CANCEL = "cancel"; var NO_RAMP = "step"; var LINEAR_RAMP = "linear"; var EXPONENTIAL_RAMP = "exponential"; var ENVELOPE = "envelope"; -var isCancellation = function (a) { - return a.type === CANCELLATION; -}; -var protoSetter = function (thingee, deprecatedTimeToSet, param) { +var protoSetter = function (thingee, ctrl, param, state) { if (param.type === SUDDEN) { thingee.value = param.value.n; + } else if (param.type === UNIT) { + if (ctrl.id) { + disconnectCtrl(ctrl.id, state); + } + state.units[param.value.i].main.connect(thingee); + ctrl.id = param.value.i; } else { if (param.type === NUMERIC) { thingee[ @@ -31,14 +35,14 @@ var protoSetter = function (thingee, deprecatedTimeToSet, param) { : param.value.t.type === EXPONENTIAL_RAMP ? "exponentialRampToValueAtTime" : "linearRampToValueAtTime" - ](param.value.n, deprecatedTimeToSet + param.value.o); - } else if (isCancellation(param)) { + ](param.value.n, param.value.o); + } else if (param.type === CANCEL) { param.value.hold - ? thingee.cancelAndHoldAtTime(deprecatedTimeToSet + param.value.o) - : thingee.cancelScheduledValues(deprecatedTimeToSet + param.value.o); + ? thingee.cancelAndHoldAtTime(param.value.o) + : thingee.cancelScheduledValues(param.value.o); } else if (param.type === ENVELOPE) { // envelope is last option - const tm = deprecatedTimeToSet + param.value.o; + const tm = param.value.o; thingee.cancelScheduledValues(Math.max(0.0, tm)); thingee.setValueCurveAtTime(param.value.p, tm, param.value.d); } else { @@ -46,15 +50,22 @@ var protoSetter = function (thingee, deprecatedTimeToSet, param) { } } }; -var workletSetter = function (unit, paramName, deprecatedTimeToSet, param) { +var workletSetter = function (state, unit, paramName, controllers, param) { + if (!controllers[paramName]) { + controllers[paramName] = {}; + } return protoSetter( unit.parameters.get(paramName), - deprecatedTimeToSet, - param + controllers[paramName], + param, + state ); }; -var genericSetter = function (unit, name, deprecatedTimeToSet, param) { - return protoSetter(unit[name], deprecatedTimeToSet, param); +var genericSetter = function (state, unit, name, controllers, param) { + if (!controllers[name]) { + controllers[name] = {}; + } + return protoSetter(unit[name], controllers[name], param, state); }; var addToScope = function (ptr, scope, state) { if (!state.scopes[scope.value]) { @@ -89,8 +100,7 @@ var mConnectXToY_ = function (x, y, state) { }; var connectXToYInternal_ = function (x, y, state) { var connectF = function () { - state.units[x].outgoing.push(y); - state.units[y].incoming.push(x); + state.units[x].audioOutgoing.push(y); if (!state.units[x].pendingOn) { state.units[x].main.connect(state.units[y].main); if (state.units[y].se) { @@ -139,37 +149,39 @@ export function connectXToY_(parameters) { }; } -var disconnectXFromY_ = function (x) { - return function (y) { - return function (state) { - return function () { - state.units[x].outgoing = state.units[x].outgoing.filter(function (i) { - return !(i === y); - }); - state.units[y].incoming = state.units[y].incoming.filter(function (i) { - return !(i === x); - }); - state.units[x].main.disconnect(state.units[y].main); - if (state.units[y].se) { - state.units[x].main.disconnect(state.units[y].se); - } - if (state.units[ptr].scope === "@fan@") { - return; - } - const scope = state.units[ptr].scope; - state.scopes[scope].forEach((scp) => { - delete state.units[scp]; - }); - delete state.scopes[scope]; - }; - }; - }; +var disconnectCtrl = function (ptr, state) { + if (state.units[ptr].scope === "@fan@") { + return; + } + const scope = state.units[ptr].scope; + state.scopes[scope].forEach((scp) => { + delete state.units[scp]; + }); + delete state.scopes[scope]; }; export function disconnectXFromY_(a) { return function (state) { return function () { - return disconnectXFromY_(a.from)(a.to)(state)(); + var x = a.from; + var y = a.to; + state.units[x].audioOutgoing = state.units[x].audioOutgoing.filter( + function (i) { + return !(i === y); + } + ); + state.units[x].main.disconnect(state.units[y].main); + if (state.units[y].se) { + state.units[x].main.disconnect(state.units[y].se); + } + if (state.units[ptr].scope === "@fan@") { + return; + } + const scope = state.units[ptr].scope; + state.scopes[scope].forEach((scp) => { + delete state.units[scp]; + }); + delete state.scopes[scope]; }; }; } @@ -180,8 +192,9 @@ export function makeAllpass_(a) { return function () { var ptr = a.id; state.units[ptr] = { - outgoing: [a.parent], - incoming: [], + controllers: {}, + audioOutgoing: [], + controlOutgoing: [], main: new BiquadFilterNode(state.context, { type: "allpass", Q: a.q, @@ -205,8 +218,9 @@ export function makeAnalyser_(a) { // unsubscribe is effect unit var unsubscribe = analyserSideEffectFunction(dest)(); state.units[ptr] = { - outgoing: [], - incoming: [], + controllers: {}, + audioOutgoing: [], + controlOutgoing: [], analyserOrig: analyserSideEffectFunction, analyser: unsubscribe, main: state.context.createGain(), @@ -226,8 +240,9 @@ export function makeAudioWorkletNode_(a) { var ptr = a.id; var opts = a.options; state.units[ptr] = { - outgoing: [], - incoming: [], + controllers: {}, + audioOutgoing: [], + controlOutgoing: [], main: new AudioWorkletNode(state.context, opts.name, { numberOfInputs: opts.numberOfInputs, numberOfOutputs: opts.numberOfOutputs, @@ -249,8 +264,9 @@ export function makeBandpass_(a) { return function () { var ptr = a.id; state.units[ptr] = { - outgoing: [a.parent], - incoming: [], + controllers: {}, + audioOutgoing: [], + controlOutgoing: [], main: new BiquadFilterNode(state.context, { type: "bandpass", Q: a.q, @@ -275,9 +291,9 @@ export function makeConstant_(a) { }; var resume = { offset: a.offset }; state.units[ptr] = { - //outgoing: [a.parent], - outgoing: [], - incoming: [], + controllers: {}, + audioOutgoing: [], + controlOutgoing: [], resume: resume, createClosure: createClosure, onOff: false, @@ -303,8 +319,9 @@ export function makeConvolver_(a) { return function () { var ptr = a.id; state.units[ptr] = { - outgoing: [], - incoming: [], + controllers: {}, + audioOutgoing: [], + controlOutgoing: [], main: new ConvolverNode(state.context, { buffer: a.buffer }), }; addToScope(ptr, a.scope, state); @@ -320,8 +337,9 @@ export function makeDelay_(a) { return function () { var ptr = a.id; state.units[ptr] = { - outgoing: [a.parent], - incoming: [], + controllers: {}, + audioOutgoing: [], + controlOutgoing: [], main: new DelayNode(state.context, { delayTime: a.delayTime, maxDelayTime: a.maxDelayTime, @@ -340,8 +358,9 @@ export function makeDynamicsCompressor_(a) { return function () { var ptr = a.id; state.units[ptr] = { - outgoing: [a.parent], - incoming: [], + controllers: {}, + audioOutgoing: [], + controlOutgoing: [], main: new DynamicsCompressorNode(state.context, { knee: a.knee, ratio: a.ratio, @@ -363,8 +382,9 @@ var makeGain_ = function (a) { return function () { var ptr = a.id; state.units[ptr] = { - outgoing: [a.parent], - incoming: [], + controllers: {}, + audioOutgoing: [], + controlOutgoing: [], main: new GainNode(state.context, { gain: a.gain, }), @@ -375,7 +395,7 @@ var makeGain_ = function (a) { }; }; }; -export {makeGain_}; +export { makeGain_ }; // highpass export function makeHighpass_(a) { @@ -383,8 +403,9 @@ export function makeHighpass_(a) { return function () { var ptr = a.id; state.units[ptr] = { - outgoing: [a.parent], - incoming: [], + controllers: {}, + audioOutgoing: [], + controlOutgoing: [], main: new BiquadFilterNode(state.context, { type: "highpass", Q: a.q, @@ -404,8 +425,9 @@ export function makeHighshelf_(a) { return function () { var ptr = a.id; state.units[ptr] = { - outgoing: [a.parent], - incoming: [], + controllers: {}, + audioOutgoing: [], + controlOutgoing: [], main: new BiquadFilterNode(state.context, { type: "highshelf", frequency: a.frequency, @@ -424,8 +446,9 @@ export function makeIIRFilter_(a) { return function () { var ptr = a.id; state.units[ptr] = { - outgoing: [], - incoming: [], + controllers: {}, + audioOutgoing: [], + controlOutgoing: [], main: new IIRFilterNode(state.context, { feedforward: a.feedforward, feedback: a.feedback, @@ -455,9 +478,9 @@ export function makeLoopBuf_(a) { playbackRate: a.playbackRate, }; state.units[ptr] = { - //outgoing: [a.parent], - outgoing: [], - incoming: [], + controllers: {}, + audioOutgoing: [], + controlOutgoing: [], resume: resume, createClosure: createClosure, onOff: false, @@ -484,8 +507,9 @@ export function makeLowpass_(a) { return function () { var ptr = a.id; state.units[ptr] = { - outgoing: [a.parent], - incoming: [], + controllers: {}, + audioOutgoing: [], + controlOutgoing: [], main: new BiquadFilterNode(state.context, { type: "lowpass", Q: a.q, @@ -505,8 +529,9 @@ export function makeLowshelf_(a) { return function () { var ptr = a.id; state.units[ptr] = { - outgoing: [a.parent], - incoming: [], + controllers: {}, + audioOutgoing: [], + controlOutgoing: [], main: new BiquadFilterNode(state.context, { type: "lowshelf", frequency: a.frequency, @@ -532,8 +557,9 @@ export function makeMediaElement_(a) { return unit; }; state.units[ptr] = { - outgoing: [a.parent], - incoming: [], + controllers: {}, + audioOutgoing: [], + controlOutgoing: [], createClosure: createClosure, resumeClosure: {}, main: createClosure(), @@ -552,8 +578,9 @@ export function makeMicrophone_(a) { var ptr = a.id; state.units[a.id] = { main: state.context.createMediaStreamSource(a.microphone), - outgoing: [a.parent], - incoming: [], + controllers: {}, + audioOutgoing: [], + controlOutgoing: [], }; addToScope(ptr, a.scope, state); doDeferredConnections(ptr, state); @@ -568,8 +595,9 @@ export function makeNotch_(a) { return function () { var ptr = a.id; state.units[ptr] = { - outgoing: [a.parent], - incoming: [], + controllers: {}, + audioOutgoing: [], + controlOutgoing: [], main: new BiquadFilterNode(state.context, { type: "notch", frequency: a.frequency, @@ -589,8 +617,9 @@ export function makePeaking_(a) { return function () { var ptr = a.id; state.units[ptr] = { - outgoing: [a.parent], - incoming: [], + controllers: {}, + audioOutgoing: [], + controlOutgoing: [], main: new BiquadFilterNode(state.context, { type: "peaking", frequency: a.frequency, @@ -626,9 +655,9 @@ export function makePeriodicOsc_(a) { }; var resume = { frequency: a.frequency, type: "custom", spec: a.spec }; state.units[ptr] = { - //outgoing: [a.parent], - outgoing: [], - incoming: [], + controllers: {}, + audioOutgoing: [], + controlOutgoing: [], resume: resume, createClosure: createClosure, onOff: false, @@ -671,9 +700,9 @@ export function makePlayBuf_(a) { duration: a.duration, }; state.units[ptr] = { - //outgoing: [a.parent], - outgoing: [], - incoming: [], + controllers: {}, + audioOutgoing: [], + controlOutgoing: [], resume: resume, createClosure: createClosure, onOff: false, @@ -707,8 +736,9 @@ export function makeRecorder_(a) { mediaRecorderSideEffectFn(mediaRecorder)(); mediaRecorder.start(); state.units[ptr] = { - outgoing: [], - incoming: [], + controllers: {}, + audioOutgoing: [], + controlOutgoing: [], recorderOrig: mediaRecorderSideEffectFn, recorder: mediaRecorder, main: state.context.createGain(), @@ -732,9 +762,9 @@ export function makeSawtoothOsc_(a) { }; var resume = { frequency: a.frequency, type: "sawtooth" }; state.units[ptr] = { - //outgoing: [a.parent], - outgoing: [], - incoming: [], + controllers: {}, + audioOutgoing: [], + controlOutgoing: [], resume: resume, createClosure: createClosure, onOff: false, @@ -766,9 +796,9 @@ export function makeSinOsc_(a) { }; var resume = { frequency: a.frequency, type: "sine" }; state.units[ptr] = { - //outgoing: [a.parent], - outgoing: [], - incoming: [], + controllers: {}, + audioOutgoing: [], + controlOutgoing: [], resume: resume, createClosure: createClosure, onOff: false, @@ -794,8 +824,9 @@ export function makeSpeaker_(a) { return function (state) { return function () { state.units[a.id] = { - outgoing: [], - incoming: [], + controllers: {}, + audioOutgoing: [], + controlOutgoing: [], main: state.context.createGain(), se: state.context.destination, }; @@ -809,8 +840,9 @@ export function makeStereoPanner_(a) { return function () { var ptr = a.id; state.units[ptr] = { - outgoing: [a.parent], - incoming: [], + controllers: {}, + audioOutgoing: [], + controlOutgoing: [], main: new StereoPannerNode(state.context, { pan: a.pan, }), @@ -833,9 +865,9 @@ export function makeSquareOsc_(a) { }; var resume = { frequency: a.frequency, type: "square" }; state.units[ptr] = { - //outgoing: [a.parent], - outgoing: [], - incoming: [], + controllers: {}, + audioOutgoing: [], + controlOutgoing: [], resume: resume, createClosure: createClosure, onOff: false, @@ -867,9 +899,9 @@ export function makeTriangleOsc_(a) { }; var resume = { frequency: a.frequency, type: "triangle" }; state.units[ptr] = { - //outgoing: [a.parent], - outgoing: [], - incoming: [], + controllers: {}, + audioOutgoing: [], + controlOutgoing: [], resume: resume, createClosure: createClosure, onOff: false, @@ -898,8 +930,9 @@ export function makeWaveShaper_(aa) { var a = aa.curve; var b = aa.oversample; state.units[ptr] = { - outgoing: [a.parent], - incoming: [], + controllers: {}, + audioOutgoing: [], + controlOutgoing: [], main: new WaveShaperNode(state.context, { curve: a, oversample: b.type, @@ -968,7 +1001,13 @@ export function setAudioWorkletParameter_(aa) { var ptr = aa.id; var a = aa.paramName; var b = aa.paramValue; - workletSetter(state.units[ptr].main, a, state.deprecatedWriteHead, b); + workletSetter( + state, + state.units[ptr].main, + a, + state.units[ptr].controllers, + b + ); }; }; } @@ -987,9 +1026,10 @@ export function setGain_(aa) { var ptr = aa.id; var a = aa.gain; genericSetter( + state, state.units[ptr].main, "gain", - state.deprecatedWriteHead, + state.units[ptr].controllers, a ); recalcResume(a, state.units[ptr], "gain"); @@ -1002,7 +1042,13 @@ export function setQ_(aa) { return function () { var ptr = aa.id; var a = aa.q; - genericSetter(state.units[ptr].main, "Q", state.deprecatedWriteHead, a); + genericSetter( + state, + state.units[ptr].main, + "Q", + state.units[ptr].controllers, + a + ); recalcResume(a, state.units[ptr], "Q"); }; }; @@ -1047,7 +1093,13 @@ export function setPan_(aa) { return function () { var ptr = aa.id; var a = aa.pan; - genericSetter(state.units[ptr].main, "pan", state.deprecatedWriteHead, a); + genericSetter( + state, + state.units[ptr].main, + "pan", + state.units[ptr].controllers, + a + ); recalcResume(a, state.units[ptr], "pan"); }; }; @@ -1059,9 +1111,10 @@ export function setThreshold_(aa) { var ptr = aa.id; var a = aa.threshold; genericSetter( + state, state.units[ptr].main, "threshold", - state.deprecatedWriteHead, + state.units[ptr].controllers, a ); recalcResume(a, state.units[ptr], "threshold"); @@ -1117,9 +1170,10 @@ export function setRelease_(aa) { var ptr = aa.id; var a = aa.release; genericSetter( + state, state.units[ptr].main, "release", - state.deprecatedWriteHead, + state.units[ptr].controllers, a ); recalcResume(a, state.units[ptr], "release"); @@ -1133,9 +1187,10 @@ export function setOffset_(aa) { var ptr = aa.id; var a = aa.offset; genericSetter( + state, state.units[ptr].main, "offset", - state.deprecatedWriteHead, + state.units[ptr].controllers, a ); recalcResume(a, state.units[ptr], "offset"); @@ -1149,9 +1204,10 @@ export function setRatio_(aa) { var ptr = aa.id; var a = aa.ratio; genericSetter( + state, state.units[ptr].main, "ratio", - state.deprecatedWriteHead, + state.units[ptr].controllers, a ); recalcResume(a, state.units[ptr], "ratio"); @@ -1165,9 +1221,10 @@ export function setAttack_(aa) { var ptr = aa.id; var a = aa.attack; genericSetter( + state, state.units[ptr].main, "attack", - state.deprecatedWriteHead, + state.units[ptr].controllers, a ); recalcResume(a, state.units[ptr], "attack"); @@ -1181,9 +1238,10 @@ export function setKnee_(aa) { var ptr = aa.id; var a = aa.knee; genericSetter( + state, state.units[ptr].main, "knee", - state.deprecatedWriteHead, + state.units[ptr].controllers, a ); recalcResume(a, state.units[ptr], "knee"); @@ -1197,9 +1255,10 @@ export function setDelay_(aa) { var ptr = aa.id; var a = aa.delayTime; genericSetter( + state, state.units[ptr].main, "delayTime", - state.deprecatedWriteHead, + state.units[ptr].controllers, a ); recalcResume(a, state.units[ptr], "delayTime"); @@ -1213,9 +1272,10 @@ export function setPlaybackRate_(aa) { var ptr = aa.id; var a = aa.playbackRate; genericSetter( + state, state.units[ptr].main, "playbackRate", - state.deprecatedWriteHead, + state.units[ptr].controllers, a ); recalcResume(a, state.units[ptr], "playbackRate"); @@ -1229,9 +1289,10 @@ export function setFrequency_(aa) { var ptr = aa.id; var a = aa.frequency; genericSetter( + state, state.units[ptr].main, "frequency", - state.deprecatedWriteHead, + state.units[ptr].controllers, a ); recalcResume(a, state.units[ptr], "frequency"); @@ -1267,8 +1328,8 @@ var setOn_ = function (ptr) { state.context, state.units[ptr].resume ); - for (var i = 0; i < state.units[ptr].outgoing.length; i++) { - var ogi = state.units[ptr].outgoing[i]; + for (var i = 0; i < state.units[ptr].audioOutgoing.length; i++) { + var ogi = state.units[ptr].audioOutgoing[i]; state.units[ptr].main.connect(state.units[ogi].main); if (state.units[ogi].se) { state.units[ptr].main.connect(state.units[ogi].se); @@ -1312,7 +1373,6 @@ var setOff_ = function (ptr) { } state.units[ptr].onOff = false; var oldMain = state.units[ptr].main; - var oldOutgoing = state.units[ptr].outgoing.slice(); // defer disconnection until stop has happened oldMain.addEventListener("ended", () => { oldMain.disconnect(); @@ -1531,7 +1591,7 @@ var makePeriodicWaveImpl = function (ctx) { }; }; }; -export {makePeriodicWaveImpl}; +export { makePeriodicWaveImpl }; export function makeFFIAudioSnapshot(audioCtx) { return function () { diff --git a/src/WAGS/Interpret.purs b/src/WAGS/Interpret.purs index f0c8990c..ac123270 100644 --- a/src/WAGS/Interpret.purs +++ b/src/WAGS/Interpret.purs @@ -19,7 +19,6 @@ import Type.Row.Homogeneous (class Homogeneous) import Unsafe.Coerce (unsafeCoerce) import WAGS.Control (class ValidateOutputChannelCount) import WAGS.Core as C -import WAGS.Parameter (AudioParameter) import WAGS.WebAPI (AudioContext, BrowserAudioBuffer) import WAGS.WebAPI as WebAPI import Web.File.Blob (Blob) @@ -177,13 +176,13 @@ foreign import getBrowserMediaStreamImpl data Audio audioWorkletAddModule - :: forall node numberOfInputs numberOfOutputs outputChannelCount parameterData + :: forall node lock payload numberOfInputs numberOfOutputs outputChannelCount parameterData processorOptions . IsSymbol node => Nat numberOfInputs => Pos numberOfOutputs => ValidateOutputChannelCount numberOfOutputs outputChannelCount - => Homogeneous parameterData AudioParameter + => Homogeneous parameterData (C.AudioParameter lock payload) => JSON.WriteForeign { | processorOptions } => WebAPI.AudioContext -> String diff --git a/src/WAGS/Parameter.purs b/src/WAGS/Parameter.purs deleted file mode 100644 index a34058e0..00000000 --- a/src/WAGS/Parameter.purs +++ /dev/null @@ -1,213 +0,0 @@ -module WAGS.Parameter where - -import Prelude - -import Data.Generic.Rep (class Generic) -import Data.Lens (Optic', over) -import Data.Lens.Iso.Newtype (_Newtype, unto) -import Data.Lens.Record (prop) -import Data.Newtype (class Newtype, unwrap, wrap) -import Data.Profunctor.Strong (class Strong) -import Data.Variant (Variant, inj, match) -import FRP.Event (Event) -import FRP.Event.Class (bang) -import Prim.Row (class Cons) -import Type.Proxy (Proxy(..)) - -newtype Transition = Transition - (Variant (linear :: Unit, exponential :: Unit, step :: Unit)) - -_linear :: Transition -_linear = Transition $ inj (Proxy :: _ "linear") unit - -_exponential :: Transition -_exponential = Transition $ inj (Proxy :: _ "exponential") unit - -_step :: Transition -_step = Transition $ inj (Proxy :: _ "step") unit - -derive instance eqTransition :: Eq Transition -derive instance ordTransition :: Ord Transition -derive instance newtypeTransition :: Newtype Transition _ -derive newtype instance showTransition :: Show Transition - -_numeric' :: AudioNumeric' -> AudioParameter -_numeric' = _numeric <<< AudioNumeric - -_numeric :: AudioNumeric -> AudioParameter -_numeric = AudioParameter <<< inj (Proxy :: _ "numeric") - -_envelope :: AudioEnvelope -> AudioParameter -_envelope = AudioParameter <<< inj (Proxy :: _ "envelope") - -_envelope' :: AudioEnvelope' -> AudioParameter -_envelope' = _envelope <<< AudioEnvelope - -_cancel :: AudioCancel -> AudioParameter -_cancel = AudioParameter <<< inj (Proxy :: _ "cancel") - -_cancel' :: AudioCancel' -> AudioParameter -_cancel' = _cancel <<< AudioCancel - -_sudden :: AudioSudden -> AudioParameter -_sudden = AudioParameter <<< inj (Proxy :: _ "sudden") - -_sudden' :: AudioSudden' -> AudioParameter -_sudden' = _sudden <<< AudioSudden - -type AudioNumeric' = { n :: Number, o :: Number, t :: Transition } -newtype AudioNumeric = AudioNumeric AudioNumeric' - -derive instance Eq AudioNumeric -derive instance Ord AudioNumeric -derive instance Newtype AudioNumeric _ -derive newtype instance Show AudioNumeric - -type AudioEnvelope' = { p :: Array Number, o :: Number, d :: Number } -newtype AudioEnvelope = AudioEnvelope AudioEnvelope' - -derive instance Eq AudioEnvelope -derive instance Ord AudioEnvelope -derive instance Newtype AudioEnvelope _ -derive newtype instance Show AudioEnvelope - -type AudioCancel' = { o :: Number } -newtype AudioCancel = AudioCancel AudioCancel' - -derive instance Eq AudioCancel -derive instance Ord AudioCancel -derive instance Newtype AudioCancel _ -derive newtype instance Show AudioCancel - -type AudioSudden' = { n :: Number } -newtype AudioSudden = AudioSudden AudioSudden' - -derive instance Eq AudioSudden -derive instance Ord AudioSudden -derive instance Newtype AudioSudden _ -derive newtype instance Show AudioSudden - -type InitialAudioParameter = Number -newtype AudioParameter = AudioParameter - ( Variant - ( numeric :: AudioNumeric - , envelope :: AudioEnvelope - , cancel :: AudioCancel - , sudden :: AudioSudden - ) - ) - -derive instance eqAudioParameter :: Eq AudioParameter -derive instance ordAudioParameter :: Ord AudioParameter -derive instance newtypeAudioParameter :: Newtype AudioParameter _ -derive newtype instance showAudioParameter :: Show AudioParameter - --- | Term-level constructor for a generator being on or off -newtype OnOff = OnOff - ( Variant - ( on :: Unit - , off :: Unit - ) - ) - -_on :: OnOff -_on = OnOff $ inj (Proxy :: _ "on") unit - -_off :: OnOff -_off = OnOff $ inj (Proxy :: _ "off") unit - -derive instance eqOnOff :: Eq OnOff -derive instance ordOnOff :: Ord OnOff -derive instance newtypeOnOff :: Newtype OnOff _ -derive instance genericOnOff :: Generic OnOff _ - -instance showOnOff :: Show OnOff where - show = unwrap >>> match - { on: const "on", off: const "off" } - -newtype AudioOnOff = AudioOnOff - { x :: OnOff - , o :: Number - } - -apOn :: AudioOnOff -apOn = AudioOnOff { x: _on, o: 0.0 } - -bangOn - :: forall nt r - . Newtype nt (Variant (onOff :: AudioOnOff | r)) - => Event nt -bangOn = bang (wrap $ inj (Proxy :: _ "onOff") apOn) - -apOff :: AudioOnOff -apOff = AudioOnOff { x: _off, o: 0.0 } - -dt - :: forall nt r - . Newtype nt { o :: Number | r } - => (Number -> Number) - -> nt - -> nt -dt = over (_Newtype <<< prop (Proxy :: _ "o")) - -derive instance eqAudioOnOff :: Eq AudioOnOff -derive instance ordAudioOnOff :: Ord AudioOnOff -derive instance newtypeAudioOnOff :: Newtype AudioOnOff _ -derive instance genericAudioOnOff :: Generic AudioOnOff _ - -class ToAudioOnOff i where - toAudioOnOff :: i -> AudioOnOff - -instance ToAudioOnOff Number where - toAudioOnOff = AudioOnOff <<< { o: _, x: _on } - -instance ToAudioOnOff OnOff where - toAudioOnOff = AudioOnOff <<< { o: 0.0, x: _ } - -instance ToAudioOnOff AudioOnOff where - toAudioOnOff = identity - -class ToAudioParameter i where - toAudioParameter :: i -> AudioParameter - -instance ToAudioParameter Number where - toAudioParameter n = _sudden (AudioSudden { n }) - -instance ToAudioParameter AudioParameter where - toAudioParameter = identity - -instance ToAudioParameter AudioNumeric where - toAudioParameter = _numeric - -instance ToAudioParameter AudioSudden where - toAudioParameter = _sudden - -instance ToAudioParameter AudioCancel where - toAudioParameter = _cancel - -instance ToAudioParameter AudioEnvelope where - toAudioParameter = _envelope - -class OpticN s where - opticN :: forall p. Strong p => Optic' p s Number - -instance (Cons "n" Number r' r) => OpticN AudioNumeric where - opticN = unto AudioNumeric <<< prop (Proxy :: _ "n") - -instance (Cons "n" Number r' r) => OpticN AudioSudden where - opticN = unto AudioSudden <<< prop (Proxy :: _ "n") - -class OpticO s where - opticO :: forall p. Strong p => Optic' p s Number - -instance (Cons "n" Number r' r) => OpticO AudioOnOff where - opticO = unto AudioOnOff <<< prop (Proxy :: _ "o") - -instance (Cons "n" Number r' r) => OpticO AudioNumeric where - opticO = unto AudioNumeric <<< prop (Proxy :: _ "o") - -instance (Cons "n" Number r' r) => OpticO AudioEnvelope where - opticO = unto AudioEnvelope <<< prop (Proxy :: _ "o") - -instance (Cons "n" Number r' r) => OpticO AudioCancel where - opticO = unto AudioCancel <<< prop (Proxy :: _ "o") \ No newline at end of file diff --git a/src/WAGS/Properties.purs b/src/WAGS/Properties.purs index 2c7877f6..098c0eb9 100644 --- a/src/WAGS/Properties.purs +++ b/src/WAGS/Properties.purs @@ -6,7 +6,7 @@ import Data.Newtype (class Newtype, wrap) import Data.Variant (Variant, inj) import Data.Variant.Maybe (Maybe) import Type.Proxy (Proxy(..)) -import WAGS.Parameter (class ToAudioOnOff, class ToAudioParameter, AudioOnOff, AudioParameter, toAudioOnOff, toAudioParameter) +import WAGS.Core (class ToAudioOnOff, class ToAudioParameter, AudioOnOff, AudioParameter, toAudioOnOff, toAudioParameter) import WAGS.WebAPI (BrowserAudioBuffer) -- props @@ -26,33 +26,33 @@ bufferOffset bufferOffset = wrap <<< inj (Proxy :: Proxy "bufferOffset") delayTime - :: forall nt r ap - . Newtype nt (Variant (delayTime :: AudioParameter | r)) - => ToAudioParameter ap + :: forall nt r ap l p + . Newtype nt (Variant (delayTime :: AudioParameter l p | r)) + => ToAudioParameter ap l p => ap -> nt delayTime = wrap <<< inj (Proxy :: Proxy "delayTime") <<< toAudioParameter frequency - :: forall nt r ap - . Newtype nt (Variant (frequency :: AudioParameter | r)) - => ToAudioParameter ap + :: forall nt r ap l p + . Newtype nt (Variant (frequency :: AudioParameter l p | r)) + => ToAudioParameter ap l p => ap -> nt frequency = wrap <<< inj (Proxy :: Proxy "frequency") <<< toAudioParameter q - :: forall nt r ap - . Newtype nt (Variant (q :: AudioParameter | r)) - => ToAudioParameter ap + :: forall nt r ap l p + . Newtype nt (Variant (q :: AudioParameter l p | r)) + => ToAudioParameter ap l p => ap -> nt q = wrap <<< inj (Proxy :: Proxy "q") <<< toAudioParameter gain - :: forall nt r ap - . Newtype nt (Variant (gain :: AudioParameter | r)) - => ToAudioParameter ap + :: forall nt r ap l p + . Newtype nt (Variant (gain :: AudioParameter l p | r)) + => ToAudioParameter ap l p => ap -> nt gain = wrap <<< inj (Proxy :: Proxy "gain") <<< toAudioParameter @@ -79,9 +79,9 @@ loopStart loopStart = wrap <<< inj (Proxy :: Proxy "loopStart") offset - :: forall nt r ap - . Newtype nt (Variant (offset :: AudioParameter | r)) - => ToAudioParameter ap + :: forall nt r ap l p + . Newtype nt (Variant (offset :: AudioParameter l p | r)) + => ToAudioParameter ap l p => ap -> nt offset = wrap <<< inj (Proxy :: Proxy "offset") <<< toAudioParameter @@ -95,9 +95,9 @@ onOff onOff = wrap <<< inj (Proxy :: Proxy "onOff") <<< toAudioOnOff playbackRate - :: forall nt r ap - . Newtype nt (Variant (playbackRate :: AudioParameter | r)) - => ToAudioParameter ap + :: forall nt r ap l p + . Newtype nt (Variant (playbackRate :: AudioParameter l p | r)) + => ToAudioParameter ap l p => ap -> nt playbackRate = wrap <<< inj (Proxy :: Proxy "playbackRate") <<< toAudioParameter \ No newline at end of file diff --git a/src/WAGS/WebAPI.purs b/src/WAGS/WebAPI.purs index 05ea854c..bfe0efb2 100644 --- a/src/WAGS/WebAPI.purs +++ b/src/WAGS/WebAPI.purs @@ -134,4 +134,4 @@ instance eqBrowserMediaElement :: Eq BrowserMediaElement where eq = unsafeRefEq instance ordBrowserMediaElement :: Ord BrowserMediaElement where - compare a b = if a == b then EQ else LT \ No newline at end of file + compare a b = if a == b then EQ else LT