auto
This commit is contained in:
parent
67c922ac10
commit
9d94540d0a
167
assets/gptcrap.md
Normal file
167
assets/gptcrap.md
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
✅ 1. Use Pitch Correction with pitch Element
|
||||||
|
Normally, speeding up audio raises its pitch. To maintain pitch (i.e., keep voices from sounding chipmunky), you can use the pitch plugin (part of the soundtouch or rubberband plugins).
|
||||||
|
|
||||||
|
Example GStreamer Pipeline (CLI):
|
||||||
|
```bash
|
||||||
|
gst-launch-1.0 filesrc location=yourfile.mp3 ! decodebin ! audioconvert ! pitch pitch=1.0 rate=1.5 ! autoaudiosink
|
||||||
|
```
|
||||||
|
|
||||||
|
pitch=1.0 keeps the pitch constant
|
||||||
|
rate=1.5 increases playback speed to 1.5x
|
||||||
|
pitch plugin is from gst-plugins-bad
|
||||||
|
|
||||||
|
✅ 2. Use Higher-Quality Resampling
|
||||||
|
When changing playback rate, audio might need resampling. Use audioresample with better quality settings.
|
||||||
|
```bash
|
||||||
|
gst-launch-1.0 filesrc location=yourfile.mp3 ! decodebin ! audioresample quality=10 ! pitch pitch=1.0 rate=1.5 ! autoaudiosink
|
||||||
|
quality=10 gives better audio fidelity (default is 4)
|
||||||
|
```
|
||||||
|
|
||||||
|
✅ 3. Adjust Buffering to Prevent Glitches
|
||||||
|
Higher playback speeds can stress the pipeline. Use queue elements or increase buffer sizes to smooth things out.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
... ! queue max-size-buffers=0 max-size-bytes=0 max-size-time=500000000 ! ...
|
||||||
|
```
|
||||||
|
✅ 4. Use playbin with Custom Filters (in Code)
|
||||||
|
If you're working in Qt and using GStreamer under the hood, you can’t easily insert these elements into the pipeline via QMediaPlayer. But you can build a custom pipeline using GStreamer directly in C++ or Python.
|
||||||
|
|
||||||
|
Example GStreamer code snippet in Python:
|
||||||
|
|
||||||
|
```python
|
||||||
|
pipeline = Gst.parse_launch(
|
||||||
|
"playbin uri=file:///path/to/file.mp3 audio-filter='pitch pitch=1.0 rate=1.5'"
|
||||||
|
)
|
||||||
|
```
|
||||||
|
In Qt, you'd need to bypass QMediaPlayer and integrate GStreamer directly, or subclass QMediaPlayer and access the backend (advanced).
|
||||||
|
|
||||||
|
✅ 5. Ensure You Have the Right Plugins Installed
|
||||||
|
Make sure you have:
|
||||||
|
|
||||||
|
gstreamer1.0-plugins-bad (for pitch)
|
||||||
|
gstreamer1.0-plugins-base
|
||||||
|
gstreamer1.0-plugins-good
|
||||||
|
gstreamer1.0-plugins-ugly (if needed)
|
||||||
|
|
||||||
|
Install on Debian/Ubuntu:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo apt install gstreamer1.0-plugins-{bad,base,good,ugly}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install PyQt5
|
||||||
|
sudo apt install python3-gi gir1.2-gst-1.0 gstreamer1.0-plugins-{bad,good,base}
|
||||||
|
```
|
||||||
|
✅ Custom Media Player with Rate & Pitch Control
|
||||||
|
```python
|
||||||
|
import sys
|
||||||
|
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QVBoxLayout, QFileDialog, QLabel, QSlider
|
||||||
|
from PyQt5.QtCore import Qt
|
||||||
|
import gi
|
||||||
|
|
||||||
|
gi.require_version('Gst', '1.0')
|
||||||
|
from gi.repository import Gst, GObject
|
||||||
|
|
||||||
|
# Initialize GStreamer
|
||||||
|
Gst.init(None)
|
||||||
|
|
||||||
|
class GStreamerPlayer(QWidget):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.setWindowTitle("Custom GStreamer Audio Player with Pitch Control")
|
||||||
|
self.setGeometry(300, 300, 400, 200)
|
||||||
|
|
||||||
|
# UI
|
||||||
|
self.play_button = QPushButton("Play")
|
||||||
|
self.pause_button = QPushButton("Pause")
|
||||||
|
self.open_button = QPushButton("Open File")
|
||||||
|
self.label = QLabel("Playback Rate: 1.0x")
|
||||||
|
self.slider = QSlider(Qt.Horizontal)
|
||||||
|
self.slider.setMinimum(50)
|
||||||
|
self.slider.setMaximum(200)
|
||||||
|
self.slider.setValue(100)
|
||||||
|
|
||||||
|
layout = QVBoxLayout()
|
||||||
|
layout.addWidget(self.open_button)
|
||||||
|
layout.addWidget(self.play_button)
|
||||||
|
layout.addWidget(self.pause_button)
|
||||||
|
layout.addWidget(self.label)
|
||||||
|
layout.addWidget(self.slider)
|
||||||
|
self.setLayout(layout)
|
||||||
|
|
||||||
|
# Signals
|
||||||
|
self.open_button.clicked.connect(self.open_file)
|
||||||
|
self.play_button.clicked.connect(self.play)
|
||||||
|
self.pause_button.clicked.connect(self.pause)
|
||||||
|
self.slider.valueChanged.connect(self.set_playback_rate)
|
||||||
|
|
||||||
|
# GStreamer pipeline
|
||||||
|
self.pipeline = Gst.ElementFactory.make("playbin", "player")
|
||||||
|
self.pipeline.set_property("audio-filter", self.build_pitch_filter(1.0))
|
||||||
|
|
||||||
|
self.uri = None
|
||||||
|
|
||||||
|
def build_pitch_filter(self, rate):
|
||||||
|
pitch = Gst.ElementFactory.make("pitch", "pitch")
|
||||||
|
pitch.set_property("pitch", 1.0)
|
||||||
|
pitch.set_property("rate", rate)
|
||||||
|
|
||||||
|
# Build filter bin
|
||||||
|
bin = Gst.Bin.new("filter_bin")
|
||||||
|
conv = Gst.ElementFactory.make("audioconvert", "conv")
|
||||||
|
resample = Gst.ElementFactory.make("audioresample", "resample")
|
||||||
|
bin.add(conv)
|
||||||
|
bin.add(resample)
|
||||||
|
bin.add(pitch)
|
||||||
|
|
||||||
|
conv.link(resample)
|
||||||
|
resample.link(pitch)
|
||||||
|
|
||||||
|
# Ghost pad to connect to playbin
|
||||||
|
pad = pitch.get_static_pad("src")
|
||||||
|
ghost = Gst.GhostPad.new("src", pad)
|
||||||
|
bin.add_pad(ghost)
|
||||||
|
|
||||||
|
conv_pad = conv.get_static_pad("sink")
|
||||||
|
ghost_sink = Gst.GhostPad.new("sink", conv_pad)
|
||||||
|
bin.add_pad(ghost_sink)
|
||||||
|
|
||||||
|
return bin
|
||||||
|
|
||||||
|
def open_file(self):
|
||||||
|
file_path, _ = QFileDialog.getOpenFileName(self, "Open Audio File")
|
||||||
|
if file_path:
|
||||||
|
self.uri = Gst.filename_to_uri(file_path)
|
||||||
|
self.pipeline.set_property("uri", self.uri)
|
||||||
|
|
||||||
|
def play(self):
|
||||||
|
if self.uri:
|
||||||
|
self.pipeline.set_state(Gst.State.PLAYING)
|
||||||
|
|
||||||
|
def pause(self):
|
||||||
|
self.pipeline.set_state(Gst.State.PAUSED)
|
||||||
|
|
||||||
|
def set_playback_rate(self, value):
|
||||||
|
rate = value / 100.0
|
||||||
|
self.label.setText(f"Playback Rate: {rate:.1f}x")
|
||||||
|
# Replace the audio-filter with a new one using the updated rate
|
||||||
|
self.pipeline.set_state(Gst.State.NULL)
|
||||||
|
self.pipeline.set_property("audio-filter", self.build_pitch_filter(rate))
|
||||||
|
if self.uri:
|
||||||
|
self.pipeline.set_property("uri", self.uri)
|
||||||
|
self.pipeline.set_state(Gst.State.PLAYING)
|
||||||
|
|
||||||
|
def closeEvent(self, event):
|
||||||
|
self.pipeline.set_state(Gst.State.NULL)
|
||||||
|
event.accept()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
GObject.threads_init()
|
||||||
|
app = QApplication(sys.argv)
|
||||||
|
player = GStreamerPlayer()
|
||||||
|
player.show()
|
||||||
|
sys.exit(app.exec_())
|
||||||
|
```
|
||||||
104
components/PMediaPlayer.py
Normal file
104
components/PMediaPlayer.py
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
import gi
|
||||||
|
import os
|
||||||
|
from gi.repository import Gst, GObject
|
||||||
|
|
||||||
|
gi.require_version("Gst", "1.0")
|
||||||
|
|
||||||
|
Gst.init(None)
|
||||||
|
|
||||||
|
|
||||||
|
class PMediaPlayer:
|
||||||
|
def __init__(self):
|
||||||
|
self.pipeline = Gst.ElementFactory.make("playbin", "player")
|
||||||
|
self.uri = None
|
||||||
|
self.rate = 1.0
|
||||||
|
|
||||||
|
# Set initial audio filter
|
||||||
|
self.pipeline.set_property("audio-filter", self._build_pitch_filter(self.rate))
|
||||||
|
|
||||||
|
def setSource(self, file_path: str):
|
||||||
|
if not os.path.isfile(file_path):
|
||||||
|
raise FileNotFoundError(f"File not found: {file_path}")
|
||||||
|
self.uri = Gst.filename_to_uri(file_path)
|
||||||
|
self.pipeline.set_property("uri", self.uri)
|
||||||
|
|
||||||
|
def play(self):
|
||||||
|
if not self.uri:
|
||||||
|
raise RuntimeError("No media source set.")
|
||||||
|
self.pipeline.set_state(Gst.State.PLAYING)
|
||||||
|
|
||||||
|
def pause(self):
|
||||||
|
self.pipeline.set_state(Gst.State.PAUSED)
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
self.pipeline.set_state(Gst.State.NULL)
|
||||||
|
|
||||||
|
def setPlaybackRate(self, rate: float):
|
||||||
|
if rate <= 0:
|
||||||
|
raise ValueError("Playback rate must be greater than 0.")
|
||||||
|
|
||||||
|
self.rate = rate
|
||||||
|
# Fully rebuild the pitch filter with new rate
|
||||||
|
self.pipeline.set_state(Gst.State.NULL)
|
||||||
|
self.pipeline.set_property("audio-filter", self._build_pitch_filter(rate))
|
||||||
|
if self.uri:
|
||||||
|
self.pipeline.set_property("uri", self.uri)
|
||||||
|
self.pipeline.set_state(Gst.State.PLAYING)
|
||||||
|
|
||||||
|
def _build_pitch_filter(self, rate: float):
|
||||||
|
pitch = Gst.ElementFactory.make("pitch", "pitch")
|
||||||
|
pitch.set_property("pitch", 1.0) # Keep pitch constant
|
||||||
|
pitch.set_property("rate", rate)
|
||||||
|
|
||||||
|
bin = Gst.Bin.new("filter_bin")
|
||||||
|
conv = Gst.ElementFactory.make("audioconvert", "conv")
|
||||||
|
resample = Gst.ElementFactory.make("audioresample", "resample")
|
||||||
|
|
||||||
|
bin.add(conv)
|
||||||
|
bin.add(resample)
|
||||||
|
bin.add(pitch)
|
||||||
|
conv.link(resample)
|
||||||
|
resample.link(pitch)
|
||||||
|
|
||||||
|
# Ghost pads
|
||||||
|
sink_pad = conv.get_static_pad("sink")
|
||||||
|
src_pad = pitch.get_static_pad("src")
|
||||||
|
bin.add_pad(Gst.GhostPad.new("sink", sink_pad))
|
||||||
|
bin.add_pad(Gst.GhostPad.new("src", src_pad))
|
||||||
|
|
||||||
|
return bin
|
||||||
|
|
||||||
|
def cleanup(self):
|
||||||
|
self.pipeline.set_state(Gst.State.NULL)
|
||||||
|
|
||||||
|
|
||||||
|
# Example usage:
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import time
|
||||||
|
|
||||||
|
GObject.threads_init()
|
||||||
|
player = PMediaPlayer()
|
||||||
|
|
||||||
|
try:
|
||||||
|
player.setSource("/path/to/your/audio/file.mp3")
|
||||||
|
player.play()
|
||||||
|
time.sleep(2)
|
||||||
|
|
||||||
|
print("Speeding up to 1.5x...")
|
||||||
|
player.setPlaybackRate(1.5)
|
||||||
|
time.sleep(5)
|
||||||
|
|
||||||
|
print("Slowing down to 0.75x...")
|
||||||
|
player.setPlaybackRate(0.75)
|
||||||
|
time.sleep(5)
|
||||||
|
|
||||||
|
print("Pausing...")
|
||||||
|
player.pause()
|
||||||
|
time.sleep(2)
|
||||||
|
|
||||||
|
print("Resuming...")
|
||||||
|
player.play()
|
||||||
|
time.sleep(5)
|
||||||
|
|
||||||
|
finally:
|
||||||
|
player.cleanup()
|
||||||
@ -9,3 +9,4 @@ from .AddToPlaylistWindow import AddToPlaylistWindow
|
|||||||
from .CreatePlaylistWindow import CreatePlaylistWindow
|
from .CreatePlaylistWindow import CreatePlaylistWindow
|
||||||
from .PlaylistsPane import PlaylistsPane
|
from .PlaylistsPane import PlaylistsPane
|
||||||
from .ExportPlaylistWindow import ExportPlaylistWindow
|
from .ExportPlaylistWindow import ExportPlaylistWindow
|
||||||
|
from .PMediaPlayer import PMediaPlayer
|
||||||
|
|||||||
8
main.py
8
main.py
@ -50,6 +50,7 @@ from components import (
|
|||||||
AudioVisualizer,
|
AudioVisualizer,
|
||||||
CreatePlaylistWindow,
|
CreatePlaylistWindow,
|
||||||
ExportPlaylistWindow,
|
ExportPlaylistWindow,
|
||||||
|
PMediaPlayer,
|
||||||
)
|
)
|
||||||
from utils.get_album_art import get_album_art
|
from utils.get_album_art import get_album_art
|
||||||
|
|
||||||
@ -171,9 +172,12 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
|
|||||||
|
|
||||||
# widget bits
|
# widget bits
|
||||||
self.album_art_scene: QGraphicsScene = QGraphicsScene()
|
self.album_art_scene: QGraphicsScene = QGraphicsScene()
|
||||||
self.player: QMediaPlayer = QMediaPlayer() # Audio player object
|
# self.player: QMediaPlayer = QMediaPlayer() # Audio player object
|
||||||
|
self.player: PMediaPlayer = PMediaPlayer()
|
||||||
self.probe: QAudioProbe = QAudioProbe() # Gets audio buffer data
|
self.probe: QAudioProbe = QAudioProbe() # Gets audio buffer data
|
||||||
self.audio_visualizer: AudioVisualizer = AudioVisualizer(self.player, self.probe, self.PlotWidget)
|
self.audio_visualizer: AudioVisualizer = AudioVisualizer(
|
||||||
|
self.player, self.probe, self.PlotWidget
|
||||||
|
)
|
||||||
self.timer = QTimer(self) # for playback slider and such
|
self.timer = QTimer(self) # for playback slider and such
|
||||||
|
|
||||||
# sharing functions with other classes and that
|
# sharing functions with other classes and that
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user