packages feed

musicw-0.2.0: src/Sound/MusicW/AudioBuffer.hs

{-# LANGUAGE JavaScriptFFI #-}
module Sound.MusicW.AudioBuffer where

import GHCJS.Types
import GHCJS.Marshal.Pure
import Control.Monad
import GHCJS.Prim (toJSArray, fromJSInt, getProp)

import Sound.MusicW.AudioContext
import Sound.MusicW.FloatArraySpec

newtype AudioBuffer = AudioBuffer JSVal
instance Show AudioBuffer where show _ = "an AudioBuffer"
instance PToJSVal AudioBuffer where pToJSVal (AudioBuffer val) = val
instance PFromJSVal AudioBuffer where pFromJSVal = AudioBuffer

newtype Float32Array = Float32Array JSVal
instance Show Float32Array where show _ = "a Float32Array"
instance PToJSVal Float32Array where pToJSVal (Float32Array val) = val
instance PFromJSVal Float32Array where pFromJSVal = Float32Array

instantiateArraySpec :: Either Float32Array FloatArraySpec -> IO Float32Array
instantiateArraySpec (Left f32Arr) = return f32Arr
instantiateArraySpec (Right spec) = do
  array <- js_createTypedArray $ arraySpecSize spec
  fillArray 0 spec array
  return array

fillArray :: Int -> FloatArraySpec -> Float32Array -> IO ()
fillArray _ EmptyArray _ = return ()
fillArray i (Const n x tl) arr = do
  js_typedArraySetConst i (i + n) x arr
  fillArray (i + n) tl arr
fillArray i (Segment xs tl) arr = do
  jsArray <- toJSArray $ fmap pToJSVal xs
  len <- fmap fromJSInt $ getProp jsArray "length"
  js_typedArraySet i jsArray arr
  fillArray (i + len) tl arr
fillArray i (Repeated rep xs tl) arr = do
  jsArray <- toJSArray $ fmap pToJSVal xs
  len <- fmap fromJSInt $ getProp jsArray "length"
  forM_ [0..rep-1] $ \it -> js_typedArraySet (i + (it * len)) jsArray arr
  fillArray (i + (rep * len)) tl arr


-- TODO: can all (or most?) of the "safe" declarations here be unsafe for efficiency?

foreign import javascript safe
  "if (Float32Array.prototype.fill !== void 0) { \
  \  $2.fill($1); \
  \} else { \
  \  for (var i = 0, len = a.length; i < len; i++) \
  \    $2[i] = $1; \
  \}"
  js_typedArrayFill :: Double -> Float32Array -> IO ()

foreign import javascript safe
  "if (Float32Array.prototype.fill !== void 0) { \
  \  $4.fill($3, $1, $2); \
  \} else { \
  \  for (var i = $1; i < Math.min($2, $4.length); i++) \
  \    $4[i] = $3; \
  \}"
  js_typedArraySetConst :: Int -> Int -> Double -> Float32Array -> IO ()
  -- fromIdx -> toIdx (exclusive) -> value -> array -> IO ()

foreign import javascript safe
  "$3.set($2, $1);"
  js_typedArraySet :: Int -> JSVal -> Float32Array -> IO ()
  -- startIdx -> fillFromJSArray -> array -> IO ()

foreign import javascript safe
  "$2[$1]"
  js_typedArrayGetAt :: Int -> Float32Array -> IO Double

foreign import javascript safe
  "$3[$1] = $2;"
  js_typedArraySetAt :: Int -> Double -> Float32Array -> IO ()

foreign import javascript safe
  "new Float32Array($1)"
  js_typedArrayFromArray :: JSVal -> IO Float32Array

foreign import javascript safe
  "new Float32Array($1)"
  js_createTypedArray :: Int -> IO Float32Array

foreign import javascript safe
  "$1.length"
  js_typedArrayLength :: Float32Array -> IO Int

foreign import javascript safe
  "$4.createBuffer($1, $2, $3)"
  js_createAudioBuffer :: Int -> Int -> Double -> AudioContext -> IO AudioBuffer
  -- numChannels -> length (in samples) -> sampleRate (frames/sec) -> ctx -> buffer

foreign import javascript safe
  "$1.getChannelData($2)"
  js_channelData :: AudioBuffer -> Int -> IO Float32Array
  -- buffer -> channel (0 indexed) -> data

foreign import javascript safe
  "$3.copyToChannel($1, $2);"
  js_copyToChannel :: Float32Array -> Int -> AudioBuffer -> IO ()

foreign import javascript safe
  "$1.length"
  js_bufferLength :: AudioBuffer -> IO Int

foreign import javascript safe
  "$1.duration"
  js_bufferDuration :: AudioBuffer -> IO AudioTime

foreign import javascript safe
  "$1.numberOfChannels"
  js_bufferNumChannels :: AudioBuffer -> IO Int

foreign import javascript safe
  "$1.inputBuffer"
  js_apeInputBuffer :: JSVal -> IO AudioBuffer

foreign import javascript safe
  "$1.outputBuffer"
  js_apeOutputBuffer :: JSVal -> IO AudioBuffer