Code cleanup

This commit is contained in:
DH4 2017-06-06 10:14:39 -05:00
parent 0948afd6e8
commit 231af74ea2
12 changed files with 1107 additions and 916 deletions

122
command.py Normal file
View File

@ -0,0 +1,122 @@
# FIXME: commandline functionality broken until we decide how to implement it
'''
class Command(QtCore.QObject):
videoTask = QtCore.pyqtSignal(str, str, str, list)
def __init__(self):
QtCore.QObject.__init__(self)
self.modules = []
self.selectedComponents = []
import argparse
self.parser = argparse.ArgumentParser(
description='Create a visualization for an audio file')
self.parser.add_argument(
'-i', '--input', dest='input', help='input audio file', required=True)
self.parser.add_argument(
'-o', '--output', dest='output',
help='output video file', required=True)
self.parser.add_argument(
'-b', '--background', dest='bgimage',
help='background image file', required=True)
self.parser.add_argument(
'-t', '--text', dest='text', help='title text', required=True)
self.parser.add_argument(
'-f', '--font', dest='font', help='title font', required=False)
self.parser.add_argument(
'-s', '--fontsize', dest='fontsize',
help='title font size', required=False)
self.parser.add_argument(
'-c', '--textcolor', dest='textcolor',
help='title text color in r,g,b format', required=False)
self.parser.add_argument(
'-C', '--viscolor', dest='viscolor',
help='visualization color in r,g,b format', required=False)
self.parser.add_argument(
'-x', '--xposition', dest='xposition',
help='x position', required=False)
self.parser.add_argument(
'-y', '--yposition', dest='yposition',
help='y position', required=False)
self.parser.add_argument(
'-a', '--alignment', dest='alignment',
help='title alignment', required=False,
type=int, choices=[0, 1, 2])
self.args = self.parser.parse_args()
self.settings = QSettings('settings.ini', QSettings.IniFormat)
LoadDefaultSettings(self)
# load colours as tuples from comma-separated strings
self.textColor = core.Core.RGBFromString(
self.settings.value("textColor", '255, 255, 255'))
self.visColor = core.Core.RGBFromString(
self.settings.value("visColor", '255, 255, 255'))
if self.args.textcolor:
self.textColor = core.Core.RGBFromString(self.args.textcolor)
if self.args.viscolor:
self.visColor = core.Core.RGBFromString(self.args.viscolor)
# font settings
if self.args.font:
self.font = QFont(self.args.font)
else:
self.font = QFont(self.settings.value("titleFont", QFont()))
if self.args.fontsize:
self.fontsize = int(self.args.fontsize)
else:
self.fontsize = int(self.settings.value("fontSize", 35))
if self.args.alignment:
self.alignment = int(self.args.alignment)
else:
self.alignment = int(self.settings.value("alignment", 0))
if self.args.xposition:
self.textX = int(self.args.xposition)
else:
self.textX = int(self.settings.value("xPosition", 70))
if self.args.yposition:
self.textY = int(self.args.yposition)
else:
self.textY = int(self.settings.value("yPosition", 375))
ffmpeg_cmd = self.settings.value("ffmpeg_cmd", expanduser("~"))
self.videoThread = QtCore.QThread(self)
self.videoWorker = video_thread.Worker(self)
self.videoWorker.moveToThread(self.videoThread)
self.videoWorker.videoCreated.connect(self.videoCreated)
self.videoThread.start()
self.videoTask.emit(self.args.bgimage,
self.args.text,
self.font,
self.fontsize,
self.alignment,
self.textX,
self.textY,
self.textColor,
self.visColor,
self.args.input,
self.args.output,
self.selectedComponents)
def videoCreated(self):
self.videoThread.quit()
self.videoThread.wait()
self.cleanUp()
def cleanUp(self):
self.settings.setValue("titleFont", self.font.toString())
self.settings.setValue("alignment", str(self.alignment))
self.settings.setValue("fontSize", str(self.fontsize))
self.settings.setValue("xPosition", str(self.textX))
self.settings.setValue("yPosition", str(self.textY))
self.settings.setValue("visColor", '%s,%s,%s' % self.visColor)
self.settings.setValue("textColor", '%s,%s,%s' % self.textColor)
sys.exit(0)
'''

View File

@ -1,5 +1,6 @@
from PyQt4 import QtGui from PyQt4 import QtGui
class Component: class Component:
def __str__(self): def __str__(self):
return self.__doc__ return self.__doc__
@ -14,7 +15,7 @@ class Component:
def reset(self): def reset(self):
self.canceled = False self.canceled = False
def preFrameRender(self, **kwargs): def preFrameRender(self, **kwargs):
for var, value in kwargs.items(): for var, value in kwargs.items():
exec('self.%s = value' % var) exec('self.%s = value' % var)
@ -22,32 +23,35 @@ class Component:
def pickColor(self): def pickColor(self):
color = QtGui.QColorDialog.getColor() color = QtGui.QColorDialog.getColor()
if color.isValid(): if color.isValid():
RGBstring = '%s,%s,%s' % (str(color.red()), str(color.green()), str(color.blue())) RGBstring = '%s,%s,%s' % (
btnStyle = "QPushButton { background-color : %s; outline: none; }" % color.name() str(color.red()), str(color.green()), str(color.blue()))
btnStyle = "QPushButton{background-color: %s; outline: none;}" \
% color.name()
return RGBstring, btnStyle return RGBstring, btnStyle
else: else:
return None, None return None, None
def RGBFromString(self, string): def RGBFromString(self, string):
''' turns an RGB string like "255, 255, 255" into a tuple ''' ''' turns an RGB string like "255, 255, 255" into a tuple '''
try: try:
tup = tuple([int(i) for i in string.split(',')]) tup = tuple([int(i) for i in string.split(',')])
if len(tup) != 3: if len(tup) != 3:
raise ValueError raise ValueError
for i in tup: for i in tup:
if i > 255 or i < 0: if i > 255 or i < 0:
raise ValueError raise ValueError
return tup return tup
except: except:
return (255, 255, 255) return (255, 255, 255)
''' '''
### Reference methods for creating a new component ### Reference methods for creating a new component
### (Inherit from this class and define these) ### (Inherit from this class and define these)
def widget(self, parent): def widget(self, parent):
self.parent = parent self.parent = parent
page = uic.loadUi(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'example.ui')) page = uic.loadUi(os.path.join(
os.path.dirname(os.path.realpath(__file__)), 'example.ui'))
# connect widgets signals # connect widgets signals
self.page = page self.page = page
return page return page
@ -55,13 +59,13 @@ class Component:
def update(self): def update(self):
# read widget values # read widget values
self.parent.drawPreview() self.parent.drawPreview()
def previewRender(self, previewWorker): def previewRender(self, previewWorker):
width = int(previewWorker.core.settings.value('outputWidth')) width = int(previewWorker.core.settings.value('outputWidth'))
height = int(previewWorker.core.settings.value('outputHeight')) height = int(previewWorker.core.settings.value('outputHeight'))
image = Image.new("RGBA", (width, height), (0,0,0,0)) image = Image.new("RGBA", (width, height), (0,0,0,0))
return image return image
def frameRender(self, moduleNo, frameNo): def frameRender(self, moduleNo, frameNo):
width = int(self.worker.core.settings.value('outputWidth')) width = int(self.worker.core.settings.value('outputWidth'))
height = int(self.worker.core.settings.value('outputHeight')) height = int(self.worker.core.settings.value('outputHeight'))
@ -70,10 +74,10 @@ class Component:
def loadPreset(self, presetDict): def loadPreset(self, presetDict):
# update widgets using a preset dict # update widgets using a preset dict
def savePreset(self): def savePreset(self):
return {} return {}
def cancel(self): def cancel(self):
self.canceled = True self.canceled = True

View File

@ -4,31 +4,39 @@ from PyQt4.QtGui import QColor
import os import os
from . import __base__ from . import __base__
class Component(__base__.Component): class Component(__base__.Component):
'''Color''' '''Color'''
def widget(self, parent): def widget(self, parent):
self.parent = parent self.parent = parent
page = uic.loadUi(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'color.ui')) page = uic.loadUi(os.path.join(
os.path.dirname(os.path.realpath(__file__)), 'color.ui'))
self.color1 = (0,0,0)
self.color2 = (133,133,133) self.color1 = (0, 0, 0)
self.color2 = (133, 133, 133)
self.x = 0 self.x = 0
self.y = 0 self.y = 0
page.lineEdit_color1.setText('%s,%s,%s' % self.color1) page.lineEdit_color1.setText('%s,%s,%s' % self.color1)
page.lineEdit_color2.setText('%s,%s,%s' % self.color2) page.lineEdit_color2.setText('%s,%s,%s' % self.color2)
page.pushButton_color1.clicked.connect(lambda: self.pickColor(1))
btnStyle = "QPushButton { background-color : %s; outline: none; }" % QColor(*self.color1).name() btnStyle = "QPushButton { background-color : %s; outline: none; }" \
% QColor(*self.color1).name()
btnStyle = "QPushButton { background-color : %s; outline: none; }" \
% QColor(*self.color2).name()
page.pushButton_color1.setStyleSheet(btnStyle) page.pushButton_color1.setStyleSheet(btnStyle)
page.pushButton_color2.clicked.connect(lambda: self.pickColor(2))
btnStyle = "QPushButton { background-color : %s; outline: none; }" % QColor(*self.color2).name()
page.pushButton_color2.setStyleSheet(btnStyle) page.pushButton_color2.setStyleSheet(btnStyle)
page.pushButton_color1.clicked.connect(lambda: self.pickColor(1))
page.pushButton_color2.clicked.connect(lambda: self.pickColor(2))
# disable color #2 until non-default 'fill' option gets changed # disable color #2 until non-default 'fill' option gets changed
page.lineEdit_color2.setDisabled(True) page.lineEdit_color2.setDisabled(True)
page.pushButton_color2.setDisabled(True) page.pushButton_color2.setDisabled(True)
page.spinBox_x.setValue(self.x) page.spinBox_x.setValue(self.x)
page.spinBox_x.setValue(self.y) page.spinBox_x.setValue(self.y)
page.lineEdit_color1.textChanged.connect(self.update) page.lineEdit_color1.textChanged.connect(self.update)
page.lineEdit_color2.textChanged.connect(self.update) page.lineEdit_color2.textChanged.connect(self.update)
page.spinBox_x.valueChanged.connect(self.update) page.spinBox_x.valueChanged.connect(self.update)
@ -42,39 +50,44 @@ class Component(__base__.Component):
self.x = self.page.spinBox_x.value() self.x = self.page.spinBox_x.value()
self.y = self.page.spinBox_y.value() self.y = self.page.spinBox_y.value()
self.parent.drawPreview() self.parent.drawPreview()
def previewRender(self, previewWorker): def previewRender(self, previewWorker):
width = int(previewWorker.core.settings.value('outputWidth')) width = int(previewWorker.core.settings.value('outputWidth'))
height = int(previewWorker.core.settings.value('outputHeight')) height = int(previewWorker.core.settings.value('outputHeight'))
return self.drawFrame(width, height) return self.drawFrame(width, height)
def preFrameRender(self, **kwargs): def preFrameRender(self, **kwargs):
super().preFrameRender(**kwargs) super().preFrameRender(**kwargs)
return ['static'] return ['static']
def frameRender(self, moduleNo, arrayNo, frameNo): def frameRender(self, moduleNo, arrayNo, frameNo):
width = int(self.worker.core.settings.value('outputWidth')) width = int(self.worker.core.settings.value('outputWidth'))
height = int(self.worker.core.settings.value('outputHeight')) height = int(self.worker.core.settings.value('outputHeight'))
return self.drawFrame(width, height) return self.drawFrame(width, height)
def drawFrame(self, width, height): def drawFrame(self, width, height):
r,g,b = self.color1 r, g, b = self.color1
return Image.new("RGBA", (width, height), (r, g, b, 255)) return Image.new("RGBA", (width, height), (r, g, b, 255))
def loadPreset(self, pr): def loadPreset(self, pr):
self.page.lineEdit_color1.setText('%s,%s,%s' % pr['color1']) self.page.lineEdit_color1.setText('%s,%s,%s' % pr['color1'])
self.page.lineEdit_color2.setText('%s,%s,%s' % pr['color2']) self.page.lineEdit_color2.setText('%s,%s,%s' % pr['color2'])
btnStyle = "QPushButton { background-color : %s; outline: none; }" % QColor(*pr['color1']).name()
btnStyle = "QPushButton { background-color : %s; outline: none; }" \
% QColor(*pr['color1']).name()
btnStyle = "QPushButton { background-color : %s; outline: none; }" \
% QColor(*pr['color2']).name()
self.page.pushButton_color1.setStyleSheet(btnStyle) self.page.pushButton_color1.setStyleSheet(btnStyle)
btnStyle = "QPushButton { background-color : %s; outline: none; }" % QColor(*pr['color2']).name()
self.page.pushButton_color2.setStyleSheet(btnStyle) self.page.pushButton_color2.setStyleSheet(btnStyle)
def savePreset(self): def savePreset(self):
return { return {
'color1' : self.color1, 'color1': self.color1,
'color2' : self.color2, 'color2': self.color2,
} }
def pickColor(self, num): def pickColor(self, num):
RGBstring, btnStyle = super().pickColor() RGBstring, btnStyle = super().pickColor()
if not RGBstring: if not RGBstring:

