Code cleanup
This commit is contained in:
parent
0948afd6e8
commit
231af74ea2
|
@ -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)
|
||||||
|
'''
|
|
@ -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__
|
||||||
|
@ -22,24 +23,26 @@ 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
|
||||||
|
@ -47,7 +50,8 @@ class Component:
|
||||||
|
|
||||||
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
|
||||||
|
|
|
@ -4,25 +4,33 @@ 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.color1 = (0, 0, 0)
|
||||||
self.color2 = (133,133,133)
|
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)
|
||||||
|
@ -58,21 +66,26 @@ class Component(__base__.Component):
|
||||||
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):
|
||||||
|
|
|
@ -3,12 +3,14 @@ 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
|
||||||
|
@ -38,7 +40,7 @@ class Component(__base__.Component):
|
||||||
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):
|
||||||
|
@ -51,13 +53,13 @@ class Component(__base__.Component):
|
||||||
|
|
||||||
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)
|
||||||
|
|
|
@ -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,18 +13,19 @@ 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
|
||||||
|
@ -37,20 +39,24 @@ 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)
|
||||||
|
@ -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)
|
||||||
|
|
|
@ -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'))
|
||||||
|
|
|
@ -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)
|
||||||
|
@ -32,7 +38,10 @@ class Video:
|
||||||
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()
|
||||||
|
|
||||||
|
@ -46,7 +55,10 @@ class Video:
|
||||||
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,17 +70,20 @@ 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
|
||||||
|
@ -92,7 +107,7 @@ class Component(__base__.Component):
|
||||||
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
|
||||||
|
|
||||||
|
@ -101,9 +116,11 @@ 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'))
|
||||||
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)
|
||||||
|
@ -113,13 +130,15 @@ class Component(__base__.Component):
|
||||||
|
|
||||||
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)
|
||||||
|
@ -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()
|
||||||
|
|
169
core.py
169
core.py
|
@ -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(
|
||||||
|
command, stdout=sp.PIPE, stderr=sp.DEVNULL, bufsize=10**8)
|
||||||
|
|
||||||
completeAudioArray = numpy.empty(0, dtype="int16")
|
completeAudioArray = numpy.empty(0, dtype="int16")
|
||||||
|
|
||||||
progress = 0
|
progress = 0
|
||||||
lastPercent = None
|
lastPercent = None
|
||||||
while True:
|
while True:
|
||||||
if self.canceled:
|
if self.canceled:
|
||||||
break
|
break
|
||||||
# read 2 seconds of audio
|
# read 2 seconds of audio
|
||||||
progress = progress + 4
|
progress = progress + 4
|
||||||
raw_audio = in_pipe.stdout.read(88200*4)
|
raw_audio = in_pipe.stdout.read(88200*4)
|
||||||
if len(raw_audio) == 0:
|
if len(raw_audio) == 0:
|
||||||
break
|
break
|
||||||
audio_array = numpy.fromstring(raw_audio, dtype="int16")
|
audio_array = numpy.fromstring(raw_audio, dtype="int16")
|
||||||
completeAudioArray = numpy.append(completeAudioArray, audio_array)
|
completeAudioArray = numpy.append(completeAudioArray, audio_array)
|
||||||
|
|
||||||
percent = int(100*(progress/duration))
|
percent = int(100*(progress/duration))
|
||||||
if percent >= 100:
|
if percent >= 100:
|
||||||
percent = 100
|
percent = 100
|
||||||
|
|
||||||
if lastPercent != percent:
|
if lastPercent != percent:
|
||||||
string = 'Loading audio file: '+str(percent)+'%'
|
string = 'Loading audio file: '+str(percent)+'%'
|
||||||
parent.progressBarSetText.emit(string)
|
parent.progressBarSetText.emit(string)
|
||||||
parent.progressBarUpdate.emit(percent)
|
parent.progressBarUpdate.emit(percent)
|
||||||
|
|
||||||
lastPercent = percent
|
lastPercent = percent
|
||||||
|
|
||||||
|
in_pipe.kill()
|
||||||
|
in_pipe.wait()
|
||||||
|
|
||||||
in_pipe.kill()
|
# add 0s the end
|
||||||
in_pipe.wait()
|
completeAudioArrayCopy = numpy.zeros(
|
||||||
|
len(completeAudioArray) + 44100, dtype="int16")
|
||||||
|
completeAudioArrayCopy[:len(completeAudioArray)] = completeAudioArray
|
||||||
|
completeAudioArray = completeAudioArrayCopy
|
||||||
|
|
||||||
# add 0s the end
|
return completeAudioArray
|
||||||
completeAudioArrayCopy = numpy.zeros(len(completeAudioArray) + 44100, dtype="int16")
|
|
||||||
completeAudioArrayCopy[:len(completeAudioArray)] = completeAudioArray
|
|
||||||
completeAudioArray = completeAudioArrayCopy
|
|
||||||
|
|
||||||
return completeAudioArray
|
def deleteTempDir(self):
|
||||||
|
try:
|
||||||
|
rmtree(self.tempDir)
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
|
|
||||||
def deleteTempDir(self):
|
def cancel(self):
|
||||||
try:
|
self.canceled = True
|
||||||
rmtree(self.tempDir)
|
|
||||||
except FileNotFoundError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def cancel(self):
|
def reset(self):
|
||||||
self.canceled = True
|
self.canceled = False
|
||||||
|
|
||||||
def reset(self):
|
@staticmethod
|
||||||
self.canceled = False
|
def stringOrderedDict(dictionary):
|
||||||
|
sorted_ = OrderedDict(sorted(dictionary.items(), key=lambda t: t[0]))
|
||||||
@staticmethod
|
return repr(sorted_)
|
||||||
def stringOrderedDict(dictionary):
|
|
||||||
sorted_ = OrderedDict(sorted(dictionary.items(), key=lambda t: t[0]))
|
|
||||||
return repr(sorted_)
|
|
||||||
|
|
689
main.py
689
main.py
|
@ -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 core
|
||||||
|
import preview_thread
|
||||||
|
import video_thread
|
||||||
|
from mainwindow import *
|
||||||
|
|
||||||
import preview_thread, core, video_thread
|
|
||||||
|
|
||||||
# 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():
|
for parm, value in default.items():
|
||||||
if self.settings.value(parm) == None:
|
if self.settings.value(parm) is None:
|
||||||
self.settings.setValue(parm,value)
|
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)
|
||||||
|
|
|
@ -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 = []
|
|
@ -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()
|
||||||
@pyqtSlot(str, list)
|
def process(self):
|
||||||
def createPreviewImage(self, components):
|
|
||||||
# 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
|
|
||||||
|
|
|
@ -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,7 +99,8 @@ 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':
|
||||||
|
@ -104,12 +109,14 @@ class Worker(QtCore.QObject):
|
||||||
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')
|
||||||
|
|
Reference in New Issue