/designing-sound/src/DesigningSound/DTMF.hs

http://github.com/8c6794b6/haskell-sc-scratch · Haskell · 140 lines · 71 code · 10 blank · 59 comment · 0 complexity · b2fc9ae8847a044ccf9257beead2e10b MD5 · raw file

  1. ------------------------------------------------------------------------------
  2. -- |
  3. -- Module : $Header$
  4. -- CopyRight : (c) 8c6794b6
  5. -- License : BSD3
  6. -- Maintainer : 8c6794b6@gmail.com
  7. -- Stability : unstable
  8. -- Portability : non-portable
  9. --
  10. -- DTMF dialing tones.
  11. --
  12. -- <http://en.wikibooks.org/wiki/Designing_Sound_in_SuperCollider/DTMF>
  13. --
  14. -- /Example/:
  15. --
  16. -- > > withSC3 reset
  17. -- > > n <- audit "dtmf" dtmf
  18. -- > > nfree n
  19. -- > > pat1
  20. -- > > pat2
  21. -- > > pat3 "01203339026"
  22. -- > > go
  23. --
  24. module DesigningSound.DTMF where
  25. import Control.Applicative (ZipList(..), (<*>), (<$>))
  26. import Control.Concurrent (threadDelay)
  27. import Sound.SC3
  28. import System.Random (newStdGen, randomRs)
  29. import qualified Data.Map as M
  30. import DesigningSound.Util
  31. -- | Lookup table for number to frequency.
  32. tbl :: M.Map Char [Double]
  33. tbl = M.fromList
  34. [ ('1', [697, 1209])
  35. , ('2', [770, 1209])
  36. , ('3', [852, 1209])
  37. , ('4', [697, 1336])
  38. , ('5', [770, 1336])
  39. , ('6', [852, 1336])
  40. , ('7', [697, 1477])
  41. , ('8', [770, 1477])
  42. , ('9', [852, 1477])
  43. , ('*', [697, 1633])
  44. , ('0', [770, 1633])
  45. , ('#', [852, 1633])
  46. , ('A', [941, 1209])
  47. , ('B', [941, 1336])
  48. , ('C', [941, 1477])
  49. , ('D', [941, 1633]) ]
  50. -- | Synthdef playing a single number at a time.
  51. --
  52. -- > SynthDef(\dtmf, {|freq=#[770, 1633], out=0, amp=0.2, gate=1|
  53. -- > var son, env;
  54. -- > son = SinOsc.ar(freq, 0, amp).sum;
  55. -- > env = EnvGen.ar(Env.asr(0.001, 1, 0.001), gate, doneAction: 2);
  56. -- > Out.ar(out, Pan2.ar(son * env * amp));
  57. -- > }).memStore;
  58. --
  59. dtmf :: UGen
  60. dtmf = out 0 $ pan2 (son * ev * amp) 0 1
  61. where
  62. son = mix $ sinOsc ar (mce [freq1, freq2]) 0 * amp
  63. ev = envGen kr g 1 0 1 RemoveSynth (envASR 0.001 1 0.001 EnvCub)
  64. amp = control kr "amp" 0.2
  65. freq1 = control kr "freq1" 770
  66. freq2 = control kr "freq2" 1633
  67. g = control kr "gate" 1
  68. -- | Pattern generating random phone number and dial it.
  69. --
  70. -- This pattern has is with /human timing/ mentioned below.
  71. --
  72. -- > Pbind(
  73. -- > \instrument, \dtmf,
  74. -- > \dur, 0.2, // or for more "human" timing, try Pwhite(0.2, 0.5, inf)
  75. -- > \sustain, 0.15,
  76. -- > \amp, 0.3,
  77. -- > \freq, Prand(~tbl.asArray, 13)
  78. -- > ).play;
  79. --
  80. pat1 :: IO ()
  81. pat1 = sequence_ =<< acts
  82. where
  83. acts = do
  84. let idx = M.keys tbl
  85. dur = repeat 0.15
  86. cs <- map (idx !!) . take 13 . randomRs (0, length idx - 1) <$> newStdGen
  87. sus <- randomRs (0.05, 0.45) <$> newStdGen
  88. return $ getZipList $ pressNumber <$> z cs <*> z dur <*> z sus
  89. -- | Dialing to '0128-27-743-866'.
  90. --
  91. -- > Pbind(
  92. -- > \instrument, \dtmf,
  93. -- > \dur, 0.2, // or for more "human" timing, try Pwhite(0.2, 0.5, inf)
  94. -- > \sustain, 0.15,
  95. -- > \amp, 0.3,
  96. -- > \freq, Pseq("012827743866".collectAs({|digit| ~tbl[digit] }, Array))
  97. -- > ).play;
  98. --
  99. pat2 :: IO ()
  100. pat2 = pat3 "012827743866"
  101. -- | Dial to given char sequence.
  102. pat3 :: [Char] -> IO ()
  103. pat3 cs = sequence_ =<< acts
  104. where
  105. acts = do
  106. let dur = repeat 0.15
  107. sus <- randomRs (0.05, 0.45) <$> newStdGen
  108. return $ getZipList $ pressNumber <$> z cs <*> z dur <*> z sus
  109. -- | Short cut for ZipList
  110. z :: [a] -> ZipList a
  111. z = ZipList
  112. -- | Press a number.
  113. pressNumber :: Char -- ^ number
  114. -> Double -- ^ duration
  115. -> Double -- ^ sustain
  116. -> IO ()
  117. pressNumber c d s = do
  118. let [f1, f2] = maybe [0, 0] id $ M.lookup c tbl
  119. n <- snew "dtmf" [("freq1", f1), ("freq2", f2)]
  120. threadDelay $ floor $ d * 1000 * 1000
  121. nfree n
  122. threadDelay $ floor $ s * 1000 * 1000
  123. -- | Get number from stdin and dial.
  124. go :: IO ()
  125. go = drecv "dtmf" dtmf >> go'
  126. where
  127. go' = do
  128. c <- getChar
  129. pressNumber c 0.1 0.1
  130. go'