0

I'm working on a music player app, but i came across a problem which I don't know how to solve. I want audio to fade in in given interval (0.6 second for example) when audio is started or unpaused and also fade out in the same interval when it is paused. I tried doing it with QThread to keep my ui responsive while gradually lowering volume with for loop (iterating 10 times and every time lowering volume and sleeping for certaing time, 0.6 / 10 seconds). But it doesn't work, buttons are still unclickable and update only after fade in or fade out. If anyone managed to solve a problem like this please help, would greatly appreciate it! Here's simple code for playing audio in pyqt:

import sys

from PyQt6 import QtWidgets
from PyQt6.QtCore import QUrl
from PyQt6.QtMultimedia import QMediaPlayer, QAudioOutput
from PyQt6.QtWidgets import QWidget, QVBoxLayout, QPushButton, QStyle


class MainWindowUi(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()

        self._setup_ui()

        self.setWindowTitle('sound fading')
        self.setGeometry(100, 100, 100, 100)

    def _setup_ui(self):
        self.central_widget = QWidget(self)
        self.central_widget_layout = QVBoxLayout(self.central_widget)
        self.setCentralWidget(self.central_widget)

        self.player = AudioPlayer()
        self.user_action = -1  # 0 - stopped, 1 - playing, 2 - paused
        self.play_button = QPushButton()
        self.play_button.clicked.connect(self.play_pause_button_clicked)

        self.play_icon = self.style().standardIcon(QStyle.StandardPixmap.SP_MediaPlay)
        self.pause_icon = self.style().standardIcon(QStyle.StandardPixmap.SP_MediaPause)
        self.play_button.setIcon(self.play_icon)

        self.central_widget_layout.addWidget(self.play_button)

    def play(self):
        print("Play")
        self.play_button.setIcon(self.pause_icon)
        self.user_action = 1
        self.player.setSource(QUrl("Some audio file path goes here."))
        self.player.play()

    def pause(self):
        print("Pause")
        self.play_button.setIcon(self.play_icon)
        self.user_action = 2
        self.player.pause()

    def unpause(self):
        print("Unpause")
        self.play_button.setIcon(self.pause_icon)
        self.user_action = 1
        self.player.play()

    def play_pause_button_clicked(self):
        if self.user_action <= 0:
            self.play()
        elif self.user_action == 1:
            self.pause()
        elif self.user_action == 2:
            self.unpause()


class AudioPlayer(QMediaPlayer):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.audio_output = QAudioOutput()
        self.setAudioOutput(self.audio_output)
        self.audioOutput().setVolume(0.3)
        self.current_volume = self.audio_output.volume()

    def play(self):
        super().play()

    def pause(self):
        super().pause()


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    mainWindow = MainWindowUi()
    mainWindow.show()

    sys.exit(app.exec())
N1teShad0w
  • 53
  • 1
  • 5
  • 1
    Sounds like you're looking for something like [`QVariantAnimation`](https://doc.qt.io/qtforpython-5/PySide2/QtCore/QVariantAnimation.html) (Link is for `PySide2` -- can't find the associated `PyQt6` doc. for some reason.) – G.M. Dec 02 '21 at 19:36
  • Yes! Thanks a lot, I used QPropertyAnimation on audio output volume and it works! – N1teShad0w Dec 02 '21 at 21:52
  • @N1teShad0w I suggest you to post your own answer with a [mre] to show how you solved the problem, as it might be useful to others too. – musicamante Dec 02 '21 at 23:42

1 Answers1

1

So I made it work with QPropertyAnimation! Still working on key values and easing curves but does is much, much closer to the result I want.

import sys

from PyQt6 import QtWidgets
from PyQt6.QtCore import QUrl, QPropertyAnimation, QEasingCurve
from PyQt6.QtMultimedia import QMediaPlayer, QAudioOutput
from PyQt6.QtWidgets import QWidget, QVBoxLayout, QPushButton, QStyle

from audio_player import AudioPlayer


class MainWindowUi(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()

        self._setup_ui()

        self.setWindowTitle('sound fading')
        self.setGeometry(100, 100, 100, 100)

    def _setup_ui(self):
        self.central_widget = QWidget(self)
        self.central_widget_layout = QVBoxLayout(self.central_widget)
        self.setCentralWidget(self.central_widget)

        self.player = AudioPlayer()
        self.user_action = -1  # 0 - stopped, 1 - playing, 2 - paused
        self.play_button = QPushButton()
        self.play_button.clicked.connect(self.play_pause_button_clicked)

        self.play_icon = self.style().standardIcon(QStyle.StandardPixmap.SP_MediaPlay)
        self.pause_icon = self.style().standardIcon(QStyle.StandardPixmap.SP_MediaPause)
        self.play_button.setIcon(self.play_icon)

        self.central_widget_layout.addWidget(self.play_button)

    def play(self):
        print("Play")
        self.play_button.setIcon(self.pause_icon)
        self.user_action = 1
        self.player.setSource(QUrl("some audio file path"))
        self.player.play()

    def pause(self):
        print("Pause")
        self.play_button.setIcon(self.play_icon)
        self.user_action = 2
        self.player.pause()

    def unpause(self):
        print("Unpause")
        self.play_button.setIcon(self.pause_icon)
        self.user_action = 1
        self.player.play()

    def play_pause_button_clicked(self):
        if self.user_action <= 0:
            self.play()
        elif self.user_action == 1:
            self.pause()
        elif self.user_action == 2:
            self.unpause()


class AudioPlayer(QMediaPlayer):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.audio_output = QAudioOutput()
        self.audio_output.volumeChanged.connect(lambda: print(self.audio_output.volume()))
        self.setAudioOutput(self.audio_output)
        self.audioOutput().setVolume(.5)
        self.current_volume = self.audio_output.volume()

        self.fade_in_anim = QPropertyAnimation(self.audio_output, b"volume")
        self.fade_in_anim.setDuration(1400)
        self.fade_in_anim.setStartValue(0.01)
        self.fade_in_anim.setEndValue(self.current_volume)
        self.fade_in_anim.setEasingCurve(QEasingCurve.Type.Linear)
        self.fade_in_anim.setKeyValueAt(0.01, 0.01)

        self.fade_out_anim = QPropertyAnimation(self.audio_output, b"volume")
        self.fade_out_anim.setDuration(600)
        self.fade_out_anim.setStartValue(self.current_volume)
        self.fade_out_anim.setEndValue(0)
        self.fade_out_anim.setEasingCurve(QEasingCurve.Type.Linear)
        self.fade_out_anim.setKeyValueAt(0.01, self.current_volume)
        self.fade_out_anim.finished.connect(super().pause)

    def play(self):
        self.audio_output.setVolume(0.01)
        self.fade_in_anim.setEndValue(self.current_volume)
        super().play()
        self.fade_in_anim.start()

    def pause(self):
        self.current_volume = self.audio_output.volume()
        self.fade_out_anim.setStartValue(self.current_volume)
        self.fade_out_anim.start()


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    mainWindow = MainWindowUi()
    mainWindow.show()

    sys.exit(app.exec())
N1teShad0w
  • 53
  • 1
  • 5