{-# OPTIONS_HADDOCK show-extensions #-} {-# LANGUAGE NoRebindableSyntax #-} {-# LANGUAGE BangPatterns #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE InstanceSigs #-} {-# LANGUAGE KindSignatures #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeSynonymInstances #-} {-# LANGUAGE NoIncoherentInstances #-} {-# LANGUAGE NoMonomorphismRestriction #-} {-# 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(..) , Node(..) -- * Buffers , BufferId(..) , makeBuffer , makeBufferFromFile , newBuffer , newBufferFromFile , saveBuffer -- * Manual management of SC server connection , createSCServerConnection , closeSCServerConnection , SCConnectConfig(..) , defaultConnectConfig , module Vivid.SCServer.State , shrinkNodeArgs ) where import Vivid.Actions.Class import Vivid.OSC import Vivid.SCServer.Connection import Vivid.SCServer.State import Vivid.SCServer.Types import qualified Data.ByteString.Char8 as BS8 (pack) 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 $ OSC "/g_freeAll" [OSC_I 0] callOSC $ OSC "/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@(BufferId bufIdInt) <- newBufferId syncId@(SyncId syncIdInt) <- newSyncId callOSC $ OSC "/b_alloc" [ OSC_I bufIdInt ,OSC_I bufferLength ,OSC_I 1 , OSC_B . encodeOSC $ OSC "/sync" [OSC_I syncIdInt] ] waitForSync 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 -- hard drive! -- -- Note that like "makeBuffer" this is synchronous newBufferFromFile :: (VividAction m) => FilePath -> m BufferId newBufferFromFile fPath = do bufId@(BufferId bufIdInt) <- newBufferId syncId@(SyncId syncIdInt) <- newSyncId callOSC $ OSC "/b_allocRead" [ OSC_I bufIdInt , OSC_S (BS8.pack fPath) , OSC_I 0 , OSC_I (-1) , OSC_B . encodeOSC $ OSC "/sync" [OSC_I syncIdInt] ] waitForSync 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 -- -- Synchronous. saveBuffer :: (VividAction m) => BufferId -> FilePath -> m () saveBuffer (BufferId theBufId) fPath = do _syncId@(SyncId syncIdInt) <- newSyncId callOSC $ OSC "/b_write" [ OSC_I theBufId ,OSC_S (BS8.pack fPath) ,OSC_S "wav" ,OSC_S "float" , OSC_I (-1) , OSC_I 0 , OSC_I 0 -- We make this synchronous because what if you send a "/b_write" then a "/quit"?(!): , OSC_B . encodeOSC $ OSC "/sync" [OSC_I syncIdInt] ]