View File

@ -3,26 +3,28 @@ from PyQt4 import uic, QtGui, QtCore
import os import os
from . import __base__ from . import __base__
class Component(__base__.Component): class Component(__base__.Component):
'''Image''' '''Image'''
def widget(self, parent): def widget(self, parent):
self.parent = parent self.parent = parent
self.settings = parent.settings self.settings = parent.settings
page = uic.loadUi(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'image.ui')) page = uic.loadUi(os.path.join(
os.path.dirname(os.path.realpath(__file__)), 'image.ui'))
self.imagePath = '' self.imagePath = ''
self.x = 0 self.x = 0
self.y = 0 self.y = 0
page.lineEdit_image.textChanged.connect(self.update) page.lineEdit_image.textChanged.connect(self.update)
page.pushButton_image.clicked.connect(self.pickImage) page.pushButton_image.clicked.connect(self.pickImage)
self.page = page self.page = page
return page return page
def update(self): def update(self):
self.imagePath = self.page.lineEdit_image.text() self.imagePath = self.page.lineEdit_image.text()
self.parent.drawPreview() self.parent.drawPreview()
def previewRender(self, previewWorker): def previewRender(self, previewWorker):
width = int(previewWorker.core.settings.value('outputWidth')) width = int(previewWorker.core.settings.value('outputWidth'))
height = int(previewWorker.core.settings.value('outputHeight')) height = int(previewWorker.core.settings.value('outputHeight'))
@ -36,9 +38,9 @@ class Component(__base__.Component):
width = int(self.worker.core.settings.value('outputWidth')) width = int(self.worker.core.settings.value('outputWidth'))
height = int(self.worker.core.settings.value('outputHeight')) height = int(self.worker.core.settings.value('outputHeight'))
return self.drawFrame(width, height) return self.drawFrame(width, height)
def drawFrame(self, width, height): def drawFrame(self, width, height):
frame = Image.new("RGBA", (width, height), (0,0,0,0)) frame = Image.new("RGBA", (width, height), (0, 0, 0, 0))
if self.imagePath and os.path.exists(self.imagePath): if self.imagePath and os.path.exists(self.imagePath):
image = Image.open(self.imagePath) image = Image.open(self.imagePath)
if image.size != (width, height): if image.size != (width, height):
@ -48,16 +50,16 @@ class Component(__base__.Component):
def loadPreset(self, pr): def loadPreset(self, pr):
self.page.lineEdit_image.setText(pr['image']) self.page.lineEdit_image.setText(pr['image'])
def savePreset(self): def savePreset(self):
return { return {
'image' : self.imagePath, 'image': self.imagePath,
} }
def pickImage(self): def pickImage(self):
imgDir = self.settings.value("backgroundDir", os.path.expanduser("~")) imgDir = self.settings.value("backgroundDir", os.path.expanduser("~"))
filename = QtGui.QFileDialog.getOpenFileName(self.page, filename = QtGui.QFileDialog.getOpenFileName(
"Choose Image", imgDir, "Image Files (*.jpg *.png)") self.page, "Choose Image", imgDir, "Image Files (*.jpg *.png)")
if filename: if filename:
self.settings.setValue("backgroundDir", os.path.dirname(filename)) self.settings.setValue("backgroundDir", os.path.dirname(filename))
self.page.lineEdit_image.setText(filename) self.page.lineEdit_image.setText(filename)

View File

