musicw 0.3.0 → 0.3.2
raw patch · 6 files changed
+93/−13 lines, 6 filesdep ~basedep ~containers
Dependency ranges changed: base, containers
Files
- musicw.cabal +5/−4
- src/Sound/MusicW/AudioContext.hs +4/−2
- src/Sound/MusicW/Node.hs +22/−0
- src/Sound/MusicW/Synth.hs +2/−0
- src/Sound/MusicW/SynthDef.hs +7/−6
- src/Sound/MusicW/Worklets.hs +53/−1
musicw.cabal view
@@ -1,8 +1,8 @@ name: musicw-version: 0.3.0+version: 0.3.2 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: http://github.com/d0kt0r0/reflex-synth/blob/master/README.md+homepage: https://github.com/dktr0/musicw/blob/master/README.md license: BSD3 license-file: LICENSE author: David Ogborn, Jamie Beverley, Luis Navarro del Angel, Spencer Park and others@@ -11,14 +11,15 @@ category: Web build-type: Simple cabal-version: >=1.10+tested-with: GHCJS == 8.6.0.1 extra-source-files: Library hs-source-dirs: src js-sources: MusicW.js Build-Depends:- base >= 4.11.1.0 && < 4.12,- containers >= 0.5.11.0 && < 0.6,+ base >= 4.11.1.0 && < 4.13,+ containers >= 0.5.11.0 && < 0.7, safe >= 0.3.17 && < 0.4, ghcjs-base >= 0.2.0.0 && < 0.3, ghcjs-dom >= 0.9.2.0 && < 0.10,
src/Sound/MusicW/AudioContext.hs view
@@ -35,7 +35,7 @@ foreign import javascript unsafe "\"closed\"" js_acClosed :: JSVal foreign import javascript safe- "new (window.AudioContext || window.webkitAudioContext)()"+ "new (window.AudioContext || window.webkitAudioContext)({ latencyHint: \"playback\", sampleRate: 48000 })" newAudioContext :: IO AudioContext foreign import javascript unsafe@@ -44,7 +44,9 @@ foreign import javascript safe "if (window.___ac == null) { \- \ window.___ac = new (window.AudioContext || window.webkitAudioContext)();\+ \ window.___ac = new (window.AudioContext || window.webkitAudioContext)(\+ \ { latencyHint: \"playback\", sampleRate: 48000 } \+ \ );\ \} $r = window.___ac;" getGlobalAudioContext :: IO AudioContext
src/Sound/MusicW/Node.hs view
@@ -179,6 +179,14 @@ setNodeField node "isSink" True setNodeField node "startable" False +createChannelSplitter :: AudioIO m => Int -> m Node+createChannelSplitter nChnls = do+ ctx <- audioContext+ node <- liftIO $ js_createChannelSplitter ctx nChnls+ setNodeField node "isSource" True+ setNodeField node "isSink" True+ setNodeField node "startable" False+ data OversampleAmount = NoOversampling | X2Oversampling@@ -275,6 +283,12 @@ | not (js_isSink toNode) = error $ (show toNode) ++ " can't be connect target." | otherwise = liftIO $ js_connect' fromNode toNode fromChannel toChannel +connectNodes'' :: MonadIO m => Node -> Int -> Node -> m ()+connectNodes'' fromNode fromChannel toNode+ | not (js_isSource fromNode) = error $ (show fromNode) ++ " can't be connect source."+ | not (js_isSink toNode) = error $ (show toNode) ++ " can't be connect target."+ | otherwise = liftIO $ js_connect'' fromNode toNode fromChannel+ disconnectNodes :: MonadIO m => Node -> Node -> m () disconnectNodes from to | not (js_isSource from) = error $ (show from) ++ " can't be disconnect source."@@ -396,6 +410,10 @@ js_createChannelMerger :: AudioContext -> Int -> IO Node foreign import javascript unsafe+ "$1.createChannelSplitter($2)"+ js_createChannelSplitter :: AudioContext -> Int -> IO Node++foreign import javascript unsafe "$1.createWaveShaper()" js_createWaveShaper :: AudioContext -> IO Node @@ -424,6 +442,10 @@ foreign import javascript unsafe "$1.connect($2,$3,$4);" js_connect' :: Node -> Node -> Int -> Int -> IO ()++foreign import javascript unsafe+ "$1.connect($2,$3);"+ js_connect'' :: Node -> Node -> Int -> IO () foreign import javascript unsafe "$1.disconnect($2);"
src/Sound/MusicW/Synth.hs view
@@ -64,6 +64,8 @@ makeConnections dest ns (NodeRef from (_,_)) DestinationRef = connectNodes (ns!!from) dest makeConnections _ ns (NodeRef from (_,_)) (NodeRef to (_,_)) = connectNodes (ns!!from) (ns!!to) makeConnections _ ns (NodeRef from (_,_)) (ParamRef to pType) = createParameter (ns!!to) pType >>= connectNodes (ns!!from)+makeConnections _ ns (NodeOutputRef fromNode fromChannel) (NodeRef to (_,_)) = connectNodes'' (ns!!fromNode) fromChannel (ns!!to)+makeConnections _ ns (NodeOutputRef fromNode fromChannel) (ParamRef to pType) = createParameter (ns!!to) pType >>= connectNodes'' (ns!!fromNode) fromChannel makeConnections _ ns (NodeOutputRef fromNode fromChannel) (NodeInputRef toNode toChannel) = connectNodes' (ns!!fromNode) fromChannel (ns!!toNode) toChannel makeConnections _ _ _ _ = error "Malformed graph structure."
src/Sound/MusicW/SynthDef.hs view
@@ -126,13 +126,14 @@ zipWithM_ (\x i -> connect' x 0 y i) xs [0..] return y --- note: channelSplitter does not actually create any new synthesis nodes--- it just turns all of the output channels of a node with outputs into--- individual node references. channelSplitter :: AudioIO m => NodeRef -> SynthDef m [NodeRef]-channelSplitter (NodeOutputRef n o) = return [NodeOutputRef n o]-channelSplitter (NodeRef n (_,oChnls)) = return $ fmap (NodeOutputRef n) [0 .. (oChnls-1)]-channelSplitter _ = error "MusicW: channelSplitter called on NodeInputRef ParamRef or DestinationRef"+channelSplitter input | nodeRefOutputCount input < 1 = error "MusicW: channelSplitter called on NodeRef not representing a node output"+ | otherwise = do+ let nchnls = nodeRefOutputCount input+ y <- addNodeBuilder (1,nchnls) $ createChannelSplitter nchnls+ let (NodeRef n _) = y+ connect input y+ return $ fmap (NodeOutputRef n) [0 .. (nchnls-1)] convolver :: AudioIO m => Either Float32Array FloatArraySpec -> Bool -> NodeRef -> SynthDef m NodeRef convolver spec normalize input = do
src/Sound/MusicW/Worklets.hs view
@@ -56,7 +56,15 @@ powWorklet :: AudioIO m => NodeRef -> NodeRef -> SynthDef m NodeRef powWorklet in1 in2 = audioWorklet "pow-processor" [in1,in2] +floorWorklet :: AudioIO m => NodeRef -> SynthDef m NodeRef+floorWorklet x = audioWorklet "floor-processor" [x] +fractWorklet :: AudioIO m => NodeRef -> SynthDef m NodeRef+fractWorklet x = audioWorklet "fract-processor" [x]++clipWorklet :: AudioIO m => NodeRef -> NodeRef -> NodeRef -> SynthDef m NodeRef+clipWorklet lo hi input = audioWorklet "clip-processor" [lo,hi,input]+ 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@@ -324,4 +332,48 @@ \ return true;\ \ }\ \ }\-\ registerProcessor('pow-processor',PowProcessor);"+\ registerProcessor('pow-processor',PowProcessor);\+\ \+\ class FloorProcessor extends AudioWorkletProcessor {\+\ static get parameterDescriptors() { return []; }\+\ constructor() { super(); }\+\ process(inputs,outputs,parameters) {\+\ const input1 = inputs[0];\+\ const output = outputs[0];\+\ for(let i = 0; i < input1[0].length; i++) {\+\ output[0][i] = Math.floor(input1[0][i]);\+\ }\+\ return true;\+\ }\+\ }\+\ registerProcessor('floor-processor',FloorProcessor);\+\ \+\ class FractProcessor extends AudioWorkletProcessor {\+\ static get parameterDescriptors() { return []; }\+\ constructor() { super(); }\+\ process(inputs,outputs,parameters) {\+\ const input1 = inputs[0];\+\ const output = outputs[0];\+\ for(let i = 0; i < input1[0].length; i++) {\+\ output[0][i] = input1[0][i] % 1;\+\ }\+\ return true;\+\ }\+\ }\+\ registerProcessor('fract-processor',FractProcessor);\+\ \+\ class ClipProcessor extends AudioWorkletProcessor {\+\ static get parameterDescriptors() { return []; }\+\ constructor() { super(); }\+\ process(inputs,outputs,parameters) {\+\ const min = inputs[0];\+\ const max = inputs[1];\+\ const input = inputs[2];\+\ const output = outputs[0];\+\ for(let i = 0; i < input[0].length; i++) {\+\ output[0][i] = Math.min(Math.max(input[0][i], min[0][i]), max[0][i]);\+\ }\+\ return true;\+\ }\+\ }\+\ registerProcessor('clip-processor',ClipProcessor);"