-
Notifications
You must be signed in to change notification settings - Fork 0
/
Components.hs
223 lines (150 loc) · 7.65 KB
/
Components.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
module Components where
import Sound.Pulse.Simple
import Signals
import Data.List
import qualified Data.Vector.Generic as V
import qualified Sound.File.Sndfile as SF
import qualified Sound.File.Sndfile.Buffer.Vector as BV
import qualified Control.Concurrent as CC
import Sound.File.Sndfile
type BasicOscillator = FrequencySignal -> AmplitudeSignal -> (Maybe PWMSignal) -> Signal
--------------------------------------
-- Raw Signal Generation Components
--------------------------------------
data SamplingRate = SamplingRate Integer
data Samples = Samples Integer
data Progression = Progression Float
data Slope = Slope Float
getSlope (Progression p) (SignalValue sv) = Slope $ (sv / p)
signalValueFromSlope (Progression p) (Slope s) = SignalValue $ p * s
data Cycle a = Cycle a
data Frequency = Frequency SafeValue
data Amplitude = Amplitude SafeValue
cycleFunc func (Cycle val_a ) (Cycle val_b ) = Cycle $ func val_a val_b
getProgression (Samples s) (SamplingRate sr) = Progression $ ((fromIntegral s) / (fromIntegral sr))
getNumSamples (Progression p) (SamplingRate sr) = Samples $ floor $ p * fromIntegral sr
fromCycleProgression (Cycle (Progression cp)) (Frequency f) = Progression (cp / f)
toCycleProgression (Progression tp) (Frequency f) = Cycle $ Progression (tp * f)
fromCycleSignalValue (Cycle (SignalValue sv)) (Amplitude a) = SignalValue (sv * a)
type BasicFunction = Cycle Progression -> Cycle Progression -> Cycle SignalValue
class Addable a where
(-:) :: a -> a -> a
(+:) :: a -> a -> a
instance Addable Progression where
(-:) (Progression a) (Progression b) = Progression (a - b)
(+:) (Progression a) (Progression b) = Progression (a + b)
instance Addable SignalValue where
(-:) (SignalValue a) (SignalValue b) = SignalValue (a - b)
(+:) (SignalValue a) (SignalValue b) = SignalValue (a + b)
instance (Addable a) => Addable (Cycle a) where
(-:) (Cycle a) (Cycle b) = Cycle (a -: b)
(+:) (Cycle a) (Cycle b) = Cycle (a +: b)
instance Eq Progression where
(==) (Progression a) (Progression b) = a == b
instance (Eq a) => Eq (Cycle a) where
(==) (Cycle a) (Cycle b) = a == b
instance Ord Progression where
compare (Progression a) (Progression b) = compare a b
instance (Ord a) => Ord (Cycle a) where
compare (Cycle a) (Cycle b) = compare a b
samplesPerCycle = Cycle $ SamplingRate 100000
oscillator :: BasicFunction -> BasicOscillator
oscillator basicFunc fSig aSig Nothing = oscillator basicFunc fSig aSig (Just $ specialize $ flatSignal 0.5)
oscillator basicFunc fSig aSig (Just pSig) = Signal $ oscillator_ fVals aVals pVals (Cycle (Progression 0)) where
fVals = sanitize fSig
aVals = sanitize aSig
pVals = sanitize pSig
oscillator_ :: [SafeValue] -> [SafeValue] -> [SafeValue] -> Cycle Progression -> [SignalValue]
oscillator_ fVals aVals pVals t | t >= (Cycle $ Progression 1) = oscillator_ fVals aVals pVals $ t -: (Cycle $ Progression 1)
| otherwise = (fromCycleSignalValue basicFunc_ (Amplitude aVal)): oscillatorRest
where
fVal:fRest = fVals
aVal:aRest = aVals
pVal:pRest = pVals
basicFunc_ = basicFunc (Cycle (Progression pVal)) t
oscillatorRest = oscillator_ fRest aRest pRest (t +: cycleProgressionDelta)
progressionDelta = getProgression (Samples 1) (SamplingRate samplesPerSecond)
-- Part of starting to incorporate the dubious Units.hs
-- Undoing this for now, maybe forever.
-- progressionDelta = __ 1 /: __ samplesPerSecond
cycleProgressionDelta = toCycleProgression progressionDelta (Frequency fVal)
osc_square = oscillator basicFunc where
basicFunc pw t | t < pw = Cycle $ SignalValue 1
| otherwise = Cycle $ SignalValue (-1)
osc_triangle = oscillator basicFunc where
basicFunc pw t | t < pw = (cycleFunc signalValueFromSlope t upslope) -: (Cycle $ SignalValue 1 )
| otherwise = (cycleFunc signalValueFromSlope (t -: pw) downslope) +: (Cycle $ SignalValue 1 )
where
upslope = cycleFunc getSlope pw (Cycle $ SignalValue 2)
downslope = cycleFunc getSlope ((Cycle $ Progression 1) -: pw) (Cycle $ SignalValue (-2) )
osc_sawtooth :: BasicOscillator
osc_sawtooth fSig aSig _ = osc_triangle fSig aSig $ Just (specialize $ flatSignal 1)
osc_sine = oscillator basicFunc where
basicFunc _ (Cycle (Progression t)) = Cycle $ SignalValue $ sin (2 * pi * t)
--------------------------------------
-- Basic Signal Manipulation Components
--------------------------------------
sig_adder :: [Signal] -> Signal
sig_adder insignals = toSignal outvalues where
invalues = map fromSignal insignals
-- transpose will automatically shrink the resultant lists as signals end. and sum of an empty list is safely zero
-- in other words, any signals that go through sig_adder, we don't need to worry about them ending. saves a lot of headache.
outvalues = map sum $ transpose invalues
sig_sequence :: [([Signal], Progression)] -> Signal
sig_sequence sequenceData = sig_sequence' sequenceData [flatSignal 0] where
sig_sequence' :: [([Signal], Progression)] -> [Signal] -> Signal
sig_sequence' [] existingSignals = sig_adder existingSignals
sig_sequence' ((newSignals, startingDelay):nextSeq) existingSignals = catSignals [beforeNewSignals, afterNewSignals] where
beforeNewSignals = (takeSeconds startingSeconds $ sig_adder existingSignals)
afterNewSignals = sig_sequence' nextSeq ( remainingOldSignals ++ newSignals ) where
remainingOldSignals = clearEmptySignals $ map (dropSeconds startingSeconds) existingSignals
Progression startingSeconds = startingDelay
envelope :: ([(SafeValue, Float)] -> Float -> SafeValue) -> [(SafeValue, Float)] -> Signal
envelope envFunc points = toSignal $ envelope_ points 0 where
envelope_ points t | t < (len * samplesPerSecond) = (envFunc points t): envelope_ points (t + 1)
| otherwise = envelope_ (tail points ) 0
where
(val, len):(next_val, _):_ = points
slideEnvelope = envelope func where
func points t = (val + (t * slope))
where
(val, len):(next_val, _):_ = points
slope = (next_val - val) / (len * samplesPerSecond)
stepEnvelope = envelope func where
func points _ = val
where
(val, len):_ = points
--------------------------------------
-- Sequencing Components
--------------------------------------
--------------------------------------
-- Sound Output Components
--------------------------------------
-- Mixer Output
buffersize = 1000
initPulse = simpleNew Nothing "example" Play Nothing "this is an example application" (SampleSpec (F32 LittleEndian) 44100 1) Nothing Nothing
outputSound s [] = do
return ()
outputSound s signal = do
let buffer = take buffersize signal
let rest = drop buffersize signal
CC.forkIO ( simpleWrite s buffer ) >> do
outputSound s rest
playRealtime :: SoundSignal -> IO ()
playRealtime soundSignal = do
s<-initPulse
outputSound s (sanitize soundSignal)
simpleDrain s
simpleFree s
play :: SoundSignal -> IO ()
play signal = do
s<-initPulse
simpleWrite s $ sanitize signal
simpleDrain s
simpleFree s
-- File Output
fileinfo = Info {frames = 1000, samplerate = 44100, channels = 1, seekable = False, format=Format {headerFormat =SF.HeaderFormatWav, sampleFormat = SF.SampleFormatPcm16, endianFormat = SF.EndianLittle }, sections = 1 }
writeSound :: SoundSignal -> FilePath -> IO ()
writeSound signal outPath = do
SF.writeFile fileinfo outPath $ BV.toBuffer $ V.fromList $ sanitize signal
return ()