diff options
| author | Aeliton G. Silva | 2026-01-12 22:39:55 -0300 |
|---|---|---|
| committer | Aeliton G. Silva | 2026-01-13 04:22:25 -0300 |
| commit | f975144f25d34f97329b2d4e52891061573cea08 (patch) | |
| tree | 226fe223b31af6f217b1dd413629ab2cf26964d4 /src/components/waveform.py | |
| parent | b8703752ffc7768b0275897b3c2a869ff41504e5 (diff) | |
Use pyproject.toml + uv_build
This replaces setup.py by a modern pyproject.toml using uv_build
backend.
Dependencies are being also managed by uv, so to install dependencies
and run the project one can execute:
```
uv sync
uv run pytest # optional
python -m avp
```
To build the both source and binary (wheel) distribution package run:
```
uv build
```
Uv can be installed with `pip install uv`.
The directory structure has been changed to reflect best practices.
- src/* -> src/avp/
- src/tests -> ../tests
Diffstat (limited to 'src/components/waveform.py')
| -rw-r--r-- | src/components/waveform.py | 230 |
1 files changed, 0 insertions, 230 deletions
diff --git a/src/components/waveform.py b/src/components/waveform.py deleted file mode 100644 index 7dc0b99..0000000 --- a/src/components/waveform.py +++ /dev/null @@ -1,230 +0,0 @@ -from PIL import Image -from PyQt6 import QtGui, QtCore, QtWidgets -from PyQt6.QtGui import QColor -import os -import math -import subprocess -import logging - -from ..component import Component -from ..toolkit.frame import BlankFrame, scale -from ..toolkit import checkOutput -from ..toolkit.ffmpeg import ( - openPipe, - closePipe, - getAudioDuration, - FfmpegVideo, - exampleSound, -) - - -log = logging.getLogger("AVP.Components.Waveform") - - -class Component(Component): - name = "Waveform" - version = "1.0.0" - - 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) - - self.trackWidgets( - { - "color": self.page.lineEdit_color, - "mode": self.page.comboBox_mode, - "amplitude": self.page.comboBox_amplitude, - "x": self.page.spinBox_x, - "y": self.page.spinBox_y, - "mirror": self.page.checkBox_mirror, - "scale": self.page.spinBox_scale, - "opacity": self.page.spinBox_opacity, - "compress": self.page.checkBox_compress, - "mono": self.page.checkBox_mono, - }, - colorWidgets={ - "color": self.page.pushButton_color, - }, - relativeWidgets=[ - "x", - "y", - ], - ) - - def previewRender(self): - self.updateChunksize() - frame = self.getPreviewFrame(self.width, self.height) - if not frame: - return BlankFrame(self.width, self.height) - else: - return frame - - def preFrameRender(self, **kwargs): - super().preFrameRender(**kwargs) - self.updateChunksize() - w, h = scale(self.scale, self.width, self.height, str) - self.video = FfmpegVideo( - inputPath=self.audioFile, - filter_=self.makeFfmpegFilter(), - width=w, - height=h, - chunkSize=self.chunkSize, - frameRate=int(self.settings.value("outputFrameRate")), - parent=self.parent, - component=self, - debug=True, - ) - - def frameRender(self, frameNo): - if FfmpegVideo.threadError is not None: - raise FfmpegVideo.threadError - return self.finalizeFrame(self.video.frame(frameNo)) - - def postFrameRender(self): - closePipe(self.video.pipe) - - def getPreviewFrame(self, width, height): - genericPreview = self.settings.value("pref_genericPreview") - startPt = 0 - if not genericPreview: - inputFile = self.parent.lineEdit_audioFile.text() - if not inputFile or not os.path.exists(inputFile): - return - duration = getAudioDuration(inputFile) - if not duration: - return - startPt = duration / 3 - if startPt + 3 > duration: - startPt += startPt - 3 - - command = [ - self.core.FFMPEG_BIN, - "-thread_queue_size", - "512", - "-r", - str(self.settings.value("outputFrameRate")), - "-ss", - "{0:.3f}".format(startPt), - "-i", - self.core.junkStream if genericPreview else inputFile, - "-f", - "image2pipe", - "-pix_fmt", - "rgba", - ] - command.extend(self.makeFfmpegFilter(preview=True, startPt=startPt)) - command.extend( - [ - "-an", - "-s:v", - "%sx%s" % scale(self.scale, self.width, self.height, str), - "-codec:v", - "rawvideo", - "-", - "-frames:v", - "1", - ] - ) - if self.core.logEnabled: - logFilename = os.path.join( - self.core.logDir, "preview_%s.log" % str(self.compPos) - ) - log.debug("Creating ffmpeg log at %s", logFilename) - with open(logFilename, "w") as logf: - logf.write(" ".join(command) + "\n\n") - with open(logFilename, "a") as logf: - pipe = openPipe( - command, - stdin=subprocess.DEVNULL, - stdout=subprocess.PIPE, - stderr=logf, - bufsize=10**8, - ) - else: - pipe = openPipe( - command, - stdin=subprocess.DEVNULL, - stdout=subprocess.PIPE, - stderr=subprocess.DEVNULL, - bufsize=10**8, - ) - byteFrame = pipe.stdout.read(self.chunkSize) - closePipe(pipe) - - frame = self.finalizeFrame(byteFrame) - return frame - - 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: - amplitude = "sqrt" - elif self.amplitude == 3: - amplitude = "cbrt" - hexcolor = QColor(*self.color).name() - opacity = "{0:.1f}".format(self.opacity / 100) - genericPreview = self.settings.value("pref_genericPreview") - if self.mode < 3: - filter_ = ( - "showwaves=" - f'r={str(self.settings.value("outputFrameRate"))}:' - f's={self.settings.value("outputWidth")}x{self.settings.value("outputHeight")}:' - f'mode={self.page.comboBox_mode.currentText().lower() if self.mode != 3 else "p2p"}:' - f"colors={hexcolor}@{opacity}:scale={amplitude}" - ) - 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"colors={hexcolor}@{opacity}" - f":ascale={amplitude}:fscale={'log' if self.mono else 'lin'}" - ) - - baselineHeight = int(self.height * (4 / 1080)) - return [ - "-filter_complex", - f"{exampleSound('wave', extra='') if preview and genericPreview else '[0:a] '}" - f"{'compand=gain=4,' if self.compress else ''}" - f"{'aformat=channel_layouts=mono,' if self.mono and self.mode < 3 else ''}" - f"{filter_}" - f"{', drawbox=x=(iw-w)/2:y=(ih-h)/2:w=iw:h=%s:color=%s@%s' % (baselineHeight, hexcolor, opacity) if self.mode < 2 else ''}" - f"{', hflip' if self.mirror else''}" - " [v1]; " - "[v1] scale=%s:%s%s [v]" - % ( - w, - h, - ", trim=duration=%s" % "{0:.3f}".format(startPt + 3) if preview else "", - ), - "-map", - "[v]", - ] - - def updateChunksize(self): - width, height = scale(self.scale, self.width, self.height, int) - self.chunkSize = 4 * width * height - - def finalizeFrame(self, imageData): - try: - image = Image.frombytes( - "RGBA", - scale(self.scale, self.width, self.height, int), - imageData, - ) - self._image = image - except ValueError: - image = self._image - if self.scale != 100 or self.x != 0 or self.y != 0: - frame = BlankFrame(self.width, self.height) - frame.paste(image, box=(self.x, self.y)) - else: - frame = image - return frame |
