From f66eb99465c61232a7f649e66bee59504bb0e52c Mon Sep 17 00:00:00 2001
From: Brianna Rainey
Date: Wed, 28 Jan 2026 17:49:58 -0500
Subject: v2.2.1 - fix #74, fix #92, add optional 64th bar to Classic
Visualizer, improve Conway default (#93)
* update gitignore
ignore profiling and coverage data
* F1 opens help window, create appName variable, move undostack class
* fix kaleidoscope effect, increase default Y values by +4
the increased y values allow the cells to continue animating for more than 60 minutes instead of 30 (at default 60f/t)
* update version number
* add minimumWidth to undo history window
* Classic Visualizer: option to include 64th bar
* Waveform component: fix #74 - new animation speed option
* move shared visualizer code into toolkit
* Waveform component: compress audio by default
* Waveform component: fix 100% animation speed
* new components receive random color
* update to Qt 6
* fix pushbutton stylesheet
* fix #92: replace ok/cancel with save/discard/cancel
* remove obsolete PaintColor subclass
* mv common shadow code into addShadow func
* add 3rd option of ok/cancel back to showMessage
the 3 options are:
- ok
- ok/cancel
- save/discard/cancel
* Image component: add shadow option
* small test of rgbFromString
* fix color tuple string
* test another way to get comp names from CLI
* rename component tests, add some more
* Image component: scale shadow based on resolution
* catch AttributeError if previewRender returns None
* Text component: fix blur radius only able to increase
the relativeWidgets system causes QDoubleSpinbox to only allow increases, because it really only works with integeres, so I changed the blur radius into a normal QSpinBox. I noted where the problem exists within component.py for future reference. This commit also removes an unneeded VerticalLayout from the ui file
* remove unnecessary QVBoxLayout
* paste shadow at x,y instead of using offset method
* fix tests due to shadow change
* don't print warning in connectWidget due to QFontComboBox---
src/avp/components/color.py | 10 +-
src/avp/components/color.ui | 36 +-
src/avp/components/image.py | 51 +-
src/avp/components/image.ui | 10 +
src/avp/components/life.py | 103 ++--
src/avp/components/life.ui | 2 +-
src/avp/components/original.py | 96 +---
src/avp/components/original.ui | 58 +-
src/avp/components/text.py | 13 +-
src/avp/components/text.ui | 1213 ++++++++++++++++++++--------------------
src/avp/components/video.ui | 325 ++++++-----
src/avp/components/waveform.py | 92 ++-
src/avp/components/waveform.ui | 340 ++++++-----
13 files changed, 1186 insertions(+), 1163 deletions(-)
(limited to 'src/avp/components')
diff --git a/src/avp/components/color.py b/src/avp/components/color.py
index 1f32c23..cb0960a 100644
--- a/src/avp/components/color.py
+++ b/src/avp/components/color.py
@@ -2,7 +2,7 @@ from PyQt6 import QtGui
import logging
from ..component import Component
-from ..toolkit.frame import BlankFrame, FloodFrame, FramePainter, PaintColor
+from ..toolkit.frame import BlankFrame, FloodFrame, FramePainter
log = logging.getLogger("AVP.Components.Color")
@@ -152,13 +152,13 @@ class Component(Component):
elif self.spread == 2:
spread = QtGui.QGradient.Spread.RepeatSpread
brush.setSpread(spread)
- brush.setColorAt(0.0, PaintColor(*self.color1))
+ brush.setColorAt(0.0, QtGui.QColor(*self.color1))
if self.trans:
- brush.setColorAt(1.0, PaintColor(0, 0, 0, 0))
+ brush.setColorAt(1.0, QtGui.QColor(0, 0, 0, 0))
elif self.fillType == 1 and self.stretch:
- brush.setColorAt(0.2, PaintColor(*self.color2))
+ brush.setColorAt(0.2, QtGui.QColor(*self.color2))
else:
- brush.setColorAt(1.0, PaintColor(*self.color2))
+ brush.setColorAt(1.0, QtGui.QColor(*self.color2))
image.setBrush(brush)
image.drawRect(self.x, self.y, self.sizeWidth, self.sizeHeight)
diff --git a/src/avp/components/color.ui b/src/avp/components/color.ui
index c1713fb..c36bdd8 100644
--- a/src/avp/components/color.ui
+++ b/src/avp/components/color.ui
@@ -74,7 +74,7 @@
- 0,0,0
+
12
@@ -84,10 +84,10 @@
-
- Qt::Horizontal
+ Qt::Orientation::Horizontal
- QSizePolicy::Fixed
+ QSizePolicy::Policy::Fixed
@@ -176,7 +176,7 @@
Width
- Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter
+ Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter
@@ -246,10 +246,10 @@
-
- Qt::Horizontal
+ Qt::Orientation::Horizontal
- QSizePolicy::Fixed
+ QSizePolicy::Policy::Fixed
@@ -370,7 +370,7 @@
-1
- QComboBox::AdjustToContentsOnFirstShow
+ QComboBox::SizeAdjustPolicy::AdjustToContentsOnFirstShow
@@ -422,10 +422,10 @@
-
- Qt::Horizontal
+ Qt::Orientation::Horizontal
- QSizePolicy::Minimum
+ QSizePolicy::Policy::Minimum
@@ -461,7 +461,7 @@
-1
0
561
- 31
+ 34
@@ -503,7 +503,7 @@
End
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+ Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter
@@ -523,7 +523,7 @@
-
- Qt::Horizontal
+ Qt::Orientation::Horizontal
@@ -543,7 +543,7 @@
-1
-1
561
- 31
+ 34
@@ -559,7 +559,7 @@
Start
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+ Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter
@@ -588,7 +588,7 @@
End
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+ Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter
@@ -617,14 +617,14 @@
Centre
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+ Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter
-
- QAbstractSpinBox::PlusMinus
+ QAbstractSpinBox::ButtonSymbols::PlusMinus
-10000
@@ -640,7 +640,7 @@
-
- Qt::Horizontal
+ Qt::Orientation::Horizontal
diff --git a/src/avp/components/image.py b/src/avp/components/image.py
index bada15f..e012cec 100644
--- a/src/avp/components/image.py
+++ b/src/avp/components/image.py
@@ -4,13 +4,13 @@ import os
from copy import copy
from ..component import Component
-from ..toolkit.frame import BlankFrame
-from .original import Component as Visualizer
+from ..toolkit.frame import BlankFrame, addShadow
+from ..toolkit.visualizer import createSpectrumArray
class Component(Component):
name = "Image"
- version = "2.0.0"
+ version = "2.1.0"
def widget(self, *args):
super().widget(*args)
@@ -35,6 +35,7 @@ class Component(Component):
"mirror": self.page.checkBox_mirror,
"respondToAudio": self.page.checkBox_respondToAudio,
"sensitivity": self.page.spinBox_sensitivity,
+ "shadow": self.page.checkBox_shadow,
},
presetNames={
"imagePath": "image",
@@ -75,31 +76,16 @@ class Component(Component):
# Trigger creation of new base image
self.existingImage = None
- smoothConstantDown = 0.08 + 0
- smoothConstantUp = 0.8 - 0
- self.lastSpectrum = None
- self.spectrumArray = {}
-
- for i in range(0, len(self.completeAudioArray), self.sampleSize):
- if self.canceled:
- break
- self.lastSpectrum = Visualizer.transformData(
- i,
- self.completeAudioArray,
- self.sampleSize,
- smoothConstantDown,
- smoothConstantUp,
- self.lastSpectrum,
- self.sensitivity,
- )
- self.spectrumArray[i] = copy(self.lastSpectrum)
-
- progress = int(100 * (i / len(self.completeAudioArray)))
- if progress >= 100:
- progress = 100
- pStr = "Analyzing audio: " + str(progress) + "%"
- self.progressBarSetText.emit(pStr)
- self.progressBarUpdate.emit(int(progress))
+ self.spectrumArray = createSpectrumArray(
+ self,
+ self.completeAudioArray,
+ self.sampleSize,
+ 0.08,
+ 0.8,
+ self.sensitivity,
+ self.progressBarUpdate,
+ self.progressBarSetText,
+ )
def frameRender(self, frameNo):
return self.drawFrame(
@@ -139,9 +125,16 @@ class Component(Component):
self.existingImage = image
# Respond to audio
+ resolutionFactor = height / 1080
+ shadX = int(resolutionFactor * 1)
+ shadY = int(resolutionFactor * -1)
+ shadBlur = resolutionFactor * 3.50
scale = 0
if dynamicScale is not None:
scale = dynamicScale[36 * 4] / 4
+ shadX += int((scale / 4) * resolutionFactor)
+ shadY += int((scale / 2) * resolutionFactor)
+ shadBlur += (scale / 8) * resolutionFactor
image = ImageOps.contain(
image,
(
@@ -161,6 +154,8 @@ class Component(Component):
)
if self.rotate != 0:
frame = frame.rotate(self.rotate)
+ if self.shadow:
+ frame = addShadow(frame, shadBlur, shadX, shadY)
return frame
diff --git a/src/avp/components/image.ui b/src/avp/components/image.ui
index 72593a3..b53c4b0 100644
--- a/src/avp/components/image.ui
+++ b/src/avp/components/image.ui
@@ -306,6 +306,16 @@
+ -
+
+
+ Qt::LayoutDirection::RightToLeft
+
+
+ Shadow
+
+
+
-
diff --git a/src/avp/components/life.py b/src/avp/components/life.py
index 9e5e202..a062617 100644
--- a/src/avp/components/life.py
+++ b/src/avp/components/life.py
@@ -8,8 +8,8 @@ import logging
from ..component import Component
-from ..toolkit.frame import BlankFrame, scale
-from .original import Component as Visualizer
+from ..toolkit.frame import BlankFrame, scale, addShadow
+from ..toolkit.visualizer import createSpectrumArray
log = logging.getLogger("AVP.Component.Life")
@@ -17,7 +17,7 @@ log = logging.getLogger("AVP.Component.Life")
class Component(Component):
name = "Conway's Game of Life"
- version = "2.0.0"
+ version = "2.0.1"
def widget(self, *args):
super().widget(*args)
@@ -27,26 +27,26 @@ class Component(Component):
# https://conwaylife.com/wiki/Queen_bee_shuttle
self.startingGrid = set(
[
- (3, 7),
- (3, 8),
- (4, 7),
- (4, 8),
- (8, 7),
- (9, 6),
- (9, 8),
- (10, 5),
+ (3, 11),
+ (3, 12),
+ (4, 11),
+ (4, 12),
+ (8, 11),
+ (9, 10),
+ (9, 12),
(10, 9),
- (11, 6),
- (11, 7),
- (11, 8),
- (12, 4),
- (12, 5),
+ (10, 13),
+ (11, 10),
+ (11, 11),
+ (11, 12),
+ (12, 8),
(12, 9),
- (12, 10),
- (23, 6),
- (23, 7),
- (24, 6),
- (24, 7),
+ (12, 13),
+ (12, 14),
+ (23, 10),
+ (23, 11),
+ (24, 10),
+ (24, 11),
]
)
@@ -163,41 +163,27 @@ class Component(Component):
def previewRender(self):
image = self.drawGrid(self.startingGrid, self.color)
image = self.addKaleidoscopeEffect(image)
- image = self.addShadow(image)
+ if self.shadow:
+ image = addShadow(image, 5.00, -2, 2)
image = self.addGridLines(image)
return image
def preFrameRender(self, *args, **kwargs):
super().preFrameRender(*args, **kwargs)
self.tickGrids = {0: self.startingGrid}
-
- smoothConstantDown = 0.08 + 0
- smoothConstantUp = 0.8 - 0
- self.lastSpectrum = None
- self.spectrumArray = {}
if self.sensitivity == 0:
return
- for i in range(0, len(self.completeAudioArray), self.sampleSize):
- if self.canceled:
- break
- self.lastSpectrum = Visualizer.transformData(
- i,
- self.completeAudioArray,
- self.sampleSize,
- smoothConstantDown,
- smoothConstantUp,
- self.lastSpectrum,
- self.sensitivity,
- )
- self.spectrumArray[i] = copy(self.lastSpectrum)
-
- progress = int(100 * (i / len(self.completeAudioArray)))
- if progress >= 100:
- progress = 100
- pStr = "Analyzing audio: " + str(progress) + "%"
- self.progressBarSetText.emit(pStr)
- self.progressBarUpdate.emit(int(progress))
+ self.spectrumArray = createSpectrumArray(
+ self,
+ self.completeAudioArray,
+ self.sampleSize,
+ 0.08,
+ 0.8,
+ 20,
+ self.progressBarUpdate,
+ self.progressBarSetText,
+ )
def properties(self):
if self.customImg and (not self.image or not os.path.exists(self.image)):
@@ -256,21 +242,11 @@ class Component(Component):
if not self.customImg:
image = Image.alpha_composite(previousGridImage, image)
image = self.addKaleidoscopeEffect(image)
- image = self.addShadow(image)
+ if self.shadow:
+ image = addShadow(image, 5.00, -2, 2)
image = self.addGridLines(image)
return image
- def addShadow(self, frame):
- if not self.shadow:
- return frame
-
- shadImg = ImageEnhance.Contrast(frame).enhance(0.0)
- shadImg = shadImg.filter(ImageFilter.GaussianBlur(5.00))
- shadImg = ImageChops.offset(shadImg, -2, 2)
- shadImg.paste(frame, box=(0, 0), mask=frame)
- frame = shadImg
- return frame
-
def addGridLines(self, frame):
if not self.showGrid:
return frame
@@ -299,11 +275,6 @@ class Component(Component):
flippedImage = frame.transpose(Image.Transpose.FLIP_LEFT_RIGHT)
frame.paste(flippedImage, (0, 0), mask=flippedImage)
- flippedImage = frame.transpose(Image.Transpose.ROTATE_90)
- frame.paste(flippedImage, (0, 0), mask=flippedImage)
-
- flippedImage = frame.transpose(Image.Transpose.ROTATE_270)
- frame.paste(flippedImage, (0, 0), mask=flippedImage)
return frame
def drawGrid(self, grid, color, spectrumData=None, didntChange=None):
@@ -506,10 +477,10 @@ class Component(Component):
for x, y in grid:
drawPtX = x * self.pxWidth
- if drawPtX > self.width:
+ if drawPtX > self.width or drawPtX + self.pxWidth < 0:
continue
drawPtY = y * self.pxHeight
- if drawPtY > self.height:
+ if drawPtY > self.height or drawPtY + self.pxHeight < 0:
continue
audioMorphWidth = (
diff --git a/src/avp/components/life.ui b/src/avp/components/life.ui
index a0c8999..0070fa4 100644
--- a/src/avp/components/life.ui
+++ b/src/avp/components/life.ui
@@ -68,7 +68,7 @@
- 255,255,255
+
diff --git a/src/avp/components/original.py b/src/avp/components/original.py
index 64eba4d..0da78dc 100644
--- a/src/avp/components/original.py
+++ b/src/avp/components/original.py
@@ -4,11 +4,12 @@ from copy import copy
from ..component import Component
from ..toolkit.frame import BlankFrame
+from ..toolkit.visualizer import createSpectrumArray
class Component(Component):
name = "Classic Visualizer"
- version = "1.0.0"
+ version = "1.1.0"
def names(*args):
return ["Original Audio Visualization"]
@@ -18,6 +19,7 @@ class Component(Component):
def widget(self, *args):
self.scale = 20
+ self.bars = 63
self.y = 0
super().widget(*args)
@@ -27,15 +29,14 @@ class Component(Component):
self.page.comboBox_visLayout.addItem("Top")
self.page.comboBox_visLayout.setCurrentIndex(0)
- self.page.lineEdit_visColor.setText("255,255,255")
-
self.trackWidgets(
{
"visColor": self.page.lineEdit_visColor,
"layout": self.page.comboBox_visLayout,
"scale": self.page.spinBox_scale,
"y": self.page.spinBox_y,
- "smooth": self.page.spinBox_smooth,
+ "smooth": self.page.spinBox_sensitivity,
+ "bars": self.page.spinBox_bars,
},
colorWidgets={
"visColor": self.page.pushButton_visColor,
@@ -59,29 +60,16 @@ class Component(Component):
super().preFrameRender(**kwargs)
smoothConstantDown = 0.08 if not self.smooth else self.smooth / 15
smoothConstantUp = 0.8 if not self.smooth else self.smooth / 15
- self.lastSpectrum = None
- self.spectrumArray = {}
-
- for i in range(0, len(self.completeAudioArray), self.sampleSize):
- if self.canceled:
- break
- self.lastSpectrum = self.transformData(
- i,
- self.completeAudioArray,
- self.sampleSize,
- smoothConstantDown,
- smoothConstantUp,
- self.lastSpectrum,
- self.scale,
- )
- self.spectrumArray[i] = copy(self.lastSpectrum)
-
- progress = int(100 * (i / len(self.completeAudioArray)))
- if progress >= 100:
- progress = 100
- pStr = "Analyzing audio: " + str(progress) + "%"
- self.progressBarSetText.emit(pStr)
- self.progressBarUpdate.emit(int(progress))
+ self.spectrumArray = createSpectrumArray(
+ self,
+ self.completeAudioArray,
+ self.sampleSize,
+ smoothConstantDown,
+ smoothConstantUp,
+ self.scale,
+ self.progressBarUpdate,
+ self.progressBarSetText,
+ )
def frameRender(self, frameNo):
arrayNo = frameNo * self.sampleSize
@@ -93,60 +81,10 @@ class Component(Component):
self.layout,
)
- @staticmethod
- def transformData(
- i,
- completeAudioArray,
- sampleSize,
- smoothConstantDown,
- smoothConstantUp,
- lastSpectrum,
- scale,
- ):
- if len(completeAudioArray) < (i + sampleSize):
- sampleSize = len(completeAudioArray) - i
-
- window = numpy.hanning(sampleSize)
- data = completeAudioArray[i : i + sampleSize][::1] * window
- paddedSampleSize = 2048
- paddedData = numpy.pad(data, (0, paddedSampleSize - sampleSize), "constant")
- spectrum = numpy.fft.fft(paddedData)
- sample_rate = 44100
- frequencies = numpy.fft.fftfreq(len(spectrum), 1.0 / sample_rate)
-
- y = abs(spectrum[0 : int(paddedSampleSize / 2) - 1])
-
- # filter the noise away
- # y[y<80] = 0
-
- with numpy.errstate(divide="ignore"):
- y = scale * numpy.log10(y)
-
- y[numpy.isinf(y)] = 0
-
- if lastSpectrum is not None:
- lastSpectrum[y < lastSpectrum] = y[
- y < lastSpectrum
- ] * smoothConstantDown + lastSpectrum[y < lastSpectrum] * (
- 1 - smoothConstantDown
- )
-
- lastSpectrum[y >= lastSpectrum] = y[
- y >= lastSpectrum
- ] * smoothConstantUp + lastSpectrum[y >= lastSpectrum] * (
- 1 - smoothConstantUp
- )
- else:
- lastSpectrum = y
-
- x = frequencies[0 : int(paddedSampleSize / 2) - 1]
-
- return lastSpectrum
-
def drawBars(self, width, height, spectrum, color, layout):
bigYCoord = height - height / 8
smallYCoord = height / 1200
- bigXCoord = width / 64
+ bigXCoord = width / (self.bars + 1)
middleXCoord = bigXCoord / 2
smallXCoord = bigXCoord / 4
@@ -155,7 +93,7 @@ class Component(Component):
r, g, b = color
color2 = (r, g, b, 125)
- for i in range(0, 63):
+ for i in range(self.bars):
x0 = middleXCoord + i * bigXCoord
y0 = bigYCoord + smallXCoord
y1 = bigYCoord + smallXCoord - spectrum[i * 4] * smallYCoord - middleXCoord
diff --git a/src/avp/components/original.ui b/src/avp/components/original.ui
index c7b7e22..8dbdaa2 100644
--- a/src/avp/components/original.ui
+++ b/src/avp/components/original.ui
@@ -44,10 +44,10 @@
-
- Qt::Horizontal
+ Qt::Orientation::Horizontal
- QSizePolicy::Fixed
+ QSizePolicy::Policy::Fixed
@@ -84,15 +84,19 @@
-
-
+
+
+
+
+
-
- Qt::Horizontal
+ Qt::Orientation::Horizontal
- QSizePolicy::Fixed
+ QSizePolicy::Policy::Fixed
@@ -112,7 +116,7 @@
-
- QAbstractSpinBox::UpDownArrows
+ QAbstractSpinBox::ButtonSymbols::UpDownArrows
-5000
@@ -131,7 +135,7 @@
-
- Qt::Horizontal
+ Qt::Orientation::Horizontal
@@ -158,7 +162,7 @@
-
- QAbstractSpinBox::PlusMinus
+ QAbstractSpinBox::ButtonSymbols::PlusMinus
1
@@ -168,13 +172,27 @@
+ -
+
+
+ Sensitivity
+
+
+
+ -
+
+
+ 5
+
+
+
-
- Qt::Horizontal
+ Qt::Orientation::Horizontal
- QSizePolicy::Expanding
+ QSizePolicy::Policy::Expanding
@@ -189,29 +207,35 @@
-
- QLayout::SetDefaultConstraint
+ QLayout::SizeConstraint::SetDefaultConstraint
4
-
-
+
- Sensitivity
+ Bars
-
-
+
+
+ 63
+
- 5
+ 64
+
+
+ 63
-
- Qt::Horizontal
+ Qt::Orientation::Horizontal
@@ -226,7 +250,7 @@
-
- Qt::Vertical
+ Qt::Orientation::Vertical
diff --git a/src/avp/components/text.py b/src/avp/components/text.py
index 40c981a..bee117e 100644
--- a/src/avp/components/text.py
+++ b/src/avp/components/text.py
@@ -5,7 +5,7 @@ import os
import logging
from ..component import Component
-from ..toolkit.frame import FramePainter, PaintColor
+from ..toolkit.frame import FramePainter, addShadow
log = logging.getLogger("AVP.Components.Text")
@@ -26,7 +26,6 @@ class Component(Component):
self.page.comboBox_textAlign.addItem("Right")
self.page.comboBox_textAlign.setCurrentIndex(int(self.alignment))
self.page.spinBox_fontSize.setValue(int(self.fontSize))
- self.page.lineEdit_title.setText(self.title)
self.page.pushButton_center.clicked.connect(self.centerXY)
self.page.fontComboBox_titleFont.currentFontChanged.connect(
@@ -35,7 +34,7 @@ class Component(Component):
# The QFontComboBox must be connected directly to the Qt Signal
# which triggers the preview to update.
# This unfortunately makes changing the font into a non-undoable action.
- # Must be something broken in the conversion to a ComponentAction
+ # Fix requires updating ComponentAction to handle fonts
self.trackWidgets(
{
@@ -173,7 +172,7 @@ class Component(Component):
path.addText(x, y, font, self.title)
path = outliner.createStroke(path)
image.setPen(QtCore.Qt.PenStyle.NoPen)
- image.setBrush(PaintColor(*self.strokeColor))
+ image.setBrush(QtGui.QColor(*self.strokeColor))
image.drawPath(path)
image.setFont(font)
@@ -183,11 +182,7 @@ class Component(Component):
# turn QImage into Pillow frame
frame = image.finalize()
if self.shadow:
- shadImg = ImageEnhance.Contrast(frame).enhance(0.0)
- shadImg = shadImg.filter(ImageFilter.GaussianBlur(self.shadBlur))
- shadImg = ImageChops.offset(shadImg, self.shadX, self.shadY)
- shadImg.paste(frame, box=(0, 0), mask=frame)
- frame = shadImg
+ frame = addShadow(frame, self.shadBlur / 10, self.shadX, self.shadY)
return frame
diff --git a/src/avp/components/text.ui b/src/avp/components/text.ui
index b62e0ed..ce961fe 100644
--- a/src/avp/components/text.ui
+++ b/src/avp/components/text.ui
@@ -15,646 +15,645 @@
-
-
-
- 6
-
-
- QLayout::SetDefaultConstraint
-
+
+
-
+
+
+ Title
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 0
+ 0
+
+
+
+ Text
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Font
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 0
+ 0
+
+
+
+
+
+
+ -
+
- 4
+ 0
-
-
-
-
-
-
- Title
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 0
- 0
-
-
-
- Testing New GUI
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- Font
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 0
- 0
-
-
-
-
-
+
+
+
+ 0
+ 0
+
+
+
+ Text Layout
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 100
+ 16777215
+
+
+
+
+ -
+
+
+ Qt::Orientation::Horizontal
+
+
+ QSizePolicy::Policy::Fixed
+
+
+
+ 5
+ 20
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Center Text
+
+
-
-
-
+
+
+ Qt::Orientation::Horizontal
+
+
+ QSizePolicy::Policy::Fixed
+
+
+
+ 5
+ 20
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ X
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 50
+ 16777215
+
+
+
+
+ 0
+ 0
+
+
+
0
-
-
-
-
-
- 0
- 0
-
-
-
- Text Layout
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 100
- 16777215
-
-
-
-
- -
-
-
- Qt::Horizontal
-
-
- QSizePolicy::Fixed
-
-
-
- 5
- 20
-
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- Center Text
-
-
-
- -
-
-
- Qt::Horizontal
-
-
- QSizePolicy::Fixed
-
-
-
- 5
- 20
-
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- X
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 50
- 16777215
-
-
-
-
- 0
- 0
-
-
-
- 0
-
-
- 999999999
-
-
- 0
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- Y
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 50
- 16777215
-
-
-
- 999999999
-
-
-
-
+
+ 999999999
+
+
+ 0
+
+
-
-
-
-
-
-
-
- 0
- 0
-
-
-
-
- 16777215
- 16777215
-
-
-
- Text Color
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 32
- 32
-
-
-
-
-
-
-
- 32
- 32
-
-
-
-
- -
-
-
- Qt::Horizontal
-
-
- QSizePolicy::Fixed
-
-
-
- 5
- 20
-
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- Font Size
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
-
-
-
-
-
- 1
-
-
- 500
-
-
-
- -
-
-
- Qt::Horizontal
-
-
- QSizePolicy::Fixed
-
-
-
- 5
- 20
-
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- Font Style
-
-
-
- -
-
-
-
-
- Normal
-
-
- -
-
- Semi-Bold
-
-
- -
-
- Bold
-
-
- -
-
- Italic
-
-
- -
-
- Bold Italic
-
-
- -
-
- Faux Italic
-
-
- -
-
- Small Caps
-
-
-
-
-
+
+
+
+ 0
+ 0
+
+
+
+ Y
+
+
-
-
-
-
-
-
-
- 0
- 0
-
-
-
-
- 0
- 16777215
-
-
-
- Qt::NoFocus
-
-
- 255,255,255
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- Stroke
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- px
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- Stroke Color
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 0
- 16777215
-
-
-
- Qt::NoFocus
-
-
- 0,0,0
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 32
- 32
-
-
-
-
-
-
-
- 32
- 32
-
-
-
-
- -
-
-
- Qt::Horizontal
-
-
-
- 40
- 20
-
-
-
-
-
+
+
+
+ 0
+ 0
+
+
+
+
+ 50
+ 16777215
+
+
+
+ 999999999
+
+
+
+
+
+ -
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+
+ 16777215
+ 16777215
+
+
+
+ Text Color
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 32
+ 32
+
+
+
+
+
+
+
+ 32
+ 32
+
+
+
+
+ -
+
+
+ Qt::Orientation::Horizontal
+
+
+ QSizePolicy::Policy::Fixed
+
+
+
+ 5
+ 20
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Font Size
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+
+
+
+
+
+ 1
+
+
+ 500
+
+
+
+ -
+
+
+ Qt::Orientation::Horizontal
+
+
+ QSizePolicy::Policy::Fixed
+
+
+
+ 5
+ 20
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Font Style
+
+
-
-
+
-
-
-
-
- 0
- 0
-
-
-
- Shadow
-
-
+
+ Normal
+
-
-
-
-
- 0
- 0
-
-
-
- Shadow Offset
-
-
+
+ Semi-Bold
+
-
-
-
-
- 0
- 0
-
-
-
- -1000
-
-
- 1000
-
-
- -4
-
-
+
+ Bold
+
-
-
-
-
- 0
- 0
-
-
-
- -1000
-
-
- 1000
-
-
- 8
-
-
+
+ Italic
+
-
-
-
-
- 0
- 0
-
-
-
- Shadow Blur
-
-
+
+ Bold Italic
+
-
-
-
-
- 0
- 0
-
-
-
- 99.000000000000000
-
-
- 0.100000000000000
-
-
- 5.000000000000000
-
-
+
+ Faux Italic
+
-
-
-
- Qt::Horizontal
-
-
- QSizePolicy::Minimum
-
-
-
- 40
- 20
-
-
-
+
+ Small Caps
+
-
+
+
+
+
+ -
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+
+ 0
+ 16777215
+
+
+
+ Qt::FocusPolicy::NoFocus
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Stroke
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ px
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Stroke Color
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 0
+ 16777215
+
+
+
+ Qt::FocusPolicy::NoFocus
+
+
+ 0,0,0
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 32
+ 32
+
+
+
+
+
+
+
+ 32
+ 32
+
+
+
+
+ -
+
+
+ Qt::Orientation::Horizontal
+
+
+ QSizePolicy::Policy::MinimumExpanding
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+ -
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+ Shadow
+
+
+
+ -
+
+
+ Qt::Orientation::Horizontal
+
+
+ QSizePolicy::Policy::Preferred
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Shadow Offset
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ -1000
+
+
+ 1000
+
+
+ 2
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ -1000
+
+
+ 1000
+
+
+ -2
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Shadow Blur
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ QAbstractSpinBox::ButtonSymbols::PlusMinus
+
+
+ QAbstractSpinBox::CorrectionMode::CorrectToPreviousValue
+
+
+ 1000
+
+
+ QAbstractSpinBox::StepType::DefaultStepType
+
+
+ 35
+
+
+ 10
+
+
-
- Qt::Vertical
+ Qt::Orientation::Vertical
diff --git a/src/avp/components/video.ui b/src/avp/components/video.ui
index 08d15d3..72ecb0c 100644
--- a/src/avp/components/video.ui
+++ b/src/avp/components/video.ui
@@ -27,168 +27,161 @@
-
-
-
- 4
-
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+
+ 31
+ 0
+
+
+
+ Video
+
+
+
+ -
+
+
+
+ 1
+ 0
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 1
+ 0
+
+
+
+
+ 32
+ 32
+
+
+
+ ...
+
+
+
+ 32
+ 32
+
+
+
+
+ -
+
+
+ Qt::Orientation::Horizontal
+
+
+ QSizePolicy::Policy::Fixed
+
+
+
+ 5
+ 20
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ X
+
+
+
-
-
-
-
-
-
-
- 0
- 0
-
-
-
-
- 31
- 0
-
-
-
- Video
-
-
-
- -
-
-
-
- 1
- 0
-
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 1
- 0
-
-
-
-
- 32
- 32
-
-
-
- ...
-
-
-
- 32
- 32
-
-
-
-
- -
-
-
- Qt::Horizontal
-
-
- QSizePolicy::Fixed
-
-
-
- 5
- 20
-
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- X
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 80
- 16777215
-
-
-
- -10000
-
-
- 10000
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- Y
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 80
- 16777215
-
-
-
-
- 0
- 0
-
-
-
- -10000
-
-
- 10000
-
-
- 0
-
-
-
-
+
+
+
+ 0
+ 0
+
+
+
+
+ 80
+ 16777215
+
+
+
+ -10000
+
+
+ 10000
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Y
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 80
+ 16777215
+
+
+
+
+ 0
+ 0
+
+
+
+ -10000
+
+
+ 10000
+
+
+ 0
+
+
@@ -204,7 +197,7 @@
-
- Qt::Horizontal
+ Qt::Orientation::Horizontal
@@ -227,14 +220,14 @@
Scale
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+ Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter
-
- QAbstractSpinBox::UpDownArrows
+ QAbstractSpinBox::ButtonSymbols::UpDownArrows
%
@@ -296,7 +289,7 @@
-
- Qt::Horizontal
+ Qt::Orientation::Horizontal
@@ -311,7 +304,7 @@
-
- Qt::Vertical
+ Qt::Orientation::Vertical
diff --git a/src/avp/components/waveform.py b/src/avp/components/waveform.py
index 7dc0b99..e10dec2 100644
--- a/src/avp/components/waveform.py
+++ b/src/avp/components/waveform.py
@@ -1,12 +1,12 @@
-from PIL import Image
-from PyQt6 import QtGui, QtCore, QtWidgets
+from PIL import Image, ImageChops
from PyQt6.QtGui import QColor
import os
-import math
import subprocess
import logging
+from copy import copy
from ..component import Component
+from ..toolkit.visualizer import transformData, createSpectrumArray
from ..toolkit.frame import BlankFrame, scale
from ..toolkit import checkOutput
from ..toolkit.ffmpeg import (
@@ -23,14 +23,20 @@ log = logging.getLogger("AVP.Components.Waveform")
class Component(Component):
name = "Waveform"
- version = "1.0.0"
+ version = "2.0.0"
+
+ @property
+ def updateInterval(self):
+ """How many frames from FFmpeg are ignored between each final frame"""
+ return 100 - self.speed
+
+ def properties(self):
+ return [] if self.speed == 100 else ["pcm"]
def widget(self, *args):
super().widget(*args)
self._image = BlankFrame(self.width, self.height)
- self.page.lineEdit_color.setText("255,255,255")
-
if hasattr(self.parent, "lineEdit_audioFile"):
self.parent.lineEdit_audioFile.textChanged.connect(self.update)
@@ -46,6 +52,7 @@ class Component(Component):
"opacity": self.page.spinBox_opacity,
"compress": self.page.checkBox_compress,
"mono": self.page.checkBox_mono,
+ "speed": self.page.spinBox_speed,
},
colorWidgets={
"color": self.page.pushButton_color,
@@ -65,6 +72,10 @@ class Component(Component):
return frame
def preFrameRender(self, **kwargs):
+ self._fadingImage = None
+ self._prevImage = None
+ self._currImage = None
+ self._lastUpdatedFrame = 0
super().preFrameRender(**kwargs)
self.updateChunksize()
w, h = scale(self.scale, self.width, self.height, str)
@@ -79,11 +90,64 @@ class Component(Component):
component=self,
debug=True,
)
+ if self.speed == 100:
+ return
+ self.spectrumArray = createSpectrumArray(
+ self,
+ self.completeAudioArray,
+ self.sampleSize,
+ 0.08,
+ 0.8,
+ 20,
+ self.progressBarUpdate,
+ self.progressBarSetText,
+ )
def frameRender(self, frameNo):
if FfmpegVideo.threadError is not None:
raise FfmpegVideo.threadError
- return self.finalizeFrame(self.video.frame(frameNo))
+ newFrame = self.finalizeFrame(self.video.frame(frameNo))
+ if self.speed == 100:
+ return newFrame
+ frameDiff = 0 if frameNo == 0 else frameNo % self.updateInterval
+ peaks = [
+ self.spectrumArray[frameNo * self.sampleSize][i * 4] for i in range(64)
+ ]
+ peakValue = 70 - (max(*peaks) - min(*peaks))
+ isValidPeak = (
+ peakValue > 27
+ and frameNo - self._lastUpdatedFrame > self.updateInterval / 2
+ )
+ if frameDiff == 0 or isValidPeak:
+ self._lastUpdatedFrame = frameNo
+ self._fadingImage = self._prevImage
+ self._prevImage = self._image
+ self._currImage = newFrame
+ usualAlpha = 0.0 + (1 / self.updateInterval) * frameDiff
+ alpha = max(
+ 0.1
+ + (
+ 1
+ / max(
+ 10,
+ peakValue,
+ )
+ ),
+ usualAlpha,
+ )
+ baseImage = self._prevImage
+ if self._fadingImage is not None:
+ # fade away the old previous frame from ages ago
+ baseImage = ImageChops.blend(
+ self._prevImage, self._fadingImage, max(0.0, 0.9 - usualAlpha)
+ )
+ blendedImage = ImageChops.blend(
+ baseImage,
+ ImageChops.lighter(self._prevImage, newFrame),
+ alpha,
+ )
+ baseImage.paste(blendedImage, (0, 0), mask=blendedImage)
+ return Image.alpha_composite(self._currImage, baseImage)
def postFrameRender(self):
closePipe(self.video.pipe)
@@ -162,17 +226,17 @@ class Component(Component):
def makeFfmpegFilter(self, preview=False, startPt=0):
w, h = scale(self.scale, self.width, self.height, str)
if self.amplitude == 0:
- amplitude = "lin"
- elif self.amplitude == 1:
amplitude = "log"
- elif self.amplitude == 2:
+ elif self.amplitude == 1:
amplitude = "sqrt"
- elif self.amplitude == 3:
+ elif self.amplitude == 2:
amplitude = "cbrt"
+ elif self.amplitude == 3:
+ amplitude = "lin"
hexcolor = QColor(*self.color).name()
opacity = "{0:.1f}".format(self.opacity / 100)
genericPreview = self.settings.value("pref_genericPreview")
- if self.mode < 3:
+ if self.mode > 1:
filter_ = (
"showwaves="
f'r={str(self.settings.value("outputFrameRate"))}:'
@@ -180,10 +244,10 @@ class Component(Component):
f'mode={self.page.comboBox_mode.currentText().lower() if self.mode != 3 else "p2p"}:'
f"colors={hexcolor}@{opacity}:scale={amplitude}"
)
- elif self.mode > 2:
+ elif self.mode < 2:
filter_ = (
f'showfreqs=s={str(self.settings.value("outputWidth"))}x{str(self.settings.value("outputHeight"))}:'
- f'mode={"line" if self.mode == 4 else "bar"}:'
+ f'mode={"line" if self.mode == 0 else "bar"}:'
f"colors={hexcolor}@{opacity}"
f":ascale={amplitude}:fscale={'log' if self.mono else 'lin'}"
)
diff --git a/src/avp/components/waveform.ui b/src/avp/components/waveform.ui
index 5473f33..434ba62 100644
--- a/src/avp/components/waveform.ui
+++ b/src/avp/components/waveform.ui
@@ -27,156 +27,144 @@
-
-
-
- 4
-
+
-
-
-
-
-
-
-
- 0
- 0
-
-
-
-
- 31
- 0
-
-
-
- Mode
-
-
-
- -
-
-
-
-
- Cline
-
-
- -
-
- Line
-
-
- -
-
- Point
-
-
- -
-
- Frequency Bar
-
-
- -
-
- Frequency Line
-
-
-
-
- -
-
-
- Qt::Horizontal
-
-
- QSizePolicy::Fixed
-
-
-
- 5
- 20
-
-
-
-
+
+
+
+ 0
+ 0
+
+
+
+
+ 31
+ 0
+
+
+
+ Mode
+
+
+
+ -
+
-
-
-
-
- 0
- 0
-
-
-
- X
-
-
+
+ Frequency Line
+
-
-
-
-
- 0
- 0
-
-
-
-
- 80
- 16777215
-
-
-
- -10000
-
-
- 10000
-
-
+
+ Frequency Bar
+
-
-
-
-
- 0
- 0
-
-
-
- Y
-
-
+
+ Cline
+
-
-
-
-
- 0
- 0
-
-
-
-
- 80
- 16777215
-
-
-
-
- 0
- 0
-
-
-
- -10000
-
-
- 10000
-
-
- 0
-
-
+
+ Line
+
-
+
+
+ -
+
+
+ Qt::Orientation::Horizontal
+
+
+ QSizePolicy::Policy::Fixed
+
+
+
+ 5
+ 20
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ X
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 80
+ 16777215
+
+
+
+ -10000
+
+
+ 10000
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Y
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 80
+ 16777215
+
+
+
+
+ 0
+ 0
+
+
+
+ -10000
+
+
+ 10000
+
+
+ 0
+
+
@@ -192,7 +180,7 @@
-
- Qt::ImhNone
+ Qt::InputMethodHint::ImhNone
@@ -224,7 +212,7 @@
-
- Qt::Horizontal
+ Qt::Orientation::Horizontal
@@ -240,14 +228,14 @@
Opacity
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+ Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter
-
- QAbstractSpinBox::UpDownArrows
+ QAbstractSpinBox::ButtonSymbols::UpDownArrows
%
@@ -269,14 +257,14 @@
Scale
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+ Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter
-
- QAbstractSpinBox::UpDownArrows
+ QAbstractSpinBox::ButtonSymbols::UpDownArrows
%
@@ -301,6 +289,9 @@
Compress
+
+ true
+
-
@@ -320,7 +311,7 @@
-
- Qt::Horizontal
+ Qt::Orientation::Horizontal
@@ -341,32 +332,75 @@
-
- Linear
+ Logarithmic
-
- Logarithmic
+ Square root
-
- Square root
+ Cubic root
-
- Cubic root
+ Linear
+ -
+
+
-
+
+
+ Animation Speed
+
+
+
+ -
+
+
+ %
+
+
+ 10
+
+
+ 100
+
+
+ 10
+
+
+ 50
+
+
+
+ -
+
+
+ Qt::Orientation::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
-
- Qt::Vertical
+ Qt::Orientation::Vertical
--
cgit v1.2.3