synthesizer-core 0.8.3 → 0.8.4
raw patch · 11 files changed
+138/−51 lines, 11 filesdep ~containersdep ~filepath
Dependency ranges changed: containers, filepath
Files
- src/Synthesizer/Plain/Oscillator.hs +37/−10
- src/Synthesizer/Plain/Oscillator/BandLimited.hs +61/−0
- synthesizer-core.cabal +9/−8
- test/Test/Sound/Synthesizer/Basic/NumberTheory.hs +3/−3
- test/Test/Sound/Synthesizer/Plain/Control.hs +5/−4
- test/Test/Sound/Synthesizer/Plain/Filter.hs +11/−10
- test/Test/Sound/Synthesizer/Plain/Filter/Allpass.hs +5/−4
- test/Test/Sound/Synthesizer/Plain/Filter/Hilbert.hs +0/−1
- test/Test/Sound/Synthesizer/Plain/Interpolation.hs +5/−4
- test/Test/Sound/Synthesizer/Storable/Cut.hs +2/−2
- test/Test/Utility.hs +0/−5
src/Synthesizer/Plain/Oscillator.hs view
@@ -57,19 +57,23 @@ zipWith Phase.increment phases (iterate (Phase.increment freq) zero) {- | oscillator with modulated shape -}-shapeMod :: (RealRing.C a) => (c -> Wave.T a b) -> (Phase a) -> a -> Sig.T c -> Sig.T b+shapeMod ::+ (RealRing.C a) => (c -> Wave.T a b) -> (Phase a) -> a -> Sig.T c -> Sig.T b shapeMod wave phase freq parameters = zipWith (Wave.apply . wave) parameters $ iterate (Phase.increment freq) (Phase.fromRepresentative phase) {- | oscillator with both phase and frequency modulation -}-phaseFreqMod :: (RealRing.C a) => Wave.T a b -> Sig.T (Phase a) -> Sig.T a -> Sig.T b+phaseFreqMod ::+ (RealRing.C a) => Wave.T a b -> Sig.T (Phase a) -> Sig.T a -> Sig.T b phaseFreqMod wave phases freqs = map (Wave.apply wave) (zipWith Phase.increment phases (freqsToPhases zero freqs)) {- | oscillator with both shape and frequency modulation -}-shapeFreqMod :: (RealRing.C a) => (c -> Wave.T a b) -> Phase a -> Sig.T c -> Sig.T a -> Sig.T b+shapeFreqMod ::+ (RealRing.C a) =>+ (c -> Wave.T a b) -> Phase a -> Sig.T c -> Sig.T a -> Sig.T b shapeFreqMod wave phase parameters freqs = zipWith (Wave.apply . wave) parameters $ freqsToPhases (Phase.fromRepresentative phase) freqs@@ -77,13 +81,17 @@ {- | oscillator with a sampled waveform with constant frequency This is essentially an interpolation with cyclic padding. -}-staticSample :: RealRing.C a => Interpolation.T a b -> [b] -> Phase a -> a -> Sig.T b+staticSample ::+ (RealRing.C a) =>+ Interpolation.T a b -> [b] -> Phase a -> a -> Sig.T b staticSample ip wave phase freq = freqModSample ip wave phase (repeat freq) {- | oscillator with a sampled waveform with modulated frequency Should behave homogenously for different types of interpolation. -}-freqModSample :: RealRing.C a => Interpolation.T a b -> [b] -> Phase a -> Sig.T a -> Sig.T b+freqModSample ::+ (RealRing.C a) =>+ Interpolation.T a b -> [b] -> Phase a -> Sig.T a -> Sig.T b freqModSample ip wave phase freqs = let len = fromIntegral (length wave) in Interpolation.multiRelativeCyclicPad@@ -108,8 +116,10 @@ because in the wave information for 'shapeFreqModSample' shape and phase are strictly separated. -}-shapeFreqModSample :: (RealRing.C c, RealRing.C b) =>- Interpolation.T c (Wave.T b a) -> [Wave.T b a] -> c -> Phase b -> Sig.T c -> Sig.T b -> Sig.T a+shapeFreqModSample ::+ (RealRing.C c, RealRing.C b) =>+ Interpolation.T c (Wave.T b a) ->+ [Wave.T b a] -> c -> Phase b -> Sig.T c -> Sig.T b -> Sig.T a shapeFreqModSample ip waves shape0 phase shapes freqs = zipWith Wave.apply (Interpolation.multiRelativeConstantPad ip shape0 shapes waves)@@ -118,8 +128,10 @@ GNUPlot.plotList [] $ take 500 $ shapeFreqModSample Interpolation.cubic (map Wave.truncOddCosine [0..3]) (0.1::Double) (0::Double) (repeat 0.005) (repeat 0.02) -} -shapePhaseFreqModSample :: (RealRing.C c, RealRing.C b) =>- Interpolation.T c (Wave.T b a) -> [Wave.T b a] -> c -> Sig.T c -> Sig.T (Phase b) -> Sig.T b -> Sig.T a+shapePhaseFreqModSample ::+ (RealRing.C c, RealRing.C b) =>+ Interpolation.T c (Wave.T b a) ->+ [Wave.T b a] -> c -> Sig.T c -> Sig.T (Phase b) -> Sig.T b -> Sig.T a shapePhaseFreqModSample ip waves shape0 shapes phases freqs = zipWith Wave.apply (Interpolation.multiRelativeConstantPad ip shape0 shapes waves)@@ -201,6 +213,21 @@ {- * Oscillators with specific waveforms -} +{- | impulse train with static frequency -}+staticImpulses :: (RealRing.C a) => a -> a -> Sig.T a+staticImpulses phase = freqModImpulses phase . repeat++{- | impulse train with modulated frequency -}+freqModImpulses :: (RealRing.C a) => a -> Sig.T a -> Sig.T a+freqModImpulses phase =+ Sig.crochetL+ (\freq p0 -> Just $+ let p1 = p0+freq+ in if p1>1+ then (1, fraction p1)+ else (0, p1))+ (fraction phase)+ {- | sine oscillator with static frequency -} staticSine :: (Trans.C a, RealRing.C a) => a -> a -> Sig.T a staticSine = static Wave.sine@@ -213,7 +240,7 @@ phaseModSine :: (Trans.C a, RealRing.C a) => a -> Sig.T a -> Sig.T a phaseModSine = phaseMod Wave.sine -{- | saw tooth oscillator with modulated frequency -}+{- | saw tooth oscillator with static frequency -} staticSaw :: RealRing.C a => a -> a -> Sig.T a staticSaw = static Wave.saw
+ src/Synthesizer/Plain/Oscillator/BandLimited.hs view
@@ -0,0 +1,61 @@+{-# LANGUAGE NoImplicitPrelude #-}+{- |+Tone generators with measures for band-limitation.++They are not exactly band-limiting because this would cause infinite lag.+Instead we use only cubic interpolation polynomials.+This still incurs a small lag.++<https://youtu.be/lpM4Tawq-XU>+-}+module Synthesizer.Plain.Oscillator.BandLimited where++import qualified Synthesizer.Plain.Signal as Sig++import qualified Algebra.RealField as RealField++import NumericPrelude.Numeric+import NumericPrelude.Base++++{-+sinc approximation, that could be used for band-limited oscillators:++GP.plotFuncs [] (GP.linearScale 1000 (-2,2::Double)) [\x -> if x<0 then (if x< -1 then (x+1)*(x+2)*(x+2) else 1-x*x*2-x*x*x) else (if x<1 then 1-x*x*2+x*x*x else -(x-1)*(x-2)*(x-2)), \x -> if x==0 then 1 else sin (pi*x)/(pi*x)]++Has the same tangent as sinc-pi at point 1.++Cf.+DSP.Filter.FIR.PolyInterp+Integral Sine: gsl_sf_Si+-}++++{- | impulse train with static frequency -}+staticImpulses :: (RealField.C a) => a -> a -> Sig.T a+staticImpulses phase = freqModImpulses phase . repeat++{- | impulse train with modulated frequency -}+freqModImpulses :: (RealField.C a) => a -> Sig.T a -> Sig.T a+freqModImpulses phase =+ (\ ~(~(_,remaining),xs) -> xs ++ remaining) .+ Sig.mapAccumL+ (\freq (p0,xs0) ->+ let p1 = p0+freq+ (p2, xs1) =+ if p1>=1+ then+ let p1frac=fraction p1+ t=p1frac/freq+ t_2 = t*t; y0 = t_2*(t-1)+ t1_2 = (t-1)*(t-1); y3 = -t1_2*t+ in (p1frac, xs0 + [y0, 1-t1_2+y3, 1-t_2+y0, y3])+ else (p1, xs0)+ (x3,xs3) =+ case xs1 of+ [] -> (0,[])+ x2:xs2 -> (x2,xs2)+ in Just $ (x3, (p2,xs3)))+ (phase,[])
synthesizer-core.cabal view
@@ -1,5 +1,5 @@ Name: synthesizer-core-Version: 0.8.3+Version: 0.8.4 License: GPL License-File: LICENSE Author: Henning Thielemann <haskell@henning-thielemann.de>@@ -38,7 +38,7 @@ Source-Repository this- Tag: 0.8.3+ Tag: 0.8.4 Type: darcs Location: http://code.haskell.org/synthesizer/core/ @@ -55,20 +55,20 @@ semigroups >=0.1 && <1.0, event-list >=0.1 && <0.2, non-negative >=0.1 && <0.2,- explicit-exception >=0.1.6 && <0.2,+ explicit-exception >=0.1.6 && <0.3, numeric-prelude >=0.4.2 && <0.5, numeric-quest >=0.1 && <0.3, utility-ht >=0.0.14 && <0.1, filepath >=1.1 && <1.5,- bytestring >=0.9 && <0.12,+ bytestring >=0.9 && <0.13, binary >=0.1 && <1,- deepseq >=1.1 && <1.5,+ deepseq >=1.1 && <1.6, storablevector >=0.2.5 && <0.3, storable-record >=0.0.1 && <0.1,- storable-tuple >=0.0.1 && <0.1,+ storable-tuple >=0.0.1 && <0.2, QuickCheck >=1 && <3, array >=0.1 && <0.6,- containers >=0.1 && <0.7,+ containers >=0.1 && <0.8, random >=1.0 && <2.0, process >=1.0 && <1.7, base >= 4 && <5@@ -138,6 +138,7 @@ Synthesizer.Plain.Modifier Synthesizer.Plain.Noise Synthesizer.Plain.Oscillator+ Synthesizer.Plain.Oscillator.BandLimited Synthesizer.Plain.ToneModulation Synthesizer.Plain.Wave Synthesizer.Plain.Instrument@@ -286,7 +287,7 @@ numeric-prelude, timeit >=1.0 && <3, storablevector >=0.2.7 && <0.3,- storable-tuple >=0.0.1 && <0.1,+ storable-tuple, utility-ht >=0.0.5 && <0.1, base >=4 && <5
test/Test/Sound/Synthesizer/Basic/NumberTheory.hs view
@@ -6,12 +6,12 @@ import Control.Applicative ((<$>), ) +import qualified Data.List.HT as ListHT import qualified Data.Set as Set import qualified Data.Bits as Bit import qualified Test.QuickCheck as QC import Test.QuickCheck (Testable, Arbitrary, arbitrary, quickCheck, )-import Test.Utility (equalList, ) import qualified Algebra.Absolute as Absolute @@ -157,10 +157,10 @@ QC.forAll (QC.choose (2,10::Integer)) $ \b (Positive n) -> NT.divideByMaximumPower b n == NT.divideByMaximumPowerRecursive b n) : ("numbers3Smooth",- QC.quickCheckWith singleArgs $ equalList $ map (take 10000) $+ QC.quickCheckWith singleArgs $ ListHT.allEqual $ map (take 10000) $ [NT.numbers3SmoothCorec, NT.numbers3SmoothFoldr, NT.numbers3SmoothSet]) : ("numbers5Smooth",- QC.quickCheckWith singleArgs $ equalList $ map (take 10000) $+ QC.quickCheckWith singleArgs $ ListHT.allEqual $ map (take 10000) $ [NT.numbers5SmoothCorec, NT.numbers5SmoothFoldr, NT.numbers5SmoothSet]) : ("ceiling3Smooth vs. is3Smooth", quickCheck $ \(Positive n) -> NT.is3Smooth $ NT.ceiling3Smooth n) :
test/Test/Sound/Synthesizer/Plain/Control.hs view
@@ -3,9 +3,10 @@ import qualified Synthesizer.Plain.Control as Control import qualified Test.QuickCheck as QC-import Test.QuickCheck (Property, quickCheck, (==>), )-import Test.Utility (equalList, approxEqualListAbs, approxEqualListRel, )+import Test.QuickCheck (Property, quickCheck, (==>))+import Test.Utility (approxEqualListAbs, approxEqualListRel) +import qualified Data.List.HT as ListHT import Data.List (transpose) import NumericPrelude.Numeric@@ -16,7 +17,7 @@ linearRing :: Int -> Int -> Bool linearRing d y0 = -- Control.linear d y0 == Control.linearMultiscale d y0- all equalList $ take 100 $ transpose $+ all ListHT.allEqual $ take 100 $ transpose $ Control.linear d y0 : Control.linearMultiscale d y0 : Control.linearStable d y0 :@@ -40,7 +41,7 @@ linearExact :: Rational -> Rational -> Bool linearExact d y0 =- all equalList $ take 100 $ transpose $+ all ListHT.allEqual $ take 100 $ transpose $ Control.linear d y0 : Control.linearMean d y0 : Control.linearMultiscale d y0 :
test/Test/Sound/Synthesizer/Plain/Filter.hs view
@@ -19,7 +19,7 @@ import qualified Test.QuickCheck as QC import Test.QuickCheck (Property, arbitrary, quickCheck, )-import Test.Utility (equalList, ArbChar, )+import Test.Utility (ArbChar) import qualified Number.GaloisField2p32m5 as GF import qualified Number.NonNegative as NonNeg@@ -28,6 +28,7 @@ import Control.Applicative (liftA2, (<$>), ) +import qualified Data.List.HT as ListHT import qualified Data.List as List import Data.Tuple.HT (sortPair, mapPair, ) import Data.Ix (inRange, )@@ -47,7 +48,7 @@ in -- this checks only for equal prefixes and can easily go wrong, -- if one list is empty and $ zipWith3 (\x y z -> x==y && y==z) naive rec pyramid- -- equalList $ naive : pyramid : rec : []+ -- ListHT.allEqual $ naive : pyramid : rec : [] sumRange :: Sig.T Int -> Property sumRange xs =@@ -59,7 +60,7 @@ pyrSt = FiltNRSt.pyramid (+) height (SigSt.fromList SigSt.defaultChunkSize xs)- in equalList $+ in ListHT.allEqual $ FiltNR.sumRange xs rng : FiltNR.sumRangeFromPyramid pyr rng : FiltNR.sumRangeFromPyramidRec pyr rng :@@ -85,7 +86,7 @@ getRange nrng pyr0 = let rng = sortPair $ mapPair (getSize, getSize) nrng pyr = map NonEmpty.toInfiniteList $ NonEmpty.toList pyr0- in equalList $+ in ListHT.allEqual $ FiltNR.getRangeFromPyramid pyr rng : FiltNRG.consumeRangeFromPyramid (:) [] pyr rng : []@@ -95,7 +96,7 @@ QC.forAll (QC.choose (0,10)) $ \height -> let ctrl = map (mapPair (getSize, getSize)) nctrl xs = NonEmpty.toInfiniteList xsc- in equalList $+ in ListHT.allEqual $ FiltNR.sumsPosModulated ctrl xs : FiltNR.sumsPosModulatedPyramid height ctrl xs : FiltNRG.sumsPosModulatedPyramid height ctrl xs :@@ -126,7 +127,7 @@ mapPair (getSize, getSize)) nctrl xs = NonEmpty.toInfiniteList xsc- in equalList $+ in ListHT.allEqual $ zipWith FiltNR.minRange (List.tails xs) ctrl : SigSt.toList (FiltNRSt.accumulateBinPosModulatedPyramid min height@@ -144,7 +145,7 @@ downSample2 :: Property downSample2 = QC.forAll genChunkyVector $ \xs ->- equalList $+ ListHT.allEqual $ FiltNRG.downsample2 SigG.defaultLazySize xs : FiltNRSt.downsample2 xs : []@@ -152,7 +153,7 @@ sumsDownSample2 :: Property sumsDownSample2 = QC.forAll genChunkyVector $ \xs ->- equalList $+ ListHT.allEqual $ FiltNRG.sumsDownsample2 SigG.defaultLazySize xs : FiltNRSt.sumsDownsample2 xs : FiltNRSt.sumsDownsample2Alt xs :@@ -163,7 +164,7 @@ sumsDownSample2 lazySize xsc = let len = Chunky.fromChunks $ filter (0/=) lazySize xs = VP.pack len $ NonEmpty.toInfiniteList xsc- in equalList $+ in ListHT.allEqual $ FiltNRG.sumsDownsample2 SigG.defaultLazySize xs : FiltNRSt.sumsDownsample2 xs : FiltNRSt.sumsDownsample2Alt xs :@@ -180,7 +181,7 @@ maxC = maximum ctrl onegf :: GF.T onegf = one- in equalList $+ in ListHT.allEqual $ pack (FiltNR.movingAverageModulatedPyramid onegf height maxC ctrl (cycle xs)) : FiltNRG.movingAverageModulatedPyramid onegf
test/Test/Sound/Synthesizer/Plain/Filter/Allpass.hs view
@@ -8,15 +8,16 @@ -- import qualified Test.Sound.Synthesizer.Plain.NonEmpty as NonEmpty import Test.QuickCheck (quickCheck, {- Property, (==>) -})-import Test.Utility (equalList, ) +import qualified Data.List.HT as ListHT++import Control.Monad.Trans.State (runState)+ -- import qualified Algebra.Module as Module -- import qualified Algebra.RealField as RealField -- import qualified Algebra.Ring as Ring -- import qualified Algebra.Additive as Additive -import Control.Monad.Trans.State (runState, )- import NumericPrelude.Numeric import NumericPrelude.Base import Prelude ()@@ -34,7 +35,7 @@ cascadeStep k u (s0,s1,ns) = let p = Allpass.Parameter k s = s0:s1:ns- in equalList $+ in ListHT.allEqual $ runState (Allpass.cascadeStepStack p u) s : runState (Allpass.cascadeStepRec p u) s : runState (Allpass.cascadeStepScanl p u) s :
test/Test/Sound/Synthesizer/Plain/Filter/Hilbert.hs view
@@ -9,7 +9,6 @@ import qualified Test.Sound.Synthesizer.Plain.NonEmpty as NonEmpty import Test.QuickCheck (quickCheck, {- Property, (==>) -})--- import Test.Utility (equalList, ) -- import qualified Algebra.Module as Module -- import qualified Algebra.RealField as RealField
test/Test/Sound/Synthesizer/Plain/Interpolation.hs view
@@ -36,10 +36,11 @@ import qualified Algebra.RealRing as RealRing import qualified Data.List.Match as Match+import qualified Data.List.HT as ListHT import Data.Tuple.HT (mapSnd, ) import qualified Test.Sound.Synthesizer.Plain.NonEmpty as NonEmpty-import Test.Utility (equalList, ArbChar, unpackArbString, )+import Test.Utility (ArbChar, unpackArbString) import NumericPrelude.Numeric@@ -104,7 +105,7 @@ (Interpol.C a v, Module.C a v, Eq v) => a -> v -> [v] -> Bool constant t x0 xs =- equalList $ map ($ (x0:xs)) $ map ($ t) $+ ListHT.allEqual $ map ($ (x0:xs)) $ map ($ t) $ Interpolation.func ExampleCustom.constant : Interpolation.func ExampleCustom.piecewiseConstant : Interpolation.func ExampleModule.constant :@@ -115,7 +116,7 @@ (Interpol.C a v, Module.C a v, Eq v) => a -> v -> v -> [v] -> Bool linear t x0 x1 xs =- equalList $ map ($ (x0:x1:xs)) $ map ($ t) $+ ListHT.allEqual $ map ($ (x0:x1:xs)) $ map ($ t) $ Interpolation.func ExampleCustom.linear : Interpolation.func ExampleCustom.piecewiseLinear : Interpolation.func ExampleModule.linear :@@ -126,7 +127,7 @@ (Interpol.C a v, VectorSpace.C a v, Eq v) => a -> v -> v -> v -> v -> [v] -> Bool cubic t x0 x1 x2 x3 xs =- equalList $ map ($ (x0:x1:x2:x3:xs)) $ map ($ t) $+ ListHT.allEqual $ map ($ (x0:x1:x2:x3:xs)) $ map ($ t) $ Interpolation.func ExampleCustom.cubic : Interpolation.func ExampleCustom.piecewiseCubic : Interpolation.func ExampleModule.cubic :
test/Test/Sound/Synthesizer/Storable/Cut.hs view
@@ -7,12 +7,12 @@ import qualified Synthesizer.Plain.Signal as Sig import qualified Data.EventList.Relative.TimeBody as EventList+import qualified Data.List.HT as ListHT import qualified Number.NonNegative as NonNeg import qualified Test.QuickCheck as QC import Test.QuickCheck (quickCheck, )-import Test.Utility (equalList, ) import NumericPrelude.Numeric import NumericPrelude.Base@@ -26,7 +26,7 @@ arrange chunkSize = QC.forAll genEventList $ \evs -> let sevs = EventList.mapBody (SigSt.fromList chunkSize) evs- in equalList $+ in ListHT.allEqual $ SigSt.fromList chunkSize (Cut.arrange evs) : CutSt.arrangeAdaptive chunkSize sevs : CutSt.arrangeList chunkSize sevs :
test/Test/Utility.hs view
@@ -15,11 +15,6 @@ import NumericPrelude.Numeric -equalList :: Eq a => [a] -> Bool-equalList xs =- and (ListHT.mapAdjacent (==) xs)-- approxEqual :: (RealRing.C a) => a -> a -> a -> Bool approxEqual eps x y = 2 * abs (x-y) <= eps * (abs x + abs y)