@ -2,7 +2,8 @@ import numpy
from PIL import Image, ImageDraw from PIL import Image, ImageDraw
from PyQt4 import uic, QtGui from PyQt4 import uic, QtGui
from PyQt4.QtGui import QColor from PyQt4.QtGui import QColor
import os, random import os
import random
from . import __base__ from . import __base__
import time import time
from copy import copy from copy import copy
@ -12,24 +13,25 @@ class Component(__base__.Component):
'''Original Audio Visualization''' '''Original Audio Visualization'''
def widget(self, parent): def widget(self, parent):
self.parent = parent self.parent = parent
self.visColor = (255,255,255) self.visColor = (255, 255, 255)
page = uic.loadUi(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'original.ui')) page = uic.loadUi(os.path.join(
os.path.dirname(os.path.realpath(__file__)), 'original.ui'))
page.comboBox_visLayout.addItem("Classic") page.comboBox_visLayout.addItem("Classic")
page.comboBox_visLayout.addItem("Split") page.comboBox_visLayout.addItem("Split")
page.comboBox_visLayout.addItem("Bottom") page.comboBox_visLayout.addItem("Bottom")
#visLayoutValue = int(self.settings.value('visLayout'))
page.comboBox_visLayout.setCurrentIndex(0) page.comboBox_visLayout.setCurrentIndex(0)
page.comboBox_visLayout.currentIndexChanged.connect(self.update) page.comboBox_visLayout.currentIndexChanged.connect(self.update)
page.lineEdit_visColor.setText('%s,%s,%s' % self.visColor) page.lineEdit_visColor.setText('%s,%s,%s' % self.visColor)
page.pushButton_visColor.clicked.connect(lambda: self.pickColor()) page.pushButton_visColor.clicked.connect(lambda: self.pickColor())
btnStyle = "QPushButton { background-color : %s; outline: none; }" % QColor(*self.visColor).name() btnStyle = "QPushButton { background-color : %s; outline: none; }" \
% QColor(*self.visColor).name()
page.pushButton_visColor.setStyleSheet(btnStyle) page.pushButton_visColor.setStyleSheet(btnStyle)
page.lineEdit_visColor.textChanged.connect(self.update) page.lineEdit_visColor.textChanged.connect(self.update)
self.page = page self.page = page
self.canceled = False self.canceled = False
return page return page
def update(self): def update(self):
self.layout = self.page.comboBox_visLayout.currentIndex() self.layout = self.page.comboBox_visLayout.currentIndex()
self.visColor = self.RGBFromString(self.page.lineEdit_visColor.text()) self.visColor = self.RGBFromString(self.page.lineEdit_visColor.text())
@ -37,21 +39,25 @@ class Component(__base__.Component):
def loadPreset(self, pr): def loadPreset(self, pr):
self.page.lineEdit_visColor.setText('%s,%s,%s' % pr['visColor']) self.page.lineEdit_visColor.setText('%s,%s,%s' % pr['visColor'])
btnStyle = "QPushButton { background-color : %s; outline: none; }" % QColor(*pr['visColor']).name() btnStyle = "QPushButton { background-color : %s; outline: none; }" \
% QColor(*pr['visColor']).name()
self.page.pushButton_visColor.setStyleSheet(btnStyle) self.page.pushButton_visColor.setStyleSheet(btnStyle)
self.page.comboBox_visLayout.setCurrentIndex(pr['layout']) self.page.comboBox_visLayout.setCurrentIndex(pr['layout'])
def savePreset(self): def savePreset(self):
return { 'layout' : self.layout, return {
'visColor' : self.visColor, 'layout': self.layout,
} 'visColor': self.visColor,
}
def previewRender(self, previewWorker): def previewRender(self, previewWorker):
spectrum = numpy.fromfunction(lambda x: 0.008*(x-128)**2, (255,), dtype="int16") spectrum = numpy.fromfunction(
lambda x: 0.008*(x-128)**2, (255,), dtype="int16")
width = int(previewWorker.core.settings.value('outputWidth')) width = int(previewWorker.core.settings.value('outputWidth'))
height = int(previewWorker.core.settings.value('outputHeight')) height = int(previewWorker.core.settings.value('outputHeight'))
return self.drawBars(width, height, spectrum, self.visColor, self.layout) return self.drawBars(
width, height, spectrum, self.visColor, self.layout)
def preFrameRender(self, **kwargs): def preFrameRender(self, **kwargs):
super().preFrameRender(**kwargs) super().preFrameRender(**kwargs)
self.smoothConstantDown = 0.08 self.smoothConstantDown = 0.08
@ -64,19 +70,24 @@ class Component(__base__.Component):
for i in range(0, len(self.completeAudioArray), self.sampleSize): for i in range(0, len(self.completeAudioArray), self.sampleSize):
if self.canceled: if self.canceled:
break break
self.lastSpectrum = self.transformData(i, self.completeAudioArray, self.sampleSize, self.lastSpectrum = self.transformData(
self.smoothConstantDown, self.smoothConstantUp, self.lastSpectrum) i, self.completeAudioArray, self.sampleSize,
self.smoothConstantDown, self.smoothConstantUp,
self.lastSpectrum)
self.spectrumArray[i] = copy(self.lastSpectrum) self.spectrumArray[i] = copy(self.lastSpectrum)
progress = int(100*(i/len(self.completeAudioArray))) progress = int(100*(i/len(self.completeAudioArray)))
if progress >= 100: if progress >= 100:
progress = 100 progress = 100
pStr = "Analyzing audio: "+ str(progress) +'%' pStr = "Analyzing audio: "+str(progress)+'%'
self.progressBarSetText.emit(pStr) self.progressBarSetText.emit(pStr)
self.progressBarUpdate.emit(int(progress)) self.progressBarUpdate.emit(int(progress))
def frameRender(self, moduleNo, arrayNo, frameNo): def frameRender(self, moduleNo, arrayNo, frameNo):
return self.drawBars(self.width, self.height, self.spectrumArray[arrayNo], self.visColor, self.layout) return self.drawBars(
self.width, self.height,
self.spectrumArray[arrayNo],
self.visColor, self.layout)
def pickColor(self): def pickColor(self):
RGBstring, btnStyle = super().pickColor() RGBstring, btnStyle = super().pickColor()
@ -85,14 +96,17 @@ class Component(__base__.Component):
self.page.lineEdit_visColor.setText(RGBstring) self.page.lineEdit_visColor.setText(RGBstring)
self.page.pushButton_visColor.setStyleSheet(btnStyle) self.page.pushButton_visColor.setStyleSheet(btnStyle)
def transformData(self, i, completeAudioArray, sampleSize, smoothConstantDown, smoothConstantUp, lastSpectrum): def transformData(
self, i, completeAudioArray, sampleSize,
smoothConstantDown, smoothConstantUp, lastSpectrum):
if len(completeAudioArray) < (i + sampleSize): if len(completeAudioArray) < (i + sampleSize):
sampleSize = len(completeAudioArray) - i sampleSize = len(completeAudioArray) - i
window = numpy.hanning(sampleSize) window = numpy.hanning(sampleSize)
data = completeAudioArray[i:i+sampleSize][::1] * window data = completeAudioArray[i:i+sampleSize][::1] * window
paddedSampleSize = 2048 paddedSampleSize = 2048
paddedData = numpy.pad(data, (0, paddedSampleSize - sampleSize), 'constant') paddedData = numpy.pad(
data, (0, paddedSampleSize - sampleSize), 'constant')
spectrum = numpy.fft.fft(paddedData) spectrum = numpy.fft.fft(paddedData)
sample_rate = 44100 sample_rate = 44100
frequencies = numpy.fft.fftfreq(len(spectrum), 1./sample_rate) frequencies = numpy.fft.fftfreq(len(spectrum), 1./sample_rate)
@ -106,8 +120,13 @@ class Component(__base__.Component):
y[numpy.isinf(y)] = 0 y[numpy.isinf(y)] = 0
if lastSpectrum is not None: if lastSpectrum is not None:
lastSpectrum[y < lastSpectrum] = y[y < lastSpectrum] * smoothConstantDown + lastSpectrum[y < lastSpectrum] * (1 - smoothConstantDown) lastSpectrum[y < lastSpectrum] = \
lastSpectrum[y >= lastSpectrum] = y[y >= lastSpectrum] * smoothConstantUp + lastSpectrum[y >= lastSpectrum] * (1 - smoothConstantUp) y[y < lastSpectrum] * smoothConstantDown + \
lastSpectrum[y < lastSpectrum] * (1 - smoothConstantDown)
lastSpectrum[y >= lastSpectrum] = \
y[y >= lastSpectrum] * smoothConstantUp + \
lastSpectrum[y >= lastSpectrum] * (1 - smoothConstantUp)
else: else:
lastSpectrum = y lastSpectrum = y
@ -120,7 +139,7 @@ class Component(__base__.Component):
bF = width / 64 bF = width / 64
bH = bF / 2 bH = bF / 2
bQ = bF / 4 bQ = bF / 4
imTop = Image.new("RGBA", (width, height),(0,0,0,0)) imTop = Image.new("RGBA", (width, height), (0, 0, 0, 0))
draw = ImageDraw.Draw(imTop) draw = ImageDraw.Draw(imTop)
r, g, b = color r, g, b = color
color2 = (r, g, b, 125) color2 = (r, g, b, 125)
@ -128,12 +147,17 @@ class Component(__base__.Component):
bP = height / 1200 bP = height / 1200
for j in range(0, 63): 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((
draw.rectangle((bH + bQ + j * bF, vH , bH + bQ + j * bF + bH, vH - spectrum[j * 4] * bP), fill=color) 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) imBottom = imTop.transpose(Image.FLIP_TOP_BOTTOM)
im = Image.new("RGBA", (width, height),(0,0,0,0)) im = Image.new("RGBA", (width, height), (0, 0, 0, 0))
if layout == 0: if layout == 0:
y = 0 - int(height/100*43) y = 0 - int(height/100*43)

View File

@ -25,9 +25,7 @@ class Component(__base__.Component):
self.yPosition = height / 2 * 1.036 self.yPosition = height / 2 * 1.036
page = uic.loadUi(os.path.join( page = uic.loadUi(os.path.join(
os.path.dirname(os.path.realpath(__file__)), os.path.dirname(os.path.realpath(__file__)), 'text.ui'))
'text.ui'
))
page.comboBox_textAlign.addItem("Left") page.comboBox_textAlign.addItem("Left")
page.comboBox_textAlign.addItem("Middle") page.comboBox_textAlign.addItem("Middle")
page.comboBox_textAlign.addItem("Right") page.comboBox_textAlign.addItem("Right")
@ -61,7 +59,8 @@ class Component(__base__.Component):
self.fontSize = self.page.spinBox_fontSize.value() self.fontSize = self.page.spinBox_fontSize.value()
self.xPosition = self.page.spinBox_xTextAlign.value() self.xPosition = self.page.spinBox_xTextAlign.value()
self.yPosition = self.page.spinBox_yTextAlign.value() self.yPosition = self.page.spinBox_yTextAlign.value()
self.textColor = self.RGBFromString(self.page.lineEdit_textColor.text()) self.textColor = self.RGBFromString(
self.page.lineEdit_textColor.text())
self.parent.drawPreview() self.parent.drawPreview()
def getXY(self): def getXY(self):
@ -95,14 +94,14 @@ class Component(__base__.Component):
def savePreset(self): def savePreset(self):
return { return {
'title': self.title, 'title': self.title,
'titleFont': self.titleFont.toString(), 'titleFont': self.titleFont.toString(),
'alignment': self.alignment, 'alignment': self.alignment,
'fontSize': self.fontSize, 'fontSize': self.fontSize,
'xPosition': self.xPosition, 'xPosition': self.xPosition,
'yPosition': self.yPosition, 'yPosition': self.yPosition,
'textColor': self.textColor 'textColor': self.textColor
} }
def previewRender(self, previewWorker): def previewRender(self, previewWorker):
width = int(previewWorker.core.settings.value('outputWidth')) width = int(previewWorker.core.settings.value('outputWidth'))

View File

@ -1,12 +1,18 @@
from PIL import Image, ImageDraw from PIL import Image, ImageDraw
from PyQt4 import uic, QtGui, QtCore from PyQt4 import uic, QtGui, QtCore
import os, subprocess, threading import os
import subprocess
import threading
from queue import PriorityQueue from queue import PriorityQueue
from . import __base__ from . import __base__
class Video: class Video:
'''Video Component Frame-Fetcher''' '''Video Component Frame-Fetcher'''
def __init__(self, ffmpeg, videoPath, width, height, frameRate, chunkSize, parent, loopVideo): def __init__(
self, ffmpeg, videoPath, width, height,
frameRate, chunkSize, parent, loopVideo):
self.parent = parent self.parent = parent
self.chunkSize = chunkSize self.chunkSize = chunkSize
self.size = (width, height) self.size = (width, height)
@ -27,15 +33,18 @@ class Video:
'-filter:v', 'scale='+str(width)+':'+str(height), '-filter:v', 'scale='+str(width)+':'+str(height),
'-vcodec', 'rawvideo', '-', '-vcodec', 'rawvideo', '-',
] ]
self.frameBuffer = PriorityQueue() self.frameBuffer = PriorityQueue()
self.frameBuffer.maxsize = int(frameRate) self.frameBuffer.maxsize = int(frameRate)
self.finishedFrames = {} self.finishedFrames = {}
self.thread = threading.Thread(target=self.fillBuffer, name=self.__doc__) self.thread = threading.Thread(
target=self.fillBuffer,
name=self.__doc__
)
self.thread.daemon = True self.thread.daemon = True
self.thread.start() self.thread.start()
def frame(self, num): def frame(self, num):
while True: while True:
if num in self.finishedFrames: if num in self.finishedFrames:
@ -44,9 +53,12 @@ class Video:
i, image = self.frameBuffer.get() i, image = self.frameBuffer.get()
self.finishedFrames[i] = image self.finishedFrames[i] = image
self.frameBuffer.task_done() self.frameBuffer.task_done()
def fillBuffer(self): def fillBuffer(self):
self.pipe = subprocess.Popen(self.command, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, bufsize=10**8) self.pipe = subprocess.Popen(
self.command, stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL, bufsize=10**8
)
while True: while True:
if self.parent.canceled: if self.parent.canceled:
break break
@ -58,26 +70,29 @@ class Video:
continue continue
self.currentFrame = self.pipe.stdout.read(self.chunkSize) self.currentFrame = self.pipe.stdout.read(self.chunkSize)
#print('creating frame #%s' % str(self.frameNo))
if len(self.currentFrame) != 0: if len(self.currentFrame) != 0:
self.frameBuffer.put((self.frameNo, self.currentFrame)) self.frameBuffer.put((self.frameNo, self.currentFrame))
self.lastFrame = self.currentFrame self.lastFrame = self.currentFrame
class Component(__base__.Component): class Component(__base__.Component):
'''Video''' '''Video'''
def widget(self, parent): def widget(self, parent):
self.parent = parent self.parent = parent
self.settings = parent.settings self.settings = parent.settings
page = uic.loadUi(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'video.ui')) page = uic.loadUi(os.path.join(
os.path.dirname(os.path.realpath(__file__)),
'video.ui'
))
self.videoPath = '' self.videoPath = ''
self.x = 0 self.x = 0
self.y = 0 self.y = 0
self.loopVideo = False self.loopVideo = False
page.lineEdit_video.textChanged.connect(self.update) page.lineEdit_video.textChanged.connect(self.update)
page.pushButton_video.clicked.connect(self.pickVideo) page.pushButton_video.clicked.connect(self.pickVideo)
page.checkBox_loop.stateChanged.connect(self.update) page.checkBox_loop.stateChanged.connect(self.update)
self.page = page self.page = page
return page return page
@ -85,46 +100,50 @@ class Component(__base__.Component):
self.videoPath = self.page.lineEdit_video.text() self.videoPath = self.page.lineEdit_video.text()
self.loopVideo = self.page.checkBox_loop.isChecked() self.loopVideo = self.page.checkBox_loop.isChecked()
self.parent.drawPreview() self.parent.drawPreview()
def previewRender(self, previewWorker): def previewRender(self, previewWorker):
width = int(previewWorker.core.settings.value('outputWidth')) width = int(previewWorker.core.settings.value('outputWidth'))
height = int(previewWorker.core.settings.value('outputHeight')) height = int(previewWorker.core.settings.value('outputHeight'))
self.chunkSize = 4*width*height self.chunkSize = 4*width*height
frame = self.getPreviewFrame(width, height) frame = self.getPreviewFrame(width, height)
if not frame: if not frame:
return Image.new("RGBA", (width, height),(0,0,0,0)) return Image.new("RGBA", (width, height), (0, 0, 0, 0))
else: else:
return frame return frame
def preFrameRender(self, **kwargs): def preFrameRender(self, **kwargs):
super().preFrameRender(**kwargs) super().preFrameRender(**kwargs)
width = int(self.worker.core.settings.value('outputWidth')) width = int(self.worker.core.settings.value('outputWidth'))
height = int(self.worker.core.settings.value('outputHeight')) height = int(self.worker.core.settings.value('outputHeight'))
self.chunkSize = 4*width*height self.chunkSize = 4*width*height
self.video = Video(self.parent.core.FFMPEG_BIN, self.videoPath, self.video = Video(
self.parent.core.FFMPEG_BIN, self.videoPath,
width, height, self.settings.value("outputFrameRate"), width, height, self.settings.value("outputFrameRate"),
self.chunkSize, self.parent, self.loopVideo) self.chunkSize, self.parent, self.loopVideo
)
def frameRender(self, moduleNo, arrayNo, frameNo): def frameRender(self, moduleNo, arrayNo, frameNo):
return self.video.frame(frameNo) return self.video.frame(frameNo)
def loadPreset(self, pr): def loadPreset(self, pr):
self.page.lineEdit_video.setText(pr['video']) self.page.lineEdit_video.setText(pr['video'])
def savePreset(self): def savePreset(self):
return { return {
'video' : self.videoPath, 'video': self.videoPath,
} }
def pickVideo(self): def pickVideo(self):
imgDir = self.settings.value("backgroundDir", os.path.expanduser("~")) imgDir = self.settings.value("backgroundDir", os.path.expanduser("~"))
filename = QtGui.QFileDialog.getOpenFileName(self.page, filename = QtGui.QFileDialog.getOpenFileName(
"Choose Video", imgDir, "Video Files (*.mp4 *.mov)") self.page, "Choose Video",
imgDir, "Video Files (*.mp4 *.mov)"
)
if filename: if filename:
self.settings.setValue("backgroundDir", os.path.dirname(filename)) self.settings.setValue("backgroundDir", os.path.dirname(filename))
self.page.lineEdit_video.setText(filename) self.page.lineEdit_video.setText(filename)
self.update() self.update()
def getPreviewFrame(self, width, height): def getPreviewFrame(self, width, height):
if not self.videoPath or not os.path.exists(self.videoPath): if not self.videoPath or not os.path.exists(self.videoPath):
return return
@ -139,7 +158,10 @@ class Component(__base__.Component):
'-ss', '90', '-ss', '90',
'-vframes', '1', '-vframes', '1',
] ]
pipe = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, bufsize=10**8) pipe = subprocess.Popen(
command, stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL, bufsize=10**8
)
byteFrame = pipe.stdout.read(self.chunkSize) byteFrame = pipe.stdout.read(self.chunkSize)
image = Image.frombytes('RGBA', (width, height), byteFrame) image = Image.frombytes('RGBA', (width, height), byteFrame)
pipe.stdout.close() pipe.stdout.close()

173
core.py
View File

