Vaatii pygame
ja pyaudio
-kirjastot.
Hyödyllisiä Pythonin ominaisuuksia:
import cmath imaginaariyksikkö = 1j eulerin_kaava = cmath.exp(1j * 2 * cmath.pi * ...)
import pygame import pyaudio import math import cmath import struct def fft(x): # TODO: toteuta fft pass def isclose(a, b): return cmath.isclose(a, b, rel_tol=1e-4, abs_tol=1e-4) def allclose(a, b): return all(isclose(x, y) for x, y in zip(a, b)) # Testejä taulukon koolle N=4 w = cmath.exp(2j * cmath.pi / 4) tulos = fft([1, 1, 1, 1]) assert allclose(tulos, [4, 0, 0, 0]), tulos tulos = fft([1, w, w**2, w**3]) assert allclose(tulos, [0, 4, 0, 0]), tulos tulos = fft([1, w**2, w**4, w**6]) assert allclose(tulos, [0, 0, 4, 0]), tulos tulos = fft([1, w**3, w**6, w**9]) assert allclose(tulos, [0, 0, 0, 4]), tulos pygame.init() screen_width = 800 screen_height = 600 screen = pygame.display.set_mode((screen_width, screen_height)) pygame.display.set_caption('FFT-spektri') clock = pygame.time.Clock() BLACK = (0, 0, 0) WHITE = (255, 255, 255) FORMAT = pyaudio.paInt16 CHANNELS = 1 RATE = 44100 CHUNK = 1024 # Kerrallaan analysoitavat näytteet p = pyaudio.PyAudio() stream = p.open(format=FORMAT, channels=CHANNELS, rate=RATE, input=True, frames_per_buffer=CHUNK) hann_window = [0.5 - 0.5 * math.cos(2 * math.pi * n / CHUNK) for n in range(CHUNK)] def draw_spectrum(spectrum, log_freq, screen_width, screen_height): screen.fill(BLACK) N = len(spectrum) for i, amp in enumerate(spectrum): # Muutetaan amplitudi desibeleiksi if amp <= 1e-8: dB = -100 else: dB = 20. * math.log10(amp) # Skaalaus: Näytöllä näkyy -60dB..10dB y_pos = (dB + 60.) / 70. * screen_height if log_freq: x_pos = math.log10(i+1) / math.log10(N) * screen_width else: x_pos = i / N * screen_width pygame.draw.line(screen, WHITE, (x_pos, screen_height), (x_pos, screen_height - y_pos), 1) log_freq = False running = True while running: for event in pygame.event.get(): if event.type == pygame.QUIT: running = False elif event.type == pygame.KEYDOWN: if event.key == pygame.K_l: # Paina "L" niin taajuudet näytetään logaritmiasteikolla log_freq = not log_freq # Skipataan ylimääräinen data avail = stream.get_read_available() if avail > CHUNK: stream.read(avail-CHUNK) # Luetaan CHUNK verran näytteitä data = stream.read(CHUNK) samples = struct.unpack(str(CHUNK) + 'h', data) # Muutetaan välille -1..1 samples = [sample / (1<<16) for sample in samples] # Ikkunointi (vältetään epäjatkuvuuskohta reunoilla) windowed_samples = [s * w for s, w in zip(samples, hann_window)] # Kutsutaan FFT-toteutusta ja käytetään vain "positiiviset" taajuudet fft_result = fft(windowed_samples)[:CHUNK//2] amplitude_spectrum = [abs(v) / math.sqrt(CHUNK) for v in fft_result] draw_spectrum(amplitude_spectrum, log_freq, screen_width, screen_height) pygame.display.flip() clock.tick(30) pygame.quit() stream.stop_stream() stream.close() p.terminate()