Класс Sound и модуль pygame.mixer.music

В Pygame для работы с аудио предназначены модули pygame.mixer и pygame.mixer.music. Модули похожи, однако pygame.mixer в первую очередь адаптирован для добавления и настройки звуковых эффектов в игре. В то время как pygame.mixer.music – для добавления фоновой музыки.

Функция pygame.mixer.music.load() загружает потоковое аудио, т. е. не грузит файл целиком, а делает это отдельными порциями. В результате можно проигрывать только один файл за раз. Однако можно ставить файлы в очередь функцией queue(). Поддерживает в том числе формат mp3.

С другой стороны, в pygame.mixer ключевым является класс Sound. Он позволяет загружать, проигрывать и выполнять ряд других действий с файлами форматов wav или ogg. При создании экземпляра Sound в конструктор передается имя файла.

В примере ниже подгружается фоновая музыка: pygame.mixer.music.load(). Функция не возвращает никакого "музыкального" объекта, поэтому результат ее вызова не присваивается переменной.

С помощью функции music.play() файл начинает проигрываться. Если требуется зациклить композицию, то в play() передается число -1. Положительный аргумент указывает на количество повторов + одно дополнительное. То есть, если надо проиграть композицию 2 раза, то в функцию передается число 1.

В программе при нажатии на клавишу 1 клавиатуры музыка ставится на паузу: music.pause(). Клавиша 2 уменьшает громкость в два раза: music.set_volum(0.5). Нажатие 3 возвращает громкость на прежний уровень. Функция unpause() вызывается на случай, если до этого музыка была выключена (клавишей 1).

В примере создаются два объекта типа Sound. У них есть свой метод play(). В данном случае файлы проигрываются при клике левой и правой кнопками мыши. Объекты Sound могут проигрываться одновременно, так как обычно принадлежат разным каналам. Если требуется более тонкое управление звуками, дополнительно используют класс Channel.

import pygame
pygame.init()
 
sc = pygame.display.set_mode((400, 300))
 
pygame.mixer.music.load('Test.mp3')
pygame.mixer.music.play()
 
sound1 = pygame.mixer.Sound('boom.wav')
sound2 = pygame.mixer.Sound('one.ogg')
 
while 1:
    for i in pygame.event.get():
        if i.type == pygame.QUIT:
            exit()
        elif i.type == pygame.KEYUP:
            if i.key == pygame.K_1:
                pygame.mixer.music.pause()
                # pygame.mixer.music.stop()
            elif i.key == pygame.K_2:
                pygame.mixer.music.unpause()
                # pygame.mixer.music.play()
                pygame.mixer.music.set_volume(0.5)
            elif i.key == pygame.K_3:
                pygame.mixer.music.unpause()
                # pygame.mixer.music.play()
                pygame.mixer.music.set_volume(1)
        elif i.type == pygame.MOUSEBUTTONUP:
            if i.button == 1:
                sound1.play()
            elif i.button == 3:
                sound2.play()
    pygame.time.delay(20)

Если закомментировать вызовы функций pause() и unpause() и раскомментировать stop() и play(), то результат будет схож. Разница в том, что при использовании комбинации stop-play файл начнет проигрываться сначала, а при pause-unpause продолжится с места останова.

Если у вас нет файлов wav или ogg для тестов, можете найти немного в каталоге data модуля pygame.examples. Модуль находится в папке библиотеки pygame, адрес которой можно посмотреть так:

>>> import pygame
>>> pygame.__file__
'/home/pl/.local/lib/python3.5/site-packages/pygame/__init__.py'

Практическая работа

Добавьте фоновую музыку и звук столкновения в игру из практической работы предыдущего урока.

Окно игры должно закрываться только после того, как звук столкновения полностью проиграется. В зависимости от решения вам может понадобиться метод get_length() объекта типа Sound. Метод возвращает продолжительность звука, выраженную в секундах (тип float).

Курс с примерами решений практических работ: android-приложение, pdf-версия.

Комментарии

Доброго времени суток. Приведите пожалуйста пример кода: как можно с помощью функции queue() составить очередь треков на воспроизведение? например как нужно переписать этот фрагмент кода:
 def play_():
    for treck in pley_liste:
        if treck.endswith(".mp3"):
            play_list.append(treck)
 
        pygame.mixer.music.load(play_list[0])
 
        pygame.mixer.music.play() 

""" добавление звука  """
from random import randint
import pygame
pygame.init()
pygame.time.set_timer(pygame.USEREVENT, 2000)# раз в 2 сек
W = 400
H = 400
WHITE = (255, 255, 255)
 
sc = pygame.display.set_mode((W, H))
CARS=('car.bmp', 'car1.bmp')#список изображений
CARS_SURF=[]
 
pygame.mixer.music.load('Песенка паровозика.mp3')
pygame.mixer.music.play()
 
sound1 = pygame.mixer.Sound('boom.wav')
 
for i in range(len(CARS)):
    CARS_SURF.append(pygame.image.load(CARS[i]).convert())#добавление изображения
 
