From 9fe370d472b84a693187316716bf7201df930b0c Mon Sep 17 00:00:00 2001
From: Brianna
Date: Sun, 25 Jun 2017 18:28:40 -0400
Subject: Update readme to match newgui
---
README.md | 20 +++++++++-----------
1 file changed, 9 insertions(+), 11 deletions(-)
(limited to 'README.md')
diff --git a/README.md b/README.md
index be5ae25..2d59979 100644
--- a/README.md
+++ b/README.md
@@ -1,28 +1,26 @@
audio-visualizer-python
=======================
-This is a little GUI tool which creates an audio visualization video from an input audio.
-You can also give it a background image and set a title text.
+This is a little GUI tool which creates an audio visualization video from an input audio file. Different components can be added and layered to change the resulting video and add images, videos, gradients, text, etc. The component setup can be saved as a Project and exporting can be automated using commandline options.
-I have tested the program on Linux (Ubuntu 14.10) and Windows (Windows 7), it should also work on Mac OS X. If you encounter problems
-running it or have other bug reports or features, that you wish to see implemented, please fork the project and send me a pull request and/or file an issue on this project.
+The program works on Linux (Ubuntu 16.04), Windows (Windows 7), and Mac OS X. If you encounter problems running it or have other bug reports or features that you wish to see implemented, please fork the project and send me a pull request and/or file an issue on this project.
I also need a good name that is not as generic as "audio-visualizer-python"!
Dependencies
------------
-You need Python 3, PyQt4, PIL (or Pillow), numpy and the program ffmpeg, which is used to read the audio and render the video.
+Python 3, PyQt5, pillow-simd, numpy, and ffmpeg 3.3
Installation
------------
-### Manual installation on Ubuntu
-* Get all the python stuff: `sudo apt-get install python3 python3-pyqt4 python3-pil python3-numpy`
-* Get ffmpeg/avconv:
-You can either use `avconv` from the standard repositories (package `libav-tools`) or get `ffmpeg` from the [website](http://ffmpeg.org/) or from a PPA (e.g. [https://launchpad.net/~jon-severinsson/+archive/ubuntu/ffmpeg](https://launchpad.net/~jon-severinsson/+archive/ubuntu/ffmpeg). The program does automatically detect if you don't have the ffmpeg binary and tries to use avconv instead.
+### Manual installation on Ubuntu 16.04
+* Install pip: `sudo apt-get install python3-pip`
+* Install dependencies: `sudo pip3 install pyqt5 numpy pillow-simd`
+* Install `ffmpeg` from the [website](http://ffmpeg.org/) or from a PPA (e.g. [https://launchpad.net/~jonathonf/+archive/ubuntu/ffmpeg-3](https://launchpad.net/~jonathonf/+archive/ubuntu/ffmpeg-3). NOTE: `ffmpeg` in the standard repos is too old (v2.8). Old versions and `avconv` may be used but full functionality is only guaranteed with `ffmpeg` 3.3 or higher.
Download audio-visualizer-python from this repository and run it with `python3 main.py`.
-### Manual installation on Windows
+### Manual installation on Windows [Outdated]
* Download and install Python 3.4 from [https://www.python.org/downloads/windows/](https://www.python.org/downloads/windows/)
* Download and install PyQt4 for Python 3.4 and Qt4 from [http://www.riverbankcomputing.co.uk/software/pyqt/download](http://www.riverbankcomputing.co.uk/software/pyqt/download)
* Download and install numpy from [http://www.scipy.org/scipylib/download.html](http://www.scipy.org/scipylib/download.html). There is an installer available, make sure to get the one for Python 3.4
@@ -32,7 +30,7 @@ Download audio-visualizer-python from this repository and run it with `python3 m
Download audio-visualizer-python from this repository and run it from the command line with `C:\Python34\python.exe main.py`.
-### Manual installation on macOS
+### Manual installation on macOS [Outdated]
* Install [Homebrew](http://brew.sh/)
* Use the following commands to install the needed dependencies:
--
cgit v1.2.3
From 134779f6e65bae36accc9fd586e41b7e80b5dc93 Mon Sep 17 00:00:00 2001
From: tassaron
Date: Tue, 4 Jul 2017 20:55:51 -0400
Subject: updated Windows installation instructions
---
README.md | 13 ++++++-------
src/toolkit.py | 2 +-
2 files changed, 7 insertions(+), 8 deletions(-)
(limited to 'README.md')
diff --git a/README.md b/README.md
index 2d59979..acf36d0 100644
--- a/README.md
+++ b/README.md
@@ -20,15 +20,14 @@ Installation
Download audio-visualizer-python from this repository and run it with `python3 main.py`.
-### Manual installation on Windows [Outdated]
-* Download and install Python 3.4 from [https://www.python.org/downloads/windows/](https://www.python.org/downloads/windows/)
-* Download and install PyQt4 for Python 3.4 and Qt4 from [http://www.riverbankcomputing.co.uk/software/pyqt/download](http://www.riverbankcomputing.co.uk/software/pyqt/download)
-* Download and install numpy from [http://www.scipy.org/scipylib/download.html](http://www.scipy.org/scipylib/download.html). There is an installer available, make sure to get the one for Python 3.4
-* Download and install Pillow from [https://pypi.python.org/pypi/Pillow/3.3.0](https://pypi.python.org/pypi/Pillow/3.3.0)
+### Manual installation on Windows
+* Download and install Python 3.6 from [https://www.python.org/downloads/windows/](https://www.python.org/downloads/windows/)
+* Add Python to your system PATH (it will ask during the installation process).
+* Open command prompt and run: `pip install pyqt5 numpy pillow-simd`
* Download and install ffmpeg from [https://www.ffmpeg.org/download.html](https://www.ffmpeg.org/download.html). You can use the static builds.
-* Add ffmpeg to your system PATH environment variable.
+* Add ffmpeg to your system PATH, too. [How to edit the PATH on Windows.](https://www.java.com/en/download/help/path.xml)
-Download audio-visualizer-python from this repository and run it from the command line with `C:\Python34\python.exe main.py`.
+Download audio-visualizer-python from this repository and run it from the command line with `python main.py`.
### Manual installation on macOS [Outdated]
diff --git a/src/toolkit.py b/src/toolkit.py
index 8dce645..589a6dc 100644
--- a/src/toolkit.py
+++ b/src/toolkit.py
@@ -40,7 +40,7 @@ def openPipe(commandList, **kwargs):
def _subprocess(func, commandList, **kwargs):
- if not sys.platform == 'win32':
+ if sys.platform == 'win32':
# Stop CMD window from appearing on Windows
# http://code.activestate.com/recipes/409002/
startupinfo = subprocess.STARTUPINFO()
--
cgit v1.2.3
From f027fd43537eb60f682b51a5018caee471bf33e2 Mon Sep 17 00:00:00 2001
From: tassaron
Date: Thu, 6 Jul 2017 19:52:46 -0400
Subject: more thorough installation directions
---
.gitignore | 1 +
MANIFEST | 2 ++
README.md | 9 +++++++--
freeze.py | 3 ++-
4 files changed, 12 insertions(+), 3 deletions(-)
create mode 100644 MANIFEST
(limited to 'README.md')
diff --git a/.gitignore b/.gitignore
index d44e3f2..1095610 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
__pycache__
.py[cod]
build/*
+dist/*
env/*
.vscode/*
*.mkv
diff --git a/MANIFEST b/MANIFEST
new file mode 100644
index 0000000..a0c51f7
--- /dev/null
+++ b/MANIFEST
@@ -0,0 +1,2 @@
+# file GENERATED by distutils, do NOT edit
+freeze.py
diff --git a/README.md b/README.md
index acf36d0..b82f3b4 100644
--- a/README.md
+++ b/README.md
@@ -15,14 +15,19 @@ Installation
------------
### Manual installation on Ubuntu 16.04
* Install pip: `sudo apt-get install python3-pip`
-* Install dependencies: `sudo pip3 install pyqt5 numpy pillow-simd`
-* Install `ffmpeg` from the [website](http://ffmpeg.org/) or from a PPA (e.g. [https://launchpad.net/~jonathonf/+archive/ubuntu/ffmpeg-3](https://launchpad.net/~jonathonf/+archive/ubuntu/ffmpeg-3). NOTE: `ffmpeg` in the standard repos is too old (v2.8). Old versions and `avconv` may be used but full functionality is only guaranteed with `ffmpeg` 3.3 or higher.
+* Install [prerequisites to compile Pillow](http://pillow.readthedocs.io/en/3.1.x/installation.html#building-on-linux):`sudo apt-get install python3-dev python3-setuptools libtiff5-dev libjpeg8-dev zlib1g-dev libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev python-tk`
+* Prerequisites on **Fedora**:`sudo dnf install python3-devel redhat-rpm-config libtiff-devel libjpeg-devel libzip-devel freetype-devel lcms2-devel libwebp-devel tcl-devel tk-devel`
+* Install dependencies from PyPI: `sudo pip3 install pyqt5 numpy pillow-simd`
+* Install `ffmpeg` from the [website](http://ffmpeg.org/) or from a PPA (e.g. [https://launchpad.net/~jonathonf/+archive/ubuntu/ffmpeg-3](https://launchpad.net/~jonathonf/+archive/ubuntu/ffmpeg-3)). NOTE: `ffmpeg` in the standard repos is too old (v2.8). Old versions and `avconv` may be used but full functionality is only guaranteed with `ffmpeg` 3.3 or higher.
Download audio-visualizer-python from this repository and run it with `python3 main.py`.
### Manual installation on Windows
+* **Not Recommended.** [Compiling Pillow is difficult on Windows](http://pillow.readthedocs.io/en/3.1.x/installation.html#building-on-windows) and required for a manual installation.
* Download and install Python 3.6 from [https://www.python.org/downloads/windows/](https://www.python.org/downloads/windows/)
* Add Python to your system PATH (it will ask during the installation process).
+* Brave treacherous valley of getting prerequisites to [compile Pillow on Windows](https://www.pypkg.com/pypi/pillow-simd/f/winbuild/README.md). This is necessary because binary builds for Pillow-SIMD are not available.
+* **Alternative:** install Pillow instead of Pillow-SIMD, for which binaries *are* available. However this will result in much slower video export times.
* Open command prompt and run: `pip install pyqt5 numpy pillow-simd`
* Download and install ffmpeg from [https://www.ffmpeg.org/download.html](https://www.ffmpeg.org/download.html). You can use the static builds.
* Add ffmpeg to your system PATH, too. [How to edit the PATH on Windows.](https://www.java.com/en/download/help/path.xml)
diff --git a/freeze.py b/freeze.py
index 3266f45..c9b7918 100644
--- a/freeze.py
+++ b/freeze.py
@@ -18,7 +18,8 @@ buildOptions = dict(
"html",
"http",
"xmlrpc",
- "nose"
+ "nose",
+ 'tkinter',
],
includes=[
"encodings",
--
cgit v1.2.3
From 94d4acc1f4f4abe4029e8f9c050932b67cae8cec Mon Sep 17 00:00:00 2001
From: tassaron
Date: Sun, 9 Jul 2017 01:10:06 -0400
Subject: more comments + warnings for outdated dependencies
---
MANIFEST | 2 --
README.md | 8 +++--
src/component.py | 55 +++++++++++++++++++--------------
src/components/color.py | 5 ++-
src/components/image.py | 2 +-
src/components/original.py | 3 +-
src/components/text.py | 2 +-
src/components/video.py | 2 +-
src/core.py | 8 ++---
src/frame.py | 15 ++++++---
src/mainwindow.py | 34 +++++++++++++++++++-
src/preview_thread.py | 12 +++++---
src/video_thread.py | 77 ++++++++++++++++++++++++++++------------------
13 files changed, 148 insertions(+), 77 deletions(-)
delete mode 100644 MANIFEST
(limited to 'README.md')
diff --git a/MANIFEST b/MANIFEST
deleted file mode 100644
index a0c51f7..0000000
--- a/MANIFEST
+++ /dev/null
@@ -1,2 +0,0 @@
-# file GENERATED by distutils, do NOT edit
-freeze.py
diff --git a/README.md b/README.md
index b82f3b4..658a22d 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@ audio-visualizer-python
This is a little GUI tool which creates an audio visualization video from an input audio file. Different components can be added and layered to change the resulting video and add images, videos, gradients, text, etc. The component setup can be saved as a Project and exporting can be automated using commandline options.
-The program works on Linux (Ubuntu 16.04), Windows (Windows 7), and Mac OS X. If you encounter problems running it or have other bug reports or features that you wish to see implemented, please fork the project and send me a pull request and/or file an issue on this project.
+The program works on Linux, macOS, and Windows. If you encounter problems running it or have other bug reports or features that you wish to see implemented, please fork the project and send me a pull request and/or file an issue on this project.
I also need a good name that is not as generic as "audio-visualizer-python"!
@@ -11,6 +11,8 @@ Dependencies
------------
Python 3, PyQt5, pillow-simd, numpy, and ffmpeg 3.3
+**Note:** Pillow may be used as a drop-in replacement for Pillow-SIMD if problems are encountered installing. However this will result in much slower video export times.
+
Installation
------------
### Manual installation on Ubuntu 16.04
@@ -23,7 +25,7 @@ Installation
Download audio-visualizer-python from this repository and run it with `python3 main.py`.
### Manual installation on Windows
-* **Not Recommended.** [Compiling Pillow is difficult on Windows](http://pillow.readthedocs.io/en/3.1.x/installation.html#building-on-windows) and required for a manual installation.
+* **Warning:** [Compiling Pillow is difficult on Windows](http://pillow.readthedocs.io/en/3.1.x/installation.html#building-on-windows) and required for the best experience.
* Download and install Python 3.6 from [https://www.python.org/downloads/windows/](https://www.python.org/downloads/windows/)
* Add Python to your system PATH (it will ask during the installation process).
* Brave treacherous valley of getting prerequisites to [compile Pillow on Windows](https://www.pypkg.com/pypi/pillow-simd/f/winbuild/README.md). This is necessary because binary builds for Pillow-SIMD are not available.
@@ -34,7 +36,7 @@ Download audio-visualizer-python from this repository and run it with `python3 m
Download audio-visualizer-python from this repository and run it from the command line with `python main.py`.
-### Manual installation on macOS [Outdated]
+### Manual installation on macOS **[Outdated]**
* Install [Homebrew](http://brew.sh/)
* Use the following commands to install the needed dependencies:
diff --git a/src/component.py b/src/component.py
index 6637eac..648a6d6 100644
--- a/src/component.py
+++ b/src/component.py
@@ -6,8 +6,11 @@ import os
class Component(QtCore.QObject):
- ''' A class for components to inherit.'''
- # modified = QtCore.pyqtSignal(int, bool)
+ '''
+ A class for components to inherit. Read comments for documentation
+ on making a valid component. All subclasses must implement this signal:
+ modified = QtCore.pyqtSignal(int, bool)
+ '''
def __init__(self, moduleIndex, compPos, core):
super().__init__()
@@ -36,30 +39,32 @@ class Component(QtCore.QObject):
# read your widget values, then call super().update()
def loadPreset(self, presetDict, presetName):
- '''Subclasses take (presetDict, presetName=None) as args.
- Must use super().loadPreset(presetDict, presetName) first,
- then update self.page widgets using the preset dict.
+ '''
+ Subclasses take (presetDict, presetName=None) as args.
+ Must use super().loadPreset(presetDict, presetName) first,
+ then update self.page widgets using the preset dict.
'''
self.currentPreset = presetName \
if presetName is not None else presetDict['preset']
def preFrameRender(self, **kwargs):
- '''Triggered only before a video is exported (video_thread.py)
- self.worker = the video thread worker
- self.completeAudioArray = a list of audio samples
- self.sampleSize = number of audio samples per video frame
- self.progressBarUpdate = signal to set progress bar number
- self.progressBarSetText = signal to set progress bar text
- Use the latter two signals to update the MainProgram if needed
- for a long initialization procedure (i.e., for a visualizer)
+ ''' Triggered only before a video is exported (video_thread.py)
+ self.worker = the video thread worker
+ self.completeAudioArray = a list of audio samples
+ self.sampleSize = number of audio samples per video frame
+ self.progressBarUpdate = signal to set progress bar number
+ self.progressBarSetText = signal to set progress bar text
+ Use the latter two signals to update the MainWindow if needed
+ for a long initialization procedure (i.e., for a visualizer)
'''
for var, value in kwargs.items():
exec('self.%s = value' % var)
def command(self, arg):
- '''Configure a component using argument from the commandline.
- Use super().command(arg) at the end of a subclass's method,
- if no arguments are found in that method first
+ '''
+ Configure a component using argument from the commandline.
+ Use super().command(arg) at the end of a subclass's method,
+ if no arguments are found in that method first
'''
if arg.startswith('preset='):
_, preset = arg.split('=', 1)
@@ -84,9 +89,10 @@ class Component(QtCore.QObject):
'''Print help text for this Component's commandline arguments'''
def pickColor(self):
- '''Use color picker to get color input from the user,
- and return this as an RGB string and QPushButton stylesheet.
- In a subclass apply stylesheet to any color selection widgets
+ '''
+ Use color picker to get color input from the user,
+ and return this as an RGB string and QPushButton stylesheet.
+ In a subclass apply stylesheet to any color selection widgets
'''
dialog = QtWidgets.QColorDialog()
dialog.setOption(QtWidgets.QColorDialog.ShowAlphaChannel, True)
@@ -101,7 +107,7 @@ class Component(QtCore.QObject):
return None, None
def RGBFromString(self, string):
- ''' Turns an RGB string like "255, 255, 255" into a tuple '''
+ '''Turns an RGB string like "255, 255, 255" into a tuple'''
try:
tup = tuple([int(i) for i in string.split(',')])
if len(tup) != 3:
@@ -135,13 +141,16 @@ class Component(QtCore.QObject):
def previewRender(self, previewWorker):
width = int(previewWorker.core.settings.value('outputWidth'))
height = int(previewWorker.core.settings.value('outputHeight'))
- image = Image.new("RGBA", (width, height), (0,0,0,0))
+ from frame import BlankFrame
+ image = BlankFrame(width, height)
return image
- def frameRender(self, moduleNo, frameNo):
+ def frameRender(self, layerNo, frameNo):
+ audioArrayIndex = frameNo * self.sampleSize
width = int(self.worker.core.settings.value('outputWidth'))
height = int(self.worker.core.settings.value('outputHeight'))
- image = Image.new("RGBA", (width, height), (0,0,0,0))
+ from frame import BlankFrame
+ image = BlankFrame(width, height)
return image
@classmethod
diff --git a/src/components/color.py b/src/components/color.py
index 4a10263..b87f3e9 100644
--- a/src/components/color.py
+++ b/src/components/color.py
@@ -104,6 +104,9 @@ class Component(Component):
self.page.checkBox_trans.setEnabled(True)
self.page.checkBox_stretch.setEnabled(True)
self.page.comboBox_spread.setEnabled(True)
+ if self.trans:
+ self.page.lineEdit_color2.setEnabled(False)
+ self.page.pushButton_color2.setEnabled(False)
self.page.fillWidget.setCurrentIndex(self.fillType)
self.parent.drawPreview()
@@ -118,7 +121,7 @@ class Component(Component):
super().preFrameRender(**kwargs)
return ['static']
- def frameRender(self, moduleNo, arrayNo, frameNo):
+ def frameRender(self, layerNo, frameNo):
width = int(self.worker.core.settings.value('outputWidth'))
height = int(self.worker.core.settings.value('outputHeight'))
return self.drawFrame(width, height)
diff --git a/src/components/image.py b/src/components/image.py
index c9da137..6edd893 100644
--- a/src/components/image.py
+++ b/src/components/image.py
@@ -51,7 +51,7 @@ class Component(Component):
super().preFrameRender(**kwargs)
return ['static']
- def frameRender(self, moduleNo, arrayNo, frameNo):
+ def frameRender(self, layerNo, frameNo):
width = int(self.worker.core.settings.value('outputWidth'))
height = int(self.worker.core.settings.value('outputHeight'))
return self.drawFrame(width, height)
diff --git a/src/components/original.py b/src/components/original.py
index 82cdc1d..638095d 100644
--- a/src/components/original.py
+++ b/src/components/original.py
@@ -107,7 +107,8 @@ class Component(Component):
self.progressBarSetText.emit(pStr)
self.progressBarUpdate.emit(int(progress))
- def frameRender(self, moduleNo, arrayNo, frameNo):
+ def frameRender(self, layerNo, frameNo):
+ arrayNo = frameNo * self.sampleSize
return self.drawBars(
self.width, self.height,
self.spectrumArray[arrayNo],
diff --git a/src/components/text.py b/src/components/text.py
index 97d7d07..2b1884f 100644
--- a/src/components/text.py
+++ b/src/components/text.py
@@ -123,7 +123,7 @@ class Component(Component):
super().preFrameRender(**kwargs)
return ['static']
- def frameRender(self, moduleNo, arrayNo, frameNo):
+ def frameRender(self, layerNo, frameNo):
width = int(self.worker.core.settings.value('outputWidth'))
height = int(self.worker.core.settings.value('outputHeight'))
return self.addText(width, height)
diff --git a/src/components/video.py b/src/components/video.py
index 19a9106..e6890e0 100644
--- a/src/components/video.py
+++ b/src/components/video.py
@@ -165,7 +165,7 @@ class Component(Component):
component=self, scale=self.scale
) if os.path.exists(self.videoPath) else None
- def frameRender(self, moduleNo, arrayNo, frameNo):
+ def frameRender(self, layerNo, frameNo):
if self.video:
return self.video.frame(frameNo)
else:
diff --git a/src/core.py b/src/core.py
index 5623039..9792e88 100644
--- a/src/core.py
+++ b/src/core.py
@@ -449,15 +449,15 @@ class Core:
else:
if sys.platform == "win32":
- return "ffmpeg.exe"
+ return "ffmpeg"
else:
try:
with open(os.devnull, "w") as f:
- sp.check_call(
- ['ffmpeg', '-version'], stdout=f, stderr=f
+ toolkit.checkOutput(
+ ['ffmpeg', '-version'], stderr=f
)
return "ffmpeg"
- except:
+ except sp.CalledProcessError:
return "avconv"
def readAudioFile(self, filename, parent):
diff --git a/src/frame.py b/src/frame.py
index 6d6d299..57d33b0 100644
--- a/src/frame.py
+++ b/src/frame.py
@@ -8,17 +8,17 @@ import sys
class FramePainter(QtGui.QPainter):
+ '''
+ A QPainter for a blank frame, which can be converted into a
+ Pillow image with finalize()
+ '''
def __init__(self, width, height):
image = BlankFrame(width, height)
self.image = ImageQt(image)
super().__init__(self.image)
def setPen(self, RgbTuple):
- if sys.byteorder == 'big':
- color = QtGui.QColor(*RgbTuple)
- else:
- color = QtGui.QColor(*RgbTuple[::-1])
- super().setPen(QtGui.QColor(color))
+ super().setPen(PaintColor(*RgbTuple))
def finalize(self):
self.end()
@@ -28,15 +28,20 @@ class FramePainter(QtGui.QPainter):
'RGBA', (self.image.width(), self.image.height()), imBytes
)
+
class PaintColor(QtGui.QColor):
+ '''Reverse the painter colour if the hardware stores RGB values backward'''
def __init__(self, r, g, b, a=255):
if sys.byteorder == 'big':
super().__init__(r, g, b, a)
else:
super().__init__(b, g, r, a)
+
def FloodFrame(width, height, RgbaTuple):
return Image.new("RGBA", (width, height), RgbaTuple)
+
def BlankFrame(width, height):
+ '''The base frame used by each component to start drawing'''
return FloodFrame(width, height, (0, 0, 0, 0))
diff --git a/src/mainwindow.py b/src/mainwindow.py
index 1c6bbc4..165b5bd 100644
--- a/src/mainwindow.py
+++ b/src/mainwindow.py
@@ -6,6 +6,7 @@
'''
from PyQt5 import QtCore, QtGui, uic, QtWidgets
from PyQt5.QtWidgets import QMenu, QShortcut
+from PIL import Image
from queue import Queue
import sys
import os
@@ -17,7 +18,7 @@ import core
import preview_thread
import video_thread
from presetmanager import PresetManager
-from toolkit import LoadDefaultSettings, disableWhenEncoding
+from toolkit import LoadDefaultSettings, disableWhenEncoding, checkOutput
class PreviewWindow(QtWidgets.QLabel):
@@ -269,6 +270,37 @@ class MainWindow(QtWidgets.QMainWindow):
self.openProject(self.currentProject, prompt=False)
self.drawPreview(True)
+ # verify Pillow version
+ if not self.settings.value("pilMsgShown") \
+ and 'post' not in Image.PILLOW_VERSION:
+ self.showMessage(
+ msg="You are using the standard version of the "
+ "Python imaging library (Pillow %s). Upgrade "
+ "to the Pillow-SIMD fork to enable hardware accelerations "
+ "and export videos faster." % Image.PILLOW_VERSION
+ )
+ self.settings.setValue("pilMsgShown", True)
+
+ # verify Ffmpeg version
+ if not self.settings.value("ffmpegMsgShown"):
+ try:
+ with open(os.devnull, "w") as f:
+ ffmpegVers = checkOutput(
+ ['ffmpeg', '-version'], stderr=f
+ )
+ goodVersion = str(ffmpegVers).split()[2].startswith('3')
+ except:
+ goodVersion = False
+ else:
+ goodVersion = True
+
+ if not goodVersion:
+ self.showMessage(
+ msg="You're using an old version of Ffmpeg. "
+ "Some features may not work as expected."
+ )
+ self.settings.setValue("ffmpegMsgShown", True)
+
# Setup Hotkeys
QtWidgets.QShortcut("Ctrl+S", self.window, self.saveCurrentProject)
QtWidgets.QShortcut("Ctrl+A", self.window, self.openSaveProjectDialog)
diff --git a/src/preview_thread.py b/src/preview_thread.py
index afb5e50..95a26ec 100644
--- a/src/preview_thread.py
+++ b/src/preview_thread.py
@@ -9,7 +9,8 @@ from PIL.ImageQt import ImageQt
import core
from queue import Queue, Empty
import os
-from copy import copy
+
+from frame import FloodFrame
class Worker(QtCore.QObject):
@@ -22,11 +23,13 @@ class Worker(QtCore.QObject):
parent.newTask.connect(self.createPreviewImage)
parent.processTask.connect(self.process)
self.parent = parent
- self.core = core.Core()
+ self.core = self.parent.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))
+
+ # create checkerboard background to represent transparency
+ self.background = FloodFrame(1920, 1080, (0, 0, 0, 0))
self.background.paste(Image.open(os.path.join(
self.core.wd, "background.png")))
@@ -49,7 +52,7 @@ class Worker(QtCore.QObject):
width = int(self.core.settings.value('outputWidth'))
height = int(self.core.settings.value('outputHeight'))
- frame = copy(self.background)
+ frame = self.background.copy()
frame = frame.resize((width, height))
components = nextPreviewInformation["components"]
@@ -58,6 +61,7 @@ class Worker(QtCore.QObject):
frame = Image.alpha_composite(
frame, component.previewRender(self)
)
+
except ValueError as e:
self.parent.showMessage(
msg="Bad frame returned by %s's previewRender method. "
diff --git a/src/video_thread.py b/src/video_thread.py
index d35a37a..e7f1ac7 100644
--- a/src/video_thread.py
+++ b/src/video_thread.py
@@ -3,7 +3,7 @@
an input file, output path, and component list. During export multiple
threads are created to render the video as quickly as possible. Signals
are emitted to update MainWindow's progress bar, detail text, and preview.
- Export can be cancelled with cancel() + reset()
+ Export can be cancelled with cancel()
'''
from PyQt5 import QtCore, QtGui, uic
from PyQt5.QtCore import pyqtSignal, pyqtSlot
@@ -16,11 +16,11 @@ import os
from queue import Queue, PriorityQueue
from threading import Thread, Event
import time
-from copy import copy
import signal
import core
from toolkit import openPipe
+from frame import FloodFrame
class Worker(QtCore.QObject):
@@ -44,49 +44,65 @@ class Worker(QtCore.QObject):
self.stopped = False
def renderNode(self):
+ '''
+ Grabs audio data indices at frames to export, from compositeQueue.
+ Sends it to the components' frameRender methods in layer order
+ to create subframes & composite them into the final frame.
+ The resulting frames are collected in the renderQueue
+ '''
while not self.stopped:
- i = self.compositeQueue.get()
+ audioI = self.compositeQueue.get()
+ bgI = int(audioI / self.sampleSize)
frame = None
for compNo, comp in reversed(list(enumerate(self.components))):
if compNo in self.staticComponents and \
self.staticComponents[compNo] is not None:
- if frame is None:
+ # static component
+ if frame is None: # bottom-most layer
frame = self.staticComponents[compNo]
else:
frame = Image.alpha_composite(
- frame, self.staticComponents[compNo])
+ frame, self.staticComponents[compNo]
+ )
else:
- if frame is None:
- frame = comp.frameRender(compNo, i[0], i[1])
+ # animated component
+ if frame is None: # bottom-most layer
+ frame = comp.frameRender(compNo, bgI)
else:
frame = Image.alpha_composite(
- frame, comp.frameRender(compNo, i[0], i[1]))
+ frame, comp.frameRender(compNo, bgI)
+ )
- self.renderQueue.put([i[0], frame])
+ self.renderQueue.put([audioI, frame])
self.compositeQueue.task_done()
def renderDispatch(self):
+ '''
+ Places audio data indices in the compositeQueue, to be used
+ by a renderNode later. All indices are multiples of self.sampleSize
+ sampleSize * frameNo = audioI, AKA audio data starting at frameNo
+ '''
print('Dispatching Frames for Compositing...')
- for i in range(0, len(self.completeAudioArray), self.sampleSize):
- self.compositeQueue.put([i, self.bgI])
- # increment tracked video frame for next iteration
- self.bgI += 1
+ for audioI in range(0, len(self.completeAudioArray), self.sampleSize):
+ self.compositeQueue.put(audioI)
def previewDispatch(self):
- background = Image.new("RGBA", (1920, 1080), (0, 0, 0, 0))
+ '''
+ Grabs frames from the previewQueue, adds them to the checkerboard
+ and emits a final QImage to the MainWindow for the live preview
+ '''
+ background = FloodFrame(1920, 1080, (0, 0, 0, 0))
background.paste(Image.open(os.path.join(
self.core.wd, "background.png")))
background = background.resize((self.width, self.height))
while not self.stopped:
- i = self.previewQueue.get()
- if time.time() - self.lastPreview >= 0.06 or i[0] == 0:
- image = copy(background)
- image = Image.alpha_composite(image, i[1])
- self._image = ImageQt(image)
- self.imageCreated.emit(QtGui.QImage(self._image))
+ audioI, frame = self.previewQueue.get()
+ if time.time() - self.lastPreview >= 0.06 or audioI == 0:
+ image = Image.alpha_composite(background.copy(), frame)
+ self.imageCreated.emit(ImageQt(image))
self.lastPreview = time.time()
self.previewQueue.task_done()
@@ -99,7 +115,6 @@ class Worker(QtCore.QObject):
self.reset()
- self.bgI = 0 # tracked video frame
self.width = int(self.core.settings.value('outputWidth'))
self.height = int(self.core.settings.value('outputHeight'))
progressBarValue = 0
@@ -194,8 +209,8 @@ class Worker(QtCore.QObject):
)
if properties and 'static' in properties:
- self.staticComponents[compNo] = copy(
- comp.frameRender(compNo, 0, 0))
+ self.staticComponents[compNo] = \
+ comp.frameRender(compNo, 0).copy()
self.progressBarUpdate.emit(100)
# Create ffmpeg pipe and queues for frames
@@ -231,9 +246,10 @@ class Worker(QtCore.QObject):
pStr = "Exporting video..."
self.progressBarSetText.emit(pStr)
if not self.canceled:
- for i in range(0, len(self.completeAudioArray), self.sampleSize):
+ for audioI in range(
+ 0, len(self.completeAudioArray), self.sampleSize):
while True:
- if i in frameBuffer or self.canceled:
+ if audioI in frameBuffer or self.canceled:
# if frame's in buffer, pipe it to ffmpeg
break
# else fetch the next frame & add to the buffer
@@ -244,15 +260,16 @@ class Worker(QtCore.QObject):
break
try:
- self.out_pipe.stdin.write(frameBuffer[i].tobytes())
- self.previewQueue.put([i, frameBuffer[i]])
- del frameBuffer[i]
+ self.out_pipe.stdin.write(frameBuffer[audioI].tobytes())
+ self.previewQueue.put([audioI, frameBuffer[audioI]])
+ del frameBuffer[audioI]
except:
break
# increase progress bar value
- if progressBarValue + 1 <= (i / len(self.completeAudioArray)) \
- * 100:
+ if progressBarValue + 1 <= (
+ audioI / len(self.completeAudioArray)
+ ) * 100:
progressBarValue = numpy.floor(
(i / len(self.completeAudioArray)) * 100)
self.progressBarUpdate.emit(progressBarValue)
--
cgit v1.2.3
From ec0abd190273b7b636c7085d7caed8220ab09172 Mon Sep 17 00:00:00 2001
From: tassaron
Date: Sun, 16 Jul 2017 14:06:11 -0400
Subject: apply complex filters to audio streams from components
tons of sound options could be given now, + installation using setup.py
---
README.md | 21 +++++-----
setup.py | 24 ++++++++---
src/component.py | 5 ++-
src/components/sound.py | 23 ++++++++++-
src/components/sound.ui | 50 +++++++++++++++++++++++
src/components/video.py | 16 +++++++-
src/components/video.ui | 75 +++++++++++++++++++++++++++++++----
src/core.py | 103 ++++++++++++++++++++++++++++++++++++++++--------
src/main.py | 2 +-
src/toolkit.py | 11 ++++--
10 files changed, 283 insertions(+), 47 deletions(-)
(limited to 'README.md')
diff --git a/README.md b/README.md
index 658a22d..9149b4f 100644
--- a/README.md
+++ b/README.md
@@ -1,28 +1,31 @@
audio-visualizer-python
=======================
+**We need a good name that is not as generic as "audio-visualizer-python"!**
-This is a little GUI tool which creates an audio visualization video from an input audio file. Different components can be added and layered to change the resulting video and add images, videos, gradients, text, etc. The component setup can be saved as a Project and exporting can be automated using commandline options.
+This is a little GUI tool which creates an audio visualization video from an input audio file. Different components can be added and layered to change the resulting video and add images, videos, gradients, text, etc. Encoding options can be changed with a variety of different output containers.
-The program works on Linux, macOS, and Windows. If you encounter problems running it or have other bug reports or features that you wish to see implemented, please fork the project and send me a pull request and/or file an issue on this project.
+Projects can be created from the GUI and used in commandline mode for easy automation of video production. Create a template project named `template` with your typical visualizers and watermarks, and add text to the top layer from commandline:
+`avp template -c 99 text "title=Episode 371" -i /this/weeks/audio.ogg -o out`
-I also need a good name that is not as generic as "audio-visualizer-python"!
+For more information use `avp --help` or for help with a particular component use `avp -c 0 componentName help`.
+
+The program works on Linux, macOS, and Windows. If you encounter problems running it or have other bug reports or features that you wish to see implemented, please fork the project and submit a pull request and/or file an issue on this project.
Dependencies
------------
-Python 3, PyQt5, pillow-simd, numpy, and ffmpeg 3.3
+Python 3.4, FFmpeg 3.3, PyQt5, Pillow-SIMD, NumPy
-**Note:** Pillow may be used as a drop-in replacement for Pillow-SIMD if problems are encountered installing. However this will result in much slower video export times.
+**Note:** Pillow may be used as a drop-in replacement for Pillow-SIMD if problems are encountered installing. However this will result in much slower video export times. For help troubleshooting installation problems, the * For any problems with installing Pillow-SIMD, see the [Pillow installation guide](http://pillow.readthedocs.io/en/3.1.x/installation.html).
Installation
------------
### Manual installation on Ubuntu 16.04
* Install pip: `sudo apt-get install python3-pip`
-* Install [prerequisites to compile Pillow](http://pillow.readthedocs.io/en/3.1.x/installation.html#building-on-linux):`sudo apt-get install python3-dev python3-setuptools libtiff5-dev libjpeg8-dev zlib1g-dev libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev python-tk`
-* Prerequisites on **Fedora**:`sudo dnf install python3-devel redhat-rpm-config libtiff-devel libjpeg-devel libzip-devel freetype-devel lcms2-devel libwebp-devel tcl-devel tk-devel`
-* Install dependencies from PyPI: `sudo pip3 install pyqt5 numpy pillow-simd`
+* If Pillow is installed, it must be removed. Nothing should break because Pillow-SIMD is simply a drop-in replacement with better performance.
+* Download audio-visualizer-python from this repository and run `sudo pip3 install .` in this directory
* Install `ffmpeg` from the [website](http://ffmpeg.org/) or from a PPA (e.g. [https://launchpad.net/~jonathonf/+archive/ubuntu/ffmpeg-3](https://launchpad.net/~jonathonf/+archive/ubuntu/ffmpeg-3)). NOTE: `ffmpeg` in the standard repos is too old (v2.8). Old versions and `avconv` may be used but full functionality is only guaranteed with `ffmpeg` 3.3 or higher.
-Download audio-visualizer-python from this repository and run it with `python3 main.py`.
+Run the program with `avp` or `python3 -m avpython`
### Manual installation on Windows
* **Warning:** [Compiling Pillow is difficult on Windows](http://pillow.readthedocs.io/en/3.1.x/installation.html#building-on-windows) and required for the best experience.
diff --git a/setup.py b/setup.py
index 4ef6077..71dc51f 100644
--- a/setup.py
+++ b/setup.py
@@ -12,11 +12,25 @@ def package_files(directory):
setup(
name='audio_visualizer_python',
- version='2.0.0',
- description='A little GUI tool to create audio visualization " \
- "videos out of audio files',
+ version='2.0.0rc1',
+ url='https://github.com/djfun/audio-visualizer-python/tree/feature-newgui',
license='MIT',
- url='https://github.com/djfun/audio-visualizer-python',
+ description='Create audio visualization videos from a GUI or commandline',
+ long_description="Create customized audio visualization videos and save "
+ "them as Projects to continue editing later. Different components can "
+ "be added and layered to add visualizers, images, videos, gradients, "
+ "text, etc. Use Projects created in the GUI with commandline mode to "
+ "automate your video production workflow without learning any complex "
+ "syntax.",
+ classifiers=[
+ 'Development Status :: 4 - Beta',
+ 'License :: OSI Approved :: MIT License',
+ 'Programming Language :: Python :: 3 :: Only',
+ 'Intended Audience :: End Users/Desktop',
+ 'Topic :: Multimedia :: Video :: Non-Linear Editor',
+ ],
+ keywords=['visualizer', 'visualization', 'commandline video',
+ 'video editor', 'ffmpeg', 'podcast']
packages=[
'avpython',
'avpython.components'
@@ -25,7 +39,7 @@ setup(
package_data={
'avpython': package_files('src'),
},
- install_requires=['olefile', 'Pillow-SIMD', 'PyQt5', 'numpy'],
+ install_requires=['Pillow-SIMD', 'PyQt5', 'numpy'],
entry_points={
'gui_scripts': [
'avp = avpython.main:main'
diff --git a/src/component.py b/src/component.py
index 2b297d1..adb170e 100644
--- a/src/component.py
+++ b/src/component.py
@@ -178,8 +178,9 @@ class Component(QtCore.QObject):
The first element can be:
- A string (path to audio file),
- Or an object that returns audio data through a pipe
- The second element must be a dictionary of ffmpeg parameters
- to apply to the input stream.
+ The second element must be a dictionary of ffmpeg filters/options
+ to apply to the input stream. See the filter docs for ideas:
+ https://ffmpeg.org/ffmpeg-filters.html
\'''
@classmethod
diff --git a/src/components/sound.py b/src/components/sound.py
index 4a5714b..bd7d002 100644
--- a/src/components/sound.py
+++ b/src/components/sound.py
@@ -17,12 +17,18 @@ class Component(Component):
page.lineEdit_sound.textChanged.connect(self.update)
page.pushButton_sound.clicked.connect(self.pickSound)
+ page.checkBox_chorus.stateChanged.connect(self.update)
+ page.spinBox_delay.valueChanged.connect(self.update)
+ page.spinBox_volume.valueChanged.connect(self.update)
self.page = page
return page
def update(self):
self.sound = self.page.lineEdit_sound.text()
+ self.delay = self.page.spinBox_delay.value()
+ self.volume = self.page.spinBox_volume.value()
+ self.chorus = self.page.checkBox_chorus.isChecked()
super().update()
def previewRender(self, previewWorker):
@@ -46,7 +52,16 @@ class Component(Component):
return "The audio file selected no longer exists!"
def audio(self):
- return (self.sound, {})
+ params = {}
+ if self.delay != 0.0:
+ params['adelay'] = '=%s' % str(int(self.delay * 1000.00))
+ if self.chorus:
+ params['chorus'] = \
+ '=0.5:0.9:50|60|40:0.4|0.32|0.3:0.25|0.4|0.3:2|2.3|1.3'
+ if self.volume != 1.0:
+ params['volume'] = '=%s:replaygain_noclip=0' % str(self.volume)
+
+ return (self.sound, params)
def pickSound(self):
sndDir = self.settings.value("componentDir", os.path.expanduser("~"))
@@ -66,10 +81,16 @@ class Component(Component):
def loadPreset(self, pr, presetName=None):
super().loadPreset(pr, presetName)
self.page.lineEdit_sound.setText(pr['sound'])
+ self.page.checkBox_chorus.setChecked(pr['chorus'])
+ self.page.spinBox_delay.setValue(pr['delay'])
+ self.page.spinBox_volume.setValue(pr['volume'])
def savePreset(self):
return {
'sound': self.sound,
+ 'chorus': self.chorus,
+ 'delay': self.delay,
+ 'volume': self.volume,
}
def commandHelp(self):
diff --git a/src/components/sound.ui b/src/components/sound.ui
index 5fc00c1..4c11332 100644
--- a/src/components/sound.ui
+++ b/src/components/sound.ui
@@ -87,6 +87,29 @@
-
+
-
+
+
+ Volume
+
+
+
+ -
+
+
+ x
+
+
+ 10.000000000000000
+
+
+ 0.100000000000000
+
+
+ 1.000000000000000
+
+
+
-
@@ -100,6 +123,33 @@
+ -
+
+
+ Delay
+
+
+
+ -
+
+
+ s
+
+
+ 9999999.990000000223517
+
+
+ 0.500000000000000
+
+
+
+ -
+
+
+ Chorus
+
+
+
-
diff --git a/src/components/video.py b/src/components/video.py
index 0b93293..e1f182c 100644
--- a/src/components/video.py
+++ b/src/components/video.py
@@ -127,6 +127,7 @@ class Component(Component):
page.checkBox_distort.stateChanged.connect(self.update)
page.checkBox_useAudio.stateChanged.connect(self.update)
page.spinBox_scale.valueChanged.connect(self.update)
+ page.spinBox_volume.valueChanged.connect(self.update)
page.spinBox_x.valueChanged.connect(self.update)
page.spinBox_y.valueChanged.connect(self.update)
@@ -139,9 +140,17 @@ class Component(Component):
self.useAudio = self.page.checkBox_useAudio.isChecked()
self.distort = self.page.checkBox_distort.isChecked()
self.scale = self.page.spinBox_scale.value()
+ self.volume = self.page.spinBox_volume.value()
self.xPosition = self.page.spinBox_x.value()
self.yPosition = self.page.spinBox_y.value()
+ if self.useAudio:
+ self.page.label_volume.setEnabled(True)
+ self.page.spinBox_volume.setEnabled(True)
+ else:
+ self.page.label_volume.setEnabled(False)
+ self.page.spinBox_volume.setEnabled(False)
+
super().update()
def previewRender(self, previewWorker):
@@ -193,7 +202,10 @@ class Component(Component):
self.badAudio = False
def audio(self):
- return (self.videoPath, {'map': '-v'})
+ params = {}
+ if self.volume != 1.0:
+ params['volume'] = '=%s:replaygain_noclip=0' % str(self.volume)
+ return (self.videoPath, params)
def preFrameRender(self, **kwargs):
super().preFrameRender(**kwargs)
@@ -222,6 +234,7 @@ class Component(Component):
self.page.checkBox_useAudio.setChecked(pr['useAudio'])
self.page.checkBox_distort.setChecked(pr['distort'])
self.page.spinBox_scale.setValue(pr['scale'])
+ self.page.spinBox_volume.setValue(pr['volume'])
self.page.spinBox_x.setValue(pr['x'])
self.page.spinBox_y.setValue(pr['y'])
@@ -233,6 +246,7 @@ class Component(Component):
'useAudio': self.useAudio,
'distort': self.distort,
'scale': self.scale,
+ 'volume': self.volume,
'x': self.xPosition,
'y': self.yPosition,
}
diff --git a/src/components/video.ui b/src/components/video.ui
index 97b7d6f..08d15d3 100644
--- a/src/components/video.ui
+++ b/src/components/video.ui
@@ -10,6 +10,18 @@
197
+
+
+ 0
+ 0
+
+
+
+
+ 0
+ 197
+
+
Form
@@ -189,13 +201,6 @@
- -
-
-
- Use Audio
-
-
-
-
@@ -247,6 +252,62 @@
+ -
+
+
-
+
+
+ Use Audio
+
+
+
+ -
+
+
+ Volume
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ x
+
+
+ 0.000000000000000
+
+
+ 10.000000000000000
+
+
+ 0.100000000000000
+
+
+ 1.000000000000000
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
-
diff --git a/src/core.py b/src/core.py
index 4c12209..324b04f 100644
--- a/src/core.py
+++ b/src/core.py
@@ -468,7 +468,8 @@ class Core:
'''
Constructs the major ffmpeg command used to export the video
'''
- duration = str(duration)
+ safeDuration = "{0:.3f}".format(duration - 0.05) # used by filters
+ duration = "{0:.3f}".format(duration + 0.1) # used by input sources
# Test if user has libfdk_aac
encoders = toolkit.checkOutput(
@@ -526,35 +527,99 @@ class Core:
'-i', inputFile
]
+ # Add extra audio inputs and any needed avfilters
+ # NOTE: Global filters are currently hard-coded here for debugging use
+ globalFilters = 0 # increase to add global filters
extraAudio = [
comp.audio() for comp in self.selectedComponents
if 'audio' in comp.properties()
]
- if extraAudio:
- unwantedVideoStreams = []
- for streamNo, params in enumerate(extraAudio):
+ if extraAudio or globalFilters > 0:
+ # Add -i options for extra input files
+ extraFilters = {}
+ for streamNo, params in enumerate(reversed(extraAudio)):
extraInputFile, params = params
ffmpegCommand.extend([
- '-t', duration,
+ '-t', safeDuration,
'-i', extraInputFile
])
- if 'map' in params and params['map'] == '-v':
- # a video stream to remove
- unwantedVideoStreams.append(streamNo + 1)
+ # Construct dataset of extra filters we'll need to add later
+ for ffmpegFilter in params:
+ if streamNo + 2 not in extraFilters:
+ extraFilters[streamNo + 2] = []
+ extraFilters[streamNo + 2].append((
+ ffmpegFilter, params[ffmpegFilter]
+ ))
+
+ # Start creating avfilters!
+ extraFilterCommand = []
+
+ if globalFilters <= 0:
+ # Dictionary of last-used tmp labels for a given stream number
+ tmpInputs = {streamNo: -1 for streamNo in extraFilters}
+ else:
+ # Insert blank entries for global filters into extraFilters
+ # so the per-stream filters know what input to source later
+ for streamNo in range(len(extraAudio), 0, -1):
+ if streamNo + 1 not in extraFilters:
+ extraFilters[streamNo + 1] = []
+ # Also filter the primary audio track
+ extraFilters[1] = []
+ tmpInputs = {
+ streamNo: globalFilters - 1
+ for streamNo in extraFilters
+ }
+
+ # Add the global filters!
+ # NOTE: list length must = globalFilters, currently hardcoded
+ if tmpInputs:
+ extraFilterCommand.extend([
+ '[%s:a] ashowinfo [%stmp0]' % (
+ str(streamNo),
+ str(streamNo)
+ )
+ for streamNo in tmpInputs
+ ])
+
+ # Now add the per-stream filters!
+ for streamNo, paramList in extraFilters.items():
+ for param in paramList:
+ source = '[%s:a]' % str(streamNo) \
+ if tmpInputs[streamNo] == -1 else \
+ '[%stmp%s]' % (
+ str(streamNo), str(tmpInputs[streamNo])
+ )
+ tmpInputs[streamNo] = tmpInputs[streamNo] + 1
+ extraFilterCommand.append(
+ '%s %s%s [%stmp%s]' % (
+ source, param[0], param[1], str(streamNo),
+ str(tmpInputs[streamNo])
+ )
+ )
- if unwantedVideoStreams:
- ffmpegCommand.extend(['-map', '0'])
- for streamNo in unwantedVideoStreams:
- ffmpegCommand.extend([
- '-map', '-%s:v' % str(streamNo)
- ])
+ # Join all the filters together and combine into 1 stream
+ extraFilterCommand = "; ".join(extraFilterCommand) + '; ' \
+ if tmpInputs else ''
ffmpegCommand.extend([
'-filter_complex',
- 'amix=inputs=%s:duration=first:dropout_transition=3' % str(
- len(extraAudio) + 1
+ extraFilterCommand +
+ '%s amix=inputs=%s:duration=first [a]'
+ % (
+ "".join([
+ '[%stmp%s]' % (str(i), tmpInputs[i])
+ if i in extraFilters else '[%s:a]' % str(i)
+ for i in range(1, len(extraAudio) + 2)
+ ]),
+ str(len(extraAudio) + 1)
),
])
+ # Only map audio from the filters, and video from the pipe
+ ffmpegCommand.extend([
+ '-map', '0:v',
+ '-map', '[a]',
+ ])
+
ffmpegCommand.extend([
# OUTPUT
'-vcodec', vencoder,
@@ -573,7 +638,7 @@ class Core:
ffmpegCommand.append(outputFile)
return ffmpegCommand
- def readAudioFile(self, filename, parent):
+ def getAudioDuration(self, filename):
command = [self.FFMPEG_BIN, '-i', filename]
try:
@@ -588,6 +653,10 @@ class Core:
d = d.split(' ')[3]
d = d.split(':')
duration = float(d[0])*3600 + float(d[1])*60 + float(d[2])
+ return duration
+
+ def readAudioFile(self, filename, parent):
+ duration = self.getAudioDuration(filename)
command = [
self.FFMPEG_BIN,
diff --git a/src/main.py b/src/main.py
index 317237c..6a9a25e 100644
--- a/src/main.py
+++ b/src/main.py
@@ -12,7 +12,7 @@ def main():
wd = os.path.dirname(os.path.realpath(__file__))
# make local imports work everywhere
- sys.path.append(wd)
+ sys.path.insert(0, wd)
mode = 'GUI'
if len(sys.argv) > 2:
diff --git a/src/toolkit.py b/src/toolkit.py
index 589d8e6..5493f37 100644
--- a/src/toolkit.py
+++ b/src/toolkit.py
@@ -13,11 +13,14 @@ def badName(name):
return any([letter in string.punctuation for letter in name])
+def alphabetizeDict(dictionary):
+ '''Alphabetizes a dict into OrderedDict '''
+ return OrderedDict(sorted(dictionary.items(), key=lambda t: t[0]))
+
+
def presetToString(dictionary):
- '''Alphabetizes a dict into OrderedDict & returns string repr'''
- return repr(
- OrderedDict(sorted(dictionary.items(), key=lambda t: t[0]))
- )
+ '''Returns string repr of a preset'''
+ return repr(alphabetizeDict(dictionary))
def presetFromString(string):
--
cgit v1.2.3
From b1713d38fa91e39f142b0c234b6405229aa149e1 Mon Sep 17 00:00:00 2001
From: tassaron
Date: Mon, 17 Jul 2017 22:07:33 -0400
Subject: combined toolkit.py & frame.py into toolkit package
---
README.md | 2 +-
src/__main__.py | 4 +-
src/component.py | 31 -----------
src/components/color.py | 9 +--
src/components/image.py | 2 +-
src/components/original.py | 7 ++-
src/components/sound.py | 2 +-
src/components/text.py | 7 ++-
src/components/video.py | 2 +-
src/core.py | 2 +-
src/frame.py | 66 ----------------------
src/preview_thread.py | 2 +-
src/toolkit.py | 99 ---------------------------------
src/toolkit/__init__.py | 1 +
src/toolkit/common.py | 133 +++++++++++++++++++++++++++++++++++++++++++++
src/toolkit/frame.py | 66 ++++++++++++++++++++++
src/video_thread.py | 2 +-
17 files changed, 223 insertions(+), 214 deletions(-)
delete mode 100644 src/frame.py
delete mode 100644 src/toolkit.py
create mode 100644 src/toolkit/__init__.py
create mode 100644 src/toolkit/common.py
create mode 100644 src/toolkit/frame.py
(limited to 'README.md')
diff --git a/README.md b/README.md
index 9149b4f..5f4e1e7 100644
--- a/README.md
+++ b/README.md
@@ -15,7 +15,7 @@ Dependencies
------------
Python 3.4, FFmpeg 3.3, PyQt5, Pillow-SIMD, NumPy
-**Note:** Pillow may be used as a drop-in replacement for Pillow-SIMD if problems are encountered installing. However this will result in much slower video export times. For help troubleshooting installation problems, the * For any problems with installing Pillow-SIMD, see the [Pillow installation guide](http://pillow.readthedocs.io/en/3.1.x/installation.html).
+**Note:** Pillow may be used as a drop-in replacement for Pillow-SIMD if problems are encountered installing. However this will result in much slower video export times. For help installing Pillow-SIMD, see the [Pillow installation guide](http://pillow.readthedocs.io/en/3.1.x/installation.html).
Installation
------------
diff --git a/src/__main__.py b/src/__main__.py
index a68739e..3babeae 100644
--- a/src/__main__.py
+++ b/src/__main__.py
@@ -1,3 +1,5 @@
+# Allows for launching with python3 -m avpython
+
from avpython.main import main
-main()
\ No newline at end of file
+main()
diff --git a/src/component.py b/src/component.py
index adb170e..7842bd6 100644
--- a/src/component.py
+++ b/src/component.py
@@ -112,37 +112,6 @@ class Component(QtCore.QObject):
def commandHelp(self):
'''Print help text for this Component's commandline arguments'''
- def pickColor(self):
- '''
- Use color picker to get color input from the user,
- and return this as an RGB string and QPushButton stylesheet.
- In a subclass apply stylesheet to any color selection widgets
- '''
- dialog = QtWidgets.QColorDialog()
- dialog.setOption(QtWidgets.QColorDialog.ShowAlphaChannel, True)
- color = dialog.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()
- 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)
-
def loadUi(self, filename):
return uic.loadUi(os.path.join(self.core.componentsPath, filename))
diff --git a/src/components/color.py b/src/components/color.py
index ef4dd95..8d2526d 100644
--- a/src/components/color.py
+++ b/src/components/color.py
@@ -5,7 +5,8 @@ from PIL.ImageQt import ImageQt
import os
from component import Component
-from frame import BlankFrame, FloodFrame, FramePainter, PaintColor
+from toolkit.frame import BlankFrame, FloodFrame, FramePainter, PaintColor
+from toolkit import rgbFromString, pickColor
class Component(Component):
@@ -76,8 +77,8 @@ class Component(Component):
return page
def update(self):
- self.color1 = self.RGBFromString(self.page.lineEdit_color1.text())
- self.color2 = self.RGBFromString(self.page.lineEdit_color2.text())
+ self.color1 = rgbFromString(self.page.lineEdit_color1.text())
+ self.color2 = rgbFromString(self.page.lineEdit_color2.text())
self.x = self.page.spinBox_x.value()
self.y = self.page.spinBox_y.value()
self.sizeWidth = self.page.spinBox_width.value()
@@ -229,7 +230,7 @@ class Component(Component):
}
def pickColor(self, num):
- RGBstring, btnStyle = super().pickColor()
+ RGBstring, btnStyle = pickColor()
if not RGBstring:
return
if num == 1:
diff --git a/src/components/image.py b/src/components/image.py
index c0d1c0d..7f3f610 100644
--- a/src/components/image.py
+++ b/src/components/image.py
@@ -3,7 +3,7 @@ from PyQt5 import QtGui, QtCore, QtWidgets
import os
from component import Component
-from frame import BlankFrame
+from toolkit.frame import BlankFrame
class Component(Component):
diff --git a/src/components/original.py b/src/components/original.py
index f5776a4..586204a 100644
--- a/src/components/original.py
+++ b/src/components/original.py
@@ -7,7 +7,8 @@ import time
from copy import copy
from component import Component
-from frame import BlankFrame
+from toolkit.frame import BlankFrame
+from toolkit import rgbFromString, pickColor
class Component(Component):
@@ -48,7 +49,7 @@ class Component(Component):
def update(self):
self.layout = self.page.comboBox_visLayout.currentIndex()
- self.visColor = self.RGBFromString(self.page.lineEdit_visColor.text())
+ self.visColor = rgbFromString(self.page.lineEdit_visColor.text())
self.scale = self.page.spinBox_scale.value()
self.y = self.page.spinBox_y.value()
@@ -116,7 +117,7 @@ class Component(Component):
self.visColor, self.layout)
def pickColor(self):
- RGBstring, btnStyle = super().pickColor()
+ RGBstring, btnStyle = pickColor()
if not RGBstring:
return
self.page.lineEdit_visColor.setText(RGBstring)
diff --git a/src/components/sound.py b/src/components/sound.py
index bd7d002..5b06405 100644
--- a/src/components/sound.py
+++ b/src/components/sound.py
@@ -2,7 +2,7 @@ from PyQt5 import QtGui, QtCore, QtWidgets
import os
from component import Component
-from frame import BlankFrame
+from toolkit.frame import BlankFrame
class Component(Component):
diff --git a/src/components/text.py b/src/components/text.py
index 19460e5..fc3ef5f 100644
--- a/src/components/text.py
+++ b/src/components/text.py
@@ -4,7 +4,8 @@ from PyQt5 import QtGui, QtCore, QtWidgets
import os
from component import Component
-from frame import FramePainter
+from toolkit.frame import FramePainter
+from toolkit import rgbFromString, pickColor
class Component(Component):
@@ -64,7 +65,7 @@ class Component(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.textColor = rgbFromString(
self.page.lineEdit_textColor.text())
btnStyle = "QPushButton { background-color : %s; outline: none; }" \
% QColor(*self.textColor).name()
@@ -146,7 +147,7 @@ class Component(Component):
return image.finalize()
def pickColor(self):
- RGBstring, btnStyle = super().pickColor()
+ RGBstring, btnStyle = pickColor()
if not RGBstring:
return
self.page.lineEdit_textColor.setText(RGBstring)
diff --git a/src/components/video.py b/src/components/video.py
index 9e3db30..a9f334e 100644
--- a/src/components/video.py
+++ b/src/components/video.py
@@ -7,7 +7,7 @@ import threading
from queue import PriorityQueue
from component import Component, BadComponentInit
-from frame import BlankFrame
+from toolkit.frame import BlankFrame
from toolkit import openPipe, checkOutput
diff --git a/src/core.py b/src/core.py
index a0a028b..07c1f71 100644
--- a/src/core.py
+++ b/src/core.py
@@ -11,7 +11,7 @@ from importlib import import_module
from PyQt5.QtCore import QStandardPaths
import toolkit
-from frame import Frame
+from toolkit.frame import Frame
import video_thread
diff --git a/src/frame.py b/src/frame.py
deleted file mode 100644
index cddb611..0000000
--- a/src/frame.py
+++ /dev/null
@@ -1,66 +0,0 @@
-'''
- Common tools for drawing compatible frames in a Component's frameRender()
-'''
-from PyQt5 import QtGui
-from PIL import Image
-from PIL.ImageQt import ImageQt
-import sys
-import os
-
-
-class Frame:
- '''Controller class for all frames.'''
-
-
-class FramePainter(QtGui.QPainter):
- '''
- A QPainter for a blank frame, which can be converted into a
- Pillow image with finalize()
- '''
- def __init__(self, width, height):
- image = BlankFrame(width, height)
- self.image = QtGui.QImage(ImageQt(image))
- super().__init__(self.image)
-
- def setPen(self, RgbTuple):
- super().setPen(PaintColor(*RgbTuple))
-
- def finalize(self):
- self.end()
- imBytes = self.image.bits().asstring(self.image.byteCount())
-
- return Image.frombytes(
- 'RGBA', (self.image.width(), self.image.height()), imBytes
- )
-
-
-class PaintColor(QtGui.QColor):
- '''Reverse the painter colour if the hardware stores RGB values backward'''
- def __init__(self, r, g, b, a=255):
- if sys.byteorder == 'big':
- super().__init__(r, g, b, a)
- else:
- super().__init__(b, g, r, a)
-
-
-def FloodFrame(width, height, RgbaTuple):
- return Image.new("RGBA", (width, height), RgbaTuple)
-
-
-def BlankFrame(width, height):
- '''The base frame used by each component to start drawing.'''
- return FloodFrame(width, height, (0, 0, 0, 0))
-
-
-def Checkerboard(width, height):
- '''
- A checkerboard to represent transparency to the user.
- TODO: Would be cool to generate this image with numpy instead.
- '''
- image = FloodFrame(1920, 1080, (0, 0, 0, 0))
- image.paste(Image.open(
- os.path.join(Frame.core.wd, "background.png")),
- (0, 0)
- )
- image = image.resize((width, height))
- return image
diff --git a/src/preview_thread.py b/src/preview_thread.py
index 6c33aff..c28e048 100644
--- a/src/preview_thread.py
+++ b/src/preview_thread.py
@@ -9,7 +9,7 @@ from PIL.ImageQt import ImageQt
from queue import Queue, Empty
import os
-from frame import Checkerboard
+from toolkit.frame import Checkerboard
class Worker(QtCore.QObject):
diff --git a/src/toolkit.py b/src/toolkit.py
deleted file mode 100644
index 5493f37..0000000
--- a/src/toolkit.py
+++ /dev/null
@@ -1,99 +0,0 @@
-'''
- Common functions
-'''
-import string
-import os
-import sys
-import subprocess
-from collections import OrderedDict
-
-
-def badName(name):
- '''Returns whether a name contains non-alphanumeric chars'''
- return any([letter in string.punctuation for letter in name])
-
-
-def alphabetizeDict(dictionary):
- '''Alphabetizes a dict into OrderedDict '''
- return OrderedDict(sorted(dictionary.items(), key=lambda t: t[0]))
-
-
-def presetToString(dictionary):
- '''Returns string repr of a preset'''
- return repr(alphabetizeDict(dictionary))
-
-
-def presetFromString(string):
- '''Turns a string repr of OrderedDict into a regular dict'''
- return dict(eval(string))
-
-
-def appendUppercase(lst):
- for form, i in zip(lst, range(len(lst))):
- lst.append(form.upper())
- return lst
-
-
-def hideCmdWin(func):
- ''' Stops CMD window from appearing on Windows.
- Adapted from here: http://code.activestate.com/recipes/409002/
- '''
- def decorator(commandList, **kwargs):
- if sys.platform == 'win32':
- startupinfo = subprocess.STARTUPINFO()
- startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
- kwargs['startupinfo'] = startupinfo
- return func(commandList, **kwargs)
- return decorator
-
-
-@hideCmdWin
-def checkOutput(commandList, **kwargs):
- return subprocess.check_output(commandList, **kwargs)
-
-
-@hideCmdWin
-def openPipe(commandList, **kwargs):
- return subprocess.Popen(commandList, **kwargs)
-
-
-def disableWhenEncoding(func):
- ''' Blocks calls to a function while the video is being exported
- in MainWindow.
- '''
- def decorator(*args, **kwargs):
- if args[0].encoding:
- return
- else:
- return func(*args, **kwargs)
- return decorator
-
-
-def LoadDefaultSettings(self):
- ''' Runs once at each program start-up. Fills in default settings
- for any settings not found in settings.ini
- '''
- self.resolutions = [
- '1920x1080',
- '1280x720',
- '854x480'
- ]
-
- default = {
- "outputWidth": 1280,
- "outputHeight": 720,
- "outputFrameRate": 30,
- "outputAudioCodec": "AAC",
- "outputAudioBitrate": "192",
- "outputVideoCodec": "H264",
- "outputVideoBitrate": "2500",
- "outputVideoFormat": "yuv420p",
- "outputPreset": "medium",
- "outputFormat": "mp4",
- "outputContainer": "MP4",
- "projectDir": os.path.join(self.dataDir, 'projects'),
- }
-
- for parm, value in default.items():
- if self.settings.value(parm) is None:
- self.settings.setValue(parm, value)
diff --git a/src/toolkit/__init__.py b/src/toolkit/__init__.py
new file mode 100644
index 0000000..3fca275
--- /dev/null
+++ b/src/toolkit/__init__.py
@@ -0,0 +1 @@
+from toolkit.common import *
diff --git a/src/toolkit/common.py b/src/toolkit/common.py
new file mode 100644
index 0000000..e3a1649
--- /dev/null
+++ b/src/toolkit/common.py
@@ -0,0 +1,133 @@
+'''
+ Common functions
+'''
+from PyQt5 import QtWidgets
+import string
+import os
+import sys
+import subprocess
+from collections import OrderedDict
+
+
+def badName(name):
+ '''Returns whether a name contains non-alphanumeric chars'''
+ return any([letter in string.punctuation for letter in name])
+
+
+def alphabetizeDict(dictionary):
+ '''Alphabetizes a dict into OrderedDict '''
+ return OrderedDict(sorted(dictionary.items(), key=lambda t: t[0]))
+
+
+def presetToString(dictionary):
+ '''Returns string repr of a preset'''
+ return repr(alphabetizeDict(dictionary))
+
+
+def presetFromString(string):
+ '''Turns a string repr of OrderedDict into a regular dict'''
+ return dict(eval(string))
+
+
+def appendUppercase(lst):
+ for form, i in zip(lst, range(len(lst))):
+ lst.append(form.upper())
+ return lst
+
+
+def hideCmdWin(func):
+ ''' Stops CMD window from appearing on Windows.
+ Adapted from here: http://code.activestate.com/recipes/409002/
+ '''
+ def decorator(commandList, **kwargs):
+ if sys.platform == 'win32':
+ startupinfo = subprocess.STARTUPINFO()
+ startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
+ kwargs['startupinfo'] = startupinfo
+ return func(commandList, **kwargs)
+ return decorator
+
+
+@hideCmdWin
+def checkOutput(commandList, **kwargs):
+ return subprocess.check_output(commandList, **kwargs)
+
+
+@hideCmdWin
+def openPipe(commandList, **kwargs):
+ return subprocess.Popen(commandList, **kwargs)
+
+
+def disableWhenEncoding(func):
+ ''' Blocks calls to a function while the video is being exported
+ in MainWindow.
+ '''
+ def decorator(*args, **kwargs):
+ if args[0].encoding:
+ return
+ else:
+ return func(*args, **kwargs)
+ return decorator
+
+
+def pickColor():
+ '''
+ Use color picker to get color input from the user,
+ and return this as an RGB string and QPushButton stylesheet.
+ In a subclass apply stylesheet to any color selection widgets
+ '''
+ dialog = QtWidgets.QColorDialog()
+ dialog.setOption(QtWidgets.QColorDialog.ShowAlphaChannel, True)
+ color = dialog.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()
+ return RGBstring, btnStyle
+ else:
+ return None, None
+
+
+def rgbFromString(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)
+
+
+def LoadDefaultSettings(self):
+ ''' Runs once at each program start-up. Fills in default settings
+ for any settings not found in settings.ini
+ '''
+ self.resolutions = [
+ '1920x1080',
+ '1280x720',
+ '854x480'
+ ]
+
+ default = {
+ "outputWidth": 1280,
+ "outputHeight": 720,
+ "outputFrameRate": 30,
+ "outputAudioCodec": "AAC",
+ "outputAudioBitrate": "192",
+ "outputVideoCodec": "H264",
+ "outputVideoBitrate": "2500",
+ "outputVideoFormat": "yuv420p",
+ "outputPreset": "medium",
+ "outputFormat": "mp4",
+ "outputContainer": "MP4",
+ "projectDir": os.path.join(self.dataDir, 'projects'),
+ }
+
+ for parm, value in default.items():
+ if self.settings.value(parm) is None:
+ self.settings.setValue(parm, value)
diff --git a/src/toolkit/frame.py b/src/toolkit/frame.py
new file mode 100644
index 0000000..cddb611
--- /dev/null
+++ b/src/toolkit/frame.py
@@ -0,0 +1,66 @@
+'''
+ Common tools for drawing compatible frames in a Component's frameRender()
+'''
+from PyQt5 import QtGui
+from PIL import Image
+from PIL.ImageQt import ImageQt
+import sys
+import os
+
+
+class Frame:
+ '''Controller class for all frames.'''
+
+
+class FramePainter(QtGui.QPainter):
+ '''
+ A QPainter for a blank frame, which can be converted into a
+ Pillow image with finalize()
+ '''
+ def __init__(self, width, height):
+ image = BlankFrame(width, height)
+ self.image = QtGui.QImage(ImageQt(image))
+ super().__init__(self.image)
+
+ def setPen(self, RgbTuple):
+ super().setPen(PaintColor(*RgbTuple))
+
+ def finalize(self):
+ self.end()
+ imBytes = self.image.bits().asstring(self.image.byteCount())
+
+ return Image.frombytes(
+ 'RGBA', (self.image.width(), self.image.height()), imBytes
+ )
+
+
+class PaintColor(QtGui.QColor):
+ '''Reverse the painter colour if the hardware stores RGB values backward'''
+ def __init__(self, r, g, b, a=255):
+ if sys.byteorder == 'big':
+ super().__init__(r, g, b, a)
+ else:
+ super().__init__(b, g, r, a)
+
+
+def FloodFrame(width, height, RgbaTuple):
+ return Image.new("RGBA", (width, height), RgbaTuple)
+
+
+def BlankFrame(width, height):
+ '''The base frame used by each component to start drawing.'''
+ return FloodFrame(width, height, (0, 0, 0, 0))
+
+
+def Checkerboard(width, height):
+ '''
+ A checkerboard to represent transparency to the user.
+ TODO: Would be cool to generate this image with numpy instead.
+ '''
+ image = FloodFrame(1920, 1080, (0, 0, 0, 0))
+ image.paste(Image.open(
+ os.path.join(Frame.core.wd, "background.png")),
+ (0, 0)
+ )
+ image = image.resize((width, height))
+ return image
diff --git a/src/video_thread.py b/src/video_thread.py
index 60db99f..1f2eaf5 100644
--- a/src/video_thread.py
+++ b/src/video_thread.py
@@ -19,7 +19,7 @@ import time
import signal
from toolkit import openPipe
-from frame import Checkerboard
+from toolkit.frame import Checkerboard
class Worker(QtCore.QObject):
--
cgit v1.2.3
From eadf0e59fda3b442bf660b562c5fc4a70ba18c33 Mon Sep 17 00:00:00 2001
From: tassaron2
Date: Sun, 15 Mar 2020 22:22:50 -0400
Subject: quick update to be somewhat compatible with newer versions of Pillow,
ffmpeg, and Ubuntu
---
README.md | 14 ++++++++------
src/gui/mainwindow.py | 7 ++++---
2 files changed, 12 insertions(+), 9 deletions(-)
(limited to 'README.md')
diff --git a/README.md b/README.md
index 5f4e1e7..c28ac35 100644
--- a/README.md
+++ b/README.md
@@ -19,13 +19,15 @@ Python 3.4, FFmpeg 3.3, PyQt5, Pillow-SIMD, NumPy
Installation
------------
-### Manual installation on Ubuntu 16.04
-* Install pip: `sudo apt-get install python3-pip`
-* If Pillow is installed, it must be removed. Nothing should break because Pillow-SIMD is simply a drop-in replacement with better performance.
-* Download audio-visualizer-python from this repository and run `sudo pip3 install .` in this directory
-* Install `ffmpeg` from the [website](http://ffmpeg.org/) or from a PPA (e.g. [https://launchpad.net/~jonathonf/+archive/ubuntu/ffmpeg-3](https://launchpad.net/~jonathonf/+archive/ubuntu/ffmpeg-3)). NOTE: `ffmpeg` in the standard repos is too old (v2.8). Old versions and `avconv` may be used but full functionality is only guaranteed with `ffmpeg` 3.3 or higher.
+### Manual installation on Ubuntu 20.04
+* Install ffmpeg: `sudo apt install ffmpeg`
+* Install pip: `sudo apt install python3-pip`
+* Install PyQt5: `sudo apt install python3-pyqt5`
+* Install dependencies to compile Pillow-SIMD: `sudo apt install python3-dev libtiff5-dev libjpeg8-dev zlib1g-dev libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev python-tk`
+* Download audio-visualizer-python from this repository and run `pip3 install .` in this directory
+* Run the program with `avp` or `python3 -m avpython`
+* (Optional Note) If using a virtual environmennt, PyQt5 doesn't seem to work when installed from the setup.py. You can use `--system-site-packages --copies` to copy the system site-packages into your venv
-Run the program with `avp` or `python3 -m avpython`
### Manual installation on Windows
* **Warning:** [Compiling Pillow is difficult on Windows](http://pillow.readthedocs.io/en/3.1.x/installation.html#building-on-windows) and required for the best experience.
diff --git a/src/gui/mainwindow.py b/src/gui/mainwindow.py
index 81c5d7c..53a6bd1 100644
--- a/src/gui/mainwindow.py
+++ b/src/gui/mainwindow.py
@@ -320,12 +320,12 @@ class MainWindow(QtWidgets.QMainWindow):
# verify Pillow version
if not self.settings.value("pilMsgShown") \
- and 'post' not in Image.PILLOW_VERSION:
+ and 'post' not in Image.__version__:
self.showMessage(
msg="You are using the standard version of the "
"Python imaging library (Pillow %s). Upgrade "
"to the Pillow-SIMD fork to enable hardware accelerations "
- "and export videos faster." % Image.PILLOW_VERSION
+ "and export videos faster." % Image.__version__
)
self.settings.setValue("pilMsgShown", True)
@@ -336,7 +336,8 @@ class MainWindow(QtWidgets.QMainWindow):
ffmpegVers = checkOutput(
['ffmpeg', '-version'], stderr=f
)
- goodVersion = str(ffmpegVers).split()[2].startswith('3')
+ goodVersion = (str(ffmpegVers).split()[2].startswith('3') or
+ str(ffmpegVers).split()[2].startswith('4'))
except Exception:
goodVersion = False
else:
--
cgit v1.2.3
From a0291e3fcde739355669e0ca40487f0eb8ee82e4 Mon Sep 17 00:00:00 2001
From: tassaron
Date: Wed, 13 Apr 2022 16:20:04 -0400
Subject: update readme with known working versions
---
README.md | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
(limited to 'README.md')
diff --git a/README.md b/README.md
index c28ac35..1dee874 100644
--- a/README.md
+++ b/README.md
@@ -13,13 +13,13 @@ The program works on Linux, macOS, and Windows. If you encounter problems runnin
Dependencies
------------
-Python 3.4, FFmpeg 3.3, PyQt5, Pillow-SIMD, NumPy
+Python 3.10, FFmpeg 4.4.1, PyQt5 (Qt v5.15.3), Pillow-SIMD, NumPy
**Note:** Pillow may be used as a drop-in replacement for Pillow-SIMD if problems are encountered installing. However this will result in much slower video export times. For help installing Pillow-SIMD, see the [Pillow installation guide](http://pillow.readthedocs.io/en/3.1.x/installation.html).
Installation
------------
-### Manual installation on Ubuntu 20.04
+### Manual installation on Ubuntu 22.04
* Install ffmpeg: `sudo apt install ffmpeg`
* Install pip: `sudo apt install python3-pip`
* Install PyQt5: `sudo apt install python3-pyqt5`
@@ -41,8 +41,9 @@ Installation
Download audio-visualizer-python from this repository and run it from the command line with `python main.py`.
-### Manual installation on macOS **[Outdated]**
+### Manual installation on macOS
+* **[Outdated]**: No one has updated these instructions for a while.
* Install [Homebrew](http://brew.sh/)
* Use the following commands to install the needed dependencies:
--
cgit v1.2.3
From b382d0620199e5b456ced30bb8ea713def98b87e Mon Sep 17 00:00:00 2001
From: tassaron
Date: Fri, 22 Apr 2022 20:39:49 -0400
Subject: some work on the readme (install instructions not done yet)
---
README.md | 83 +++++++++++++++++++++++++++++++++------------------------------
1 file changed, 44 insertions(+), 39 deletions(-)
(limited to 'README.md')
diff --git a/README.md b/README.md
index 1dee874..abb9a73 100644
--- a/README.md
+++ b/README.md
@@ -1,52 +1,58 @@
-audio-visualizer-python
-=======================
+# audio-visualizer-python
**We need a good name that is not as generic as "audio-visualizer-python"!**
This is a little GUI tool which creates an audio visualization video from an input audio file. Different components can be added and layered to change the resulting video and add images, videos, gradients, text, etc. Encoding options can be changed with a variety of different output containers.
-Projects can be created from the GUI and used in commandline mode for easy automation of video production. Create a template project named `template` with your typical visualizers and watermarks, and add text to the top layer from commandline:
-`avp template -c 99 text "title=Episode 371" -i /this/weeks/audio.ogg -o out`
+Projects can be created from the GUI and used in commandline mode for easy automation of video production. For more information use `avp --help` or for help with a particular component use `avp -c 0 componentName help`.
-For more information use `avp --help` or for help with a particular component use `avp -c 0 componentName help`.
+The program works on Linux, macOS, and Windows. If you encounter problems running it or have other bug reports or features that you wish to see implemented, please fork the project and submit a pull request and/or file an issue on this project. To gather extra information to help us debug the problem, run `avp -t` and include the text file it creates.
-The program works on Linux, macOS, and Windows. If you encounter problems running it or have other bug reports or features that you wish to see implemented, please fork the project and submit a pull request and/or file an issue on this project.
-Dependencies
-------------
-Python 3.10, FFmpeg 4.4.1, PyQt5 (Qt v5.15.3), Pillow-SIMD, NumPy
+# Examples
+## What the app creates
+* **[YouTube: A day in spring](https://www.youtube.com/watch?v=-M3jR1NuJHM)**
-**Note:** Pillow may be used as a drop-in replacement for Pillow-SIMD if problems are encountered installing. However this will result in much slower video export times. For help installing Pillow-SIMD, see the [Pillow installation guide](http://pillow.readthedocs.io/en/3.1.x/installation.html).
+## Graphical version demo
+* [YouTube: Audio Visualizer demonstration](https://www.youtube.com/watch?v=EVt2ckQs1Yg)
-Installation
-------------
-### Manual installation on Ubuntu 22.04
+## Commandline snippets
+* Create a simple visualization: `avp -c 0 classic -i something.mp3 -o output.mp4`
+* Create the same visualization but with split layout and more extreme fluctuations: `avp -c 0 classic layout=split scale=40 -i something.mp3 -o output.mp4`
+* Create a template project named `template` with your typical visualizers and watermarks using the GUI, then add text to the top layer from commandline: `avp template -c 99 text "title=Episode 371" -i /this/weeks/audio.ogg -o out`
+
+
+# Dependencies
+* Python 3.10
+* FFmpeg 4.4.1
+* PyQt5 (Qt v5.15.3)
+* Pillow
+* NumPy
+* Pytest
+
+
+# Installation
+## Manual installation on Linux (tested on Ubuntu 22.04)
* Install ffmpeg: `sudo apt install ffmpeg`
* Install pip: `sudo apt install python3-pip`
-* Install PyQt5: `sudo apt install python3-pyqt5`
-* Install dependencies to compile Pillow-SIMD: `sudo apt install python3-dev libtiff5-dev libjpeg8-dev zlib1g-dev libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev python-tk`
-* Download audio-visualizer-python from this repository and run `pip3 install .` in this directory
-* Run the program with `avp` or `python3 -m avpython`
-* (Optional Note) If using a virtual environmennt, PyQt5 doesn't seem to work when installed from the setup.py. You can use `--system-site-packages --copies` to copy the system site-packages into your venv
-
-
-### Manual installation on Windows
-* **Warning:** [Compiling Pillow is difficult on Windows](http://pillow.readthedocs.io/en/3.1.x/installation.html#building-on-windows) and required for the best experience.
-* Download and install Python 3.6 from [https://www.python.org/downloads/windows/](https://www.python.org/downloads/windows/)
-* Add Python to your system PATH (it will ask during the installation process).
-* Brave treacherous valley of getting prerequisites to [compile Pillow on Windows](https://www.pypkg.com/pypi/pillow-simd/f/winbuild/README.md). This is necessary because binary builds for Pillow-SIMD are not available.
-* **Alternative:** install Pillow instead of Pillow-SIMD, for which binaries *are* available. However this will result in much slower video export times.
-* Open command prompt and run: `pip install pyqt5 numpy pillow-simd`
-* Download and install ffmpeg from [https://www.ffmpeg.org/download.html](https://www.ffmpeg.org/download.html). You can use the static builds.
-* Add ffmpeg to your system PATH, too. [How to edit the PATH on Windows.](https://www.java.com/en/download/help/path.xml)
+* Download this repo and run `pip3 install --user .` in this directory
+* Run the program with `avp` or `python3 -m avp`
+* **Sidenote:** If using a Python virtual environment, `pip3 install --system-site-packages --copies` may be useful to copy system packages when installing. This can be useful if you have difficulty installing PyQt5 using pip or wish to use the distro's package manager instead.
-Download audio-visualizer-python from this repository and run it from the command line with `python main.py`.
-### Manual installation on macOS
+## Manual installation on Windows
+* Install Python from the Windows Store
+* Add Python to your system PATH (it should ask during the installation process)
+* Download this repo
+* Open command prompt, `cd` into the repo directory, and run: `pip install --user .`
+* Download and install ffmpeg from [https://www.ffmpeg.org/download.html](https://www.ffmpeg.org/download.html). You can use the static builds.
+* Add ffmpeg to the system PATH as well, or copy ffmpeg.exe into the directory with the rest of the app
+* Now run `avp` from a command prompt window to start the app
+
+## Manual installation on macOS
* **[Outdated]**: No one has updated these instructions for a while.
* Install [Homebrew](http://brew.sh/)
* Use the following commands to install the needed dependencies:
-
```
brew install python3
brew install ffmpeg --with-fdk-aac --with-ffplay --with-freetype --with-libass --with-libquvi --with-libvorbis --with-libvpx --with-opus --with-x265
@@ -57,14 +63,13 @@ pip3 install --upgrade pip
pip3 install pillow
pip3 install numpy
```
-
Download audio-visualizer-python from this repository and run it with `python3 main.py`.
-Example
--------
-You can find an example video here:
-[Youtube: A day in spring](https://www.youtube.com/watch?v=-M3jR1NuJHM)
-License
--------
+# Faster Export Times
+* [Pillow-SIMD](https://github.com/uploadcare/pillow-simd) may be used as a drop-in replacement for Pillow if you desire faster video export times, but it must be compiled from source. For help installing dependencies to compile Pillow-SIMD, see the [Pillow installation guide](http://pillow.readthedocs.io/en/3.1.x/installation.html).
+* **Warning:** [Compiling from source is difficult on Windows](http://pillow.readthedocs.io/en/3.1.x/installation.html#building-on-windows).
+
+
+# License
audio-visualizer-python is licensed under the MIT license.
--
cgit v1.2.3
From b4fc89cfbeacb9dc99eba01b0dbfb0023709fe40 Mon Sep 17 00:00:00 2001
From: tassaron
Date: Fri, 29 Apr 2022 00:22:26 -0400
Subject: document keyboard shortcuts. fix "show ffmpeg command"
---
README.md | 24 ++++++++++++++++++++++++
src/gui/mainwindow.py | 8 +++++---
2 files changed, 29 insertions(+), 3 deletions(-)
(limited to 'README.md')
diff --git a/README.md b/README.md
index abb9a73..3ca5305 100644
--- a/README.md
+++ b/README.md
@@ -71,5 +71,29 @@ Download audio-visualizer-python from this repository and run it with `python3 m
* **Warning:** [Compiling from source is difficult on Windows](http://pillow.readthedocs.io/en/3.1.x/installation.html#building-on-windows).
+# Keyboard Shortcuts
+| Key Combo | Effect |
+| ------------------------- | -------------------------------------------------- |
+| Ctrl+S | Save Current Project |
+| Ctrl+A | Save Project As... |
+| Ctrl+O | Open Project |
+| Ctrl+N | New Project (prompts to save current project) |
+| Ctrl+Z | Undo |
+| Ctrl+Shift+Z _or_ Ctrl+Y | Redo |
+| Ctrl+T _or_ Insert | Add Component |
+| Ctrl+R _or_ Delete | Remove Component |
+| Ctrl+Space | Focus Component List |
+| Ctrl+Shift+S | Save Component Preset |
+| Ctrl+Shift+C | Remove Preset from Component |
+| Ctrl+Up | Move Selected Component Up |
+| Ctrl+Down | Move Selected Component Down |
+| Ctrl+Home | Move Selected Component to Top |
+| Ctrl+End | Move Selected Component to Bottom |
+| Ctrl+Shift+U | Open Undo History |
+| Ctrl+Shift+F | Show FFmpeg Command |
+| Ctrl+Alt+Shift+R | Force redraw preview (must use `--debug`) |
+| Ctrl+Alt+Shift+A | Dump MainWindow data into log (must use `--debug`) |
+
+
# License
audio-visualizer-python is licensed under the MIT license.
diff --git a/src/gui/mainwindow.py b/src/gui/mainwindow.py
index da8370d..f60befd 100644
--- a/src/gui/mainwindow.py
+++ b/src/gui/mainwindow.py
@@ -758,15 +758,17 @@ class MainWindow(QtWidgets.QMainWindow):
def showFfmpegCommand(self):
from textwrap import wrap
- from toolkit.ffmpeg import createFfmpegCommand
+ from ..toolkit.ffmpeg import createFfmpegCommand
command = createFfmpegCommand(
self.window.lineEdit_audioFile.text(),
self.window.lineEdit_outputFile.text(),
self.core.selectedComponents
)
- lines = wrap(" ".join(command), 49)
+ command = " ".join(command)
+ log.info(f"FFmpeg command: {command}")
+ lines = wrap(command, 49)
self.showMessage(
- msg="Current FFmpeg command:\n\n %s" % " ".join(lines)
+ msg=f"Current FFmpeg command:\n\n{' '.join(lines)}"
)
def addComponent(self, compPos, moduleIndex):
--
cgit v1.2.3
From 9610631f0299151b5e8cb0b77e2f00ca2084d1c4 Mon Sep 17 00:00:00 2001
From: tassaron
Date: Fri, 29 Apr 2022 23:29:47 -0400
Subject: license may be affected by packaging
https://github.com/djfun/audio-visualizer-python/issues/28#issuecomment-314791421
---
README.md | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
(limited to 'README.md')
diff --git a/README.md b/README.md
index 3ca5305..2c33b56 100644
--- a/README.md
+++ b/README.md
@@ -96,4 +96,6 @@ Download audio-visualizer-python from this repository and run it with `python3 m
# License
-audio-visualizer-python is licensed under the MIT license.
+Source code of audio-visualizer-python is licensed under the MIT license.
+
+Some dependencies of this application are under the GPL license. When packaged with these dependencies, audio-visualizer-python may also be under the terms of this GPL license.
\ No newline at end of file
--
cgit v1.2.3
From d2c88294171c7f16d7caa3e6d368cbc6da779107 Mon Sep 17 00:00:00 2001
From: tassaron
Date: Sat, 30 Apr 2022 23:56:08 -0400
Subject: tested working instructions for Ubuntu 22.04 using the default
environment, Gnome on Wayland, in a clean virtual machine
---
README.md | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
(limited to 'README.md')
diff --git a/README.md b/README.md
index 2c33b56..7d3e17c 100644
--- a/README.md
+++ b/README.md
@@ -31,22 +31,22 @@ The program works on Linux, macOS, and Windows. If you encounter problems runnin
# Installation
-## Manual installation on Linux (tested on Ubuntu 22.04)
+## Manual installation on Ubuntu 22.04
* Install ffmpeg: `sudo apt install ffmpeg`
* Install pip: `sudo apt install python3-pip`
-* Download this repo and run `pip3 install --user .` in this directory
-* Run the program with `avp` or `python3 -m avp`
-* **Sidenote:** If using a Python virtual environment, `pip3 install --system-site-packages --copies` may be useful to copy system packages when installing. This can be useful if you have difficulty installing PyQt5 using pip or wish to use the distro's package manager instead.
+* Install PyQt5: `sudo apt install python3-pyqt5`
+* Download this repo and run `pip install .` in this directory
+* Run the program with `python3 -m avp`
## Manual installation on Windows
* Install Python from the Windows Store
* Add Python to your system PATH (it should ask during the installation process)
* Download this repo
-* Open command prompt, `cd` into the repo directory, and run: `pip install --user .`
+* Open command prompt, `cd` into the repo directory, and run: `pip install .`
* Download and install ffmpeg from [https://www.ffmpeg.org/download.html](https://www.ffmpeg.org/download.html). You can use the static builds.
* Add ffmpeg to the system PATH as well, or copy ffmpeg.exe into the directory with the rest of the app
-* Now run `avp` from a command prompt window to start the app
+* Now run `python3 -m avp` from a command prompt window to start the app
## Manual installation on macOS
--
cgit v1.2.3