@ -1,4 +1,6 @@
import sys, io, os import sys
import io
import os
from PyQt4 import QtCore, QtGui, uic from PyQt4 import QtCore, QtGui, uic
from os.path import expanduser from os.path import expanduser
import subprocess as sp import subprocess as sp
@ -10,103 +12,106 @@ import atexit
import time import time
from collections import OrderedDict from collections import OrderedDict
class Core(): class Core():
def __init__(self): def __init__(self):
self.FFMPEG_BIN = self.findFfmpeg() self.FFMPEG_BIN = self.findFfmpeg()
self.tempDir = os.path.join(tempfile.gettempdir(), 'audio-visualizer-python-data') self.tempDir = os.path.join(
if not os.path.exists(self.tempDir): tempfile.gettempdir(), 'audio-visualizer-python-data')
os.makedirs(self.tempDir) if not os.path.exists(self.tempDir):
atexit.register(self.deleteTempDir) os.makedirs(self.tempDir)
atexit.register(self.deleteTempDir)
def findFfmpeg(self): def findFfmpeg(self):
if sys.platform == "win32": if sys.platform == "win32":
return "ffmpeg.exe" return "ffmpeg.exe"
else: else:
try: try:
with open(os.devnull, "w") as f: with open(os.devnull, "w") as f:
sp.check_call(['ffmpeg', '-version'], stdout=f, stderr=f) sp.check_call(['ffmpeg', '-version'], stdout=f, stderr=f)
return "ffmpeg" return "ffmpeg"
except: except:
return "avconv" return "avconv"
def readAudioFile(self, filename, parent): def readAudioFile(self, filename, parent):
command = [ self.FFMPEG_BIN, command = [self.FFMPEG_BIN, '-i', filename]
'-i', filename]
try: try:
fileInfo = sp.check_output(command, stderr=sp.STDOUT, shell=False) fileInfo = sp.check_output(command, stderr=sp.STDOUT, shell=False)
except sp.CalledProcessError as ex: except sp.CalledProcessError as ex:
fileInfo = ex.output fileInfo = ex.output
pass pass
info = fileInfo.decode("utf-8").split('\n') info = fileInfo.decode("utf-8").split('\n')
for line in info: for line in info:
if 'Duration' in line: if 'Duration' in line:
d = line.split(',')[0] d = line.split(',')[0]
d = d.split(' ')[3] d = d.split(' ')[3]
d = d.split(':') d = d.split(':')
duration = float(d[0])*3600 + float(d[1])*60 + float(d[2]) duration = float(d[0])*3600 + float(d[1])*60 + float(d[2])
command = [ self.FFMPEG_BIN, command = [
'-i', filename, self.FFMPEG_BIN,
'-f', 's16le', '-i', filename,
'-acodec', 'pcm_s16le', '-f', 's16le',
'-ar', '44100', # ouput will have 44100 Hz '-acodec', 'pcm_s16le',
'-ac', '1', # mono (set to '2' for stereo) '-ar', '44100', # ouput will have 44100 Hz
'-'] '-ac', '1', # mono (set to '2' for stereo)
in_pipe = sp.Popen(command, stdout=sp.PIPE, stderr=sp.DEVNULL, bufsize=10**8) '-']
in_pipe = sp.Popen(
completeAudioArray = numpy.empty(0, dtype="int16") command, stdout=sp.PIPE, stderr=sp.DEVNULL, bufsize=10**8)
progress = 0 completeAudioArray = numpy.empty(0, dtype="int16")
lastPercent = None
while True:
if self.canceled:
break
# read 2 seconds of audio
progress = progress + 4
raw_audio = in_pipe.stdout.read(88200*4)
if len(raw_audio) == 0:
break
audio_array = numpy.fromstring(raw_audio, dtype="int16")
completeAudioArray = numpy.append(completeAudioArray, audio_array)
percent = int(100*(progress/duration)) progress = 0
if percent >= 100: lastPercent = None
percent = 100 while True:
if self.canceled:
break
# read 2 seconds of audio
progress = progress + 4
raw_audio = in_pipe.stdout.read(88200*4)
if len(raw_audio) == 0:
break
audio_array = numpy.fromstring(raw_audio, dtype="int16")
completeAudioArray = numpy.append(completeAudioArray, audio_array)
if lastPercent != percent: percent = int(100*(progress/duration))
string = 'Loading audio file: '+str(percent)+'%' if percent >= 100:
parent.progressBarSetText.emit(string) percent = 100
parent.progressBarUpdate.emit(percent)
lastPercent = percent if lastPercent != percent:
string = 'Loading audio file: '+str(percent)+'%'
parent.progressBarSetText.emit(string)
parent.progressBarUpdate.emit(percent)
in_pipe.kill() lastPercent = percent
in_pipe.wait()
# add 0s the end in_pipe.kill()
completeAudioArrayCopy = numpy.zeros(len(completeAudioArray) + 44100, dtype="int16") in_pipe.wait()
completeAudioArrayCopy[:len(completeAudioArray)] = completeAudioArray
completeAudioArray = completeAudioArrayCopy
return completeAudioArray # add 0s the end
completeAudioArrayCopy = numpy.zeros(
len(completeAudioArray) + 44100, dtype="int16")
completeAudioArrayCopy[:len(completeAudioArray)] = completeAudioArray
completeAudioArray = completeAudioArrayCopy
def deleteTempDir(self): return completeAudioArray
try:
rmtree(self.tempDir)
except FileNotFoundError:
pass
def cancel(self): def deleteTempDir(self):
self.canceled = True try:
rmtree(self.tempDir)
except FileNotFoundError:
pass
def reset(self): def cancel(self):
self.canceled = False self.canceled = True
@staticmethod def reset(self):
def stringOrderedDict(dictionary): self.canceled = False
sorted_ = OrderedDict(sorted(dictionary.items(), key=lambda t: t[0]))
return repr(sorted_) @staticmethod
def stringOrderedDict(dictionary):
sorted_ = OrderedDict(sorted(dictionary.items(), key=lambda t: t[0]))
return repr(sorted_)

691
main.py
View File

