フェーズ 7 BGM を作って流す

step0:星空を飛ぶ宇宙船

トップページstep0 概要 / フェーズ 7

ゴール

リソースエディタで BGM を自分で作り、ゲーム中ずっと流し続けます。 フェーズ 4 で絵を描いたのと同じ assets.pyxres に、今度は音を入れます。

今回はコードよりエディタが主役 書き足すコードはたった 1 行です。時間の大半は音づくりに使います。 そのかわり、この 1 行が入ると作品の印象が一気に変わります。

1. 考え方

1-1. 「サウンド」と「ミュージック」は別のもの

Pyxel の音は 2 段構えになっています。ここを取り違えると「作ったのに鳴らない」となります。

用語正体
サウンド
(SOUND)
フレーズ 1 本。音符を横に並べたもの。 メロディ、ベース、効果音などを 1 本ずつ作る 0 〜 63
ミュージック
(MUSIC)
組み立て表。4 本のチャンネルそれぞれに、 どのサウンドを順に鳴らすかを書いたもの 0 〜 7
チャンネル
(CH)
同時に鳴らせる音の本数。1 チャンネルにつき同時に 1 音だけ。 Pyxel は 4 本あるので、和音や伴奏は最大 4 声まで 0 〜 3
① サウンド(フレーズ) SOUND 0 メロディ SOUND 1 ベース SOUND 2 〜 63 ② ミュージック(組み立て表) MUSIC 0 CH0 0 CH1 1 CH2 (空) CH3 (空) ③ 再生 pyxel.playm( 0, loop=True) ♪ 4 声まで同時に鳴る サウンドを作っただけでは鳴らない。ミュージックのチャンネルへ置いて、はじめて曲になる
フレーズ(SOUND)を作り、組み立て表(MUSIC)へ置き、playm で鳴らす

1-2. 音符 1 つが持っている 4 つの情報

サウンドの中では、1 つの音符が 4 つの情報を持っています。 エディタの画面にも、この 4 つがそのまま並んでいます。

項目意味入力に使うキー
NOTE
(音の高さ)
ドレミ。休符も置ける ピアノ鍵盤のキー + Enter、または画面をクリック
TON
(音色)
T 三角波(やわらかい)/S 矩形波(ファミコンらしい)/ P パルス波(細い)/N ノイズ(打楽器) T S P N
VOL
(音量)
0(無音)〜 7(最大) 07
EFX
(効果)
N なし/S スライド/V ビブラート/ F フェードアウト/H 半分でフェード/Q 4 分の 1 でフェード N S V F H Q

そして、サウンド全体にかかる設定が SPEED(1 音の長さ)です。 120 で 1 音がちょうど 1 秒。数字が小さいほど速くなります。 初期値の 30 なら 1 音 0.25 秒なので、16 音で 4 秒のループになります。

TON・VOL・EFX は 1 個だけ入れれば全体に効く この 3 つは、音符の数より短ければ先頭から繰り返して使われます。 つまり P を 1 個入れておけば、16 音すべてがパルス波になります。 16 個ぶん打ち込む必要はありません。 (空のままでも鳴ります。その場合は T7N と同じ扱いです)

2. リソースエディタで BGM を作る

フェーズ 4 と同じコマンドで、同じファイルを開きます。

pyxel edit step0/assets.pyxres

2-1. 画面の見かた(SOUND タブ)

右上のタブ、または Alt + / SOUND タブへ移動します。

タブ(IMAGE / TILEMAP / SOUND / MUSIC)と 保存ボタン SOUND 0 SPEED 30 L 再生 / 停止 / ループ OCTAVE ピアノ ロール (NOTE) TON VOL EFX P 5 N 3 行の 設定欄
SOUND タブの見取り図。 でピアノロールと TON / VOL / EFX の行を行き来し、 でステップを移動する

2-2. メロディを作る(SOUND 0)

次の順で操作します。

  1. 左上の SOUND0SPEED30 であることを確認する
  2. OCTAVE を 2 にする(次の「OCTAVE の合わせ方」を参照)
  3. 下の打鍵表のとおり、キーを押して Enter を 16 回くり返す

OCTAVE の合わせ方

OCTAVE は「今、キーボードのキーがどの高さの音に割り当てられているか」の設定です。 キーは 2 オクターブぶん(下段 12 音+上段 12 音)しかないので、 それを 5 オクターブある音域のどこに当てるかを、この設定で決めます。

