|
|
- import math
-
- import matplotlib.pyplot as plt
- import librosa.display
- import numpy as np
-
-
- # binary search
- import pygame
-
-
- def bin_search(arr, target):
- index = int(len(arr) / 2)
- min_index = 0
- max_index = len(arr) - 1
- found = False
-
- if target < arr[0]:
- return 0
-
- if target > arr[len(arr) - 1]:
- return len(arr) - 1
-
- while not found:
-
- if min_index == len(arr) - 2:
- return len(arr) - 1
-
- if arr[index] < target < arr[index + 1] or arr[index] == target:
- return index
-
- if arr[index] > target:
- max_index = index
- else:
- min_index = index
-
- index = int((min_index + max_index) / 2)
-
- def rotate(xy, theta):
- # https://en.wikipedia.org/wiki/Rotation_matrix#In_two_dimensions
- cos_theta, sin_theta = math.cos(theta), math.sin(theta)
-
- return (
- xy[0] * cos_theta - xy[1] * sin_theta,
- xy[0] * sin_theta + xy[1] * cos_theta
- )
-
-
- def translate(xy, offset):
- return xy[0] + offset[0], xy[1] + offset[1]
-
-
- def clamp(min_value, max_value, value):
-
- if value < min_value:
- return min_value
-
- if value > max_value:
- return max_value
-
- return value
-
-
- class AudioAnalyzer:
-
- def __init__(self):
-
- self.frequencies_index_ratio = 0 # array for frequencies
- self.time_index_ratio = 0 # array of time periods
- self.spectrogram = None # a matrix that contains decibel values according to frequency and time indexes
-
- def load(self, filename):
-
- time_series, sample_rate = librosa.load(filename) # getting information from the file
-
- # getting a matrix which contains amplitude values according to frequency and time indexes
- stft = np.abs(librosa.stft(time_series, hop_length=512, n_fft=2048*4))
-
- self.spectrogram = librosa.amplitude_to_db(stft, ref=np.max) # converting the matrix to decibel matrix
-
- frequencies = librosa.core.fft_frequencies(n_fft=2048*4) # getting an array of frequencies
-
- # getting an array of time periodic
- times = librosa.core.frames_to_time(np.arange(self.spectrogram.shape[1]), sr=sample_rate, hop_length=512, n_fft=2048*4)
-
- self.time_index_ratio = len(times)/times[len(times) - 1]
-
- self.frequencies_index_ratio = len(frequencies)/frequencies[len(frequencies)-1]
-
-
-
-
- def show(self):
-
- librosa.display.specshow(self.spectrogram,
- y_axis='log', x_axis='time')
-
- plt.title('spectrogram')
- plt.colorbar(format='%+2.0f dB')
- plt.tight_layout()
- plt.show()
-
- def get_decibel(self, target_time, freq):
-
- return self.spectrogram[int(freq*self.frequencies_index_ratio)][int(target_time*self.time_index_ratio)]
-
- # returning the current decibel according to the indexes which found by binary search
- # return self.spectrogram[bin_search(self.frequencies, freq), bin_search(self.times, target_time)]
-
- def get_decibel_array(self, target_time, freq_arr):
-
- arr = []
-
- for f in freq_arr:
- arr.append(self.get_decibel(target_time,f))
-
- return arr
-
-
- class AudioBar:
-
- def __init__(self, x, y, freq, color, width=50, min_height=10, max_height=100, min_decibel=-80, max_decibel=0):
-
- self.x, self.y, self.freq = x, y, freq
-
- self.color = color
-
- self.width, self.min_height, self.max_height = width, min_height, max_height
-
- self.height = min_height
-
- self.min_decibel, self.max_decibel = min_decibel, max_decibel
-
- self.__decibel_height_ratio = (self.max_height - self.min_height)/(self.max_decibel - self.min_decibel)
-
- def update(self, dt, decibel):
-
- desired_height = decibel * self.__decibel_height_ratio + self.max_height
-
- speed = (desired_height - self.height)/0.1
-
- self.height += speed * dt
-
- self.height = clamp(self.min_height, self.max_height, self.height)
-
- def render(self, screen):
-
- pygame.draw.rect(screen, self.color, (self.x, self.y + self.max_height - self.height, self.width, self.height))
-
-
- class AverageAudioBar(AudioBar):
-
- def __init__(self, x, y, rng, color, width=50, min_height=10, max_height=100, min_decibel=-80, max_decibel=0):
- super().__init__(x, y, 0, color, width, min_height, max_height, min_decibel, max_decibel)
-
- self.rng = rng
-
- self.avg = 0
-
- def update_all(self, dt, time, analyzer):
-
- self.avg = 0
-
- for i in self.rng:
- self.avg += analyzer.get_decibel(time, i)
-
- self.avg /= len(self.rng)
- self.update(dt, self.avg)
-
-
- class RotatedAverageAudioBar(AverageAudioBar):
-
- def __init__(self, x, y, rng, color, angle=0, width=50, min_height=10, max_height=100, min_decibel=-80, max_decibel=0):
- super().__init__(x, y, 0, color, width, min_height, max_height, min_decibel, max_decibel)
-
- self.rng = rng
-
- self.rect = None
-
- self.angle = angle
-
-
- def render(self, screen):
-
- pygame.draw.polygon(screen, self.color, self.rect.points)
-
- def render_c(self, screen, color):
-
- pygame.draw.polygon(screen, color, self.rect.points)
-
- def update_rect(self):
- self.rect = Rect(self.x, self.y, self.width, self.height)
-
- self.rect.rotate(self.angle)
-
-
- class Rect:
-
- def __init__(self,x ,y, w, h):
- self.x, self.y, self.w, self.h = x,y, w, h
-
- self.points = []
-
- self.origin = [self.w/2,0]
- self.offset = [self.origin[0] + x, self.origin[1] + y]
-
- self.rotate(0)
-
- def rotate(self, angle):
-
- template = [
- (-self.origin[0], self.origin[1]),
- (-self.origin[0] + self.w, self.origin[1]),
- (-self.origin[0] + self.w, self.origin[1] - self.h),
- (-self.origin[0], self.origin[1] - self.h)
- ]
-
- self.points = [translate(rotate(xy, math.radians(angle)), self.offset) for xy in template]
-
- def draw(self,screen):
- pygame.draw.polygon(screen, (255,255, 0), self.points)
|