@ -1,668 +1,67 @@
import sys, io, os, shutil, atexit, string, signal, filecmp, time
from os.path import expanduser
from queue import Queue
from importlib import import_module from importlib import import_module
from collections import OrderedDict from collections import OrderedDict
from PyQt4 import QtCore, QtGui, uic from PyQt4 import QtGui, uic
from PyQt4.QtCore import QSettings, QModelIndex, Qt from PyQt4.QtCore import Qt
from PyQt4.QtGui import QDesktopServices, QMenu import sys
import io
import os
import atexit
import signal
import preview_thread, core, video_thread import core
import preview_thread
import video_thread
from mainwindow import *
# FIXME: commandline functionality broken until we decide how to implement it
'''
class Command(QtCore.QObject):
videoTask = QtCore.pyqtSignal(str, str, str, list)
def __init__(self):
QtCore.QObject.__init__(self)
self.modules = []
self.selectedComponents = []
import argparse
self.parser = argparse.ArgumentParser(description='Create a visualization for an audio file')
self.parser.add_argument('-i', '--input', dest='input', help='input audio file', required=True)
self.parser.add_argument('-o', '--output', dest='output', help='output video file', required=True)
self.parser.add_argument('-b', '--background', dest='bgimage', help='background image file', required=True)
self.parser.add_argument('-t', '--text', dest='text', help='title text', required=True)
self.parser.add_argument('-f', '--font', dest='font', help='title font', required=False)
self.parser.add_argument('-s', '--fontsize', dest='fontsize', help='title font size', required=False)
self.parser.add_argument('-c', '--textcolor', dest='textcolor', help='title text color in r,g,b format', required=False)
self.parser.add_argument('-C', '--viscolor', dest='viscolor', help='visualization color in r,g,b format', required=False)
self.parser.add_argument('-x', '--xposition', dest='xposition', help='x position', required=False)
self.parser.add_argument('-y', '--yposition', dest='yposition', help='y position', required=False)
self.parser.add_argument('-a', '--alignment', dest='alignment', help='title alignment', required=False, type=int, choices=[0, 1, 2])
self.args = self.parser.parse_args()
self.settings = QSettings('settings.ini', QSettings.IniFormat)
LoadDefaultSettings(self)
# load colours as tuples from comma-separated strings
self.textColor = core.Core.RGBFromString(self.settings.value("textColor", '255, 255, 255'))
self.visColor = core.Core.RGBFromString(self.settings.value("visColor", '255, 255, 255'))
if self.args.textcolor:
self.textColor = core.Core.RGBFromString(self.args.textcolor)
if self.args.viscolor:
self.visColor = core.Core.RGBFromString(self.args.viscolor)
# font settings
if self.args.font:
self.font = QFont(self.args.font)
else:
self.font = QFont(self.settings.value("titleFont", QFont()))
if self.args.fontsize:
self.fontsize = int(self.args.fontsize)
else:
self.fontsize = int(self.settings.value("fontSize", 35))
if self.args.alignment:
self.alignment = int(self.args.alignment)
else:
self.alignment = int(self.settings.value("alignment", 0))
if self.args.xposition:
self.textX = int(self.args.xposition)
else:
self.textX = int(self.settings.value("xPosition", 70))
if self.args.yposition:
self.textY = int(self.args.yposition)
else:
self.textY = int(self.settings.value("yPosition", 375))
ffmpeg_cmd = self.settings.value("ffmpeg_cmd", expanduser("~"))
self.videoThread = QtCore.QThread(self)
self.videoWorker = video_thread.Worker(self)
self.videoWorker.moveToThread(self.videoThread)
self.videoWorker.videoCreated.connect(self.videoCreated)
self.videoThread.start()
self.videoTask.emit(self.args.bgimage,
self.args.text,
self.font,
self.fontsize,
self.alignment,
self.textX,
self.textY,
self.textColor,
self.visColor,
self.args.input,
self.args.output,
self.selectedComponents)
def videoCreated(self):
self.videoThread.quit()
self.videoThread.wait()
self.cleanUp()
def cleanUp(self):
self.settings.setValue("titleFont", self.font.toString())
self.settings.setValue("alignment", str(self.alignment))
self.settings.setValue("fontSize", str(self.fontsize))
self.settings.setValue("xPosition", str(self.textX))
self.settings.setValue("yPosition", str(self.textY))
self.settings.setValue("visColor", '%s,%s,%s' % self.visColor)
self.settings.setValue("textColor", '%s,%s,%s' % self.textColor)
sys.exit(0)
'''
class PreviewWindow(QtGui.QLabel):
def __init__(self, parent, img):
super(PreviewWindow, self).__init__()
self.parent = parent
self.setFrameStyle(QtGui.QFrame.StyledPanel)
self.pixmap = QtGui.QPixmap(img)
def paintEvent(self, event):
size = self.size()
painter = QtGui.QPainter(self)
point = QtCore.QPoint(0,0)
scaledPix = self.pixmap.scaled(size, Qt.KeepAspectRatio, transformMode = Qt.SmoothTransformation)
# start painting the label from left upper corner
point.setX((size.width() - scaledPix.width())/2)
point.setY((size.height() - scaledPix.height())/2)
#print point.x(), ' ', point.y()
painter.drawPixmap(point, scaledPix)
def changePixmap(self, img):
self.pixmap = QtGui.QPixmap(img)
self.repaint()
class Main(QtCore.QObject):
newTask = QtCore.pyqtSignal(list)
processTask = QtCore.pyqtSignal()
videoTask = QtCore.pyqtSignal(str, str, list)
def __init__(self, window):
QtCore.QObject.__init__(self)
# print('main thread id: {}'.format(QtCore.QThread.currentThreadId()))
self.window = window
self.core = core.Core()
self.pages = []
self.selectedComponents = []
self.lastAutosave = time.time()
# create data directory, load/create settings
self.dataDir = QDesktopServices.storageLocation(QDesktopServices.DataLocation)
self.autosavePath = os.path.join(self.dataDir, 'autosave.avp')
self.presetDir = os.path.join(self.dataDir, 'presets')
self.settings = QSettings(os.path.join(self.dataDir, 'settings.ini'), QSettings.IniFormat)
LoadDefaultSettings(self)
if not os.path.exists(self.dataDir):
os.makedirs(self.dataDir)
for neededDirectory in (self.presetDir, self.settings.value("projectDir")):
if not os.path.exists(neededDirectory):
os.mkdir(neededDirectory)
#
self.previewQueue = Queue()
self.previewThread = QtCore.QThread(self)
self.previewWorker = preview_thread.Worker(self, self.previewQueue)
self.previewWorker.moveToThread(self.previewThread)
self.previewWorker.imageCreated.connect(self.showPreviewImage)
self.previewThread.start()
self.timer = QtCore.QTimer(self)
self.timer.timeout.connect(self.processTask.emit)
self.timer.start(500)
# begin decorating the window and connecting events
window.toolButton_selectAudioFile.clicked.connect(self.openInputFileDialog)
window.toolButton_selectOutputFile.clicked.connect(self.openOutputFileDialog)
window.progressBar_createVideo.setValue(0)
window.pushButton_createVideo.clicked.connect(self.createAudioVisualisation)
window.pushButton_Cancel.clicked.connect(self.stopVideo)
window.setWindowTitle("Audio Visualizer")
self.previewWindow = PreviewWindow(self, os.path.join(os.path.dirname(os.path.realpath(__file__)), "background.png"))
window.verticalLayout_previewWrapper.addWidget(self.previewWindow)
self.modules = self.findComponents()
self.compMenu = QMenu()
for i, comp in enumerate(self.modules):
action = self.compMenu.addAction(comp.Component.__doc__)
action.triggered[()].connect( lambda item=i: self.insertComponent(item))
self.window.pushButton_addComponent.setMenu(self.compMenu)
window.listWidget_componentList.clicked.connect(lambda _: self.changeComponentWidget())
self.window.pushButton_removeComponent.clicked.connect(lambda _: self.removeComponent())
currentRes = str(self.settings.value('outputWidth'))+'x'+str(self.settings.value('outputHeight'))
for i, res in enumerate(self.resolutions):
window.comboBox_resolution.addItem(res)
if res == currentRes:
currentRes = i
window.comboBox_resolution.setCurrentIndex(currentRes)
window.comboBox_resolution.currentIndexChanged.connect(self.updateResolution)
self.window.pushButton_listMoveUp.clicked.connect(self.moveComponentUp)
self.window.pushButton_listMoveDown.clicked.connect(self.moveComponentDown)
self.window.pushButton_savePreset.clicked.connect(self.openSavePresetDialog)
self.window.comboBox_openPreset.currentIndexChanged.connect(self.openPreset)
self.window.pushButton_saveAs.clicked.connect(self.openSaveProjectDialog)
self.window.pushButton_saveProject.clicked.connect(self.saveCurrentProject)
self.window.pushButton_openProject.clicked.connect(self.openOpenProjectDialog)
# show the window and load current project
window.show()
self.currentProject = self.settings.value("currentProject")
if self.currentProject and os.path.exists(self.autosavePath) \
and filecmp.cmp(self.autosavePath, self.currentProject):
# delete autosave if it's identical to the project
os.remove(self.autosavePath)
if self.currentProject and os.path.exists(self.autosavePath):
ch = self.showMessage("Restore unsaved changes in project '%s'?" % os.path.basename(self.currentProject)[:-4], True)
if ch:
os.remove(self.currentProject)
os.rename(self.autosavePath, self.currentProject)
else:
os.remove(self.autosavePath)
self.openProject(self.currentProject)
self.drawPreview()
def cleanUp(self):
self.timer.stop()
self.previewThread.quit()
self.previewThread.wait()
self.autosave()
def autosave(self):
if time.time() - self.lastAutosave >= 1.0:
if os.path.exists(self.autosavePath):
os.remove(self.autosavePath)
self.createProjectFile(self.autosavePath)
self.lastAutosave = time.time()
def openInputFileDialog(self):
inputDir = self.settings.value("inputDir", expanduser("~"))
fileName = QtGui.QFileDialog.getOpenFileName(self.window,
"Open Music File", inputDir, "Music Files (*.mp3 *.wav *.ogg *.fla *.aac)");
if not fileName == "":
self.settings.setValue("inputDir", os.path.dirname(fileName))
self.window.lineEdit_audioFile.setText(fileName)
def openOutputFileDialog(self):
outputDir = self.settings.value("outputDir", expanduser("~"))
fileName = QtGui.QFileDialog.getSaveFileName(self.window,
"Set Output Video File", outputDir, "Video Files (*.mp4 *.mov *.mkv *.avi *.webm *.flv)");
if not fileName == "":
self.settings.setValue("outputDir", os.path.dirname(fileName))
self.window.lineEdit_outputFile.setText(fileName)
def stopVideo(self):
print('stop')
self.videoWorker.cancel()
self.canceled = True
def createAudioVisualisation(self):
# create output video if mandatory settings are filled in
if self.window.lineEdit_audioFile.text() and self.window.lineEdit_outputFile.text():
self.canceled = False
self.progressBarUpdated(-1)
ffmpeg_cmd = self.settings.value("ffmpeg_cmd", expanduser("~"))
self.videoThread = QtCore.QThread(self)
self.videoWorker = video_thread.Worker(self)
self.videoWorker.moveToThread(self.videoThread)
self.videoWorker.videoCreated.connect(self.videoCreated)
self.videoWorker.progressBarUpdate.connect(self.progressBarUpdated)
self.videoWorker.progressBarSetText.connect(self.progressBarSetText)
self.videoWorker.imageCreated.connect(self.showPreviewImage)
self.videoWorker.encoding.connect(self.changeEncodingStatus)
self.videoThread.start()
self.videoTask.emit(self.window.lineEdit_audioFile.text(),
self.window.lineEdit_outputFile.text(),
self.selectedComponents)
else:
self.showMessage("You must select an audio file and output filename.")
def progressBarUpdated(self, value):
self.window.progressBar_createVideo.setValue(value)
def changeEncodingStatus(self, status):
if status:
self.window.pushButton_createVideo.setEnabled(False)
self.window.pushButton_Cancel.setEnabled(True)
self.window.comboBox_resolution.setEnabled(False)
self.window.stackedWidget.setEnabled(False)
self.window.tab_encoderSettings.setEnabled(False)
self.window.label_audioFile.setEnabled(False)
self.window.toolButton_selectAudioFile.setEnabled(False)
self.window.label_outputFile.setEnabled(False)
self.window.toolButton_selectOutputFile.setEnabled(False)
self.window.lineEdit_audioFile.setEnabled(False)
self.window.lineEdit_outputFile.setEnabled(False)
self.window.pushButton_addComponent.setEnabled(False)
self.window.pushButton_removeComponent.setEnabled(False)
self.window.pushButton_listMoveDown.setEnabled(False)
self.window.pushButton_listMoveUp.setEnabled(False)
self.window.comboBox_openPreset.setEnabled(False)
self.window.pushButton_removePreset.setEnabled(False)
self.window.pushButton_savePreset.setEnabled(False)
self.window.pushButton_openProject.setEnabled(False)
self.window.listWidget_componentList.setEnabled(False)
else:
self.window.pushButton_createVideo.setEnabled(True)
self.window.pushButton_Cancel.setEnabled(False)
self.window.comboBox_resolution.setEnabled(True)
self.window.stackedWidget.setEnabled(True)
self.window.tab_encoderSettings.setEnabled(True)
self.window.label_audioFile.setEnabled(True)
self.window.toolButton_selectAudioFile.setEnabled(True)
self.window.lineEdit_audioFile.setEnabled(True)
self.window.label_outputFile.setEnabled(True)
self.window.toolButton_selectOutputFile.setEnabled(True)
self.window.lineEdit_outputFile.setEnabled(True)
self.window.pushButton_addComponent.setEnabled(True)
self.window.pushButton_removeComponent.setEnabled(True)
self.window.pushButton_listMoveDown.setEnabled(True)
self.window.pushButton_listMoveUp.setEnabled(True)
self.window.comboBox_openPreset.setEnabled(True)
self.window.pushButton_removePreset.setEnabled(True)
self.window.pushButton_savePreset.setEnabled(True)
self.window.pushButton_openProject.setEnabled(True)
self.window.listWidget_componentList.setEnabled(True)
def progressBarSetText(self, value):
self.window.progressBar_createVideo.setFormat(value)
def videoCreated(self):
self.videoThread.quit()
self.videoThread.wait()
def updateResolution(self):
resIndex = int(window.comboBox_resolution.currentIndex())
res = self.resolutions[resIndex].split('x')
self.settings.setValue('outputWidth',res[0])
self.settings.setValue('outputHeight',res[1])
self.drawPreview()
def drawPreview(self):
self.newTask.emit(self.selectedComponents)
# self.processTask.emit()
self.autosave()
def showPreviewImage(self, image):
self.previewWindow.changePixmap(image)
def findComponents(self):
def findComponents():
srcPath = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'components')
if os.path.exists(srcPath):
for f in sorted(os.listdir(srcPath)):
name, ext = os.path.splitext(f)
if name.startswith("__"):
continue
elif ext == '.py':
yield name
return [import_module('components.%s' % name) for name in findComponents()]
def addComponent(self, moduleIndex):
index = len(self.pages)
self.selectedComponents.append(self.modules[moduleIndex].Component())
self.window.listWidget_componentList.addItem(self.selectedComponents[-1].__doc__)
self.pages.append(self.selectedComponents[-1].widget(self))
self.window.listWidget_componentList.setCurrentRow(index)
self.window.stackedWidget.addWidget(self.pages[-1])
self.window.stackedWidget.setCurrentIndex(index)
self.selectedComponents[-1].update()
self.updateOpenPresetComboBox(self.selectedComponents[-1])
def insertComponent(self, moduleIndex):
self.selectedComponents.insert(0, self.modules[moduleIndex].Component())
self.window.listWidget_componentList.insertItem(0, self.selectedComponents[0].__doc__)
self.pages.insert(0, self.selectedComponents[0].widget(self))
self.window.listWidget_componentList.setCurrentRow(0)
self.window.stackedWidget.insertWidget(0, self.pages[0])
self.window.stackedWidget.setCurrentIndex(0)
self.selectedComponents[0].update()
self.updateOpenPresetComboBox(self.selectedComponents[0])
def removeComponent(self):
for selected in self.window.listWidget_componentList.selectedItems():
index = self.window.listWidget_componentList.row(selected)
self.window.stackedWidget.removeWidget(self.pages[index])
self.window.listWidget_componentList.takeItem(index)
self.selectedComponents.pop(index)
self.pages.pop(index)
self.changeComponentWidget()
self.drawPreview()
def changeComponentWidget(self):
selected = self.window.listWidget_componentList.selectedItems()
if selected:
index = self.window.listWidget_componentList.row(selected[0])
self.window.stackedWidget.setCurrentIndex(index)
self.updateOpenPresetComboBox(self.selectedComponents[index])
def moveComponentUp(self):
row = self.window.listWidget_componentList.currentRow()
if row > 0:
module = self.selectedComponents[row]
self.selectedComponents.pop(row)
self.selectedComponents.insert(row - 1,module)
page = self.pages[row]
self.pages.pop(row)
self.pages.insert(row - 1, page)
item = self.window.listWidget_componentList.takeItem(row)
self.window.listWidget_componentList.insertItem(row - 1, item)
widget = self.window.stackedWidget.removeWidget(page)
self.window.stackedWidget.insertWidget(row - 1, page)
self.window.listWidget_componentList.setCurrentRow(row - 1)
self.window.stackedWidget.setCurrentIndex(row -1)
self.drawPreview()
def moveComponentDown(self):
row = self.window.listWidget_componentList.currentRow()
if row != -1 and row < len(self.pages)+1:
module = self.selectedComponents[row]
self.selectedComponents.pop(row)
self.selectedComponents.insert(row + 1,module)
page = self.pages[row]
self.pages.pop(row)
self.pages.insert(row + 1, page)
item = self.window.listWidget_componentList.takeItem(row)
self.window.listWidget_componentList.insertItem(row + 1, item)
widget = self.window.stackedWidget.removeWidget(page)
self.window.stackedWidget.insertWidget(row + 1, page)
self.window.listWidget_componentList.setCurrentRow(row + 1)
self.window.stackedWidget.setCurrentIndex(row + 1)
self.drawPreview()
def updateOpenPresetComboBox(self, component):
self.window.comboBox_openPreset.clear()
self.window.comboBox_openPreset.addItem("Component Presets")
destination = os.path.join(self.presetDir,
str(component).strip(), str(component.version()))
if not os.path.exists(destination):
os.makedirs(destination)
for f in os.listdir(destination):
self.window.comboBox_openPreset.addItem(f)
def openSavePresetDialog(self):
if self.window.listWidget_componentList.currentRow() == -1:
return
while True:
newName, OK = QtGui.QInputDialog.getText(QtGui.QWidget(), 'Audio Visualizer', 'New Preset Name:')
badName = False
for letter in newName:
if letter in string.punctuation:
badName = True
if badName:
# some filesystems don't like bizarre characters
self.showMessage("Preset names must contain only letters, numbers, and spaces.")
continue
if OK and newName:
index = self.window.listWidget_componentList.currentRow()
if index != -1:
saveValueStore = self.selectedComponents[index].savePreset()
componentName = str(self.selectedComponents[index]).strip()
vers = self.selectedComponents[index].version()
self.createPresetFile(componentName, vers, saveValueStore, newName)
break
def createPresetFile(self, componentName, version, saveValueStore, filename):
dirname = os.path.join(self.presetDir, componentName, str(version))
if not os.path.exists(dirname):
os.makedirs(dirname)
filepath = os.path.join(dirname, filename)
if os.path.exists(filepath):
ch = self.showMessage("%s already exists! Overwrite it?" % filename, True, QtGui.QMessageBox.Warning)
if not ch:
return
# remove old copies of the preset
for i in range(0, self.window.comboBox_openPreset.count()):
if self.window.comboBox_openPreset.itemText(i) == filename:
self.window.comboBox_openPreset.removeItem(i)
with open(filepath, 'w') as f:
f.write(core.Core.stringOrderedDict(saveValueStore))
self.window.comboBox_openPreset.addItem(filename)
self.window.comboBox_openPreset.setCurrentIndex(self.window.comboBox_openPreset.count()-1)
def openPreset(self):
if self.window.comboBox_openPreset.currentIndex() < 1:
return
index = self.window.listWidget_componentList.currentRow()
if index == -1:
return
filename = self.window.comboBox_openPreset.itemText(self.window.comboBox_openPreset.currentIndex())
componentName = str(self.selectedComponents[index]).strip()
version = self.selectedComponents[index].version()
dirname = os.path.join(self.presetDir, componentName, str(version))
filepath = os.path.join(dirname, filename)
if not os.path.exists(filepath):
self.window.comboBox_openPreset.removeItem(self.window.comboBox_openPreset.currentIndex())
return
with open(filepath, 'r') as f:
for line in f:
saveValueStore = dict(eval(line.strip()))
break
self.selectedComponents[index].loadPreset(saveValueStore)
self.drawPreview()
def saveCurrentProject(self):
if self.currentProject:
self.createProjectFile(self.currentProject)
else:
self.openSaveProjectDialog()
def openSaveProjectDialog(self):
filename = QtGui.QFileDialog.getSaveFileName(self.window,
"Create Project File", self.settings.value("projectDir"),
"Project Files (*.avp)")
if not filename:
return
self.createProjectFile(filename)
def createProjectFile(self, filepath):
if not filepath.endswith(".avp"):
filepath += '.avp'
with open(filepath, 'w') as f:
print('creating %s' % filepath)
f.write('[Components]\n')
for comp in self.selectedComponents:
saveValueStore = comp.savePreset()
f.write('%s\n' % str(comp))
f.write('%s\n' % str(comp.version()))
f.write('%s\n' % core.Core.stringOrderedDict(saveValueStore))
if filepath != self.autosavePath:
self.settings.setValue("projectDir", os.path.dirname(filepath))
self.settings.setValue("currentProject", filepath)
self.currentProject = filepath
def openOpenProjectDialog(self):
filename = QtGui.QFileDialog.getOpenFileName(self.window,
"Open Project File", self.settings.value("projectDir"),
"Project Files (*.avp)")
self.openProject(filename)
def openProject(self, filepath):
if not filepath or not os.path.exists(filepath) or not filepath.endswith('.avp'):
return
self.clear()
self.currentProject = filepath
self.settings.setValue("currentProject", filepath)
self.settings.setValue("projectDir", os.path.dirname(filepath))
compNames = [mod.Component.__doc__ for mod in self.modules]
try:
with open(filepath, 'r') as f:
validSections = ('Components')
section = ''
def parseLine(line):
line = line.strip()
newSection = ''
if line.startswith('[') and line.endswith(']') and line[1:-1] in validSections:
newSection = line[1:-1]
return line, newSection
i = 0
for line in f:
line, newSection = parseLine(line)
if newSection:
section = str(newSection)
continue
if line and section == 'Components':
if i == 0:
compIndex = compNames.index(line)
self.addComponent(compIndex)
i += 1
elif i == 1:
# version, not used yet
i += 1
elif i == 2:
saveValueStore = dict(eval(line))
self.selectedComponents[-1].loadPreset(saveValueStore)
i = 0
except (IndexError, ValueError, KeyError, NameError, SyntaxError, AttributeError, TypeError) as e:
self.clear()
typ, value, _ = sys.exc_info()
msg = '%s: %s' % (typ.__name__, value)
self.showMessage("Project file '%s' is corrupted." % filepath, False,
QtGui.QMessageBox.Warning, msg)
def showMessage(self, string, showCancel=False, icon=QtGui.QMessageBox.Information, detail=None):
msg = QtGui.QMessageBox()
msg.setIcon(icon)
msg.setText(string)
msg.setDetailedText(detail)
if showCancel:
msg.setStandardButtons(QtGui.QMessageBox.Ok | QtGui.QMessageBox.Cancel)
else:
msg.setStandardButtons(QtGui.QMessageBox.Ok)
ch = msg.exec_()
if ch == 1024:
return True
return False
def clear(self):
''' empty out all components and fields, get a blank slate '''
self.selectedComponents = []
self.window.listWidget_componentList.clear()
for widget in self.pages:
self.window.stackedWidget.removeWidget(widget)
self.pages = []
def LoadDefaultSettings(self): def LoadDefaultSettings(self):
self.resolutions = [ self.resolutions = [
'1920x1080', '1920x1080',
'1280x720', '1280x720',
'854x480' '854x480'
] ]
default = { default = {
"outputWidth": 1280, "outputWidth": 1280,
"outputHeight": 720, "outputHeight": 720,
"outputFrameRate": 30, "outputFrameRate": 30,
"outputAudioCodec": "aac", "outputAudioCodec": "aac",
"outputAudioBitrate": "192k", "outputAudioBitrate": "192k",
"outputVideoCodec": "libx264", "outputVideoCodec": "libx264",
"outputVideoFormat": "yuv420p", "outputVideoFormat": "yuv420p",
"outputPreset": "medium", "outputPreset": "medium",
"outputFormat": "mp4", "outputFormat": "mp4",
"projectDir" : os.path.join(self.dataDir, 'projects'), "projectDir": os.path.join(self.dataDir, 'projects'),
} }
for parm, value in default.items():
if self.settings.value(parm) == None:
self.settings.setValue(parm,value)
for parm, value in default.items():
if self.settings.value(parm) is None:
self.settings.setValue(parm, value)
''' ####### commandline functionality broken until we decide how to implement it
if len(sys.argv) > 1:
# command line mode
app = QtGui.QApplication(sys.argv, False)
command = Command()
signal.signal(signal.SIGINT, command.cleanUp)
sys.exit(app.exec_())
else:
'''
# gui mode
if __name__ == "__main__": if __name__ == "__main__":
''' FIXME commandline functionality broken until we decide how to implement
if len(sys.argv) > 1:
# command line mode
app = QtGui.QApplication(sys.argv, False)
command = Command()
signal.signal(signal.SIGINT, command.cleanUp)
sys.exit(app.exec_())
else:
'''
app = QtGui.QApplication(sys.argv) app = QtGui.QApplication(sys.argv)
app.setApplicationName("audio-visualizer") app.setApplicationName("audio-visualizer")
app.setOrganizationName("audio-visualizer") app.setOrganizationName("audio-visualizer")
window = uic.loadUi(os.path.join(os.path.dirname(os.path.realpath(__file__)), "mainwindow.ui")) window = uic.loadUi(os.path.join(
os.path.dirname(os.path.realpath(__file__)), "mainwindow.ui"))
# window.adjustSize() # window.adjustSize()
desc = QtGui.QDesktopWidget() desc = QtGui.QDesktopWidget()
dpi = desc.physicalDpiX() dpi = desc.physicalDpiX()
topMargin = 0 if (dpi == 96) else int(10 * (dpi / 96)) topMargin = 0 if (dpi == 96) else int(10 * (dpi / 96))
window.resize(window.width() * (dpi / 96), window.height() * (dpi / 96)) window.resize(window.width() * (dpi / 96), window.height() * (dpi / 96))
#window.verticalLayout_2.setContentsMargins(0, topMargin, 0, 0) # window.verticalLayout_2.setContentsMargins(0, topMargin, 0, 0)
main = Main(window) main = MainWindow(window)
signal.signal(signal.SIGINT, main.cleanUp) signal.signal(signal.SIGINT, main.cleanUp)
atexit.register(main.cleanUp) atexit.register(main.cleanUp)

