module Haskore.Interface.SuperCollider.Render (
fileFromMelody, fileFromMelodyMonad,
byteStringFromSchedule, runSCSynth,
HeaderFormat(..), SampleFormat(..),
) where
import qualified Haskore.Interface.SuperCollider.Schedule as Schedule
import qualified Haskore.Interface.SuperCollider.Play as Play
import Haskore.Interface.SuperCollider.Schedule (Time)
import Haskore.Interface.SuperCollider.SoundMap (Instrument)
import qualified Sound.SC3.Server.NRT as SCNRT
import qualified Sound.SC3.Server.PlayEasy as SCPlay
import Sound.OpenSoundControl.OSC (OSC(Bundle))
import qualified Sound.OpenSoundControl.Time as OSCTime
import qualified Data.ByteString.Lazy as B
import qualified Haskore.Melody as Melody
import qualified Haskore.RealTime.Timer.Immediate as TimerImmediate
import qualified Haskore.RealTime.EventList.TimeBody as TimeList
import qualified Data.EventList.Absolute.TimeBody as AbsoluteEventList
import qualified Numeric.NonNegative.Wrapper as NonNeg
import System.Cmd (rawSystem)
import System.Exit (ExitCode)
import Data.Array(Array, Ix, (!), listArray)
fileFromMelody :: FilePath -> Instrument -> Melody.T () -> IO ()
fileFromMelody fileName sound =
B.writeFile fileName .
byteStringFromSchedule .
Schedule.fromMelody sound
fileFromMelodyMonad :: FilePath -> Instrument -> Melody.T () -> IO ()
fileFromMelodyMonad fileName sound =
SCPlay.withSC3File fileName .
Play.scheduleWithPlayer
(Play.messagesGrouped TimerImmediate.timer 0) .
Schedule.fromMelody sound
data HeaderFormat = AIFF | Wave | NeXT
deriving (Read, Show, Eq, Ord, Ix, Bounded)
headerExtension :: Array HeaderFormat String
headerExtension =
listArray (minBound,maxBound) ["aiff", "wav", "au"]
headerName :: Array HeaderFormat String
headerName =
listArray (minBound,maxBound) ["AIFF", "WAVE", "NeXT"]
data SampleFormat = Int16 | Int24 | Int32 | Float | Double
deriving (Read, Show, Eq, Ord, Ix, Bounded)
sampleName :: Array SampleFormat String
sampleName =
listArray (minBound,maxBound)
["int16", "int24", "int32", "float", "double"]
runSCSynth ::
[String] ->
Int ->
HeaderFormat ->
SampleFormat ->
Int ->
FilePath ->
IO ExitCode
runSCSynth
options numChannels headerFormat sampleFormat sampleRate fileName =
rawSystem "scsynth"
(options ++
["-D", "0", "-o", show numChannels, "-N",
fileName++".osc", "_", fileName ++ '.' : headerExtension ! headerFormat,
show sampleRate, headerName ! headerFormat, sampleName ! sampleFormat])
byteStringFromSchedule :: Schedule.T -> B.ByteString
byteStringFromSchedule =
SCNRT.encodeNRT . scheduleToStream
scheduleToStream ::
Schedule.T ->
[OSC]
scheduleToStream sc =
timeStamp 0 (Schedule.initial sc) :
messagesToStream (Schedule.body sc)
messagesToStream ::
TimeList.T Time OSC ->
[OSC]
messagesToStream =
map (uncurry timeStamp) .
AbsoluteEventList.toPairList .
AbsoluteEventList.collectCoincident .
TimeList.toAbsoluteEventList 0
timeStamp :: Time -> [OSC] -> OSC
timeStamp = Bundle . OSCTime.NTPr . NonNeg.toNumber