In theory, 1-channel signals is all we need, as multichannel can be handled by a list of signals (scsynth does this). The main problem is how to wrap external effects that return 2 or more channels. We should consider:
1) Represent multichannel streams streams as more than one signal
2) Represent multichannel streams as one signal (i.e. change semantics)
Approach 1
If we choose approach 1), then we need some kind of primitive (D -> D -> (D,D)) -> S -> S -> (S,S) for lifting in stereo effects and similar. The main drawback is complexity.
Actually no! There is no problem of implementing
liftStereo :: (D -> D -> (D,D)) -> S -> S -> (S,S)
liftStereo f a b =
(lift2 (fst . f) a b, lift2 (snd . f) a b)
This is only a problem if we are dealing with impure functions (such as external effects). The solution is to treat the effects as extra I/O units (similar to auxes in a mixer). This is related to #5.
Approach 2
If we choose approach 2), then stereo effects could simply have the type (D -> D -> (D,D)) -> S -> S -> S. The drawback is possibly more complicated semantics. What would happen with the other primitives if this approach is taken? Consider:
data Signal
= Time
| Random
| Constant Double
| Lift String (Double -> Double) Signal
| Lift2 String (Double -> Double -> Double) Signal Signal
| Loop (Signal -> Signal)
| Delay Int Signal
| Input Int
| Output Int Int Signal
Time and Random would continoue to be mono channels.
Lift would map the function over all channels (i.e. map).
Lift2 would zip the function over the input channels (i.e. zipWith or zipWithRepeatingShortest).
Loop would assume a single signal or take an extra numberOfChannels parameter.
Delay would simply delay all signals.
Input and output would work as before but write consecutive channels.
In theory, 1-channel signals is all we need, as multichannel can be handled by a list of signals (scsynth does this). The main problem is how to wrap external effects that return 2 or more channels. We should consider:
1) Represent multichannel streams streams as more than one signal 2) Represent multichannel streams as one signal (i.e. change semantics)
Approach 1
If we choose approach 1), then we need some kind of primitive
(D -> D -> (D,D)) -> S -> S -> (S,S)
for lifting in stereo effects and similar. The main drawback is complexity. Actually no! There is no problem of implementingThis is only a problem if we are dealing with impure functions (such as external effects). The solution is to treat the effects as extra I/O units (similar to auxes in a mixer). This is related to #5.
Approach 2
If we choose approach 2), then stereo effects could simply have the type
(D -> D -> (D,D)) -> S -> S -> S
. The drawback is possibly more complicated semantics. What would happen with the other primitives if this approach is taken? Consider:Time and Random would continoue to be mono channels. Lift would map the function over all channels (i.e. map). Lift2 would zip the function over the input channels (i.e. zipWith or zipWithRepeatingShortest). Loop would assume a single signal or take an extra numberOfChannels parameter. Delay would simply delay all signals. Input and output would work as before but write consecutive channels.