This repository has been archived on 2020-08-22. You can view files and clone it, but cannot push or open issues or pull requests.
pyaudviz/src/components/original.py

245 lines
8.6 KiB
Python

import numpy
from PIL import Image, ImageDraw
from PyQt5 import QtGui, QtCore, QtWidgets
from PyQt5.QtGui import QColor
import os
import time
from copy import copy
from component import Component
from frame import BlankFrame
class Component(Component):
'''Classic Visualizer'''
modified = QtCore.pyqtSignal(int, dict)
@classmethod
def names(cls):
return ['Original Audio Visualization']
def widget(self, parent):
self.parent = parent
self.settings = self.parent.core.settings
self.visColor = (255, 255, 255)
self.scale = 20
self.y = 0
self.canceled = False
page = self.loadUi('original.ui')
page.comboBox_visLayout.addItem("Classic")
page.comboBox_visLayout.addItem("Split")
page.comboBox_visLayout.addItem("Bottom")
page.comboBox_visLayout.addItem("Top")
page.comboBox_visLayout.setCurrentIndex(0)
page.comboBox_visLayout.currentIndexChanged.connect(self.update)
page.lineEdit_visColor.setText('%s,%s,%s' % self.visColor)
page.pushButton_visColor.clicked.connect(lambda: self.pickColor())
btnStyle = "QPushButton { background-color : %s; outline: none; }" \
% QColor(*self.visColor).name()
page.pushButton_visColor.setStyleSheet(btnStyle)
page.lineEdit_visColor.textChanged.connect(self.update)
page.spinBox_scale.valueChanged.connect(self.update)
page.spinBox_y.valueChanged.connect(self.update)
self.page = page
return page
def update(self):
self.layout = self.page.comboBox_visLayout.currentIndex()
self.visColor = self.RGBFromString(self.page.lineEdit_visColor.text())
self.scale = self.page.spinBox_scale.value()
self.y = self.page.spinBox_y.value()
super().update()
def loadPreset(self, pr, presetName=None):
super().loadPreset(pr, presetName)
self.page.lineEdit_visColor.setText('%s,%s,%s' % pr['visColor'])
btnStyle = "QPushButton { background-color : %s; outline: none; }" \
% QColor(*pr['visColor']).name()
self.page.pushButton_visColor.setStyleSheet(btnStyle)
self.page.comboBox_visLayout.setCurrentIndex(pr['layout'])
self.page.spinBox_scale.setValue(pr['scale'])
self.page.spinBox_y.setValue(pr['y'])
def savePreset(self):
return {
'preset': self.currentPreset,
'layout': self.layout,
'visColor': self.visColor,
'scale': self.scale,
'y': self.y,
}
def previewRender(self, previewWorker):
spectrum = numpy.fromfunction(
lambda x: float(self.scale)/2500*(x-128)**2, (255,), dtype="int16")
width = int(self.settings.value('outputWidth'))
height = int(self.settings.value('outputHeight'))
return self.drawBars(
width, height, spectrum, self.visColor, self.layout
)
def preFrameRender(self, **kwargs):
super().preFrameRender(**kwargs)
self.smoothConstantDown = 0.08
self.smoothConstantUp = 0.8
self.lastSpectrum = None
self.spectrumArray = {}
self.width = int(self.settings.value('outputWidth'))
self.height = int(self.settings.value('outputHeight'))
for i in range(0, len(self.completeAudioArray), self.sampleSize):
if self.canceled:
break
self.lastSpectrum = self.transformData(
i, self.completeAudioArray, self.sampleSize,
self.smoothConstantDown, self.smoothConstantUp,
self.lastSpectrum)
self.spectrumArray[i] = copy(self.lastSpectrum)
progress = int(100*(i/len(self.completeAudioArray)))
if progress >= 100:
progress = 100
pStr = "Analyzing audio: "+str(progress)+'%'
self.progressBarSetText.emit(pStr)
self.progressBarUpdate.emit(int(progress))
def frameRender(self, layerNo, frameNo):
arrayNo = frameNo * self.sampleSize
return self.drawBars(
self.width, self.height,
self.spectrumArray[arrayNo],
self.visColor, self.layout)
def pickColor(self):
RGBstring, btnStyle = super().pickColor()
if not RGBstring:
return
self.page.lineEdit_visColor.setText(RGBstring)
self.page.pushButton_visColor.setStyleSheet(btnStyle)
def transformData(
self, i, completeAudioArray, sampleSize,
smoothConstantDown, smoothConstantUp, lastSpectrum):
if len(completeAudioArray) < (i + sampleSize):
sampleSize = len(completeAudioArray) - i
window = numpy.hanning(sampleSize)
data = completeAudioArray[i:i+sampleSize][::1] * window
paddedSampleSize = 2048
paddedData = numpy.pad(
data, (0, paddedSampleSize - sampleSize), 'constant')
spectrum = numpy.fft.fft(paddedData)
sample_rate = 44100
frequencies = numpy.fft.fftfreq(len(spectrum), 1./sample_rate)
y = abs(spectrum[0:int(paddedSampleSize/2) - 1])
# filter the noise away
# y[y<80] = 0
y = self.scale * numpy.log10(y)
y[numpy.isinf(y)] = 0
if lastSpectrum is not None:
lastSpectrum[y < lastSpectrum] = \
y[y < lastSpectrum] * smoothConstantDown + \
lastSpectrum[y < lastSpectrum] * (1 - smoothConstantDown)
lastSpectrum[y >= lastSpectrum] = \
y[y >= lastSpectrum] * smoothConstantUp + \
lastSpectrum[y >= lastSpectrum] * (1 - smoothConstantUp)
else:
lastSpectrum = y
x = frequencies[0:int(paddedSampleSize/2) - 1]
return lastSpectrum
def drawBars(self, width, height, spectrum, color, layout):
vH = height-height/8
bF = width / 64
bH = bF / 2
bQ = bF / 4
imTop = BlankFrame(width, height)
draw = ImageDraw.Draw(imTop)
r, g, b = color
color2 = (r, g, b, 125)
bP = height / 1200
for j in range(0, 63):
draw.rectangle((
bH + j * bF, vH+bQ, bH + j * bF + bF, vH + bQ -
spectrum[j * 4] * bP - bH), fill=color2)
draw.rectangle((
bH + bQ + j * bF, vH, bH + bQ + j * bF + bH, vH -
spectrum[j * 4] * bP), fill=color)
imBottom = imTop.transpose(Image.FLIP_TOP_BOTTOM)
im = BlankFrame(width, height)
if layout == 0: # Classic
y = self.y - int(height/100*43)
im.paste(imTop, (0, y), mask=imTop)
y = self.y + int(height/100*43)
im.paste(imBottom, (0, y), mask=imBottom)
if layout == 1: # Split
y = self.y + int(height/100*10)
im.paste(imTop, (0, y), mask=imTop)
y = self.y - int(height/100*10)
im.paste(imBottom, (0, y), mask=imBottom)
if layout == 2: # Bottom
y = self.y + int(height/100*10)
im.paste(imTop, (0, y), mask=imTop)
if layout == 3: # Top
y = self.y - int(height/100*10)
im.paste(imBottom, (0, y), mask=imBottom)
return im
def command(self, arg):
if not arg.startswith('preset=') and '=' in arg:
key, arg = arg.split('=', 1)
try:
if key == 'color':
self.page.lineEdit_visColor.setText(arg)
return
elif key == 'layout':
if arg == 'classic':
self.page.comboBox_visLayout.setCurrentIndex(0)
elif arg == 'split':
self.page.comboBox_visLayout.setCurrentIndex(1)
elif arg == 'bottom':
self.page.comboBox_visLayout.setCurrentIndex(2)
elif arg == 'top':
self.page.comboBox_visLayout.setCurrentIndex(3)
return
elif key == 'scale':
arg = int(arg)
self.page.spinBox_scale.setValue(arg)
return
elif key == 'y':
arg = int(arg)
self.page.spinBox_y.setValue(arg)
return
except ValueError:
print('You must enter a number.')
quit(1)
super().command(arg)
def commandHelp(self):
print('Give a layout name:\n layout=[classic/split/bottom/top]')
print('Specify a color:\n color=255,255,255')
print('Visualizer scale (20 is default):\n scale=number')
print('Y position:\n y=number')