Pygameでオーディオ再生
ゲーム用途ではなく、音声処理ライブラリとして、ChatGPTがなんか推奨してきたので、ちょっと調べてみる。
GitHubレポジトリ
Pygame
Pygame は、Python を使用してビデオゲームのようなマルチメディアアプリケーションを開発するための、無料でオープンソースのクロスプラットフォームライブラリです。
Simple DirectMedia Layer ライブラリ やその他の人気のあるライブラリを利用し、一般的な機能を抽象化することで、これらのプログラムの作成をより直感的に行えるようにします。
特徴
Pygame はゲーム開発に適した強力なライブラリであり、コーディングを簡単にする多くの機能を提供します。その主な特徴を紹介します。
グラフィックス
Pygame を使用すると、動的で魅力的なグラフィックを簡単に作成できます。2D グラフィックやアニメーションのためのツールが充実しており、画像、長方形、ポリゴンの描画をサポートします。
サウンド
Pygame には、サウンドや音楽を再生・操作する機能も含まれています。WAV、MP3、OGG などのファイルフォーマットに対応しており、効果音や BGM を簡単に追加できます。
入力処理
キーボード、マウス、ジョイスティックの入力処理が直感的に行えます。これにより、プレイヤーの操作をスムーズに実装できます。
ゲーム開発機能
Pygame は、ゲーム開発に特化した包括的なツールセットを提供します。衝突検出、スプライト管理など、さまざまな機能を備えており、プラットフォーマーやパズルゲームなど、あらゆるジャンルのゲーム開発に対応できます。
公式サイト
目的は、サウンド部分の処理を知りたいだけなのだけど、まずはGetting Startedを簡単にやってみる。
インストール
作業ディレクトリ作成
mkdir pygame-work && cd pygame-work
Python仮想環境作成。最近はuvを使っている。
uv venv -p 3.12.8
ではpygameをインストール
uv pip install pygame
Installed 1 package in 16ms
+ pygame==2.6.1
サンプルのゲームを起動
uv run python -m pygame.examples.aliens
Quick start
ちょっとリンクがわかりにくかったけど、これかな。
でこれをやる前に、まず以下を見ると良さそう。冒頭8分だけでいいと思うので、以下をざっくり理解する。(もちろんこの動画の内容をそのまま進めるのもいいと思う。)
- How games work(ゲームはどう動いているのか)
- What Pygame does(Pygameは何をやってくれるのか)
NotebookLMにまとめてもらった
How games work(ゲームはどう動いているのか)
ビデオゲームの仕組みの要約は以下の通りです。
- ビデオゲームは、映画と似ており、多数の画像を高速で連続して再生することで動いているように見せている。
- 映画とビデオゲームの主な違いは、ビデオゲームでは、各画像が固定されておらず、動的に作成される点。
- ビデオゲームでは、プレイヤーからの入力に応じて画像が変化する。例えば、敵の位置やプレイヤーのボタン操作に応じて、表示される画像が異なる。
- ビデオゲームは、プレイヤーの入力をチェックし、その情報を使用して画面上に要素を配置する。
- ビデオゲームの基本的な流れは、プレイヤーからの入力を受け取り(イベントループ)、その情報に基づいて画面上に要素を配置し、画像を完成させる。そして、このプロセスを何度も繰り返す。
- Pygameは、画像の表示、サウンドの再生、プレイヤー入力の取得など、ゲーム開発に必要な機能を提供する。
- Pygameを使用すると、ウィンドウを作成し、画像を表示したり、マウスの位置やキーボード入力を取得したりできる。また、衝突判定やテキストの作成、タイマーなどの機能も利用できる。
What Pygame does(Pygameは何をやってくれるのか)
Pygameの機能は以下の通りです。
- 画像の表示に優れており、Pythonでウィンドウを作成したり、画像やアニメーションを表示したりするのが簡単になる。
- サウンドも再生できる。
- プレイヤーからの入力をサポートする。マウスの位置、キーボード、ゲームパッドから入力を得ることが可能。標準的なPythonの
input
メソッドとは異なり、ゲームを中断せずに入力を取得できる。- 衝突検出、テキストの作成、タイマーなど、ゲームの開発に役立つ機能もある。
- 高度なゲームの開発には向いていない。より複雑なゲーム(Dark SoulsやAssassin's Creedなど)では、Unreal、Unity、Godotなどのゲームエンジンが推奨される。
- Pygameは、コーディングの学習やシステム構築に非常に役立ち、プログラミング・スキルの向上に役立つ。
- Pygameは、本格的なゲーム開発だけでなく、映画やグラフの表示にも利用できる。
特に重要なのは「ゲームループ」という考え方なのだと思う。
ということでQuickstartに戻って、サンプルコード。
# 基本的なpygame「ゲームループ」の例
import pygame
# pygameの初期化
pygame.init()
screen = pygame.display.set_mode((1280, 720))
clock = pygame.time.Clock()
running = True
while running:
# イベントをポーリング
# pygame.QUITイベントはユーザーがウィンドウを閉じるためにXをクリックしたことを意味します
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# 前のフレームの内容を消すために画面を色で塗りつぶす
screen.fill("purple")
# ここにゲームの描画処理を記述
# ディスプレイを更新して作業内容を画面に表示
pygame.display.flip()
clock.tick(60) # FPSを60に制限
pygame.quit()
while
の中がゲームループにあたる。イベントを拾う→何らかの処理をする→画面を更新する、というのを高速に回すことで、ゲームが進行するということ。
実行してみる。
uv run pygame_quickstart.py
ウインドウが開く。
「ウインドウを閉じる」というイベントで、ループを抜けて、処理が終了する。
もう1つのサンプル
# 円が動くサンプル
import pygame
# pygameの初期化
pygame.init()
screen = pygame.display.set_mode((1280, 720))
clock = pygame.time.Clock()
running = True
dt = 0
player_pos = pygame.Vector2(screen.get_width() / 2, screen.get_height() / 2)
while running:
# イベントをポーリング
# pygame.QUITイベントはユーザーがウィンドウを閉じるためにXをクリックしたことを意味します
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# 前のフレームの内容を消すために画面を色で塗りつぶす
screen.fill("purple")
pygame.draw.circle(screen, "red", player_pos, 40)
keys = pygame.key.get_pressed()
if keys[pygame.K_w]:
player_pos.y -= 300 * dt
if keys[pygame.K_s]:
player_pos.y += 300 * dt
if keys[pygame.K_a]:
player_pos.x -= 300 * dt
if keys[pygame.K_d]:
player_pos.x += 300 * dt
# ディスプレイを更新して作業内容を画面に表示
pygame.display.flip()
# FPSを60に制限
# dtは前のフレームからの経過時間(秒)で、フレームレートに依存しない物理計算に使用されます
dt = clock.tick(60) / 1000
pygame.quit()
実際にオブジェクトを操作する例。
実行。
uv run pygame_quickstart2.py
円が表示される。
以下で操作ができる。
w
: 上
s
: 下
a
: 左
d
: 右
ゲームループの考え方は以下のチュートリアルのコードも参考になる。
Quickstartの2つ目のコードをクラスで書き換えたもの。
import pygame
class App:
def __init__(self):
# アプリケーションの実行状態と表示サーフェスを初期化
self._running = True
self._display_surf = None
# 画面サイズを設定
self.size = self.width, self.height = 1280, 720
# プレイヤーの位置と経過時間を初期化
self.player_pos = pygame.Vector2(self.width / 2, self.height / 2)
self.dt = 0
def on_init(self):
# pygameを初期化し、表示サーフェスを作成
pygame.init()
self._display_surf = pygame.display.set_mode(self.size, pygame.HWSURFACE | pygame.DOUBLEBUF)
self._running = True
# フレームレート制御用のClockオブジェクトを作成
self.clock = pygame.time.Clock()
def on_event(self, event):
# イベント処理: ウィンドウの閉じるボタンが押されたら終了
if event.type == pygame.QUIT:
self._running = False
def on_loop(self):
# キー入力処理
keys = pygame.key.get_pressed()
if keys[pygame.K_w]:
self.player_pos.y -= 300 * self.dt
if keys[pygame.K_s]:
self.player_pos.y += 300 * self.dt
if keys[pygame.K_a]:
self.player_pos.x -= 300 * self.dt
if keys[pygame.K_d]:
self.player_pos.x += 300 * self.dt
def on_render(self):
# 画面を紫色で塗りつぶす
self._display_surf.fill("purple")
# プレイヤーを赤い円として描画
pygame.draw.circle(self._display_surf, "red", self.player_pos, 40)
# ディスプレイを更新
pygame.display.flip()
def on_cleanup(self):
# pygameを終了
pygame.quit()
def on_execute(self):
# 初期化処理
if self.on_init() == False:
self._running = False
# メインゲームループ
while( self._running ):
# イベント処理
for event in pygame.event.get():
self.on_event(event)
# ゲームロジック更新
self.on_loop()
# 描画処理
self.on_render()
# FPSを60に制限し、経過時間を計算
self.dt = self.clock.tick(60) / 1000
# 終了処理
self.on_cleanup()
if __name__ == "__main__" :
# アプリケーションのインスタンスを作成して実行
theApp = App()
theApp.on_execute()
あとは公式ドキュメントの上のリンクや、いろいろなチュートリアル集を見つつ試していく感じかな。
公式ドキュメント
いろいろなチュートリアルへのリンク集
GitHubにいろいろサンプルコードがある。
今回知りたいオーディオ関連はこのへんかな。
-
audiocapture.py
マイクからの音声を録音し、再生する例。 -
music_drop_fade.py
音楽ファイルをドロップして、再生時にフェード効果を適用する方法を示しています。 -
playmus.py
コマンドラインから音楽ファイルを再生するためにmixerモジュールを使用する例です。 -
sound.py
サウンドエフェクトや音楽の実装方法を紹介しています。 -
sound_array_demos.py
エコーやディレイなど、配列を使った音処理の例を示しています。
ドキュメントだと多分これ
ちょっと自分がイメージしていた使い方とはやっぱり違うかもしれない。
また気が向いたらもう少し試してみる。
改めてオーディオ周りを触ってみる。
まず、pygame.mixer
だけど、クラス構成的にはこうなるみたい
-
pygame
-
mixer
: ミキサーサブモジュール-
Sound
: 効果音クラス。オーディオデータをメモリに読み込んで扱う。短い効果音・サウンドエフェクト再生向け。 -
music
: 音楽制御用の関数モジュール。ストリーミング再生や長時間のオーディオデータ向け。
-
-
pygame.mixer.Sound
で効果音を使ってみる。
以下はキー入力があれば効果音を再生する例。効果音は以下のサイトのものを使用させていただいた。
import sys
import pygame as pg
# 初期化
pg.init()
pg.mixer.init()
# キー入力を検出するにはウインドウを作成する必要がある
screen = pg.display.set_mode((400, 300))
pg.display.set_caption("pygame.mixer.Soundの効果音再生サンプル")
# サウンドの読み込み
sound_start = pg.mixer.Sound("クイズ出題1.mp3")
sound_correct = pg.mixer.Sound("クイズ正解1.mp3")
sound_incorrect = pg.mixer.Sound("クイズ不正解1.mp3")
running = True
while running:
for event in pg.event.get():
if event.type == pg.QUIT:
running = False
if event.type == pg.KEYDOWN:
if event.key == pg.K_s:
print("問題です!")
sound_start.play()
elif event.key == pg.K_c:
print("正解!")
sound_correct.play()
elif event.key == pg.K_i:
print("不正解!")
sound_incorrect.play()
elif event.key == pg.K_ESCAPE:
running = False
pg.quit()
sys.exit()
実行すると、ウインドウが開いて、キーボードからs
/ c
/ i
を入力すればそれぞれ効果音が流れる。
効果音向け、といっても、別に長い音声ファイルが再生できないわけではない。以下のサンプルを少し変更。
import sys
import pygame as pg
# 初期化
pg.mixer.init()
# 音声ファイルの読み込み(1分程度)
sound = pg.mixer.Sound("sample_1min.wav")
# 再生開始
print("再生開始...")
channel = sound.play()
# 再生が終了するまで待機
while channel.get_busy():
print("...再生中...")
pg.time.wait(1000) # 1秒待機
print("再生終了。")
pg.quit()
実行してみると以下のようになる。get_busy()
で再生中かどうかを確認できる。
pygame 2.6.1 (SDL 2.28.4, Python 3.12.8)
Hello from the pygame community. https://www.pygame.org/contribute.html
再生開始..
...再生中...
...再生中...
...再生中...
...再生中...
(snip)
...再生中...
...再生中...
...再生中...
...再生中...
再生終了。
プレイヤー的なものもこんな感じで
import sys
import pygame as pg
# 初期化
pg.init()
pg.mixer.init()
# ウィンドウの作成(キー入力検出のため)
screen = pg.display.set_mode((400, 200))
pg.display.set_caption("Sound Controller")
# 音声ファイルの読み込み
sound = pg.mixer.Sound("sample_1min.wav")
# 状態管理
paused = False
running = True
while running:
for event in pg.event.get():
if event.type == pg.QUIT:
running = False
elif event.type == pg.KEYDOWN:
if event.key == pg.K_SPACE:
if not pg.mixer.get_busy():
sound.play()
paused = False
print("再生開始")
elif not paused:
pg.mixer.pause()
paused = True
print("一時停止")
else:
pg.mixer.unpause()
paused = False
print("再開")
elif event.key == pg.K_s:
pg.mixer.stop()
playing = False
paused = False
print("停止")
elif event.key == pg.K_ESCAPE:
running = False
pg.quit()
sys.exit()
pygame.mixer.music
を使用してみる。
``pygame.mixer(.Sound)`との違いとしては、上の方で述べた通りユースケースが長時間オーディオデータ向けで、あと
- 巻き戻し
- 再生位置の指定
- プレイリストへの追加
- ボリュームの指定
-
ogg
やmp3
が使える(Sound
だとwav
のみっぽい)
あたりが使えるところかな?
以下にサンプルがある。
余計なものを全部取っ払ってみた。
import sys
import pygame as pg
# 初期化
pg.init()
pg.mixer.init()
# ウィンドウの作成(キー入力検出のため)
screen = pg.display.set_mode((400, 200))
pg.display.set_caption("Sound Controller")
# 音声ファイルの読み込み
pg.mixer.music.load("voice_lunch_jp_1min.wav")
# 状態管理
paused = False
running = True
while running:
for event in pg.event.get():
if event.type == pg.QUIT:
running = False
elif event.type == pg.KEYDOWN:
if event.key == pg.K_SPACE:
if not pg.mixer.music.get_busy() and not paused:
pg.mixer.music.play()
paused = False
print("再生開始")
elif not paused:
pg.mixer.music.pause()
paused = True
print("一時停止")
else:
pg.mixer.music.unpause()
paused = False
print("再開")
elif event.key == pg.K_r:
pg.mixer.music.rewind()
print("巻き戻し")
elif event.key == pg.K_s:
pg.mixer.music.stop()
playing = False
paused = False
print("停止")
elif event.key == pg.K_ESCAPE:
running = False
pg.quit()
sys.exit()
再生状態を知れるってのが使い勝手が良さそう。