File: spectrum.sml

package info (click to toggle)
smlsharp 4.2.0-1~exp1
  • links: PTS, VCS
  • area: main
  • in suites: experimental
  • size: 125,348 kB
  • sloc: ansic: 16,737; sh: 4,347; makefile: 2,228; java: 742; haskell: 493; ruby: 305; cpp: 284; pascal: 256; ml: 255; lisp: 141; asm: 97; sql: 74
file content (174 lines) | stat: -rw-r--r-- 4,203 bytes parent folder | download | duplicates (3)
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
(**
 * spectrum.sml
 *
 * @copyright (C) 2021 SML# Development Team.
 * @author UENO Katsuhiro
 * @version $Id: spectrum.sml,v 1.3 2007/04/02 09:42:29 katsu Exp $
 *)

functor PowerSpectrum (
  val numSamples : int
  val samplingFreq : real
) : sig

  val numSamples : int
  val samplingFreq : real
  val samplingCycle : real
  val interval : real

  val samples : real array
  val spectrum : real array
  val calc : unit -> unit

end =
struct

  val numSamples = numSamples

  val samplingFreq = samplingFreq
  val samplingCycle = 1.0 / samplingFreq
  fun freq x = real x / samplingCycle / real numSamples

  val interval = samplingCycle * real numSamples  (* sec *)

  (* sine window *)
  val window =
      Vector.tabulate (numSamples,
                       fn n => Libm.sin (Math.pi * real n / real numSamples))

  val samples = Array.array (numSamples, 0.0)
  val spectrum = Array.array (numSamples div 2 - 1, 0.0)

  val factor = real (numSamples div 2)

  fun applyWindow 0 = ()
    | applyWindow n =
      let
        val n = n - 1
        val x = Array.sub (samples, n) * Vector.sub (window, n)
      in
        Array.update (samples, n, x);
        applyWindow n
      end

  fun calcSpectrum (r, i) =
      if r >= i then ()
      else
        let
          val re = Array.sub (samples, r) / factor
          val im = Array.sub (samples, i) / factor
          val power = re * re + im * im
          val db = 10.0 * Libm.log10 power
        in
          Array.update (spectrum, r - 1, db);
          calcSpectrum (r + 1, i - 1)
        end

  (*
   * calculate `spectrum' from `samples'.
   * `samples' will be destructed.
   *)
  fun calc () =
      (applyWindow numSamples;
       GSL.fft_real_radix2_transform (samples, 1, numSamples);
       calcSpectrum (1, numSamples - 1))

end


functor SpectrumAnalyzer (
  val numSamples : int
  val samplingFreq : real
) =
struct

  val minSample = 1.0 / 32768.0
  val minPower = 10.0 * Libm.log10 (minSample * minSample)

  (* 0 - ~100dB -> height 100 - 0 *)
  fun powerToHeight ary =
      Array.modifyi
          (fn (n,x) =>
              let val x = 100.0 + x
              in if x < 0.0 then 0.0
                 else if x > 100.0 then 100.0
                 else x
              end)
          ary

  (* make a summary of src to dst *)
  local
    fun max (ary, i, j, r:real) =
        if i >= j then r
        else
          let
            val x = Array.sub (ary, i)
            val r = if r > x then r else x
          in
            max (ary, i + 1, j, r)
          end

    fun summary (src, offset, len, dst, n) =
        let
          val dstlen = Array.length dst
        in
          if n >= dstlen then ()
          else
            let
              val i = offset + n * len div dstlen
              val j = offset + (n + 1) * len div dstlen
              val x = max (src, i, j, 0.0)
            in
              Array.update (dst, n, x);
              summary (src, offset, len, dst, n + 1)
            end
        end
  in
  fun summarize (src, offset, len, dst) =
      summary (src, offset, len, dst, 0)

  end

  fun toMonoral (ary, dst) =
      Array.appi
        (fn (i,x) =>
            let
              val c1 = Word32.toInt (Word32.>> (x, 0w16))
              val c2 = Word32.toInt (Word32.andb (x, 0wxffff))
              val c1 = if c1 >= 32768 then c1 - 65536 else c1
              val c2 = if c2 >= 32768 then c2 - 65536 else c2
              val x = if c1 > c2 then c1 else c2
            in
              Array.update (dst, i, real x / 32768.0)
            end)
        ary


  (* for debug *)
  fun readBlockPulse dst =
      (
        Array.modifyi
          (fn (n,_) =>
              if numSamples div 4 <= n andalso n < numSamples * 3 div 4
              then 1.0 else 0.0)
          dst;
        false
      )

  fun readSineWave (speed, dst) =
      (
        Array.modifyi
          (fn (n,_) =>
              Libm.sin (speed * 2.0 * Math.pi * real n / real numSamples))
          dst;
        false
      )
              
  fun printAry ary =
      Array.appi
        (fn (n, x) =>
            print (Int.toString n^" : "
                   ^Real.fmt (StringCvt.FIX (SOME 6)) x^"\n"))
        ary

end