pythonpyqtpyqt6qmediaplayerqslider

PyQt6 How to set volume with slider


I can't change volume of playing music with slider.

I tried to change volume with audio_output, but it doesn't work.

import sys
from PyQt6.QtCore import Qt, QUrl
from PyQt6.QtMultimedia import QMediaPlayer, QAudioOutput
from PyQt6.QtWidgets import QApplication, QWidget, QPushButton, QSlider, QVBoxLayout, QHBoxLayout, QLabel


class MusicPlayer(QWidget):
    def __init__(self):
        super().__init__()

        self.setWindowTitle("Музыкальный плеер")
        self.setGeometry(100, 100, 400, 200)

        self.initUI()
        self.initPlayer()

    def initPlayer(self):
        self.player = QMediaPlayer()
        self.audio_output = QAudioOutput()
        self.player.setAudioOutput(self.audio_output)
        file_name = "song.mp3"
        self.player.setSource(QUrl.fromLocalFile(file_name))
        self.audio_output.setVolume(50)
        self.player.play()

    def initUI(self):


        self.play_button = QPushButton("Play")
        self.play_button.clicked.connect(self.play_music)

        self.pause_button = QPushButton("Pause")
        self.pause_button.clicked.connect(self.pause_music)

        self.stop_button = QPushButton("Stop")
        self.stop_button.clicked.connect(self.stop_music)

        self.volume_slider = QSlider(Qt.Orientation.Horizontal)
        self.volume_slider.setValue(50)
        self.volume_slider.setMaximum(100)
        self.volume_slider.setToolTip("Volume")
        self.volume_slider.valueChanged.connect(self.set_volume)

        self.track_label = QLabel("Название трека")

        vbox = QVBoxLayout()
        hbox = QHBoxLayout()

        hbox.addWidget(self.play_button)
        hbox.addWidget(self.pause_button)
        hbox.addWidget(self.stop_button)
        hbox.addWidget(self.volume_slider)

        vbox.addWidget(self.track_label)
        vbox.addLayout(hbox)

        self.setLayout(vbox)
        
    def play_music(self):
        self.player.play()

    def pause_music(self):
        self.player.pause()

    def stop_music(self):
        self.player.stop()

    def set_volume(self):
        self.volume = self.volume_slider.value()
        print(self.volume)
        print(type(self.volume))
        self.audio_output.setVolume(self.volume)


app = QApplication(sys.argv)
playerM = MusicPlayer()
playerM.show()
sys.exit(app.exec())

Solution

  • The documentation of the volume property of QAudioOutput explains it quite clearly:

    The volume is scaled linearly, ranging from 0 (silence) to 1 (full volume).

    This means that you need to change the range accordingly: if you aim for a full scale of 0-100, then the value must be divided by 100.
    Since multiplication is usually faster than division, for simple ratios like these it's normally preferable to multiply by 0.01 (1/100) instead.

    class MusicPlayer(QWidget):
        ...
        def initPlayer(self):
            ...
            self.audio_output.setVolume(.5) # 50 / 100
    
        ...
    
        def set_volume(self):
            self.volume = self.volume_slider.value()
            self.audio_output.setVolume(self.volume * .01)
    

    The documentation also notes that the volume value is linear, but human perception of volume is logarithmic, so you should consider the static QAudio.convertVolume():

    from PyQt6.QtMultimedia import QMediaPlayer, QAudioOutput, QAudio
    ...
    
    class MusicPlayer(QWidget):
        ...
        def real_volume(self, slider_value):
            return QAudio.convertVolume(
                slider_value * .01, 
                QAudio.VolumeScale.LogarithmicVolumeScale, 
                QAudio.VolumeScale.LinearVolumeScale
            )
    
        def initPlayer(self):
            ...
            self.audio_output.setVolume(self.real_volume(50))
    
        def set_volume(self):
            self.volume = self.volume_slider.value()
            self.audio_output.setVolume(self.real_volume(self.volume))