586
mainwindow.py Normal file
View File

@ -0,0 +1,586 @@
from os.path import expanduser
from queue import Queue
from importlib import import_module
from collections import OrderedDict
from PyQt4 import QtCore, QtGui
from PyQt4.QtCore import QSettings, Qt
from PyQt4.QtGui import QDesktopServices, QMenu
import sys
import io
import os
import string
import signal
import filecmp
import time
import core
import preview_thread
import video_thread
from main import LoadDefaultSettings
class PreviewWindow(QtGui.QLabel):
def __init__(self, parent, img):
super(PreviewWindow, self).__init__()
self.parent = parent
self.setFrameStyle(QtGui.QFrame.StyledPanel)
self.pixmap = QtGui.QPixmap(img)
def paintEvent(self, event):
size = self.size()
painter = QtGui.QPainter(self)
point = QtCore.QPoint(0, 0)
scaledPix = self.pixmap.scaled(
size, Qt.KeepAspectRatio, transformMode=Qt.SmoothTransformation)
# start painting the label from left upper corner
point.setX((size.width() - scaledPix.width())/2)
point.setY((size.height() - scaledPix.height())/2)
painter.drawPixmap(point, scaledPix)
def changePixmap(self, img):
self.pixmap = QtGui.QPixmap(img)
self.repaint()
class MainWindow(QtCore.QObject):
newTask = QtCore.pyqtSignal(list)
processTask = QtCore.pyqtSignal()
videoTask = QtCore.pyqtSignal(str, str, list)
def __init__(self, window):
QtCore.QObject.__init__(self)
# print('main thread id: {}'.format(QtCore.QThread.currentThreadId()))
self.window = window
self.core = core.Core()
self.pages = []
self.selectedComponents = []
self.lastAutosave = time.time()
# create data directory, load/create settings
self.dataDir = QDesktopServices.storageLocation(
QDesktopServices.DataLocation)
self.autosavePath = os.path.join(self.dataDir, 'autosave.avp')
self.presetDir = os.path.join(self.dataDir, 'presets')
self.settings = QSettings(
os.path.join(self.dataDir, 'settings.ini'), QSettings.IniFormat)
LoadDefaultSettings(self)
if not os.path.exists(self.dataDir):
os.makedirs(self.dataDir)
for neededDirectory in (
self.presetDir, self.settings.value("projectDir")):
if not os.path.exists(neededDirectory):
os.mkdir(neededDirectory)
#
self.previewQueue = Queue()
self.previewThread = QtCore.QThread(self)
self.previewWorker = preview_thread.Worker(self, self.previewQueue)
self.previewWorker.moveToThread(self.previewThread)
self.previewWorker.imageCreated.connect(self.showPreviewImage)
self.previewThread.start()
self.timer = QtCore.QTimer(self)
self.timer.timeout.connect(self.processTask.emit)
self.timer.start(500)
# begin decorating the window and connecting events
window.toolButton_selectAudioFile.clicked.connect(
self.openInputFileDialog)
window.toolButton_selectOutputFile.clicked.connect(
self.openOutputFileDialog)
window.progressBar_createVideo.setValue(0)
window.pushButton_createVideo.clicked.connect(
self.createAudioVisualisation)
window.pushButton_Cancel.clicked.connect(self.stopVideo)
window.setWindowTitle("Audio Visualizer")
self.previewWindow = PreviewWindow(self, os.path.join(
os.path.dirname(os.path.realpath(__file__)), "background.png"))
window.verticalLayout_previewWrapper.addWidget(self.previewWindow)
self.modules = self.findComponents()
self.compMenu = QMenu()
for i, comp in enumerate(self.modules):
action = self.compMenu.addAction(comp.Component.__doc__)
action.triggered[()].connect(
lambda item=i: self.insertComponent(item))
self.window.pushButton_addComponent.setMenu(self.compMenu)
window.listWidget_componentList.clicked.connect(
lambda _: self.changeComponentWidget())
self.window.pushButton_removeComponent.clicked.connect(
lambda _: self.removeComponent())
currentRes = str(self.settings.value('outputWidth'))+'x' + \
str(self.settings.value('outputHeight'))
for i, res in enumerate(self.resolutions):
window.comboBox_resolution.addItem(res)
if res == currentRes:
currentRes = i
window.comboBox_resolution.setCurrentIndex(currentRes)
window.comboBox_resolution.currentIndexChanged.connect(
self.updateResolution)
self.window.pushButton_listMoveUp.clicked.connect(
self.moveComponentUp)
self.window.pushButton_listMoveDown.clicked.connect(
self.moveComponentDown)
self.window.pushButton_savePreset.clicked.connect(
self.openSavePresetDialog)
self.window.comboBox_openPreset.currentIndexChanged.connect(
self.openPreset)
self.window.pushButton_saveAs.clicked.connect(
self.openSaveProjectDialog)
self.window.pushButton_saveProject.clicked.connect(
self.saveCurrentProject)
self.window.pushButton_openProject.clicked.connect(
self.openOpenProjectDialog)
# show the window and load current project
window.show()
self.currentProject = self.settings.value("currentProject")
if self.currentProject and os.path.exists(self.autosavePath) \
and filecmp.cmp(self.autosavePath, self.currentProject):
# delete autosave if it's identical to the project
os.remove(self.autosavePath)
if self.currentProject and os.path.exists(self.autosavePath):
ch = self.showMessage(
"Restore unsaved changes in project '%s'?"
% os.path.basename(self.currentProject)[:-4], True)
if ch:
os.remove(self.currentProject)
os.rename(self.autosavePath, self.currentProject)
else:
os.remove(self.autosavePath)
self.openProject(self.currentProject)
self.drawPreview()
def cleanUp(self):
self.timer.stop()
self.previewThread.quit()
self.previewThread.wait()
self.autosave()
def autosave(self):
if time.time() - self.lastAutosave >= 1.0:
if os.path.exists(self.autosavePath):
os.remove(self.autosavePath)
self.createProjectFile(self.autosavePath)
self.lastAutosave = time.time()
def openInputFileDialog(self):
inputDir = self.settings.value("inputDir", expanduser("~"))
fileName = QtGui.QFileDialog.getOpenFileName(
self.window, "Open Music File",
inputDir, "Music Files (*.mp3 *.wav *.ogg *.fla *.aac)")
if not fileName == "":
self.settings.setValue("inputDir", os.path.dirname(fileName))
self.window.lineEdit_audioFile.setText(fileName)
def openOutputFileDialog(self):
outputDir = self.settings.value("outputDir", expanduser("~"))
fileName = QtGui.QFileDialog.getSaveFileName(
self.window, "Set Output Video File",
outputDir, "Video Files (*.mp4 *.mov *.mkv *.avi *.webm *.flv)")
if not fileName == "":
self.settings.setValue("outputDir", os.path.dirname(fileName))
self.window.lineEdit_outputFile.setText(fileName)
def stopVideo(self):
print('stop')
self.videoWorker.cancel()
self.canceled = True
def createAudioVisualisation(self):
# create output video if mandatory settings are filled in
if self.window.lineEdit_audioFile.text() and \
self.window.lineEdit_outputFile.text():
self.canceled = False
self.progressBarUpdated(-1)
ffmpeg_cmd = self.settings.value("ffmpeg_cmd", expanduser("~"))
self.videoThread = QtCore.QThread(self)
self.videoWorker = video_thread.Worker(self)
self.videoWorker.moveToThread(self.videoThread)
self.videoWorker.videoCreated.connect(self.videoCreated)
self.videoWorker.progressBarUpdate.connect(self.progressBarUpdated)
self.videoWorker.progressBarSetText.connect(
self.progressBarSetText)
self.videoWorker.imageCreated.connect(self.showPreviewImage)
self.videoWorker.encoding.connect(self.changeEncodingStatus)
self.videoThread.start()
self.videoTask.emit(
self.window.lineEdit_audioFile.text(),
self.window.lineEdit_outputFile.text(),
self.selectedComponents)
else:
self.showMessage(
"You must select an audio file and output filename.")
def progressBarUpdated(self, value):
self.window.progressBar_createVideo.setValue(value)
def changeEncodingStatus(self, status):
if status:
self.window.pushButton_createVideo.setEnabled(False)
self.window.pushButton_Cancel.setEnabled(True)
self.window.comboBox_resolution.setEnabled(False)
self.window.stackedWidget.setEnabled(False)
self.window.tab_encoderSettings.setEnabled(False)
self.window.label_audioFile.setEnabled(False)
self.window.toolButton_selectAudioFile.setEnabled(False)
self.window.label_outputFile.setEnabled(False)
self.window.toolButton_selectOutputFile.setEnabled(False)
self.window.lineEdit_audioFile.setEnabled(False)
self.window.lineEdit_outputFile.setEnabled(False)
self.window.pushButton_addComponent.setEnabled(False)
self.window.pushButton_removeComponent.setEnabled(False)
self.window.pushButton_listMoveDown.setEnabled(False)
self.window.pushButton_listMoveUp.setEnabled(False)
self.window.comboBox_openPreset.setEnabled(False)
self.window.pushButton_removePreset.setEnabled(False)
self.window.pushButton_savePreset.setEnabled(False)
self.window.pushButton_openProject.setEnabled(False)
self.window.listWidget_componentList.setEnabled(False)
else:
self.window.pushButton_createVideo.setEnabled(True)
self.window.pushButton_Cancel.setEnabled(False)
self.window.comboBox_resolution.setEnabled(True)
self.window.stackedWidget.setEnabled(True)
self.window.tab_encoderSettings.setEnabled(True)
self.window.label_audioFile.setEnabled(True)
self.window.toolButton_selectAudioFile.setEnabled(True)
self.window.lineEdit_audioFile.setEnabled(True)
self.window.label_outputFile.setEnabled(True)
self.window.toolButton_selectOutputFile.setEnabled(True)
self.window.lineEdit_outputFile.setEnabled(True)
self.window.pushButton_addComponent.setEnabled(True)
self.window.pushButton_removeComponent.setEnabled(True)
self.window.pushButton_listMoveDown.setEnabled(True)
self.window.pushButton_listMoveUp.setEnabled(True)
self.window.comboBox_openPreset.setEnabled(True)
self.window.pushButton_removePreset.setEnabled(True)
self.window.pushButton_savePreset.setEnabled(True)
self.window.pushButton_openProject.setEnabled(True)
self.window.listWidget_componentList.setEnabled(True)
def progressBarSetText(self, value):
self.window.progressBar_createVideo.setFormat(value)
def videoCreated(self):
self.videoThread.quit()
self.videoThread.wait()
def updateResolution(self):
resIndex = int(window.comboBox_resolution.currentIndex())
res = self.resolutions[resIndex].split('x')
self.settings.setValue('outputWidth', res[0])
self.settings.setValue('outputHeight', res[1])
self.drawPreview()
def drawPreview(self):
self.newTask.emit(self.selectedComponents)
# self.processTask.emit()
self.autosave()
def showPreviewImage(self, image):
self.previewWindow.changePixmap(image)
def findComponents(self):
def findComponents():
srcPath = os.path.join(
os.path.dirname(os.path.realpath(__file__)), 'components')
if os.path.exists(srcPath):
for f in sorted(os.listdir(srcPath)):
name, ext = os.path.splitext(f)
if name.startswith("__"):
continue
elif ext == '.py':
yield name
return [
import_module('components.%s' % name)
for name in findComponents()]
def addComponent(self, moduleIndex):
index = len(self.pages)
self.selectedComponents.append(self.modules[moduleIndex].Component())
self.window.listWidget_componentList.addItem(
self.selectedComponents[-1].__doc__)
self.pages.append(self.selectedComponents[-1].widget(self))
self.window.listWidget_componentList.setCurrentRow(index)
self.window.stackedWidget.addWidget(self.pages[-1])
self.window.stackedWidget.setCurrentIndex(index)
self.selectedComponents[-1].update()
self.updateOpenPresetComboBox(self.selectedComponents[-1])
def insertComponent(self, moduleIndex):
self.selectedComponents.insert(
0, self.modules[moduleIndex].Component())
self.window.listWidget_componentList.insertItem(
0, self.selectedComponents[0].__doc__)
self.pages.insert(0, self.selectedComponents[0].widget(self))
self.window.listWidget_componentList.setCurrentRow(0)
self.window.stackedWidget.insertWidget(0, self.pages[0])
self.window.stackedWidget.setCurrentIndex(0)
self.selectedComponents[0].update()
self.updateOpenPresetComboBox(self.selectedComponents[0])
def removeComponent(self):
for selected in self.window.listWidget_componentList.selectedItems():
index = self.window.listWidget_componentList.row(selected)
self.window.stackedWidget.removeWidget(self.pages[index])
self.window.listWidget_componentList.takeItem(index)
self.selectedComponents.pop(index)
self.pages.pop(index)
self.changeComponentWidget()
self.drawPreview()
def changeComponentWidget(self):
selected = self.window.listWidget_componentList.selectedItems()
if selected:
index = self.window.listWidget_componentList.row(selected[0])
self.window.stackedWidget.setCurrentIndex(index)
self.updateOpenPresetComboBox(self.selectedComponents[index])
def moveComponentUp(self):
row = self.window.listWidget_componentList.currentRow()
if row > 0:
module = self.selectedComponents[row]
self.selectedComponents.pop(row)
self.selectedComponents.insert(row - 1, module)
page = self.pages[row]
self.pages.pop(row)
self.pages.insert(row - 1, page)
item = self.window.listWidget_componentList.takeItem(row)
self.window.listWidget_componentList.insertItem(row - 1, item)
widget = self.window.stackedWidget.removeWidget(page)
self.window.stackedWidget.insertWidget(row - 1, page)
self.window.listWidget_componentList.setCurrentRow(row - 1)
self.window.stackedWidget.setCurrentIndex(row - 1)
self.drawPreview()
def moveComponentDown(self):
row = self.window.listWidget_componentList.currentRow()
if row != -1 and row < len(self.pages)+1:
module = self.selectedComponents[row]
self.selectedComponents.pop(row)
self.selectedComponents.insert(row + 1, module)
page = self.pages[row]
self.pages.pop(row)
self.pages.insert(row + 1, page)
item = self.window.listWidget_componentList.takeItem(row)
self.window.listWidget_componentList.insertItem(row + 1, item)
widget = self.window.stackedWidget.removeWidget(page)
self.window.stackedWidget.insertWidget(row + 1, page)
self.window.listWidget_componentList.setCurrentRow(row + 1)
self.window.stackedWidget.setCurrentIndex(row + 1)
self.drawPreview()
def updateOpenPresetComboBox(self, component):
self.window.comboBox_openPreset.clear()
self.window.comboBox_openPreset.addItem("Component Presets")
destination = os.path.join(
self.presetDir, str(component).strip(), str(component.version()))
if not os.path.exists(destination):
os.makedirs(destination)
for f in os.listdir(destination):
self.window.comboBox_openPreset.addItem(f)
def openSavePresetDialog(self):
if self.window.listWidget_componentList.currentRow() == -1:
return
while True:
newName, OK = QtGui.QInputDialog.getText(
QtGui.QWidget(), 'Audio Visualizer', 'New Preset Name:')
badName = False
for letter in newName:
if letter in string.punctuation:
badName = True
if badName:
# some filesystems don't like bizarre characters
self.showMessage("Preset names must contain only letters, \
numbers, and spaces.")
continue
if OK and newName:
index = self.window.listWidget_componentList.currentRow()
if index != -1:
saveValueStore = \
self.selectedComponents[index].savePreset()
componentName = str(self.selectedComponents[index]).strip()
vers = self.selectedComponents[index].version()
self.createPresetFile(
componentName, vers, saveValueStore, newName)
break
def createPresetFile(
self, componentName, version, saveValueStore, filename):
dirname = os.path.join(self.presetDir, componentName, str(version))
if not os.path.exists(dirname):
os.makedirs(dirname)
filepath = os.path.join(dirname, filename)
if os.path.exists(filepath):
ch = self.showMessage(
"%s already exists! Overwrite it?" % filename,
True, QtGui.QMessageBox.Warning)
if not ch:
return
# remove old copies of the preset
for i in range(0, self.window.comboBox_openPreset.count()):
if self.window.comboBox_openPreset.itemText(i) == filename:
self.window.comboBox_openPreset.removeItem(i)
with open(filepath, 'w') as f:
f.write(core.Core.stringOrderedDict(saveValueStore))
self.window.comboBox_openPreset.addItem(filename)
self.window.comboBox_openPreset.setCurrentIndex(
self.window.comboBox_openPreset.count()-1)
def openPreset(self):
if self.window.comboBox_openPreset.currentIndex() < 1:
return
index = self.window.listWidget_componentList.currentRow()
if index == -1:
return
filename = self.window.comboBox_openPreset.itemText(
self.window.comboBox_openPreset.currentIndex())
componentName = str(self.selectedComponents[index]).strip()
version = self.selectedComponents[index].version()
dirname = os.path.join(self.presetDir, componentName, str(version))
filepath = os.path.join(dirname, filename)
if not os.path.exists(filepath):
self.window.comboBox_openPreset.removeItem(
self.window.comboBox_openPreset.currentIndex())
return
with open(filepath, 'r') as f:
for line in f:
saveValueStore = dict(eval(line.strip()))
break
self.selectedComponents[index].loadPreset(saveValueStore)
self.drawPreview()
def saveCurrentProject(self):
if self.currentProject:
self.createProjectFile(self.currentProject)
else:
self.openSaveProjectDialog()
def openSaveProjectDialog(self):
filename = QtGui.QFileDialog.getSaveFileName(
self.window, "Create Project File",
self.settings.value("projectDir"),
"Project Files (*.avp)")
if not filename:
return
self.createProjectFile(filename)
def createProjectFile(self, filepath):
if not filepath.endswith(".avp"):
filepath += '.avp'
with open(filepath, 'w') as f:
print('creating %s' % filepath)
f.write('[Components]\n')
for comp in self.selectedComponents:
saveValueStore = comp.savePreset()
f.write('%s\n' % str(comp))
f.write('%s\n' % str(comp.version()))
f.write('%s\n' % core.Core.stringOrderedDict(saveValueStore))
if filepath != self.autosavePath:
self.settings.setValue("projectDir", os.path.dirname(filepath))
self.settings.setValue("currentProject", filepath)
self.currentProject = filepath
def openOpenProjectDialog(self):
filename = QtGui.QFileDialog.getOpenFileName(
self.window, "Open Project File",
self.settings.value("projectDir"),
"Project Files (*.avp)")
self.openProject(filename)
def openProject(self, filepath):
if not filepath or not os.path.exists(filepath) \
or not filepath.endswith('.avp'):
return
self.clear()
self.currentProject = filepath
self.settings.setValue("currentProject", filepath)
self.settings.setValue("projectDir", os.path.dirname(filepath))
compNames = [mod.Component.__doc__ for mod in self.modules]
try:
with open(filepath, 'r') as f:
validSections = ('Components')
section = ''
def parseLine(line):
line = line.strip()
newSection = ''
if line.startswith('[') and line.endswith(']') \
and line[1:-1] in validSections:
newSection = line[1:-1]
return line, newSection
i = 0
for line in f:
line, newSection = parseLine(line)
if newSection:
section = str(newSection)
continue
if line and section == 'Components':
if i == 0:
compIndex = compNames.index(line)
self.addComponent(compIndex)
i += 1
elif i == 1:
# version, not used yet
i += 1
elif i == 2:
saveValueStore = dict(eval(line))
self.selectedComponents[-1].loadPreset(
saveValueStore)
i = 0
except (IndexError, ValueError, KeyError, NameError,
SyntaxError, AttributeError, TypeError) as e:
self.clear()
typ, value, _ = sys.exc_info()
msg = '%s: %s' % (typ.__name__, value)
self.showMessage(
"Project file '%s' is corrupted." % filepath, False,
QtGui.QMessageBox.Warning, msg)
def showMessage(
self, string, showCancel=False,
icon=QtGui.QMessageBox.Information, detail=None):
msg = QtGui.QMessageBox()
msg.setIcon(icon)
msg.setText(string)
msg.setDetailedText(detail)
if showCancel:
msg.setStandardButtons(
QtGui.QMessageBox.Ok | QtGui.QMessageBox.Cancel)
else:
msg.setStandardButtons(QtGui.QMessageBox.Ok)
ch = msg.exec_()
if ch == 1024:
return True
return False
def clear(self):
''' empty out all components and fields, get a blank slate '''
self.selectedComponents = []
self.window.listWidget_componentList.clear()
for widget in self.pages:
self.window.stackedWidget.removeWidget(widget)
self.pages = []