OCTAVE下段(Z S X D C V G B H N J M上段(Q 2 W 3 E R 5 T 6 Y 7 U
3C3 〜 B3C4 〜 B4(いちばん高い)
2C2 〜 B2C3 〜 B3
1C1 〜 B1C2 〜 B2
0C0 〜 B0(いちばん低い)C1 〜 B1
OCTAVE の数字は、画面のどこにも表示されません ここが分かりにくいところです。今いくつなのかは、 画面の左右にある細いバーの、明るい部分の位置でしか読み取れません。 バーは上から順に 3210 の 4 段になっていて、 明るい帯が今の OCTAVE を示します(上ほど高い音)。
画面の左右にある細いバー(明るい帯の位置で読み取る) OCTAVE 3 OCTAVE 2 OCTAVE 1 OCTAVE 0 ▲ 今回はこれ 高い 低い 明るい帯は キーで届く 2 オクターブ ぶんの範囲
明るい帯の縦の長さは、ちょうど 2 オクターブぶん。ピアノロールのうち「今キーで音を置ける範囲」を示しています
確実に 2 に合わせる方法 バーの位置を目で読むのが不安なら、数えて合わせられます。 OCTAVE は 0 より下、3 より上には行かない(それ以上押しても変わらない)ので、
  1. PageDown4 回以上押す → 確実にいちばん下(0)になる
  2. そこから PageUpちょうど 2 回押す → 2 になる
なお、エディタを開いた直後は 2 です。まだ PageUp / PageDown を 押しておらず、バーをクリックもしていなければ、そのままで大丈夫です。
耳で確かめる方法 OCTAVE 2 のとき、N キーの音は A2 = 440 Hz。 楽器のチューニングに使われる、いちばん有名な「ラ」の音です。 キーを押している間は音が鳴る(=試聴できる)ので、押してみて 高すぎず低すぎない、はっきりした「ラ」に聞こえれば合っています。
もし OCTAVE がずれていても、曲は成立します 1 つずれると全体が高く(低く)なるだけで、メロディの形は変わりません。 打ち込んでみて「なんだか高すぎる」と思ったら、 Ctrl + Z でまとめて戻すか、OCTAVE を直して打ち直してください。 失敗しても壊れるものはありません。

鍵盤はパソコンのキーボードに割り当てられています。下段が今の OCTAVE、 上段がその 1 つ上の高さです。

OCTAVE で選んだ高さ その 1 つ上 S D G H J 2 3 5 6 7 Z X C V B N M Q W E R T Y U C D E F G A B C D E F G A B 音名 キー A キー = 休符(音を置かない) Enter = カーソル位置に置いて 1 つ進む
音名の「A(ラ)」は N キーです。A キーは休符なので、混同しないよう注意してください

この割り当てで、次の 16 ステップを打ち込みます。

ステップ押すキー入る音
1 / 2N EnterA Enterラ(A2)/ 休符
3 / 4Q EnterA Enterド(C3)/ 休符
5 / 6E EnterA Enterミ(E3)/ 休符
7 / 8W EnterA Enterレ(D3)/ 休符
9 / 10Q EnterA Enterド(C3)/ 休符
11 / 12N EnterA Enterラ(A2)/ 休符
13 / 14B EnterA Enterソ(G2)/ 休符
15 / 16A EnterA Enter休符 / 休符

続いて キーで TON の行へ移り、次の 3 つを入れます。

SPACE キーで試聴できます(もう一度押すと停止)。 L キーでループ再生に切り替わります。

2-3. ベースを作る(SOUND 1)

左上の SOUND の数字を 1 に変え、OCTAVE を 1 に下げますPageDown を 1 回。バーの明るい帯が 1 段ぶん下へ動きます)。 今度は 4 拍に 1 回だけ、低い音を置きます。

ステップ押すキー入る音
1 〜 4N EnterA Enter ×3ラ(A1)+ 休符 3 つ
5 〜 8V EnterA Enter ×3ファ(F1)+ 休符 3 つ
9 〜 12Q EnterA Enter ×3ド(C2)+ 休符 3 つ
13 〜 16C EnterA Enter ×3ミ(E1)+ 休符 3 つ
この音の並びについて メロディは「ラ・ド・ミ・レ・ド・ラ・ソ」、ベースは「ラ → ファ → ド → ミ」です。 暗めで宇宙っぽい響きになるよう選んでいますが、気に入らなければ自由に変えて構いません。 むしろ、自分で音を置き換えてみるのがいちばんの練習になります。 なぜこの並びなのか、自分で曲を作るにはどうすればよいかは、 別ページ BGM の作り方 にまとめました。

2-4. MUSIC タブで組み立てる

Alt + MUSIC タブへ移ります。 画面は上から CH0 〜 CH3 の 4 段、その下に サウンド選択ボタン(0 〜 63)が並んでいます。

MUSIC 0 SPACE で再生 CH0 0 CH1 1 CH2 CH3 サウンド選択ボタン(クリックでカーソル位置に入る) 0 1 2 3 4 5 6 7 …… 63 まで並ぶ ボタンの上にマウスを乗せると、そのサウンドが試聴できる
CH0 にサウンド 0(メロディ)、CH1 にサウンド 1(ベース)を置く
  1. 左上の MUSIC0 であることを確認する
  2. CH0 の左端をクリックしてカーソルを置き、下のボタンの 0 をクリックする
  3. CH1 の左端にカーソルを置き、下のボタンの 1 をクリックする
  4. SPACE で再生して、メロディとベースが重なって鳴ることを確かめる
  5. Ctrl + S で保存する
保存を忘れると鳴りません エディタの中では鳴っているのに、ゲームを実行すると無音 —— という失敗の いちばんの原因が保存忘れです。Ctrl + S を必ず押してください。

3. このフェーズで使う API

API意味
pyxel.playm(msc, loop=False) ミュージック msc(0 〜 7)を再生する。 loop=True でくり返し
pyxel.play(ch, snd, loop=False) チャンネル ch(0 〜 3)でサウンド snd(0 〜 63)を鳴らす。 効果音向け(step1 で使います)
pyxel.stop() すべてのチャンネルを止める。pyxel.stop(0) のように チャンネルを指定することもできる
音は pyxel.load ですでに読み込まれています フェーズ 5 で書いた pyxel.load("assets.pyxres") は、 絵だけでなく音も一緒に読み込みます。だから今回、読み込みのコードは足しません。

4. コードを書き換える

4-1. pyxel.run の直前に 1 行足す

        self.ship_x = (SCREEN_WIDTH - SHIP_WIDTH) // 2
        self.ship_y = 80

        pyxel.playm(0, loop=True)       # ← 追加:BGM をループ再生する

        pyxel.run(self.update, self.draw)

変更はこれだけです。

4-2. 実行して確認

python step0/star_voyager.py

5. コードの解説

なぜ __init__ に書くのか

これは今回いちばん大事な注意点です。 pyxel.playm は「再生を始めろ」という命令であって、 「鳴らし続けろ」ではありません。1 回呼べば、あとは Pyxel が勝手に鳴らし続けます。

もし update の中に書くと、1 秒間に 30 回「頭から鳴らし直せ」と命令することになります。 その結果、曲の先頭の一瞬だけが延々とくり返され、 ブツブツと途切れた音になります。音が出るのに曲にならないときは、まずここを疑ってください。

公式サンプルも同じ書き方です Pyxel 付属の 02_jump_game.py でも、 pyxel.playm(0, loop=True)pyxel.run(...) の直前、 __init__ の最後に置かれています。 (pyxel/examples/ フォルダにあります。備忘録の「付属のサンプルはどこにある?」を参照)

loop=True を忘れるとどうなるか

loop の初期値は False です。省略すると 4 秒で曲が終わり、 そのあとは無音のままになります。BGM には必ず loop=True を付けます。

playplaym の使い分け

playmplay
鳴らすものミュージック(最大 4 声)サウンド 1 本
チャンネルミュージックの指定どおり自分で番号を選ぶ
向いている用途BGM効果音(ジャンプ音、爆発音)

step1 の「お菓子を取る音」「お化けに捕まる音」は pyxel.play で鳴らします。 そのとき、BGM が使っていないチャンネル(今回なら 2 か 3)を選ぶのがコツです。 BGM と同じチャンネルを使うと、効果音が鳴っているあいだ BGM のその声部が止まります。

SPEED と曲の長さの関係

SPEED は 1 音ぶんの長さで、120 で 1 秒です。したがって —

1 音の長さ = SPEED ÷ 120 秒
曲の長さ   = 1 音の長さ × 音符の数

今回: 30 ÷ 120 = 0.25 秒 × 16 音 = 4 秒

メロディとベースの SPEED をそろえ、音符の数もそろえておくと、 ループしたときにずれません。片方だけ 8 音にすると、 ベースが 2 周するあいだにメロディが 1 周する形になります(それが狙いなら、それでも構いません)。

6. 試してみよう

音は耳で確かめるのがいちばんです。1 つずつ変えて聴き比べてください。

やってみること確かめたいこと
SOUND 0 の SPEED1560 に変える 倍速・半分の速さになる。曲の印象がいちばん大きく変わる設定
TONS(矩形波)や N(ノイズ)に変える 同じ音符でも音色でまったく別の曲に聞こえる。N は打楽器の音になる
VOL72 に変える 音量が変わる。0 にすると無音になる
EFXV(ビブラート)や F(フェードアウト)に変える 音の伸び方が変わる。F は音の終わりが自然に消える
コードの loop=True を消す 4 秒で BGM が終わり、そのあと無音になる
MUSIC の CH2 にもサウンドを置く 3 声になる。ノイズ(N)で 16 分のリズムを作るとドラムになる
MUSIC 0 の CH0 に 02 つ並べる メロディが 2 回くり返される。1 曲を複数のサウンドでつなげられる

ミュート機能を付けてみる(応用)

フェーズ 6 で覚えた pyxel.btnp の出番です。 M キーで BGM の入/切を切り替えられるようにします。まず __init__ に 1 行足します。

        self.bgm_on = True          # BGM が鳴っているかどうか
        pyxel.playm(0, loop=True)

そして update の先頭に、次を足します。

        if pyxel.btnp(pyxel.KEY_M):     # M を押した瞬間だけ
            if self.bgm_on:
                pyxel.stop()            # 鳴っていたら止める
            else:
                pyxel.playm(0, loop=True)   # 止まっていたら鳴らす
            self.bgm_on = not self.bgm_on   # 状態を反転させる
not で反転させる書き方 self.bgm_on = not self.bgm_on は、True なら False に、 False なら True に切り替えるという意味です。 「オン/オフを切り替える」ときの定番の書き方で、 if を 2 つ書くより短くて読みやすくなります。 ここで btn ではなく btnp を使うのも大事な点です。 btn だと、押しているあいだ 1 秒に 30 回も切り替わってしまいます。

7. うまくいかないときは

症状原因と対処
まったく音が鳴らない エディタで Ctrl + S による保存を忘れている。 または MUSIC のチャンネルにサウンド番号を置いていない
エディタでは鳴るのに、ゲームでは無音 保存忘れ、または pyxel.playm を書いていない。 pyxel.run より後ろに書いた場合も鳴らない (run から先へは進みません)
ブツブツと途切れた音になる pyxel.playmupdate の中に書いている。 __init__ へ移す
4 秒で音が止まる loop=True を書いていない
メロディだけ、ベースだけしか鳴らない MUSIC の CH0 と CH1 の両方に置けているか確認する。 片方の VOL が 0 になっていないかも確認する
2 つの音が少しずつずれていく SOUND 0 と SOUND 1 の SPEED が違う、 または音符の数が違う。どちらもそろえる
音が高すぎる/低すぎる OCTAVE の設定が違う状態で打ち込んでいる。 PageUp / PageDown で変えられる
ピアノロールに音符が入らない キーを押しただけでは入りません。Enter を押して確定します。 また、カーソルが TON / VOL / EFX の行にあると音符は入りません ( でピアノロールの行に戻す)
「ラ」を入れたいのに A キーで休符になる 音名の A(ラ)は N キーです。A キーは休符の割り当てです
間違えた音符を消したい BackSpace(手前を削除)または Delete(カーソル位置を削除)。 Ctrl + Z で元に戻すこともできます
パソコンからは音が出るのに聞こえない OS 側の音量・ミュート、出力先のデバイスを確認する
次のフェーズ フェーズ 8 は最後の仕上げです。pyxel package で作品を 1 つのファイルにまとめ、 pyxel app2html で HTML 化して、GitHub Pages で公開します。 ブラウザで遊べる形になり、URL を渡すだけで人に見てもらえるようになります。