1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 from timeside.core import implements, interfacedoc
23 from timeside.api import IGrapher
24 from timeside.grapher.core import *
28 """ Builds a PIL image representing a spectrogram of the audio stream (level vs. frequency vs. time).
29 Adds pixels iteratively thanks to the adapter providing fixed size frame buffers."""
30
31 implements(IGrapher)
32
33 @interfacedoc
34 - def __init__(self, width=1024, height=256, bg_color=(0,0,0), color_scheme='default'):
40
41 @staticmethod
42 @interfacedoc
44 return "spectrogram_log"
45
46 @staticmethod
47 @interfacedoc
49 return "SpectrogramLog"
50
51 @interfacedoc
52 - def setup(self, channels=None, samplerate=None, blocksize=None, totalframes=None):
58
60 """generate the lookup which translates y-coordinate to fft-bin"""
61
62 f_min = float(self.lower_freq)
63 f_max = float(self.higher_freq)
64 y_min = math.log10(f_min)
65 y_max = math.log10(f_max)
66 for y in range(self.image_height):
67 freq = math.pow(10.0, y_min + y / (self.image_height - 1.0) *(y_max - y_min))
68 fft_bin = freq / f_max * (self.fft_size/2 + 1)
69 if fft_bin < self.fft_size/2:
70 alpha = fft_bin - int(fft_bin)
71 self.y_to_bin.append((int(fft_bin), alpha * 255))
72
74 for (index, alpha) in self.y_to_bin:
75 self.pixels.append( int( ((255.0-alpha) * spectrum[index] + alpha * spectrum[index + 1] )) )
76 for y in range(len(self.y_to_bin), self.image_height):
77 self.pixels.append(0)
78
79 @interfacedoc
80 - def process(self, frames, eod=False):
81 if len(frames) != 1:
82 chunk = frames[:,0].copy()
83 chunk.shape = (len(chunk),1)
84 for samples, end in self.pixels_adapter.process(chunk, eod):
85 if self.pixel_cursor < self.image_width:
86 (spectral_centroid, db_spectrum) = self.spectrum.process(samples, True)
87 self.draw_spectrum(self.pixel_cursor, db_spectrum)
88 self.pixel_cursor += 1
89 return frames, eod
90
91 @interfacedoc
92 - def post_process(self):
93 """ Apply last 2D transforms"""
94 self.image.putdata(self.pixels)
95 self.image = self.image.transpose(Image.ROTATE_90)
96