View File

@ -9,53 +9,52 @@ import numpy
import os import os
from copy import copy from copy import copy
class Worker(QtCore.QObject): class Worker(QtCore.QObject):
imageCreated = pyqtSignal(['QImage']) imageCreated = pyqtSignal(['QImage'])
def __init__(self, parent=None, queue=None): def __init__(self, parent=None, queue=None):
QtCore.QObject.__init__(self) QtCore.QObject.__init__(self)
parent.newTask.connect(self.createPreviewImage) parent.newTask.connect(self.createPreviewImage)
parent.processTask.connect(self.process) parent.processTask.connect(self.process)
self.core = core.Core() self.core = core.Core()
self.queue = queue self.queue = queue
self.core.settings = parent.settings self.core.settings = parent.settings
self.stackedWidget = parent.window.stackedWidget self.stackedWidget = parent.window.stackedWidget
self.background = Image.new("RGBA", (1920, 1080),(0,0,0,0)) self.background = Image.new("RGBA", (1920, 1080), (0, 0, 0, 0))
self.background.paste(Image.open(os.path.join(os.path.dirname(os.path.realpath(__file__)),"background.png"))) self.background.paste(Image.open(os.path.join(
os.path.dirname(os.path.realpath(__file__)), "background.png")))
@pyqtSlot(str, list)
def createPreviewImage(self, components):
dic = {
"components": components,
}
self.queue.put(dic)
@pyqtSlot(str, list) @pyqtSlot()
def createPreviewImage(self, components): def process(self):
# print('worker thread id: {}'.format(QtCore.QThread.currentThreadId()))
dic = {
"components": components,
}
self.queue.put(dic)
@pyqtSlot()
def process(self):
try:
nextPreviewInformation = self.queue.get(block=False)
while self.queue.qsize() >= 2:
try: try:
self.queue.get(block=False) nextPreviewInformation = self.queue.get(block=False)
while self.queue.qsize() >= 2:
try:
self.queue.get(block=False)
except Empty:
continue
width = int(self.core.settings.value('outputWidth'))
height = int(self.core.settings.value('outputHeight'))
frame = copy(self.background)
frame = frame.resize((width, height))
components = nextPreviewInformation["components"]
for component in reversed(components):
frame = Image.alpha_composite(
frame, component.previewRender(self))
self._image = ImageQt(frame)
self.imageCreated.emit(QtGui.QImage(self._image))
except Empty: except Empty:
continue True
width = int(self.core.settings.value('outputWidth'))
height = int(self.core.settings.value('outputHeight'))
frame = copy(self.background)
frame = frame.resize((width,height))
components = nextPreviewInformation["components"]
for component in reversed(components):
#newFrame = Image.alpha_composite(frame,)
frame = Image.alpha_composite(frame,component.previewRender(self))
self._image = ImageQt(frame)
self.imageCreated.emit(QtGui.QImage(self._image))
except Empty:
True

