Добавляем звук в игровой процесс. Модули mixer и music

Ссылка на проект занятия (lesson 11.flowballs.zip): https://github.com/selfedu-rus/pygame

На этом занятии мы с вами рассмотрим работу со звуком. В библиотеке Pygame для этого имеются два модуля:

  • pygame.mixer – для работы со звуковыми эффектами;
  • pygame.mixer.music – для добавления фоновой музыки.

Полная их документация доступна по адресам:

https://www.pygame.org/docs/ref/mixer.html

https://www.pygame.org/docs/ref/music.html

Давайте для начала разберемся, как использовать эти модули и какие наиболее часто используемые методы они содержат. Начнем с модуля pygame.mixer.music.

Этот модуль поддерживает основные звуковые форматы:

mp3, ogg, wav

и загружает их с помощью функции load:

import pygame
pygame.init()
 
pygame.mixer.music.load("sounds/bird.mp3")
 
W, H = 500, 300
sc = pygame.display.set_mode((W, H))
 
clock = pygame.time.Clock()
FPS = 60
 
while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            exit()
 
    clock.tick(FPS)

После успешной загрузки мы можем воспроизвести этот файл с помощью функции

play(loops=0, start=0.0, fade_ms = 0)

  • loops – число повторений (0 – проигрывать один раз, 1 – два раза и т.д, -1 – бесконечное повторение);
  • start – начальное время проигрывания файла (в секундах);
  • fade_ms – затухание звука при окончании проигрывания (в мс).

Например, запустим фоновую музыку с бесконечным повторением:

pygame.mixer.music.play(-1)

Добавим возможность останавливать проигрывание при нажатии на клавишу пробел:

        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_SPACE:
                flPause = not flPause
                if flPause:
                    pygame.mixer.music.pause()
                else:
                    pygame.mixer.music.unpause()

Смотрите, здесь используются две функции:

  • pause() – поставить воспроизведение на паузу;
  • unpause() – продолжить воспроизведения с места паузы.

Соответственно, у нас при изменении флага flPause воспроизведение то ставится на паузу, то снимается с паузы. Начальное значение этого флага определим как False:

flPause = False

Если нам нужно остановить воспроизведение, то используется метод:

pygame.mixer.music.stop()

а для воспроизведения с начала метод:

pygame.mixer.music.rewind()

Также часто используются методы:

  • set_volume(volume) – установить уровень громкости (volume – вещественное значение от 0 до 1);
  • get_volume() – получить текущее значение уровня громкости.

Например, сделаем, чтобы при нажатии на курсорные клавиши влево/вправо звук уменьшался и увеличивался:

            elif event.key == pygame.K_LEFT:
                vol -= 0.1
                pygame.mixer.music.set_volume(vol)
                print( pygame.mixer.music.get_volume() )
            elif event.key == pygame.K_RIGHT:
                vol += 0.1
                pygame.mixer.music.set_volume(vol)
                print( pygame.mixer.music.get_volume() )

А сам параметр vol изначально установим в 1.0:

vol = 1.0

По аналогии используются и все остальные функции модуля music.

Модуль pygame.mixer

Теперь рассмотрим работу модуля pygame.mixer. Он служит для работы со звуковыми эффектами, например, выдавать звук при столкновениях, ловле профитов, начисления очков и т.п. Данный модуль поддерживает многоканальный звук, т.е. одновременное воспроизведение нескольких звуковых дорожек.

Первое что нужно сделать, это проинициализировать этот модуль. Для этого у него есть две функции:

init(frequency=44100, size=-16, channels=2, buffer=512, devicename=None, allowedchanges=AUDIO_ALLOW_FREQUENCY_CHANGE | AUDIO_ALLOW_CHANNELS_CHANGE)

и

pre_init(frequency=44100, size=-16, channels=2, buffer=512, devicename=None)

Что они делают, как и когда их вызывать? Очевидно, что они нужны для тонкой настройки миксера в нашем приложении. Названия параметров говорят сами за себя:

  • frequency – частота воспроизведения звуковых файлов;
  • size – число бит для представления аудио данных (знак минус говорит, что используются знаковые числа);
  • channels – число каналов;
  • buffer – размер буфера (в байтах);
  • allowedchanges – дополнительные флаговые настройки.

