From 830ed35f21870199e3082eb42a6debd435febf21 Mon Sep 17 00:00:00 2001 From: Avery Huang Date: Fri, 10 Jun 2022 23:06:52 -0400 Subject: [PATCH] add basline vectorscope --- .gitignore | 3 ++- main.py | 10 +++++++--- vector.py | 38 ++++++++++++++++++++++++++++++++++---- 3 files changed, 43 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index 6c3b03a..d1f53bb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ todo.txt __pycache__ imgs -*.mp4 \ No newline at end of file +*.mp4 +testaudio/ \ No newline at end of file diff --git a/main.py b/main.py index 2bae50f..e253791 100644 --- a/main.py +++ b/main.py @@ -1,21 +1,21 @@ # you might need to install these: # pip install pydub # pip install pygame +# pip install scipy from pydub import AudioSegment import pygame -from scipy import signal import math -import sys import numpy as np import random from star import Star from nebula import Nebula from oscilloscope import Oscilloscope +from vector import Vectorscope # config vars - edit these to alter the visualizer -FILENAME = "audio/mightaswellbedead.wav" +FILENAME = "testaudio/sinetest.mp3" WINDOW_SIZE = 400 HOP_SIZE = 100 @@ -99,7 +99,10 @@ def rand_color(base: int) -> pygame.Color: RECORDING = False oscope = Oscilloscope(screen, SCOPE_WIDTH, SCOPE_HEIGHT, SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, MAX_AMP) +vscope = Vectorscope(screen, SCOPE_WIDTH, SCOPE_HEIGHT, SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, MAX_AMP) +# visualizer still lags behind by small amounts - will investigate further +# current theory is that clock tick lags a bit while music plays uninterrupted while total_elapsed * smp_per_second + WINDOW_SIZE < NUM_SAMPLES and RUNNING: for event in pygame.event.get(): if event.type == pygame.QUIT: @@ -173,6 +176,7 @@ def rand_color(base: int) -> pygame.Color: neb.draw() oscope.draw(window) + vscope.draw(left_win, right_win, 10) pygame.display.flip() diff --git a/vector.py b/vector.py index 0d8d1a1..5e54283 100644 --- a/vector.py +++ b/vector.py @@ -1,5 +1,10 @@ import pygame +import math +import numpy as np +import scipy +from scipy import signal +# vectorscope based off of Wave Candy's vectorscope class Vectorscope: def __init__(self, screen, width, height, x, y, max_amp): self.screen = screen @@ -13,9 +18,33 @@ def draw(self, left_samples, right_samples, resolution): # x is determined by proportion of magnitude on left/right # i.e. 100% of signal left = max on left axis # y is determined by ??? - needs more experimentation - pass - def samples_to_hsla(samples: list) -> pygame.Color: + # split the left, right samples into indivudal windows, based on resolution, and get their averages + left_windows = np.split(np.array(left_samples), np.arange(resolution, len(left_samples), resolution))[:-1] + right_windows = np.split(np.array(right_samples), np.arange(resolution, len(right_samples), resolution))[:-1] + + left_windows = list(map(lambda l: np.sum(l) / len(l), left_windows)) + right_windows = list(map(lambda l: np.sum(l) / len(l), right_windows)) + + points = [] + for left_avg, right_avg in zip(left_windows, right_windows): + l_x = self.x - (self.width / 2) * (left_avg / self.max_amp) + l_y = self.y - (self.height / 2) * (left_avg / self.max_amp) + + r_x = self.x + (self.width / 2) * (right_avg / self.max_amp) + r_y = self.y - (self.height / 2) * (right_avg / self.max_amp) + + l_prop = abs(left_avg / (abs(left_avg) + abs(right_avg))) + r_prop = abs(right_avg / (abs(left_avg) + abs(right_avg))) + points.append( ( l_x * l_prop + r_x * r_prop, l_y * l_prop + r_y * r_prop ) ) + + color = pygame.Color(255, 0, 0) #self.samples_to_hsla(list(map(lambda l, r: l + r / 2, left_samples, right_samples))) + + for point in points: + pygame.draw.circle(self.screen, color, point, 5, 1) + + + def samples_to_hsla(self, samples: list) -> pygame.Color: color: pygame.Color = pygame.Color(0, 0, 0) window = signal.windows.blackmanharris(10000) @@ -27,5 +56,6 @@ def samples_to_hsla(samples: list) -> pygame.Color: return color # formula adapted from https://en.wikipedia.org/wiki/Piano_key_frequencies - hue: int = math.ceil(41.9 * math.log(freq / 440, 2) + 171.11) - # saturation = rms(samples) + hue = math.ceil(41.9 * math.log(freq / 440, 2) + 171.11) + color.hsla = (hue, 100, 100, 100) + return color