From: yomguy Date: Tue, 2 Mar 2010 16:18:28 +0000 (+0000) Subject: - add a spectrogram example X-Git-Tag: 0.3.2~170 X-Git-Url: https://git.parisson.com/?a=commitdiff_plain;h=5ba11d4d94d50e52e5c3c53fa0beafa4f6c36bbc;p=timeside.git - add a spectrogram example --- diff --git a/grapher/core.py b/grapher/core.py index fc277c6..a4cd641 100644 --- a/grapher/core.py +++ b/grapher/core.py @@ -250,23 +250,26 @@ class WaveformImage(object): class SpectrogramImage(object): - def __init__(self, image_width, image_height, fft_size, bg_color = None, color_scheme = None): - - #FIXME: bg_color is ignored - - if not color_scheme: - color_scheme = 'default' - - self.image = Image.new("P", (image_height, image_width)) + def __init__(self, image_width, image_height, nframes, samplerate, buffer_size, bg_color=None, color_scheme=None, filename=None): self.image_width = image_width self.image_height = image_height - self.fft_size = fft_size - + self.nframes = nframes + self.samplerate = samplerate + self.fft_size = buffer_size + self.buffer_size = buffer_size + self.filename = filename + if not color_scheme: + color_scheme = 'default' + self.image = Image.new("P", (self.image_height, self.image_width)) colors = color_schemes[color_scheme]['spectrogram'] - self.image.putpalette(interpolate_colors(colors, True)) + self.samples_per_pixel = self.nframes / float(self.image_width) + self.peaks_adapter = FixedSizeInputAdapter(int(self.samples_per_pixel), 1, pad=False) + self.peaks_adapter_nframes = self.peaks_adapter.nframes(self.nframes) + self.spectral_centroid = SpectralCentroid(self.fft_size, self.nframes, self.samplerate, numpy.hanning) + # generate the lookup which translates y-coordinate to fft-bin self.y_to_bin = [] f_min = 100.0 @@ -286,6 +289,7 @@ class SpectrogramImage(object): # a lot slower than using image.putadata and then rotating the image # so we store all the pixels in an array and then create the image when saving self.pixels = [] + self.pixel_cursor = 0 def draw_spectrum(self, x, spectrum): for (index, alpha) in self.y_to_bin: @@ -294,34 +298,25 @@ class SpectrogramImage(object): for y in range(len(self.y_to_bin), self.image_height): self.pixels.append(0) - def save(self, filename): - self.image.putdata(self.pixels) - self.image.transpose(Image.ROTATE_90).save(filename) - - -def create_spectrogram_png(input_filename, output_filename_s, image_width, image_height, fft_size, - bg_color = None, color_scheme = None): - audio_file = audiolab.sndfile(input_filename, 'read') - - samples_per_pixel = audio_file.get_nframes() / float(image_width) - processor = AudioProcessor(audio_file, fft_size, numpy.hanning) - - spectrogram = SpectrogramImage(image_width, image_height, fft_size, bg_color, color_scheme) - - for x in range(image_width): - - if x % (image_width/10) == 0: - sys.stdout.write('.') - sys.stdout.flush() - - seek_point = int(x * samples_per_pixel) - next_seek_point = int((x + 1) * samples_per_pixel) - (spectral_centroid, db_spectrum) = processor.spectral_centroid(seek_point) - spectrogram.draw_spectrum(x, db_spectrum) + def process(self, frames, eod): + if len(frames) == 1: + frames.shape = (len(frames),1) + buffer = frames.copy() + else: + buffer = frames[:,0].copy() + buffer.shape = (len(buffer),1) - spectrogram.save(output_filename_s) + for samples, end in self.peaks_adapter.process(buffer, eod): + if self.pixel_cursor == self.image_width: + break + # FIXME : too much repeated spectrum computation cycle + (spectral_centroid, db_spectrum) = self.spectral_centroid.process(buffer, True) + self.draw_spectrum(self.pixel_cursor, db_spectrum) + self.pixel_cursor += 1 - print " done" + def save(self): + self.image.putdata(self.pixels) + self.image.transpose(Image.ROTATE_90).save(self.filename) class Noise(object): diff --git a/tests/api/examples.py b/tests/api/examples.py index 506b209..3a9f582 100644 --- a/tests/api/examples.py +++ b/tests/api/examples.py @@ -264,6 +264,63 @@ class Waveform(Processor): return self.graph.image +class Spectrogram(Processor): + implements(IGrapher) + + BUFFER_SIZE = 512 + + @interfacedoc + def __init__(self, width=None, height=None, output=None, bg_color=None, color_scheme=None): + if width: + self.width = width + else: + self.width = 1500 + if height: + self.height = height + else: + self.height = 200 + self.bg_color = bg_color + self.color_scheme = color_scheme + self.filename = output + self.graph = None + + @staticmethod + @interfacedoc + def id(): + return "test_spectrogram" + + @staticmethod + @interfacedoc + def name(): + return "Spectrogram test" + + @interfacedoc + def set_colors(self, background, scheme): + self.bg_color = background + self.color_scheme = scheme + + @interfacedoc + def setup(self, channels=None, samplerate=None, nframes=None): + super(Spectrogram, self).setup(channels, samplerate, nframes) + if self.graph: + self.graph = None + self.adapter = FixedSizeInputAdapter(self.BUFFER_SIZE, channels, pad=True) + self.graph = SpectrogramImage(self.width, self.height, self.nframes(), self.samplerate(), self.BUFFER_SIZE, + bg_color=self.bg_color, color_scheme=self.color_scheme, filename=self.filename) + + @interfacedoc + def process(self, frames, eod=False): + for buffer, end in self.adapter.process(frames, eod): + self.graph.process(buffer, end) + return frames, eod + + @interfacedoc + def render(self): + if self.filename: + self.graph.save() + return self.graph.image + + class Duration(Processor): """A rather useless duration analyzer. Its only purpose is to test the nframes characteristic.""" diff --git a/tests/api/test_pipe_spectrogram.py b/tests/api/test_pipe_spectrogram.py new file mode 100644 index 0000000..742e4c1 --- /dev/null +++ b/tests/api/test_pipe_spectrogram.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +from timeside.tests.api import examples +from timeside.core import * +from timeside.api import * +import os.path + +use_gst = 1 +if use_gst: + from timeside.tests.api.gstreamer import FileDecoder, WavEncoder +else: + from timeside.tests.api.examples import FileDecoder, WavEncoder + +image_file = './spectrogram.png' +source = os.path.join(os.path.dirname(__file__), "../samples/sweep_source.mp3") + +decoder = FileDecoder(source) +spectrogram = examples.Spectrogram(width=1024, height=256, output=image_file, bg_color=(0,0,0), color_scheme='default') + +(decoder | spectrogram).run() + +print 'frames per pixel = ', spectrogram.graph.samples_per_pixel +print "render spectrogram to: %s" % image_file +spectrogram.render() + +