View File

@ -13,6 +13,7 @@ import time
from copy import copy from copy import copy
import signal import signal
class Worker(QtCore.QObject): class Worker(QtCore.QObject):
imageCreated = pyqtSignal(['QImage']) imageCreated = pyqtSignal(['QImage'])
@ -40,16 +41,19 @@ class Worker(QtCore.QObject):
frame = None frame = None
for compNo, comp in reversed(list(enumerate(self.components))): for compNo, comp in reversed(list(enumerate(self.components))):
if compNo in self.staticComponents and self.staticComponents[compNo] != None: if compNo in self.staticComponents and \
self.staticComponents[compNo] is not None:
if frame is None: if frame is None:
frame = self.staticComponents[compNo] frame = self.staticComponents[compNo]
else: else:
frame = Image.alpha_composite(frame, self.staticComponents[compNo]) frame = Image.alpha_composite(
frame, self.staticComponents[compNo])
else: else:
if frame is None: if frame is None:
frame = comp.frameRender(compNo, i[0], i[1]) frame = comp.frameRender(compNo, i[0], i[1])
else: else:
frame = Image.alpha_composite(frame, comp.frameRender(compNo, i[0], i[1])) frame = Image.alpha_composite(
frame, comp.frameRender(compNo, i[0], i[1]))
self.renderQueue.put([i[0], frame]) self.renderQueue.put([i[0], frame])
self.compositeQueue.task_done() self.compositeQueue.task_done()
@ -63,8 +67,9 @@ class Worker(QtCore.QObject):
self.bgI += 1 self.bgI += 1
def previewDispatch(self): def previewDispatch(self):
background = Image.new("RGBA", (1920, 1080),(0,0,0,0)) background = Image.new("RGBA", (1920, 1080), (0, 0, 0, 0))
background.paste(Image.open(os.path.join(os.path.dirname(os.path.realpath(__file__)), "background.png"))) background.paste(Image.open(os.path.join(
os.path.dirname(os.path.realpath(__file__)), "background.png")))
background = background.resize((self.width, self.height)) background = background.resize((self.width, self.height))
while not self.stopped: while not self.stopped:
@ -83,11 +88,10 @@ class Worker(QtCore.QObject):
self.encoding.emit(True) self.encoding.emit(True)
self.components = components self.components = components
self.outputFile = outputFile self.outputFile = outputFile
self.bgI = 0 # tracked video frame self.bgI = 0 # tracked video frame
self.reset() self.reset()
self.width = int(self.core.settings.value('outputWidth')) self.width = int(self.core.settings.value('outputWidth'))
self.height = int(self.core.settings.value('outputHeight')) self.height = int(self.core.settings.value('outputHeight'))
# print('worker thread id: {}'.format(QtCore.QThread.currentThreadId()))
progressBarValue = 0 progressBarValue = 0
self.progressBarUpdate.emit(progressBarValue) self.progressBarUpdate.emit(progressBarValue)
@ -95,21 +99,24 @@ class Worker(QtCore.QObject):
self.completeAudioArray = self.core.readAudioFile(inputFile, self) self.completeAudioArray = self.core.readAudioFile(inputFile, self)
# test if user has libfdk_aac # test if user has libfdk_aac
encoders = sp.check_output(self.core.FFMPEG_BIN + " -encoders -hide_banner", shell=True) encoders = sp.check_output(
self.core.FFMPEG_BIN + " -encoders -hide_banner", shell=True)
acodec = self.core.settings.value('outputAudioCodec') acodec = self.core.settings.value('outputAudioCodec')
if b'libfdk_aac' in encoders and acodec == 'aac': if b'libfdk_aac' in encoders and acodec == 'aac':
acodec = 'libfdk_aac' acodec = 'libfdk_aac'
ffmpegCommand = [ ffmpegCommand = [
self.core.FFMPEG_BIN, self.core.FFMPEG_BIN,
'-thread_queue_size', '512', '-thread_queue_size', '512',
'-y', # (optional) means overwrite the output file if it already exists. '-y', # overwrite the output file if it already exists.
'-f', 'rawvideo', '-f', 'rawvideo',
'-vcodec', 'rawvideo', '-vcodec', 'rawvideo',
'-s', str(self.width)+'x'+str(self.height), # size of one frame '-s', str(self.width)+'x'+str(self.height), # size of one frame
'-pix_fmt', 'rgba', '-pix_fmt', 'rgba',
'-r', self.core.settings.value('outputFrameRate'), # frames per second
# frames per second
'-r', self.core.settings.value('outputFrameRate'),
'-i', '-', # The input comes from a pipe '-i', '-', # The input comes from a pipe
'-an', '-an',
'-i', inputFile, '-i', inputFile,
@ -126,14 +133,16 @@ class Worker(QtCore.QObject):
ffmpegCommand.append('-2') ffmpegCommand.append('-2')
ffmpegCommand.append(outputFile) ffmpegCommand.append(outputFile)
self.out_pipe = sp.Popen(ffmpegCommand, stdin=sp.PIPE,stdout=sys.stdout, stderr=sys.stdout) self.out_pipe = sp.Popen(
ffmpegCommand, stdin=sp.PIPE, stdout=sys.stdout, stderr=sys.stdout)
# create video for output # create video for output
numpy.seterr(divide='ignore') numpy.seterr(divide='ignore')
# initialize components # initialize components
print('loaded components:', print('loaded components:',
["%s%s" % (num, str(component)) for num, component in enumerate(self.components)]) ["%s%s" % (num, str(component)) for num,
component in enumerate(self.components)])
self.staticComponents = {} self.staticComponents = {}
numComps = len(self.components) numComps = len(self.components)
for compNo, comp in enumerate(self.components): for compNo, comp in enumerate(self.components):
@ -149,7 +158,8 @@ class Worker(QtCore.QObject):
) )
if properties and 'static' in properties: if properties and 'static' in properties:
self.staticComponents[compNo] = copy(comp.frameRender(compNo, 0, 0)) self.staticComponents[compNo] = copy(
comp.frameRender(compNo, 0, 0))
self.progressBarUpdate.emit(100) self.progressBarUpdate.emit(100)
self.compositeQueue = Queue() self.compositeQueue = Queue()
@ -159,17 +169,20 @@ class Worker(QtCore.QObject):
self.previewQueue = PriorityQueue() self.previewQueue = PriorityQueue()
self.renderThreads = [] self.renderThreads = []
# create threads to render frames and send them back here for piping out # Threads to render frames and send them back here for piping out
for i in range(3): for i in range(3):
self.renderThreads.append(Thread(target=self.renderNode, name="Render Thread")) self.renderThreads.append(
Thread(target=self.renderNode, name="Render Thread"))
self.renderThreads[i].daemon = True self.renderThreads[i].daemon = True
self.renderThreads[i].start() self.renderThreads[i].start()
self.dispatchThread = Thread(target=self.renderDispatch, name="Render Dispatch Thread") self.dispatchThread = Thread(
target=self.renderDispatch, name="Render Dispatch Thread")
self.dispatchThread.daemon = True self.dispatchThread.daemon = True
self.dispatchThread.start() self.dispatchThread.start()
self.previewDispatch = Thread(target=self.previewDispatch, name="Render Dispatch Thread") self.previewDispatch = Thread(
target=self.previewDispatch, name="Render Dispatch Thread")
self.previewDispatch.daemon = True self.previewDispatch.daemon = True
self.previewDispatch.start() self.previewDispatch.start()
@ -197,10 +210,13 @@ class Worker(QtCore.QObject):
break break
# increase progress bar value # increase progress bar value
if progressBarValue + 1 <= (i / len(self.completeAudioArray)) * 100: if progressBarValue + 1 <= (i / len(self.completeAudioArray)) \
progressBarValue = numpy.floor((i / len(self.completeAudioArray)) * 100) * 100:
progressBarValue = numpy.floor(
(i / len(self.completeAudioArray)) * 100)
self.progressBarUpdate.emit(progressBarValue) self.progressBarUpdate.emit(progressBarValue)
pStr = "Exporting video: " + str(int(progressBarValue)) + "%" pStr = "Exporting video: " + str(int(progressBarValue)) \
+ "%"
self.progressBarSetText.emit(pStr) self.progressBarSetText.emit(pStr)
numpy.seterr(all='print') numpy.seterr(all='print')
@ -220,7 +236,7 @@ class Worker(QtCore.QObject):
pass pass
self.progressBarUpdate.emit(0) self.progressBarUpdate.emit(0)
self.progressBarSetText.emit('Export Canceled') self.progressBarSetText.emit('Export Canceled')
else: else:
if self.error: if self.error:
print("Export Failed") print("Export Failed")
@ -230,14 +246,14 @@ class Worker(QtCore.QObject):
print("Export Complete") print("Export Complete")
self.progressBarUpdate.emit(100) self.progressBarUpdate.emit(100)
self.progressBarSetText.emit('Export Complete') self.progressBarSetText.emit('Export Complete')
self.error = False self.error = False
self.canceled = False self.canceled = False
self.parent.drawPreview() self.parent.drawPreview()
self.stopped = True self.stopped = True
self.encoding.emit(False) self.encoding.emit(False)
self.videoCreated.emit() self.videoCreated.emit()
def updateProgress(self, pStr, pVal): def updateProgress(self, pStr, pVal):
self.progressBarValue.emit(pVal) self.progressBarValue.emit(pVal)
self.progressBarSetText.emit(pStr) self.progressBarSetText.emit(pStr)
@ -245,10 +261,10 @@ class Worker(QtCore.QObject):
def cancel(self): def cancel(self):
self.canceled = True self.canceled = True
self.core.cancel() self.core.cancel()
for comp in self.components: for comp in self.components:
comp.cancel() comp.cancel()
try: try:
self.out_pipe.send_signal(signal.SIGINT) self.out_pipe.send_signal(signal.SIGINT)
except: except: