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
|
||||
|
||||
|
||||
class Component:
|
||||
def __str__(self):
|
||||
return self.__doc__
|
||||
|
@ -22,24 +23,26 @@ class Component:
|
|||
def pickColor(self):
|
||||
color = QtGui.QColorDialog.getColor()
|
||||
if color.isValid():
|
||||
RGBstring = '%s,%s,%s' % (str(color.red()), str(color.green()), str(color.blue()))
|
||||
btnStyle = "QPushButton { background-color : %s; outline: none; }" % color.name()
|
||||
RGBstring = '%s,%s,%s' % (
|
||||
str(color.red()), str(color.green()), str(color.blue()))
|
||||
btnStyle = "QPushButton{background-color: %s; outline: none;}" \
|
||||
% color.name()
|
||||
return RGBstring, btnStyle
|
||||
else:
|
||||
return None, None
|
||||
|
||||
def RGBFromString(self, string):
|
||||
''' turns an RGB string like "255, 255, 255" into a tuple '''
|
||||
try:
|
||||
tup = tuple([int(i) for i in string.split(',')])
|
||||
if len(tup) != 3:
|
||||
raise ValueError
|
||||
for i in tup:
|
||||
if i > 255 or i < 0:
|
||||
raise ValueError
|
||||
return tup
|
||||
except:
|
||||
return (255, 255, 255)
|
||||
''' turns an RGB string like "255, 255, 255" into a tuple '''
|
||||
try:
|
||||
tup = tuple([int(i) for i in string.split(',')])
|
||||
if len(tup) != 3:
|
||||
raise ValueError
|
||||
for i in tup:
|
||||
if i > 255 or i < 0:
|
||||
raise ValueError
|
||||
return tup
|
||||
except:
|
||||
return (255, 255, 255)
|
||||
|
||||
'''
|
||||
### Reference methods for creating a new component
|
||||
|
@ -47,7 +50,8 @@ class Component:
|
|||
|
||||
def widget(self, 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
|
||||
self.page = page
|
||||
return page
|
||||
|
|
|
@ -4,25 +4,33 @@ from PyQt4.QtGui import QColor
|
|||
import os
|
||||
from . import __base__
|
||||
|
||||
|
||||
class Component(__base__.Component):
|
||||
'''Color'''
|
||||
def widget(self, parent):
|
||||
self.parent = parent
|
||||
page = uic.loadUi(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'color.ui'))
|
||||
page = uic.loadUi(os.path.join(
|
||||
os.path.dirname(os.path.realpath(__file__)), 'color.ui'))
|
||||
|
||||
self.color1 = (0,0,0)
|
||||
self.color2 = (133,133,133)
|
||||
self.color1 = (0, 0, 0)
|
||||
self.color2 = (133, 133, 133)
|
||||
self.x = 0
|
||||
self.y = 0
|
||||
|
||||
page.lineEdit_color1.setText('%s,%s,%s' % self.color1)
|
||||
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_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_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
|
||||
page.lineEdit_color2.setDisabled(True)
|
||||
page.pushButton_color2.setDisabled(True)
|
||||
|
@ -58,21 +66,26 @@ class Component(__base__.Component):
|
|||
return self.drawFrame(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))
|
||||
|
||||
def loadPreset(self, pr):
|
||||
self.page.lineEdit_color1.setText('%s,%s,%s' % pr['color1'])
|
||||
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)
|
||||
btnStyle = "QPushButton { background-color : %s; outline: none; }" % QColor(*pr['color2']).name()
|
||||
self.page.pushButton_color2.setStyleSheet(btnStyle)
|
||||
|
||||
def savePreset(self):
|
||||
return {
|
||||
'color1' : self.color1,
|
||||
'color2' : self.color2,
|
||||
'color1': self.color1,
|
||||
'color2': self.color2,
|
||||
}
|
||||
|
||||
def pickColor(self, num):
|
||||
|
|
|
@ -3,12 +3,14 @@ from PyQt4 import uic, QtGui, QtCore
|
|||
import os
|
||||
from . import __base__
|
||||
|
||||
|
||||
class Component(__base__.Component):
|
||||
'''Image'''
|
||||
def widget(self, parent):
|
||||
self.parent = parent
|
||||
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.x = 0
|
||||
self.y = 0
|
||||
|
@ -38,7 +40,7 @@ class Component(__base__.Component):
|
|||
return self.drawFrame(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):
|
||||
image = Image.open(self.imagePath)
|
||||
if image.size != (width, height):
|
||||
|
@ -51,13 +53,13 @@ class Component(__base__.Component):
|
|||
|
||||
def savePreset(self):
|
||||
return {
|
||||
'image' : self.imagePath,
|
||||
'image': self.imagePath,
|
||||
}
|
||||
|
||||
def pickImage(self):
|
||||
imgDir = self.settings.value("backgroundDir", os.path.expanduser("~"))
|
||||
filename = QtGui.QFileDialog.getOpenFileName(self.page,
|
||||
"Choose Image", imgDir, "Image Files (*.jpg *.png)")
|
||||
filename = QtGui.QFileDialog.getOpenFileName(
|
||||
self.page, "Choose Image", imgDir, "Image Files (*.jpg *.png)")
|
||||
if filename:
|
||||
self.settings.setValue("backgroundDir", os.path.dirname(filename))
|
||||
self.page.lineEdit_image.setText(filename)
|
||||
|
|
|
@ -2,7 +2,8 @@ import numpy
|
|||
from PIL import Image, ImageDraw
|
||||
from PyQt4 import uic, QtGui
|
||||
from PyQt4.QtGui import QColor
|
||||
import os, random
|
||||
import os
|
||||
import random
|
||||
from . import __base__
|
||||
import time
|
||||
from copy import copy
|
||||
|
@ -12,18 +13,19 @@ class Component(__base__.Component):
|
|||
'''Original Audio Visualization'''
|
||||
def widget(self, 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("Split")
|
||||
page.comboBox_visLayout.addItem("Bottom")
|
||||
#visLayoutValue = int(self.settings.value('visLayout'))
|
||||
page.comboBox_visLayout.setCurrentIndex(0)
|
||||
page.comboBox_visLayout.currentIndexChanged.connect(self.update)
|
||||
page.lineEdit_visColor.setText('%s,%s,%s' % self.visColor)
|
||||
page.pushButton_visColor.clicked.connect(lambda: self.pickColor())
|
||||
btnStyle = "QPushButton { background-color : %s; outline: none; }" % QColor(*self.visColor).name()
|
||||
btnStyle = "QPushButton { background-color : %s; outline: none; }" \
|
||||
% QColor(*self.visColor).name()
|
||||
page.pushButton_visColor.setStyleSheet(btnStyle)
|
||||
page.lineEdit_visColor.textChanged.connect(self.update)
|
||||
self.page = page
|
||||
|
@ -37,20 +39,24 @@ class Component(__base__.Component):
|
|||
|
||||
def loadPreset(self, pr):
|
||||
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.comboBox_visLayout.setCurrentIndex(pr['layout'])
|
||||
|
||||
def savePreset(self):
|
||||
return { 'layout' : self.layout,
|
||||
'visColor' : self.visColor,
|
||||
}
|
||||
return {
|
||||
'layout': self.layout,
|
||||
'visColor': self.visColor,
|
||||
}
|
||||
|
||||
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'))
|
||||
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):
|
||||
super().preFrameRender(**kwargs)
|
||||
|
@ -64,19 +70,24 @@ class Component(__base__.Component):
|
|||
for i in range(0, len(self.completeAudioArray), self.sampleSize):
|
||||
if self.canceled:
|
||||
break
|
||||
self.lastSpectrum = self.transformData(i, self.completeAudioArray, self.sampleSize,
|
||||
self.smoothConstantDown, self.smoothConstantUp, self.lastSpectrum)
|
||||
self.lastSpectrum = self.transformData(
|
||||
i, self.completeAudioArray, self.sampleSize,
|
||||
self.smoothConstantDown, self.smoothConstantUp,
|
||||
self.lastSpectrum)
|
||||
self.spectrumArray[i] = copy(self.lastSpectrum)
|
||||
|
||||
progress = int(100*(i/len(self.completeAudioArray)))
|
||||
if progress >= 100:
|
||||
progress = 100
|
||||
pStr = "Analyzing audio: "+ str(progress) +'%'
|
||||
pStr = "Analyzing audio: "+str(progress)+'%'
|
||||
self.progressBarSetText.emit(pStr)
|
||||
self.progressBarUpdate.emit(int(progress))
|
||||
|
||||
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):
|
||||
RGBstring, btnStyle = super().pickColor()
|
||||
|
@ -85,14 +96,17 @@ class Component(__base__.Component):
|
|||
self.page.lineEdit_visColor.setText(RGBstring)
|
||||
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):
|
||||
sampleSize = len(completeAudioArray) - i
|
||||
|
||||
window = numpy.hanning(sampleSize)
|
||||
data = completeAudioArray[i:i+sampleSize][::1] * window
|
||||
paddedSampleSize = 2048
|
||||
paddedData = numpy.pad(data, (0, paddedSampleSize - sampleSize), 'constant')
|
||||
paddedData = numpy.pad(
|
||||
data, (0, paddedSampleSize - sampleSize), 'constant')
|
||||
spectrum = numpy.fft.fft(paddedData)
|
||||
sample_rate = 44100
|
||||
frequencies = numpy.fft.fftfreq(len(spectrum), 1./sample_rate)
|
||||
|
@ -106,8 +120,13 @@ class Component(__base__.Component):
|
|||
y[numpy.isinf(y)] = 0
|
||||
|
||||
if lastSpectrum is not None:
|
||||
lastSpectrum[y < lastSpectrum] = y[y < lastSpectrum] * smoothConstantDown + lastSpectrum[y < lastSpectrum] * (1 - smoothConstantDown)
|
||||
lastSpectrum[y >= lastSpectrum] = y[y >= lastSpectrum] * smoothConstantUp + lastSpectrum[y >= lastSpectrum] * (1 - smoothConstantUp)
|
||||
lastSpectrum[y < lastSpectrum] = \
|
||||
y[y < lastSpectrum] * smoothConstantDown + \
|
||||
lastSpectrum[y < lastSpectrum] * (1 - smoothConstantDown)
|
||||
|
||||
lastSpectrum[y >= lastSpectrum] = \
|
||||
y[y >= lastSpectrum] * smoothConstantUp + \
|
||||
lastSpectrum[y >= lastSpectrum] * (1 - smoothConstantUp)
|
||||
else:
|
||||
lastSpectrum = y
|
||||
|
||||
|
@ -120,7 +139,7 @@ class Component(__base__.Component):
|
|||
bF = width / 64
|
||||
bH = bF / 2
|
||||
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)
|
||||
r, g, b = color
|
||||
color2 = (r, g, b, 125)
|
||||
|
@ -128,12 +147,17 @@ class Component(__base__.Component):
|
|||
bP = height / 1200
|
||||
|
||||
for j in range(0, 63):
|
||||
draw.rectangle((bH + j * bF, vH+bQ, bH + j * bF + bF, vH + bQ - spectrum[j * 4] * bP - bH), fill=color2)
|
||||
draw.rectangle((bH + bQ + j * bF, vH , bH + bQ + j * bF + bH, vH - spectrum[j * 4] * bP), fill=color)
|
||||
draw.rectangle((
|
||||
bH + j * bF, vH+bQ, bH + j * bF + bF, vH + bQ -
|
||||
spectrum[j * 4] * bP - bH), fill=color2)
|
||||
|
||||
draw.rectangle((
|
||||
bH + bQ + j * bF, vH, bH + bQ + j * bF + bH, vH -
|
||||
spectrum[j * 4] * bP), fill=color)
|
||||
|
||||
imBottom = imTop.transpose(Image.FLIP_TOP_BOTTOM)
|
||||
|
||||
im = Image.new("RGBA", (width, height),(0,0,0,0))
|
||||
im = Image.new("RGBA", (width, height), (0, 0, 0, 0))
|
||||
|
||||
if layout == 0:
|
||||
y = 0 - int(height/100*43)
|
||||
|
|
|
@ -25,9 +25,7 @@ class Component(__base__.Component):
|
|||
self.yPosition = height / 2 * 1.036
|
||||
|
||||
page = uic.loadUi(os.path.join(
|
||||
os.path.dirname(os.path.realpath(__file__)),
|
||||
'text.ui'
|
||||
))
|
||||
os.path.dirname(os.path.realpath(__file__)), 'text.ui'))
|
||||
page.comboBox_textAlign.addItem("Left")
|
||||
page.comboBox_textAlign.addItem("Middle")
|
||||
page.comboBox_textAlign.addItem("Right")
|
||||
|
@ -61,7 +59,8 @@ class Component(__base__.Component):
|
|||
self.fontSize = self.page.spinBox_fontSize.value()
|
||||
self.xPosition = self.page.spinBox_xTextAlign.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()
|
||||
|
||||
def getXY(self):
|
||||
|
@ -95,14 +94,14 @@ class Component(__base__.Component):
|
|||
|
||||
def savePreset(self):
|
||||
return {
|
||||
'title': self.title,
|
||||
'titleFont': self.titleFont.toString(),
|
||||
'alignment': self.alignment,
|
||||
'fontSize': self.fontSize,
|
||||
'xPosition': self.xPosition,
|
||||
'yPosition': self.yPosition,
|
||||
'textColor': self.textColor
|
||||
}
|
||||
'title': self.title,
|
||||
'titleFont': self.titleFont.toString(),
|
||||
'alignment': self.alignment,
|
||||
'fontSize': self.fontSize,
|
||||
'xPosition': self.xPosition,
|
||||
'yPosition': self.yPosition,
|
||||
'textColor': self.textColor
|
||||
}
|
||||
|
||||
def previewRender(self, previewWorker):
|
||||
width = int(previewWorker.core.settings.value('outputWidth'))
|
||||
|
|
|
@ -1,12 +1,18 @@
|
|||
from PIL import Image, ImageDraw
|
||||
from PyQt4 import uic, QtGui, QtCore
|
||||
import os, subprocess, threading
|
||||
import os
|
||||
import subprocess
|
||||
import threading
|
||||
from queue import PriorityQueue
|
||||
from . import __base__
|
||||
|
||||
|
||||
class Video:
|
||||
'''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.chunkSize = chunkSize
|
||||
self.size = (width, height)
|
||||
|
@ -32,7 +38,10 @@ class Video:
|
|||
self.frameBuffer.maxsize = int(frameRate)
|
||||
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.start()
|
||||
|
||||
|
@ -46,7 +55,10 @@ class Video:
|
|||
self.frameBuffer.task_done()
|
||||
|
||||
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:
|
||||
if self.parent.canceled:
|
||||
break
|
||||
|
@ -58,17 +70,20 @@ class Video:
|
|||
continue
|
||||
|
||||
self.currentFrame = self.pipe.stdout.read(self.chunkSize)
|
||||
#print('creating frame #%s' % str(self.frameNo))
|
||||
if len(self.currentFrame) != 0:
|
||||
self.frameBuffer.put((self.frameNo, self.currentFrame))
|
||||
self.lastFrame = self.currentFrame
|
||||
|
||||
|
||||
class Component(__base__.Component):
|
||||
'''Video'''
|
||||
def widget(self, parent):
|
||||
self.parent = parent
|
||||
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.x = 0
|
||||
self.y = 0
|
||||
|
@ -92,7 +107,7 @@ class Component(__base__.Component):
|
|||
self.chunkSize = 4*width*height
|
||||
frame = self.getPreviewFrame(width, height)
|
||||
if not frame:
|
||||
return Image.new("RGBA", (width, height),(0,0,0,0))
|
||||
return Image.new("RGBA", (width, height), (0, 0, 0, 0))
|
||||
else:
|
||||
return frame
|
||||
|
||||
|
@ -101,9 +116,11 @@ class Component(__base__.Component):
|
|||
width = int(self.worker.core.settings.value('outputWidth'))
|
||||
height = int(self.worker.core.settings.value('outputHeight'))
|
||||
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"),
|
||||
self.chunkSize, self.parent, self.loopVideo)
|
||||
self.chunkSize, self.parent, self.loopVideo
|
||||
)
|
||||
|
||||
def frameRender(self, moduleNo, arrayNo, frameNo):
|
||||
return self.video.frame(frameNo)
|
||||
|
@ -113,13 +130,15 @@ class Component(__base__.Component):
|
|||
|
||||
def savePreset(self):
|
||||
return {
|
||||
'video' : self.videoPath,
|
||||
'video': self.videoPath,
|
||||
}
|
||||
|
||||
def pickVideo(self):
|
||||
imgDir = self.settings.value("backgroundDir", os.path.expanduser("~"))
|
||||
filename = QtGui.QFileDialog.getOpenFileName(self.page,
|
||||
"Choose Video", imgDir, "Video Files (*.mp4 *.mov)")
|
||||
filename = QtGui.QFileDialog.getOpenFileName(
|
||||
self.page, "Choose Video",
|
||||
imgDir, "Video Files (*.mp4 *.mov)"
|
||||
)
|
||||
if filename:
|
||||
self.settings.setValue("backgroundDir", os.path.dirname(filename))
|
||||
self.page.lineEdit_video.setText(filename)
|
||||
|
@ -139,7 +158,10 @@ class Component(__base__.Component):
|
|||
'-ss', '90',
|
||||
'-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)
|
||||
image = Image.frombytes('RGBA', (width, height), byteFrame)
|
||||
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 os.path import expanduser
|
||||
import subprocess as sp
|
||||
|
@ -10,103 +12,106 @@ import atexit
|
|||
import time
|
||||
from collections import OrderedDict
|
||||
|
||||
|
||||
class Core():
|
||||
|
||||
def __init__(self):
|
||||
self.FFMPEG_BIN = self.findFfmpeg()
|
||||
self.tempDir = os.path.join(tempfile.gettempdir(), 'audio-visualizer-python-data')
|
||||
if not os.path.exists(self.tempDir):
|
||||
os.makedirs(self.tempDir)
|
||||
atexit.register(self.deleteTempDir)
|
||||
def __init__(self):
|
||||
self.FFMPEG_BIN = self.findFfmpeg()
|
||||
self.tempDir = os.path.join(
|
||||
tempfile.gettempdir(), 'audio-visualizer-python-data')
|
||||
if not os.path.exists(self.tempDir):
|
||||
os.makedirs(self.tempDir)
|
||||
atexit.register(self.deleteTempDir)
|
||||
|
||||
def findFfmpeg(self):
|
||||
if sys.platform == "win32":
|
||||
return "ffmpeg.exe"
|
||||
else:
|
||||
try:
|
||||
with open(os.devnull, "w") as f:
|
||||
sp.check_call(['ffmpeg', '-version'], stdout=f, stderr=f)
|
||||
return "ffmpeg"
|
||||
except:
|
||||
return "avconv"
|
||||
def findFfmpeg(self):
|
||||
if sys.platform == "win32":
|
||||
return "ffmpeg.exe"
|
||||
else:
|
||||
try:
|
||||
with open(os.devnull, "w") as f:
|
||||
sp.check_call(['ffmpeg', '-version'], stdout=f, stderr=f)
|
||||
return "ffmpeg"
|
||||
except:
|
||||
return "avconv"
|
||||
|
||||
def readAudioFile(self, filename, parent):
|
||||
command = [ self.FFMPEG_BIN,
|
||||
'-i', filename]
|
||||
def readAudioFile(self, filename, parent):
|
||||
command = [self.FFMPEG_BIN, '-i', filename]
|
||||
|
||||
try:
|
||||
fileInfo = sp.check_output(command, stderr=sp.STDOUT, shell=False)
|
||||
except sp.CalledProcessError as ex:
|
||||
fileInfo = ex.output
|
||||
pass
|
||||
try:
|
||||
fileInfo = sp.check_output(command, stderr=sp.STDOUT, shell=False)
|
||||
except sp.CalledProcessError as ex:
|
||||
fileInfo = ex.output
|
||||
pass
|
||||
|
||||
info = fileInfo.decode("utf-8").split('\n')
|
||||
for line in info:
|
||||
if 'Duration' in line:
|
||||
d = line.split(',')[0]
|
||||
d = d.split(' ')[3]
|
||||
d = d.split(':')
|
||||
duration = float(d[0])*3600 + float(d[1])*60 + float(d[2])
|
||||
info = fileInfo.decode("utf-8").split('\n')
|
||||
for line in info:
|
||||
if 'Duration' in line:
|
||||
d = line.split(',')[0]
|
||||
d = d.split(' ')[3]
|
||||
d = d.split(':')
|
||||
duration = float(d[0])*3600 + float(d[1])*60 + float(d[2])
|
||||
|
||||
command = [ self.FFMPEG_BIN,
|
||||
'-i', filename,
|
||||
'-f', 's16le',
|
||||
'-acodec', 'pcm_s16le',
|
||||
'-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)
|
||||
command = [
|
||||
self.FFMPEG_BIN,
|
||||
'-i', filename,
|
||||
'-f', 's16le',
|
||||
'-acodec', 'pcm_s16le',
|
||||
'-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)
|
||||
|
||||
completeAudioArray = numpy.empty(0, dtype="int16")
|
||||
completeAudioArray = numpy.empty(0, dtype="int16")
|
||||
|
||||
progress = 0
|
||||
lastPercent = None
|
||||
while True:
|
||||
if self.canceled:
|
||||
break
|
||||
# read 2 seconds of audio
|
||||
progress = progress + 4
|
||||
raw_audio = in_pipe.stdout.read(88200*4)
|
||||
if len(raw_audio) == 0:
|
||||
break
|
||||
audio_array = numpy.fromstring(raw_audio, dtype="int16")
|
||||
completeAudioArray = numpy.append(completeAudioArray, audio_array)
|
||||
progress = 0
|
||||
lastPercent = None
|
||||
while True:
|
||||
if self.canceled:
|
||||
break
|
||||
# read 2 seconds of audio
|
||||
progress = progress + 4
|
||||
raw_audio = in_pipe.stdout.read(88200*4)
|
||||
if len(raw_audio) == 0:
|
||||
break
|
||||
audio_array = numpy.fromstring(raw_audio, dtype="int16")
|
||||
completeAudioArray = numpy.append(completeAudioArray, audio_array)
|
||||
|
||||
percent = int(100*(progress/duration))
|
||||
if percent >= 100:
|
||||
percent = 100
|
||||
percent = int(100*(progress/duration))
|
||||
if percent >= 100:
|
||||
percent = 100
|
||||
|
||||
if lastPercent != percent:
|
||||
string = 'Loading audio file: '+str(percent)+'%'
|
||||
parent.progressBarSetText.emit(string)
|
||||
parent.progressBarUpdate.emit(percent)
|
||||
if lastPercent != percent:
|
||||
string = 'Loading audio file: '+str(percent)+'%'
|
||||
parent.progressBarSetText.emit(string)
|
||||
parent.progressBarUpdate.emit(percent)
|
||||
|
||||
lastPercent = percent
|
||||
lastPercent = percent
|
||||
|
||||
in_pipe.kill()
|
||||
in_pipe.wait()
|
||||
|
||||
in_pipe.kill()
|
||||
in_pipe.wait()
|
||||
# add 0s the end
|
||||
completeAudioArrayCopy = numpy.zeros(
|
||||
len(completeAudioArray) + 44100, dtype="int16")
|
||||
completeAudioArrayCopy[:len(completeAudioArray)] = completeAudioArray
|
||||
completeAudioArray = completeAudioArrayCopy
|
||||
|
||||
# add 0s the end
|
||||
completeAudioArrayCopy = numpy.zeros(len(completeAudioArray) + 44100, dtype="int16")
|
||||
completeAudioArrayCopy[:len(completeAudioArray)] = completeAudioArray
|
||||
completeAudioArray = completeAudioArrayCopy
|
||||
return completeAudioArray
|
||||
|
||||
return completeAudioArray
|
||||
def deleteTempDir(self):
|
||||
try:
|
||||
rmtree(self.tempDir)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
def deleteTempDir(self):
|
||||
try:
|
||||
rmtree(self.tempDir)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
def cancel(self):
|
||||
self.canceled = True
|
||||
|
||||
def cancel(self):
|
||||
self.canceled = True
|
||||
def reset(self):
|
||||
self.canceled = False
|
||||
|
||||
def reset(self):
|
||||
self.canceled = False
|
||||
|
||||
@staticmethod
|
||||
def stringOrderedDict(dictionary):
|
||||
sorted_ = OrderedDict(sorted(dictionary.items(), key=lambda t: t[0]))
|
||||
return repr(sorted_)
|
||||
@staticmethod
|
||||
def stringOrderedDict(dictionary):
|
||||
sorted_ = OrderedDict(sorted(dictionary.items(), key=lambda t: t[0]))
|
||||
return repr(sorted_)
|
||||
|
|
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 collections import OrderedDict
|
||||
from PyQt4 import QtCore, QtGui, uic
|
||||
from PyQt4.QtCore import QSettings, QModelIndex, Qt
|
||||
from PyQt4.QtGui import QDesktopServices, QMenu
|
||||
from PyQt4 import QtGui, uic
|
||||
from PyQt4.QtCore import Qt
|
||||
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):
|
||||
self.resolutions = [
|
||||
'1920x1080',
|
||||
'1280x720',
|
||||
'854x480'
|
||||
self.resolutions = [
|
||||
'1920x1080',
|
||||
'1280x720',
|
||||
'854x480'
|
||||
]
|
||||
|
||||
default = {
|
||||
"outputWidth": 1280,
|
||||
"outputHeight": 720,
|
||||
"outputFrameRate": 30,
|
||||
"outputAudioCodec": "aac",
|
||||
"outputAudioBitrate": "192k",
|
||||
"outputVideoCodec": "libx264",
|
||||
"outputVideoFormat": "yuv420p",
|
||||
"outputPreset": "medium",
|
||||
"outputFormat": "mp4",
|
||||
"projectDir" : os.path.join(self.dataDir, 'projects'),
|
||||
}
|
||||
default = {
|
||||
"outputWidth": 1280,
|
||||
"outputHeight": 720,
|
||||
"outputFrameRate": 30,
|
||||
"outputAudioCodec": "aac",
|
||||
"outputAudioBitrate": "192k",
|
||||
"outputVideoCodec": "libx264",
|
||||
"outputVideoFormat": "yuv420p",
|
||||
"outputPreset": "medium",
|
||||
"outputFormat": "mp4",
|
||||
"projectDir": os.path.join(self.dataDir, 'projects'),
|
||||
}
|
||||
|
||||
for parm, value in default.items():
|
||||
if self.settings.value(parm) == None:
|
||||
self.settings.setValue(parm,value)
|
||||
for parm, value in default.items():
|
||||
if self.settings.value(parm) is None:
|
||||
self.settings.setValue(parm, value)
|
||||
|
||||
|
||||
''' ####### commandline functionality broken until we decide how to implement it
|
||||
if len(sys.argv) > 1:
|
||||
# command line mode
|
||||
app = QtGui.QApplication(sys.argv, False)
|
||||
command = Command()
|
||||
signal.signal(signal.SIGINT, command.cleanUp)
|
||||
sys.exit(app.exec_())
|
||||
else:
|
||||
'''
|
||||
# gui mode
|
||||
if __name__ == "__main__":
|
||||
''' 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.setApplicationName("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()
|
||||
desc = QtGui.QDesktopWidget()
|
||||
dpi = desc.physicalDpiX()
|
||||
|
||||
topMargin = 0 if (dpi == 96) else int(10 * (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)
|
||||
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
|
||||
from copy import copy
|
||||
|
||||
|
||||
class Worker(QtCore.QObject):
|
||||
|
||||
imageCreated = pyqtSignal(['QImage'])
|
||||
imageCreated = pyqtSignal(['QImage'])
|
||||
|
||||
def __init__(self, parent=None, queue=None):
|
||||
QtCore.QObject.__init__(self)
|
||||
parent.newTask.connect(self.createPreviewImage)
|
||||
parent.processTask.connect(self.process)
|
||||
self.core = core.Core()
|
||||
self.queue = queue
|
||||
self.core.settings = parent.settings
|
||||
self.stackedWidget = parent.window.stackedWidget
|
||||
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")))
|
||||
def __init__(self, parent=None, queue=None):
|
||||
QtCore.QObject.__init__(self)
|
||||
parent.newTask.connect(self.createPreviewImage)
|
||||
parent.processTask.connect(self.process)
|
||||
self.core = core.Core()
|
||||
self.queue = queue
|
||||
self.core.settings = parent.settings
|
||||
self.stackedWidget = parent.window.stackedWidget
|
||||
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")))
|
||||
|
||||
@pyqtSlot(str, list)
|
||||
def createPreviewImage(self, components):
|
||||
dic = {
|
||||
"components": components,
|
||||
}
|
||||
self.queue.put(dic)
|
||||
|
||||
|
||||
@pyqtSlot(str, list)
|
||||
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:
|
||||
@pyqtSlot()
|
||||
def process(self):
|
||||
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:
|
||||
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):
|
||||
#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
|
||||
True
|
||||
|
|
|
@ -13,6 +13,7 @@ import time
|
|||
from copy import copy
|
||||
import signal
|
||||
|
||||
|
||||
class Worker(QtCore.QObject):
|
||||
|
||||
imageCreated = pyqtSignal(['QImage'])
|
||||
|
@ -40,16 +41,19 @@ class Worker(QtCore.QObject):
|
|||
frame = None
|
||||
|
||||
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:
|
||||
frame = self.staticComponents[compNo]
|
||||
else:
|
||||
frame = Image.alpha_composite(frame, self.staticComponents[compNo])
|
||||
frame = Image.alpha_composite(
|
||||
frame, self.staticComponents[compNo])
|
||||
else:
|
||||
if frame is None:
|
||||
frame = comp.frameRender(compNo, i[0], i[1])
|
||||
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.compositeQueue.task_done()
|
||||
|
@ -63,8 +67,9 @@ class Worker(QtCore.QObject):
|
|||
self.bgI += 1
|
||||
|
||||
def previewDispatch(self):
|
||||
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 = 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 = background.resize((self.width, self.height))
|
||||
|
||||
while not self.stopped:
|
||||
|
@ -83,11 +88,10 @@ class Worker(QtCore.QObject):
|
|||
self.encoding.emit(True)
|
||||
self.components = components
|
||||
self.outputFile = outputFile
|
||||
self.bgI = 0 # tracked video frame
|
||||
self.bgI = 0 # tracked video frame
|
||||
self.reset()
|
||||
self.width = int(self.core.settings.value('outputWidth'))
|
||||
self.height = int(self.core.settings.value('outputHeight'))
|
||||
# print('worker thread id: {}'.format(QtCore.QThread.currentThreadId()))
|
||||
progressBarValue = 0
|
||||
self.progressBarUpdate.emit(progressBarValue)
|
||||
|
||||
|
@ -95,7 +99,8 @@ class Worker(QtCore.QObject):
|
|||
self.completeAudioArray = self.core.readAudioFile(inputFile, self)
|
||||
|
||||
# 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')
|
||||
|
||||
if b'libfdk_aac' in encoders and acodec == 'aac':
|
||||
|
@ -104,12 +109,14 @@ class Worker(QtCore.QObject):
|
|||
ffmpegCommand = [
|
||||
self.core.FFMPEG_BIN,
|
||||
'-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',
|
||||
'-vcodec', 'rawvideo',
|
||||
'-s', str(self.width)+'x'+str(self.height), # size of one frame
|
||||
'-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
|
||||
'-an',
|
||||
'-i', inputFile,
|
||||
|
@ -126,14 +133,16 @@ class Worker(QtCore.QObject):
|
|||
ffmpegCommand.append('-2')
|
||||
|
||||
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
|
||||
numpy.seterr(divide='ignore')
|
||||
|
||||
# initialize 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 = {}
|
||||
numComps = len(self.components)
|
||||
for compNo, comp in enumerate(self.components):
|
||||
|
@ -149,7 +158,8 @@ class Worker(QtCore.QObject):
|
|||
)
|
||||
|
||||
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.compositeQueue = Queue()
|
||||
|
@ -159,17 +169,20 @@ class Worker(QtCore.QObject):
|
|||
self.previewQueue = PriorityQueue()
|
||||
|
||||
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):
|
||||
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].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.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.start()
|
||||
|
||||
|
@ -197,10 +210,13 @@ class Worker(QtCore.QObject):
|
|||
break
|
||||
|
||||
# increase progress bar value
|
||||
if progressBarValue + 1 <= (i / len(self.completeAudioArray)) * 100:
|
||||
progressBarValue = numpy.floor((i / len(self.completeAudioArray)) * 100)
|
||||
if progressBarValue + 1 <= (i / len(self.completeAudioArray)) \
|
||||
* 100:
|
||||
progressBarValue = numpy.floor(
|
||||
(i / len(self.completeAudioArray)) * 100)
|
||||
self.progressBarUpdate.emit(progressBarValue)
|
||||
pStr = "Exporting video: " + str(int(progressBarValue)) + "%"
|
||||
pStr = "Exporting video: " + str(int(progressBarValue)) \
|
||||
+ "%"
|
||||
self.progressBarSetText.emit(pStr)
|
||||
|
||||
numpy.seterr(all='print')
|
||||
|
|
Reference in New Issue