タイトル画像
琴葉茜

最後はカスタマイズについて色々やってみようか

琴葉茜

今回はゲーム配信などでOBSのクロマキー合成(透過)にそのまま使えるように、「配信向けグリーンバック演出」にアレンジしていくよ!

琴葉葵

YouTubeとかの配信で、大きな声を出した回数を画面に表示するやつだね!かっこいい!

琴葉茜

まずは、完成したプログラムを動かしてみよう!

琴葉葵

オッケー!

import numpy as np
import sounddevice as sd
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import japanize_matplotlib

SAMPLE_RATE = 44100
BLOCK_SIZE = 1024
HISTORY_LEN = 100
THRESHOLD = 0.15
COOLDOWN_FRAMES = 15
GREEN_BACK = "#00FF00"


class StreamingCounter:
    """マイクの音量を監視し、配信向けの見た目でカウントを表示するクラス"""

    def __init__(self, fig, ax, threshold=THRESHOLD, cooldown_frames=COOLDOWN_FRAMES,
                 history_len=HISTORY_LEN):
        self.threshold = threshold
        self.cooldown_frames = cooldown_frames
        self.cooldown = 0
        self.count = 0

        self.history_len = history_len
        self.volume_history = [0.0] * history_len
        self.current_volume = 0.0

        fig.patch.set_facecolor(GREEN_BACK)
        ax.set_facecolor(GREEN_BACK)

        self.line, = ax.plot(self.volume_history, color="white", linewidth=2)
        ax.axhline(threshold, color="black", linestyle="--", label="閾値")
        ax.set_ylim(0, 1.0)
        ax.set_title("Step6: 配信向けグリーンバック演出")
        ax.legend(loc="upper right")

        self.count_text = ax.text(
            0.02, 0.92, "", transform=ax.transAxes,
            fontsize=16, color="white", fontweight="bold"
        )

    def audio_callback(self, indata, frames, time, status):
        self.current_volume = float(np.sqrt(np.mean(indata ** 2)))

    def update(self, frame):
        self.volume_history.append(self.current_volume)
        self.volume_history.pop(0)
        self.line.set_ydata(self.volume_history)

        if self.cooldown > 0:
            self.cooldown -= 1
        elif self.current_volume > self.threshold:
            self.count += 1
            self.cooldown = self.cooldown_frames

        if self.current_volume > self.threshold:
            self.line.set_color("red")
        else:
            self.line.set_color("white")

        self.count_text.set_text(f"カウント: {self.count}")
        return self.line, self.count_text


fig, ax = plt.subplots()
counter = StreamingCounter(fig, ax)

stream = sd.InputStream(
    channels=1, samplerate=SAMPLE_RATE, blocksize=BLOCK_SIZE,
    callback=counter.audio_callback,
)

with stream:
    ani = animation.FuncAnimation(fig, counter.update, interval=50)
    plt.show()
配信向けグリーンバック演出
琴葉葵

おおー!背景が緑色になって、普段は白いグラフの線が、大声を出すと一瞬「赤色」に変わる!
一目で「あ、今叫んだな!」ってわかるからすごく見やすいね!

琴葉茜

そうでしょ?

琴葉茜

今回は「見た目の演出」を強化したんだけど、コードのポイントは「Matplotlibのグラフ構造のパーツ分け」と「動的な色の変更」の2点やね

琴葉葵

パーツ分け...?

琴葉茜

そう!

琴葉茜

Matplotlibのグラフって、実は複数のパーツが重なってできてるんだよね
1. ウィンドウ全体の外枠にあたる「fig」
2. 目盛りや軸があるグラフの描画エリア「ax」
3. データを表す線そのものの「line」

琴葉葵

あ、今まで普通に「fig, ax = plt.subplots()」についてはなんとなくで流してたけど、外枠と描画エリアで別物なんだ!

琴葉茜

そういうこと!

琴葉茜

だから「背景をグリーンバックにする」時は、29行目の「fig.patch.set_facecolor(GREEN_BACK)」で外枠の色を変えて、30行目の「ax.set_facecolor(GREEN_BACK)」で内側のエリアの色を変えてるんだよ

GREEN_BACK = "#00FF00"

fig.patch.set_facecolor(GREEN_BACK)  # ウィンドウ全体(外枠)
ax.set_facecolor(GREEN_BACK)         # グラフ描画エリア
琴葉葵

片方だけじゃなくて、両方の背景色を緑に変えることで画面全体がグリーンバックになるんだね!

琴葉茜

そして2つ目のポイントは、大声をあげる(閾値を超えた)瞬間にグラフの線の色を変える演出!
57〜60行目を見てほしいんだけど、「update()」の中で「音量が閾値を超えている部分」ではなく「今までに」でもなく「今まさに音量が閾値を超えているか」を判定して、「self.line.set_color("red")」と「self.line.set_color("white")」でリアルタイムに線全体の色を切り替えているんだよ

if self.current_volume > self.threshold:
    self.line.set_color("red")
else:
    self.line.set_color("white")
琴葉葵

あ!カウント用の「self.cooldown」とは別に、純粋に「今の音量」を見て色を変えてるんだね!

琴葉茜

そういうこと

琴葉茜

カウント判定は冷却時間で連打を防ぎつつ、見た目の演出は音量に合わせて毎フレーム即座に反応するように設計してるわけだね!

琴葉葵

演出と判定のロジックがしっかり分けられててすごく綺麗!

琴葉茜

まとめると、今回のポイントは
1. OBS等のクロマキー合成用に背景を緑にする(「fig.patch.set_facecolor」&「ax.set_facecolor」, 29-30行目)
2. 閾値超過時に線の色を白から赤に動的変更して視覚的効果を高める(「self.line.set_color()」, 58, 60行目)
の2点やね!

琴葉葵

これでついに、マイク音量をリアルタイムで監視してカウント&演出する配信アプリが完成したね!

琴葉茜

だね!

琴葉茜

今回は標準マイクを使ったシンプルな構成で解説してきたけど、Step3で紹介したマイク選択処理を追加したり、他のGUIライブラリと組み合わせたり、色々なカスタマイズができるよ!ぜひ自分だけのアレンジにも挑戦してみてね!

琴葉葵

はーい!とりあえずまずは、これを使ってホラーゲームの実況配信をしてみようかな!