musicw 0.3.2 → 0.3.5
raw patch · 5 files changed
+204/−6 lines, 5 files
Files
- musicw.cabal +1/−1
- src/Sound/MusicW/AudioContext.hs +17/−1
- src/Sound/MusicW/Node.hs +16/−0
- src/Sound/MusicW/SynthDef.hs +3/−0
- src/Sound/MusicW/Worklets.hs +167/−4
musicw.cabal view
@@ -1,5 +1,5 @@ name: musicw-version: 0.3.2+version: 0.3.5 synopsis: Sound synthesis library, to be used with GHCJS and Web Audio API description: A library for sound synthesis, currently targeting GHCJS and the Web Audio API. Used in the Inner Ear and Estuary projects. homepage: https://github.com/dktr0/musicw/blob/master/README.md
src/Sound/MusicW/AudioContext.hs view
@@ -45,10 +45,26 @@ foreign import javascript safe "if (window.___ac == null) { \ \ window.___ac = new (window.AudioContext || window.webkitAudioContext)(\- \ { latencyHint: \"playback\", sampleRate: 48000 } \+ \ { sampleRate: 48000 } \ \ );\ \} $r = window.___ac;" getGlobalAudioContext :: IO AudioContext++foreign import javascript safe+ "if (window.___ac == null) { \+ \ window.___ac = new (window.AudioContext || window.webkitAudioContext)(\+ \ { latencyHint: \"playback\", sampleRate: 48000 } \+ \ );\+ \} $r = window.___ac;"+ getGlobalAudioContextPlayback :: IO AudioContext++foreign import javascript safe+ "if (window.___ac == null) { \+ \ window.___ac = new (window.AudioContext || window.webkitAudioContext)(\+ \ { latencyHint: \"interactive\", sampleRate: 48000 } \+ \ );\+ \} $r = window.___ac;"+ getGlobalAudioContextInteractive :: IO AudioContext foreign import javascript unsafe "$1.currentTime"
src/Sound/MusicW/Node.hs view
@@ -243,6 +243,14 @@ setNodeField node "isSink" True setNodeField node "startable" False +createMicrophone :: AudioIO m => m Node+createMicrophone = do+ ctx <- audioContext+ node <- liftIO $ js_createMicrophone ctx+ setNodeField node "isSource" True+ setNodeField node "isSink" False+ setNodeField node "startable" False+ createMediaStreamDestination :: AudioIO m => m Node createMediaStreamDestination = do ctx <- audioContext@@ -420,6 +428,14 @@ foreign import javascript unsafe "$1.createScriptProcessor(void 0, $2, $3)" js_createScriptProcessor :: AudioContext -> Int -> Int -> IO Node++foreign import javascript safe+ "$r = $1.createGain();\+ \navigator.mediaDevices.getUserMedia({ audio: true, video: false}).then(function(stream) {\+ \ var x = new MediaStreamAudioSourceNode($1,{mediaStream: stream});\+ \ x.connect($r);\+ \});"+ js_createMicrophone :: AudioContext -> IO Node foreign import javascript unsafe "$1.createMediaStreamDestination()"
src/Sound/MusicW/SynthDef.hs view
@@ -181,6 +181,9 @@ audioIn :: AudioIO m => SynthDef m NodeRef audioIn = addNodeBuilder (0,1) $ createGain 1.0 -- TODO: hard-coded single input channel will be problematic later +microphone :: AudioIO m => SynthDef m NodeRef+microphone = addNodeBuilder (0,1) createMicrophone + resink :: AudioIO m => NodeRef -> NodeRef -> SynthDef m NodeRef resink target input = connect input target >> return target
src/Sound/MusicW/Worklets.hs view
@@ -53,6 +53,9 @@ safeDivideWorklet :: AudioIO m => NodeRef -> NodeRef -> SynthDef m NodeRef safeDivideWorklet in1 in2 = audioWorklet "safeDivide-processor" [in1,in2] +unsafeDivideWorklet :: AudioIO m => NodeRef -> NodeRef -> SynthDef m NodeRef+unsafeDivideWorklet in1 in2 = audioWorklet "unsafeDivide-processor" [in1,in2]+ powWorklet :: AudioIO m => NodeRef -> NodeRef -> SynthDef m NodeRef powWorklet in1 in2 = audioWorklet "pow-processor" [in1,in2] @@ -65,20 +68,52 @@ clipWorklet :: AudioIO m => NodeRef -> NodeRef -> NodeRef -> SynthDef m NodeRef clipWorklet lo hi input = audioWorklet "clip-processor" [lo,hi,input] +maxWorklet :: AudioIO m => NodeRef -> NodeRef -> SynthDef m NodeRef+maxWorklet in1 in2 = audioWorklet "max-processor" [in1,in2]++minWorklet :: AudioIO m => NodeRef -> NodeRef -> SynthDef m NodeRef+minWorklet in1 in2 = audioWorklet "min-processor" [in1,in2]++whiteNoiseWorklet :: AudioIO m => SynthDef m NodeRef+whiteNoiseWorklet = audioWorklet "white-noise-processor" []++sinToSqrWorklet :: AudioIO m => NodeRef -> SynthDef m NodeRef+sinToSqrWorklet x = audioWorklet "sin-to-sqr-processor" [x]++sinToTriWorklet :: AudioIO m => NodeRef -> SynthDef m NodeRef+sinToTriWorklet x = audioWorklet "sin-to-tri-processor" [x]+++-- | sinToSawWorklet is for producing an ideal (ie. not band-limited) sawtooth+-- wave (a phasor, in other words) from two phase-aligned sine waves. The first+-- sine wave argument should be at one quarter the desired frequency, and the second+-- sine wave argument should be at half the desired frequency.++sinToSawWorklet :: AudioIO m => NodeRef -> NodeRef -> SynthDef m NodeRef+sinToSawWorklet x y = audioWorklet "sin-to-saw-processor" [x,y]++-- | stepWorklet produces an output by selecting from a list of inputs on the basis+-- of another, "selector" input from from 0-1. The space of 0-1 is divided over however+-- many inputs are provided in the argument, and the output at any given sample only+-- comes from one of the inputs (there is no mixing or cross-fading between the inputs).++stepWorklet :: AudioIO m => [NodeRef] -> NodeRef -> SynthDef m NodeRef+stepWorklet xs y = audioWorklet "step-processor" (y:xs)+ audioWorklet :: AudioIO m => String -> [NodeRef] -> SynthDef m NodeRef audioWorklet workletName inputs = do let iChnls = length inputs -- NOTE limiting assumption that each input NodeRef provides only one channel of input let oChnls = 1 -- NOTE limiting assumption that each audio worklet provides only one channel of output y <- addNodeBuilder (iChnls,oChnls) $ createAudioWorkletNode iChnls oChnls workletName- zipWithM (\x n -> connect' x 0 y n) inputs [0..]+ zipWithM_ (\x n -> connect' x 0 y n) inputs [0..] return y createAudioWorkletNode :: AudioIO m => Int -> Int -> String -> m Node createAudioWorkletNode inChnls outChnls workletName = do ctx <- audioContext node <- liftIO $ js_createAudioWorkletNode ctx (toJSString workletName) inChnls outChnls- setNodeField node "isSource" (outChnls > 0)- setNodeField node "isSink" (inChnls > 0)+ _ <- setNodeField node "isSource" (outChnls > 0)+ _ <- setNodeField node "isSink" (inChnls > 0) setNodeField node "startable" False foreign import javascript safe@@ -319,6 +354,21 @@ \ }\ \ registerProcessor('safeDivide-processor',SafeDivideProcessor);\ \ \+\ class UnsafeDivideProcessor extends AudioWorkletProcessor {\+\ static get parameterDescriptors() { return []; }\+\ constructor() { super(); }\+\ process(inputs,outputs,parameters) {\+\ const input1 = inputs[0];\+\ const input2 = inputs[1];\+\ const output = outputs[0];\+\ for(let i = 0; i < input1[0].length; i++) {\+\ output[0][i] = input1[0][i] / input2[0][i];\+\ }\+\ return true;\+\ }\+\ }\+\ registerProcessor('unsafeDivide-processor',UnsafeDivideProcessor);\+\ \ \ class PowProcessor extends AudioWorkletProcessor {\ \ static get parameterDescriptors() { return []; }\ \ constructor() { super(); }\@@ -376,4 +426,117 @@ \ return true;\ \ }\ \ }\-\ registerProcessor('clip-processor',ClipProcessor);"+\ registerProcessor('clip-processor',ClipProcessor);\+\ \+\ class MaxProcessor extends AudioWorkletProcessor {\+\ static get parameterDescriptors() { return []; }\+\ constructor() { super(); }\+\ process(inputs,outputs,parameters) {\+\ const in1 = inputs[0];\+\ const in2 = inputs[1];\+\ const output = outputs[0];\+\ for(let i = 0; i < in1[0].length; i++) {\+\ output[0][i] = Math.max(in1[0][i],in2[0][i]);\+\ }\+\ return true;\+\ }\+\ }\+\ registerProcessor('max-processor',MaxProcessor);\+\ \+\ class MinProcessor extends AudioWorkletProcessor {\+\ static get parameterDescriptors() { return []; }\+\ constructor() { super(); }\+\ process(inputs,outputs,parameters) {\+\ const in1 = inputs[0];\+\ const in2 = inputs[1];\+\ const output = outputs[0];\+\ for(let i = 0; i < in1[0].length; i++) {\+\ output[0][i] = Math.min(in1[0][i],in2[0][i]);\+\ }\+\ return true;\+\ }\+\ }\+\ registerProcessor('min-processor',MinProcessor);\+\ \+\ class WhiteNoiseProcessor extends AudioWorkletProcessor {\+\ \+\ static get parameterDescriptors() { return []; }\+\ constructor() { super(); }\+\ process(inputs,outputs,parameters) {\+\ const output = outputs[0];\+\ for(let i = 0; i < output[0].length; i++) {\+\ output[0][i] = Math.random()*2-1;\+\ }\+\ return true;\+\ }\+\ }\+\ registerProcessor('white-noise-processor',WhiteNoiseProcessor);\+\ \+\ class SinToSqrProcessor extends AudioWorkletProcessor {\+\ \+\ static get parameterDescriptors() { return []; }\+\ constructor() { super(); }\+\ process(inputs,outputs,parameters) {\+\ const input = inputs[0];\+\ const output = outputs[0];\+\ for(let i = 0; i < output[0].length; i++) {\+\ output[0][i] = (input[0][i] >= 0) ? 1 : -1;\+\ }\+\ return true;\+\ }\+\ }\+\ registerProcessor('sin-to-sqr-processor',SinToSqrProcessor);\+\ \+\ class SinToTriProcessor extends AudioWorkletProcessor {\+\ \+\ static get parameterDescriptors() { return []; }\+\ constructor() { super(); this.MULT = 4/Math.PI; }\+\ process(inputs,outputs,parameters) {\+\ const input = inputs[0];\+\ const output = outputs[0];\+\ for(let i = 0; i < output[0].length; i++) {\+\ output[0][i] = Math.abs(Math.asin(input[0][i]))*this.MULT-1;\+\ }\+\ return true;\+\ }\+\ }\+\ registerProcessor('sin-to-tri-processor',SinToTriProcessor);\+\ \+\ class SinToSawProcessor extends AudioWorkletProcessor {\+\ static get parameterDescriptors() { return []; }\+\ constructor() { super(); this.MULT = 4/Math.PI; }\+\ process(inputs,outputs,parameters) {\+\ const input1 = inputs[0];\+\ const input2 = inputs[1];\+\ const output = outputs[0];\+\ var a;\+\ for(let i = 0; i < output[0].length; i++) {\+\ a = Math.abs(Math.asin(input1[0][i]))*this.MULT;\+\ output[0][i] = (input2[0][i] >= 0) ? (a-1) : (1-a);\+\ }\+\ return true;\+\ }\+\ }\+\ registerProcessor('sin-to-saw-processor',SinToSawProcessor);\+\ \+\ class StepProcessor extends AudioWorkletProcessor {\+\ static get parameterDescriptors() { return []; }\+\ constructor() { super(); }\+\ process(inputs,outputs,parameters) {\+\ const output = outputs[0];\+\ if(inputs.length < 2) { for(let i = 0; i < output[0].length; i++) output[0][i] = 0;}\+\ else if(inputs.length == 2) { for(let i = 0; i < output[0].length; i++) output[0][i] = inputs[1][i];}\+\ else {\+\ var a,n = inputs.length - 1;\+\ for(let i = 0; i < output[0].length; i++) {\+\ a = inputs[0][0][i] * 0.5 + 0.5;\+\ a = Math.floor ((a - Math.trunc(a)) * n);\+\ a = (a >= 0) ? a : 0;\+\ a = (a < n) ? a : (n-1);\+\ output[0][i] = inputs[1+a][0][i];\+\ }\+\ }\+\ return true;\+\ }\+\ }\+\ registerProcessor('step-processor',StepProcessor);"