-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathSpectrogram.hs
111 lines (102 loc) · 4.14 KB
/
Spectrogram.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
{-# LANGUAGE ConstraintKinds #-}
module Spectrogram(
spectrogram
) where
import Signal
import Windows
import qualified Data.Vector.Unboxed as U
import Data.Vector.Unboxed((!))
import Data.Complex
import Data.Bits
import Common
import Transform
import Graphics.PDF
import Plot
import Control.Applicative((<$>))
import Data.List(foldl1')
import Debug.Trace
debug a = trace (show a) a
_spectrum :: (Sample a, Num a)
=> Int
-> Double
-> U.Vector a
-> U.Vector Double
_spectrum n t d =
let l = 1 `shiftL` n :: Int
complexd = U.map (:+ 0) . U.map toDouble $ d
m (x :+ y) =
let x' = toDouble x
y' = toDouble y
in
t*(x'*x' + y'*y') / fromIntegral l
in
U.map m . fft $ complexd
prologSpect l b wi hi Nothing _ = return ()
prologSpect l b wi hi (Just r) _ = do
withNewContext $ do
applyMatrix $ translate (l :+ b)
drawXObject r
genPicture :: ((Double, Double) -> Double)
-> Int
-> Int
-> CoordinateMapping Double Double
-> PDF (Maybe (PDFReference RawImage))
genPicture value w h (plotToPixel, pixelToPlot) = Just <$> do
runPixmap w h $ do
let drawValue p = do
let pl = pixelToPlot p
v = log (1 + value pl) / log 2.0
col = Rgb v 0 0
setColor col 1.0
pixel p
mapM_ drawValue ( [ fromIntegral col :+ fromIntegral row | col <- [0..w-1], row <- [0..h-1]])
-- We display only up to sampling frequency / 2 because the spectrum is even.
-- So, it allow a zooming a the frequency that matters instead of displaying redudant information
spectrogram :: (Sample a, Show a)
=> Sampled Time a -- ^ Signal
-> Time -- ^ Duration
-> (Int -> Int -> a -> a) -- ^ Window
-> Int -- ^ Overlap
-> StyledSignal Double Double
spectrogram signal duration window overlap =
let samplingF = rate signal
winSize :: Int
winExp = 8
winSize = 1 `shiftL` winExp
freqResolution = (getF samplingF) / fromIntegral winSize
frames = frameWithWinAndOverlap winSize overlap window signal
nbFrames :: Int
nbFrames = (floor (getT duration * getF (rate frames)))
listOfSpectra = map (U.slice 0 (winSize `shiftR` 1)) .
map (_spectrum winExp (1.0 / getF samplingF)) . takeS nbFrames $ (getSignal frames)
theSpectrum = U.concat listOfSpectra
thePeak = U.maximum theSpectrum
energyLocation = foldl1' (U.zipWith max) listOfSpectra
(lowest,_) = U.span (<= thePeak * 0.1) energyLocation
(highest,_) = U.span (<= thePeak * 0.1) (U.reverse energyLocation)
startFrequency = fromIntegral (U.length lowest) / fromIntegral winSize * getF samplingF
stopFrequency = fromIntegral (U.length energyLocation - U.length highest) / fromIntegral winSize * getF samplingF
normalizedSpectrum = U.map (/ thePeak) theSpectrum
totalElements = U.length normalizedSpectrum
value (i,j) =
let pos = floor i*(winSize `shiftR` 1) + floor (j / freqResolution)
in
if pos >= totalElements
then
normalizedSpectrum ! (totalElements - 1)
else
normalizedSpectrum ! pos
left = leftMargin (defaultPlotStyle)
bottom = bottomMargin (defaultPlotStyle)
style = defaultPlotStyle { title = Just "Spectrogram"
, horizontalBounds = Just (0, fromIntegral nbFrames)
, verticalBounds = Just (startFrequency,stopFrequency)
, prolog = prologSpect left bottom
, prologRsrc = genPicture value
, horizontalLabel = Just "frame"
, verticalLabel = Just "Hz"
}
l=[] :: [[Double]]
theFrames = [] :: [Double]
in
signalsWithStyle False theFrames l style