Функция init вызывается автоматически при инициализации Pygame:

pygame.init()

Но с аргументами по умолчанию. Если нужно поменять эти параметры, то следует до pygame.init вызвать метод pygame.pre_init() с нужным набором параметров. Мало того, как мы увидим, вызов pre_init устраняет один из неприятных эффектов – задержку в воспроизведении звуков.

Итак, у миксера также есть похожий набор функций для останова и пауз:

  • stop() – остановить вопроизведение;
  • pause() – пауза воспроизведения;
  • unpause() – снятие с паузы.

Но вот загрузка и воспроизведение звуковых файлов выполняется немного по другому – с помощью класса:

pygame.mixer.Sound(filename)

Здесь filename – это звуковой файл в формате

wav или ogg

Обратите внимание, класс Sound не поддерживает файлы в формате mp3. Итак, чтобы загрузить какой-либо звуковой эффект, достаточно выполнить вот такую строчку:

s = pygame.mixer.Sound("sounds/catch.ogg")

И, далее, уже через переменную s можем работать с воспроизведение данного эффекта. Класс Sound поддерживает такие основные методы:

  • play(loops=0, maxtime=0, fade_ms=0) – воспроизведение звука;
  • stop() – останов воспроизведения;
  • set_volume(value) – настройка громкости звучания;
  • get_volume() – возвращает текущее значение громкости (число от 0 до 1).

Например, сделаем, чтобы при нажатии на клавишу Enter звучал файл catch.ogg:

            elif event.key == pygame.K_RETURN:
                s.play()

Однако, смотрите, нажимая клавишу, звук появляется с задержкой. Это из-за того, что нужно выполнить команду pre_init() миксера до инициализации самого Pygame:

pygame.mixer.pre_init(44100, -16, 1, 512) # важно вызвать до pygame.init()

Теперь, при воспроизведении звуковых эффектов задержек не будет.

Также вы можете заметить, что класс Sound не содержит методов для пауз. Дело в том, что весь этот функционал выносится на уровень канала или миксера в целом. Что это за каналы? Смотрите, когда мы запускаем воспроизведение из класса Sound, то автоматически создается канал, в котором происходит обработка звука. И уже на уровне канала существуют методы для пауз, остановок, затухания звучания и т.п.

Это сделано для возможности одновременного воспроизведения нескольких звуковых дорожек: каждый звук работает в своем отдельном канале. А для получения ссылки на канал достаточно записать команду:

ch = s.play()

То есть, метод play возвращает ссылку на канал, в котором происходит воспроизведение. И далее, уже через переменную ch можно вызывать методы канала, например:

ch.pause()

Если же мы хотим поставить на паузу все каналы, то следует вызвать такую же функцию на уровне всего микшера:

pygame.mixer.pause()

По аналогии работают остальные методы и функции микшера.

Добавление звуковых эффектов в игру

Теперь, когда мы в целом разобрались с работой звука в Pygame, давайте добавим звуковые эффекты в нашу игру с шариками.

Смотрите, здесь вначале идет инициализация микшера:

pygame.mixer.pre_init(44100, -16, 1, 512) # важно прописать до pygame.init()

так, чтобы не было задержек при воспроизведения звука.

Далее, мы загружаем и проигрываем фоновый звук, зациклив воспроизведение:

pygame.mixer.music.load('sounds/bird.mp3')
pygame.mixer.music.play(-1)

Затем, формируем звуковой эффект для ловли шара:

s_catch = pygame.mixer.Sound('sounds/catch.ogg')

И в функции collideBalls проигрываем этот звук, если шар попадает в телегу:

def collideBalls():
    global game_score
    for ball in balls:
        if t_rect.collidepoint(ball.rect.center):
            s_catch.play()
            game_score += ball.score
            ball.kill()

Запускаем игру и теперь у нас все работает со звуковыми эффектами. По аналогии можно добавить и другие звуки, но это вы уже сможете сделать самостоятельно. Ссылка на эту программу в начале страницы и каждый из вас сможет внимательно ее изучить и изменять по своему усмотрению.