class Car(pygame.sprite.Sprite):#класс машин 
    def __init__(self, x, surf,group):
        pygame.sprite.Sprite.__init__(self)
        self.image = surf
        self.rect = self.image.get_rect(center=(x, W))
        self.add(group)
        self.speed=(randint(1,5))
        self.image.set_colorkey((255,255,255))#добавил 
    def update(self):#обновление /изменяем св-ва класса за его пределами 
        if self.rect.y > -100:
            self.rect.y -= self.speed
        else:
            self.kill
 
cars = pygame.sprite.Group()#
Car(randint(25,W-25), CARS_SURF[0],cars)#создание первой машины - спрайта
 
class Car_up(pygame.sprite.Sprite):#класс машин едущих навстречу
    def __init__(self,x,surf):
        pygame.sprite.Sprite.__init__(self)
        self.image = surf
        self.rect = self.image.get_rect(center=(x, 50))
        self.image.set_colorkey((255,255,255))
 
car_up=Car_up(200, CARS_SURF[1])#создание спрайта-машины едущей навстречу
sc.blit(car_up.image, car_up.rect)#
 
while 1:
    a=pygame.event.get()
    for i in a:
        if i.type== pygame.USEREVENT:#        
            Car(randint(25,W-25), CARS_SURF[0],cars)#создание новой машины
 
    keys = pygame.key.get_pressed()#отслеживает была ли зажата и какая кнопка
    if keys[pygame.K_RIGHT]:#вправо движение
        car_up.rect.x+=2
    elif keys[pygame.K_LEFT]:#влево движение
        car_up.rect.x-=2
 
    if pygame.sprite.spritecollide(car_up,cars,False):#если было соприкосновение одного с кем-то из группы то
        sound1.play()#воспроизводим звук удара
        pygame.time.delay(int(sound1.get_length())*1000)#задаем время после которого следует закрыть окно
        #умножать на 1000 нужно т.к. длина возвращается в секундах а не миллисекундах
        pygame.quit()#      то выход
 
    sc.fill(WHITE)#
    cars.draw(sc)#отрисовать поверх-ть
    sc.blit(car_up.image, car_up.rect)#отрисовать поверх-ть
    pygame.display.update()#обновить
    pygame.time.delay(20)
    cars.update()#обновить класс машин

Ответ на от Алексей

import pygame
import random
 
pygame.init()
 
CARS = ['car1.png', 'car2.png', 'car3.png']
 
 
pygame.time.set_timer(pygame.USEREVENT, 200)
FPS = 60
SCREEN = (600, 400)
BLACK = (0, 0, 0)
GAME_OVER = False
sc = pygame.display.set_mode(SCREEN)
 
 
pygame.mixer.music.load('background.wav')
pygame.mixer.music.play(-1)
boom = pygame.mixer.Sound("boom.wav")
 
game_over_text = pygame.font.SysFont('arial', 36).render("GAME OVER", 1, (255, 0, 0))
game_over_rect = game_over_text.get_rect(center=(SCREEN[0]//2, SCREEN[1]//2))
 
 
class Car(pygame.sprite.Sprite):
    def __init__(self, x, surf, group):
        super().__init__()
        self.image = surf
        self.rect = self.image.get_rect(center=(x, 0))
        self.speed = random.randint(2, 7)
        self.add(group)
 
    def update(self, *args):
        if self.rect.y < SCREEN[1]:
            self.rect.y += self.speed
        else:
            self.kill()
 
 
class HeroCar(pygame.sprite.Sprite):
    def __init__(self, X, path, group):
        super().__init__()
        self.image = pygame.image.load(path).convert_alpha()
        self.rect = self.image.get_rect(center=(X, SCREEN[1]))
        self.add(group)
 
    def update(self, move):
        if move == "LEFT":
            self.rect.x -= 3
        elif move == "RIGHT":
            self.rect.x += 3
 
 
hero_group = pygame.sprite.Group()
hero = HeroCar(SCREEN[0]//2, 'car.png', hero_group)
 
 
cars = pygame.sprite.Group()
play = True
 
while play:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            play = False
 
        if event.type == pygame.USEREVENT and not GAME_OVER:
            surf = pygame.image.load(CARS[random.randint(0, 2)]).convert_alpha()
            x = random.randint(1, SCREEN[0])
            Car(x, surf, cars)
 
        if event.type == pygame.KEYDOWN and event.key == pygame.K_r:
            pygame.mixer.music.play(-1)
            GAME_OVER = False
 
    keys = pygame.key.get_pressed()
    if keys[pygame.K_RIGHT] and hero.rect.x + hero.rect.width < SCREEN[0] and not GAME_OVER:
        hero_group.update('RIGHT')
 
    if keys[pygame.K_LEFT] and hero.rect.x > 0 and not GAME_OVER:
        hero_group.update('LEFT')
 
    sc.fill(BLACK)
 
    hero_group.draw(sc)
 
    cars.draw(sc)
    cars.update()
 
    if pygame.sprite.spritecollideany(hero, cars):
        GAME_OVER = True
        cars.empty()
        hero.rect.x = SCREEN[0]//2
        pygame.mixer.music.stop()
        boom.play()
 
    if GAME_OVER:
        sc.blit(game_over_text, game_over_rect)
 
    pygame.display.update()
 
    pygame.time.Clock().tick(FPS)