vivid-0.5.0.0: Vivid/SCServer.hs
{-# OPTIONS_HADDOCK show-extensions #-}
-- {-# LANGUAGE BangPatterns #-}
-- {-# LANGUAGE FlexibleInstances #-}
-- {-# LANGUAGE InstanceSigs #-}
-- {-# LANGUAGE KindSignatures #-}
-- {-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
-- {-# LANGUAGE ScopedTypeVariables #-}
-- {-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE NoIncoherentInstances #-}
{-# LANGUAGE NoMonomorphismRestriction #-}
{-# LANGUAGE NoRebindableSyntax #-}
{-# LANGUAGE NoUndecidableInstances #-}
-- | Library for interacting with the SuperCollider server.
--
-- You don't need to use much of this day-to-day
--
-- There's a toplevel 'scServerState' that stores the current state of the SC server
module Vivid.SCServer (
cmdPeriod
, freeAll
, Timestamp(..)
-- * Nodes
, NodeId(..)
, Synth(..)
, Group(..)
, ParGroup(..)
, defaultGroup
-- * Buffers
, BufferId(..)
, makeBuffer
, makeBufferFromFile
, newBuffer
, newBufferFromFile
, newBufferFromFileBetween
, saveBuffer
, writeBuffer
, writeBufferWith
, WriteBufArgs(..)
, defaultWBArgs
, closeBuf
, closeBuffer
, zeroBuf
-- * Manual management of SC server connection
, createSCServerConnection'
, closeSCServerConnection'
, SCConnectConfig(..)
, defaultConnectConfig
, module Vivid.SCServer.State
, shrinkSynthArgs
) where
import Vivid.OSC
import Vivid.OSC.Bundles (initTreeCommand)
import qualified Vivid.SC.Server.Commands as SCCmd
import Vivid.SC.Server.Types (Group(..), ParGroup(..))
import Vivid.Actions.Class
import Vivid.SCServer.Connection
import Vivid.SCServer.State
import Vivid.SCServer.Types
-- import qualified Data.ByteString.UTF8 as UTF8 (fromString)
import Data.Int (Int32)
-- BBP hack:
import Prelude
-- | Your \"emergency\" button. Run this and everything playing on the SC server
-- will be freed -- silence!
--
-- Corresponds to the cmd-. \/ ctrl-. key command in the SuperCollider IDE
cmdPeriod :: (VividAction m) => m ()
cmdPeriod = do
callOSC $ SCCmd.g_freeAll [NodeId 1] -- 1 instead of 0 is temp! (is it? 1 is default group...)
callOSC $ SCCmd.clearSched
initTree
-- | Alias of 'cmdPeriod'
freeAll :: VividAction m => m ()
freeAll = cmdPeriod
initTree :: (VividAction m) => m ()
initTree = callOSC initTreeCommand
-- | Make an empty buffer
--
-- The Int32 is the buffer length /in samples/. Multiply seconds by
-- the default sample rate of the server (usually 48000) to get the number
-- of samples
--
-- Note that this is synchronous -- it doesn't return until the buffer is allocated
-- (in theory, this could hang if e.g. the UDP packet is lost)
newBuffer :: VividAction m => Int32 -> m BufferId
newBuffer bufferLength = do
bufId <- newBufferId
oscWSync $ \syncId ->
callOSC $
SCCmd.b_alloc bufId bufferLength 1 (Just $ SCCmd.sync syncId)
return bufId
-- | Make a buffer and fill it with sound data from a file
--
-- The file path should be absolute (not relative), and if you're connecting
-- to a non-localhost server don't expect it to be able to read files from
-- your local hard drive!
--
-- Note that like 'makeBuffer' this is synchronous
newBufferFromFile :: (VividAction m) => FilePath -> m BufferId
newBufferFromFile = newBufferFromFileBetween 0 Nothing
newBufferFromFileBetween :: VividAction m => Int32 -> Maybe Int32 -> FilePath -> m BufferId
newBufferFromFileBetween startTime endTimeMay fPath = do
bufId <- newBufferId
oscWSync $ \syncId -> callOSC $
SCCmd.b_allocRead bufId fPath startTime endTimeMay (Just $ SCCmd.sync syncId)
return bufId
makeBufferFromFile :: (VividAction m) => FilePath -> m BufferId
makeBufferFromFile = newBufferFromFile
makeBuffer :: (VividAction m) => Int32 -> m BufferId
makeBuffer = newBuffer
-- | Write a buffer to a file
--
-- Alias of 'writeBuffer'
--
-- Synchronous.
saveBuffer :: (VividAction m) => BufferId -> FilePath -> m ()
saveBuffer = writeBuffer
writeBuffer :: VividAction m => BufferId -> FilePath -> m ()
writeBuffer = writeBufferWith defaultWBArgs
writeBufferWith :: VividAction m => WriteBufArgs -> BufferId -> FilePath -> m ()
writeBufferWith args bufId fPath =
oscWSync $ \syncId -> callOSC $
SCCmd.b_write
bufId
fPath
"wav" -- TODO!
"float" -- less important, but TODO
-- Num frames:
Nothing
-- Start frame:
0
-- Whether to leave the file open (useful for diskOut):
(_wb_keepOpen args)
-- We make this synchronous because what if you send a
-- "/b_write" then a "/quit"?(!):
(Just $ SCCmd.sync syncId)
-- | We may add arguments in the future ; to future-proof your code, just update
-- fields of 'defaultWBArgs'
data WriteBufArgs
= WriteBufArgs {
_wb_keepOpen :: Bool
}
deriving (Show, Read, Eq, Ord)
defaultWBArgs :: WriteBufArgs
defaultWBArgs = WriteBufArgs {
_wb_keepOpen = False
}
-- | Close an open soundfile and write header information
--
-- Synchronous
closeBuffer :: VividAction m => BufferId -> m ()
closeBuffer bufId = oscWSync $ \syncId ->
callOSC $ SCCmd.b_close bufId (Just $ SCCmd.sync syncId)
closeBuf :: VividAction m => BufferId -> m ()
closeBuf = closeBuffer
-- | Zero the sample data in a buffer
--
-- Synchronous
zeroBuf :: VividAction m => BufferId -> m ()
zeroBuf bufId = oscWSync $ \syncId ->
callOSC $ SCCmd.b_zero bufId (Just $ SCCmd.sync syncId)
-- More info is available in HelpSource/Reference/default_group.schelp
defaultGroup :: Group ; defaultGroup = Group (NodeId 1)