def start(self): print("Starting spectrum visualizer...") pygame.init() self.screen = pygame.display.set_mode((self.WIDTH, self.HEIGHT)) self.screen.fill((self.bg_color,self.bg_color,self.bg_color)) if self.plot_audio_history: self.screen.set_alpha(255) self.prev_screen = self.screen pygame.display.set_caption('Spectrum Analyzer -- (FFT-Peak: %05d Hz)' %self.ear.strongest_frequency) self.bin_font = pygame.font.Font('freesansbold.ttf', round(0.025*self.HEIGHT)) self.fps_font = pygame.font.Font('freesansbold.ttf', round(0.05*self.HEIGHT)) for i in range(self.ear.n_frequency_bins): if i == 0 or i == (self.ear.n_frequency_bins - 1): continue if i % self.tag_every_n_bins == 0: f_centre = self.ear.frequency_bin_centres[i] text = self.bin_font.render('%d Hz' %f_centre, True, (255, 255, 255) , (self.bg_color, self.bg_color, self.bg_color)) textRect = text.get_rect() x = i*(self.WIDTH / self.ear.n_frequency_bins) + (self.bar_width - textRect.x)/2 y = 0.98*self.HEIGHT textRect.center = (int(x),int(y)) self.bin_text_tags.append(text) self.bin_rectangles.append(textRect) self._is_running = True #Interactive components: self.button_height = round(0.05*self.HEIGHT) self.history_button = Button(text="Toggle Hist Mode", right=self.WIDTH, top=0, width=None, height=self.button_height) self.slow_bar_button = Button(text="Toggle Slow Bars", right=self.WIDTH, top=self.history_button.height, width=None, height=self.button_height)
class Spectrum_Visualizer: """ The Spectrum_Visualizer visualizes spectral FFT data using a simple PyGame GUI """ def __init__(self, ear): self.plot_audio_history = True self.ear = ear self.HEIGHT = 450 window_ratio = 24/9 self.HEIGHT = round(self.HEIGHT) self.WIDTH = round(window_ratio*self.HEIGHT) self.y_ext = [round(0.05*self.HEIGHT), self.HEIGHT] self.cm = cm.plasma #self.cm = cm.inferno self.toggle_history_mode() self.add_slow_bars = 1 self.add_fast_bars = 1 self.slow_bar_thickness = max(0.00002*self.HEIGHT, 1.25 / self.ear.n_frequency_bins) self.tag_every_n_bins = max(1,round(5 * (self.ear.n_frequency_bins / 51))) # Occasionally display Hz tags on the x-axis self.fast_bar_colors = [list((255*np.array(self.cm(i))[:3]).astype(int)) for i in np.linspace(0,255,self.ear.n_frequency_bins).astype(int)] self.slow_bar_colors = [list(np.clip((255*3.5*np.array(self.cm(i))[:3]).astype(int) , 0, 255)) for i in np.linspace(0,255,self.ear.n_frequency_bins).astype(int)] self.fast_bar_colors = self.fast_bar_colors[::-1] self.slow_bar_colors = self.slow_bar_colors[::-1] self.slow_features = [0]*self.ear.n_frequency_bins self.frequency_bin_max_energies = np.zeros(self.ear.n_frequency_bins) self.bin_text_tags, self.bin_rectangles = [], [] #Fixed init params: self.start_time = None self.vis_steps = 0 self.fps_interval = 10 self.fps = 0 self._is_running = False def toggle_history_mode(self): if self.plot_audio_history: self.bg_color = 10 #Background color self.decay_speed = 0.90 #Vertical decay of slow bars self.inter_bar_distance = 0 self.avg_energy_height = 0.1125 self.alpha_multiplier = 0.995 self.move_fraction = 0.0099 self.shrink_f = 0.994 else: self.bg_color = 60 self.decay_speed = 0.94 self.inter_bar_distance = int(0.2*self.WIDTH / self.ear.n_frequency_bins) self.avg_energy_height = 0.225 self.bar_width = (self.WIDTH / self.ear.n_frequency_bins) - self.inter_bar_distance #Configure the bars: self.slow_bars, self.fast_bars, self.bar_x_positions = [],[],[] for i in range(self.ear.n_frequency_bins): x = int(i* self.WIDTH / self.ear.n_frequency_bins) fast_bar = [int(x), int(self.y_ext[0]), math.ceil(self.bar_width), None] slow_bar = [int(x), None, math.ceil(self.bar_width), None] self.bar_x_positions.append(x) self.fast_bars.append(fast_bar) self.slow_bars.append(slow_bar) def start(self): print("Starting spectrum visualizer...") pygame.init() self.screen = pygame.display.set_mode((self.WIDTH, self.HEIGHT)) self.screen.fill((self.bg_color,self.bg_color,self.bg_color)) if self.plot_audio_history: self.screen.set_alpha(255) self.prev_screen = self.screen pygame.display.set_caption('Spectrum Analyzer -- (FFT-Peak: %05d Hz)' %self.ear.strongest_frequency) self.bin_font = pygame.font.Font('freesansbold.ttf', round(0.025*self.HEIGHT)) self.fps_font = pygame.font.Font('freesansbold.ttf', round(0.05*self.HEIGHT)) for i in range(self.ear.n_frequency_bins): if i == 0 or i == (self.ear.n_frequency_bins - 1): continue if i % self.tag_every_n_bins == 0: f_centre = self.ear.frequency_bin_centres[i] text = self.bin_font.render('%d Hz' %f_centre, True, (255, 255, 255) , (self.bg_color, self.bg_color, self.bg_color)) textRect = text.get_rect() x = i*(self.WIDTH / self.ear.n_frequency_bins) + (self.bar_width - textRect.x)/2 y = 0.98*self.HEIGHT textRect.center = (int(x),int(y)) self.bin_text_tags.append(text) self.bin_rectangles.append(textRect) self._is_running = True #Interactive components: self.button_height = round(0.05*self.HEIGHT) self.history_button = Button(text="Toggle Hist Mode", right=self.WIDTH, top=0, width=None, height=self.button_height) self.slow_bar_button = Button(text="Toggle Slow Bars", right=self.WIDTH, top=self.history_button.height, width=None, height=self.button_height) def stop(self): print("Stopping spectrum visualizer...") del self.fps_font del self.bin_font del self.screen del self.prev_screen pygame.quit() self._is_running = False def toggle_display(self): ''' This function can be triggered to turn on/off the display ''' if self._is_running: self.stop() else: self.start() def update(self): for event in pygame.event.get(): if self.history_button.click(): self.plot_audio_history = not self.plot_audio_history self.toggle_history_mode() if self.slow_bar_button.click(): self.add_slow_bars = not self.add_slow_bars self.slow_features = [0]*self.ear.n_frequency_bins if np.min(self.ear.bin_mean_values) > 0: self.ear.frequency_bin_energies = self.avg_energy_height * self.ear.frequency_bin_energies / self.ear.bin_mean_values if self.plot_audio_history: new_w, new_h = int((2+self.shrink_f)/3*self.WIDTH), int(self.shrink_f*self.HEIGHT) #new_w, new_h = int(self.shrink_f*self.WIDTH), int(self.shrink_f*self.HEIGHT) horizontal_pixel_difference = self.WIDTH - new_w prev_screen = pygame.transform.scale(self.prev_screen, (new_w, new_h)) self.screen.fill((self.bg_color,self.bg_color,self.bg_color)) if self.plot_audio_history: new_pos = int(self.move_fraction*self.WIDTH - (0.0133*self.WIDTH)), int(self.move_fraction*self.HEIGHT) self.screen.blit(pygame.transform.rotate(prev_screen, 180), new_pos) if self.start_time is None: self.start_time = time.time() self.vis_steps += 1 if self.vis_steps%self.fps_interval == 0: self.fps = self.fps_interval / (time.time()-self.start_time) self.start_time = time.time() self.text = self.fps_font.render('Fps: %.1f' %(self.fps), True, (255, 255, 255) , (self.bg_color, self.bg_color, self.bg_color)) self.textRect = self.text.get_rect() self.textRect.x, self.textRect.y = round(0.015*self.WIDTH), round(0.03*self.HEIGHT) pygame.display.set_caption('Spectrum Analyzer -- (FFT-Peak: %05d Hz)' %self.ear.strongest_frequency) self.plot_bars() #Draw text tags: self.screen.blit(self.text, self.textRect) if len(self.bin_text_tags) > 0: cnt = 0 for i in range(self.ear.n_frequency_bins): if i == 0 or i == (self.ear.n_frequency_bins - 1): continue if i % self.tag_every_n_bins == 0: self.screen.blit(self.bin_text_tags[cnt], self.bin_rectangles[cnt]) cnt += 1 self.history_button.draw(self.screen) self.slow_bar_button.draw(self.screen) pygame.display.flip() def plot_bars(self): bars, slow_bars, new_slow_features = [], [], [] local_height = self.y_ext[1] - self.y_ext[0] feature_values = self.ear.frequency_bin_energies[::-1] for i in range(len(self.ear.frequency_bin_energies)): feature_value = feature_values[i] * local_height self.fast_bars[i][3] = int(feature_value) if self.plot_audio_history: self.fast_bars[i][3] = int(feature_value + 0.02*self.HEIGHT) if self.add_slow_bars: slow_feature_value = max(self.slow_features[i]*self.decay_speed, feature_value) new_slow_features.append(slow_feature_value) self.slow_bars[i][1] = int(self.fast_bars[i][1] + slow_feature_value) self.slow_bars[i][3] = int(self.slow_bar_thickness * local_height) if self.add_fast_bars: for i, fast_bar in enumerate(self.fast_bars): pygame.draw.rect(self.screen,self.fast_bar_colors[i],fast_bar,0) if self.plot_audio_history: self.prev_screen = self.screen.copy() self.prev_screen = pygame.transform.rotate(self.prev_screen, 180) self.prev_screen.set_alpha(self.prev_screen.get_alpha()*self.alpha_multiplier) if self.add_slow_bars: for i, slow_bar in enumerate(self.slow_bars): pygame.draw.rect(self.screen,self.slow_bar_colors[i],slow_bar,0) self.slow_features = new_slow_features #Draw everything: self.screen.blit(pygame.transform.rotate(self.screen, 180), (0, 0))