3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

KinoMemberane で音声ファイルの変換、再生、合成処理を視覚化する

Last updated at Posted at 2024-11-14

はじめに

Membrane は音声や映像に対する処理をパイプラインで記述できるフレームワークです

例えば
「MP3 ファイルを Web からダウンロードする」
|>「RAW オーディオに変換する」
|>「AAC 形式に変換する」
|>「ブラウザで再生する」
といったようなパイプラインをストリーミングで実行します

KinoMembrane は Livebook 上で Membrane のパイプラインを図として視覚化します

文章では分かりにくいので、実行して結果を確認しましょう

Membrane と KinoMembrane の公式サンプルを参考にしていますが、 Docker 上で音声再生できるように変更しています

元のサンプル

https://github.com/membraneframework/kino_membrane/blob/master/examples/pipeline_in_livebook.livemd

https://github.com/membraneframework/membrane_demo/blob/master/livebooks/audio_mixer/audio_mixer.livemd

具体的には、ファイル読込は Web からの読込に、 PortAudio による音声再生をブラウザ上での音声再生に変更しています

実装したノートブックはこちら

環境構築

今回実行する処理は OS へのパッケージインストールが必要です

私は普段自分用の Docker コンテナで Livebook を起動しているので、 Dockerfile 上に依存パッケージを追加しました

Ubuntu の場合

sudo apt-get install \
    clang-format \
    ffmpeg \
    libavutil-dev \
    libswresample-dev \
    libmad0-dev \
    libfdk-aac-dev

macOS の場合

brew install \
    clang-format \
    ffmpeg \
    libmad \
    pkg-config \
    fdk-aac

セットアップ

KinoMembrane と Membrane の各種プラグインをインストールします

KinoMembrane と MembraneKinoPlugin が依存する Kino バージョンが食い違っているため、 Kino のバージョンを override: true で指定しています

Mix.install([
  {:kino, "~> 0.13", override: true},
  {:kino_membrane, "~> 0.3"},
  {:membrane_hackney_plugin, "~> 0.11"},
  {:membrane_ffmpeg_swresample_plugin, "~> 0.20"},
  {:membrane_mp3_mad_plugin, "~> 0.18"},
  {:membrane_aac_fdk_plugin, "~> 0.18"},
  {:membrane_audio_mix_plugin, "~> 0.16"},
  {:membrane_tee_plugin, "~> 0.12"},
  {:membrane_kino_plugin,
   github: "membraneframework-labs/membrane_kino_plugin", tag: "v0.3.2"}
])

Membrane や各種プラグインの関数を使うための準備をします

import Membrane.ChildrenSpec

alias Membrane.{
  AAC,
  AudioMixer,
  Hackney,
  MP3,
  RawAudio,
  RCPipeline,
  Tee,
  Time
}

alias Membrane.FFmpeg.SWResample.Converter

MP3 ファイルの再生

ブラウザ上に音声プレイヤーを作成します

MembraneKinoPlugin で定義されています

https://github.com/membraneframework-labs/membrane_kino_plugin

kino = Membrane.Kino.Player.new(audio: true)

コードを実行すると、以下のようなプレイヤーが表示されます

スクリーンショット 2024-11-13 19.30.55.png

再生する MP3 ファイルのダウンロード元を定義します

base_repo_url = "https://raw.githubusercontent.com/membraneframework/membrane_demo/master"

source_url = "#{base_repo_url}/simple_pipeline/sample.mp3"

音声処理のパイプラインを定義します

pipeline = RCPipeline.start_link!()

spec =
  child(:hackney, %Hackney.Source{
    location: source_url,
    hackney_opts: [follow_redirect: true]
  })
  |> via_in(:input, auto_demand_size: 10)
  |> child(:decoder, MP3.MAD.Decoder)
  |> child(:converter, %Converter{
    output_stream_format: %RawAudio{
      sample_format: :s16le,
      sample_rate: 48000,
      channels: 2
    }
  })
  |> child(:encoder_aac, AAC.FDK.Encoder)
  |> via_in(:audio)
  |> child(:player, %Membrane.Kino.Player.Sink{kino: kino})

パイプラインを実行すると、作っておいたプレイヤーで音声が再生されます

RCPipeline.exec_actions(pipeline, spec: spec)

いよいよ KinoMembrane の登場です

pipeline_dashboard を実行すると、パイプラインが図として表示され、各ブロックをクリックすると処理の状況を確認できます

KinoMembrane.pipeline_dashboard(pipeline)

:hackney:converter など、パイプラインで定義しておいた各処理が矢印で繋がっています

スクリーンショット 2024-11-13 21.18.48.png

処理中の状況がグラフで表示され、リアルタイムに更新されます

(グラフの X 軸ラベルはおかしいですが、、、)

スクリーンショット 2024-11-13 21.19.44.png

最後にパイプラインを終了しておきます

Membrane.Pipeline.terminate(pipeline)

音声を合成する

先ほどの例だと一直線だったので、もっと見た目に面白い例を実装します

まず、ビープ音(「ポーン」という感じの短い音)のダウンロード、デコード(RAW オーディオへの変換)を定義します

beep_url = "#{base_repo_url}/livebooks/audio_mixer/assets/beep.aac"

beep_audio_input =
  child(:beep_hackney, %Hackney.Source{
    location: beep_url,
    hackney_opts: [follow_redirect: true]
  })
  |> child({:beep_decoder_aac, :beep}, AAC.FDK.Decoder)
  |> child(:beeps, Tee.PushOutput)

ビープ音を 1 秒間隔で 30 回繰り返し入力するよう定義します

n_beeps = 30

beeps_split =
  for i <- 1..n_beeps do
    get_child(:beeps)
    |> via_in(:input, options: [offset: Time.seconds(i)])
    |> get_child(:mixer)
  end

BGM (MP3 ファイル)をダウンロードし、ビープ音と同じビット深度、サンプリングレートに合わせる変換を定義します

background_url = "#{base_repo_url}/livebooks/audio_mixer/assets/sample.mp3"

background_audio_input =
  child(:background_hackney, %Hackney.Source{
    location: background_url,
    hackney_opts: [follow_redirect: true]
  })
  |> child(:background_decoder_mp3, MP3.MAD.Decoder)
  |> child(:background_converter, %Converter{
    input_stream_format: %RawAudio{channels: 2, sample_format: :s24le, sample_rate: 48_000},
    output_stream_format: %RawAudio{channels: 1, sample_format: :s16le, sample_rate: 44_100}
  })
  |> get_child(:mixer)

新しいプレイヤーを用意します

mixer_kino = Membrane.Kino.Player.new(audio: true)

音声を合成し、 AAC 形式に変換してからプレイヤーで再生するよう定義します

mixer_output =
  child(:mixer, AudioMixer)
  |> child(:mixer_encoder_aac, AAC.FDK.Encoder)
  |> via_in(:audio)
  |> child(:mixer_player, %Membrane.Kino.Player.Sink{kino: mixer_kino})

ここまで個別に定義したパイプラインを結合します

mixer_spec = beeps_split ++ [beep_audio_input, background_audio_input, mixer_output]

パイプラインを実行すると、 BGM 上にビープ音が重なって再生されます

mixer_pipeline = RCPipeline.start_link!()

RCPipeline.exec_actions(mixer_pipeline, spec: mixer_spec)

パイプラインを視覚化してみましょう

KinoMembrane.pipeline_dashboard(mixer_pipeline)

ビープ音が大量に増えて、 BGM と合成してから再生される流れが分かりやすく視覚化されました

スクリーンショット 2024-11-13 21.35.36.png

こちらのパイプラインも終了させます

Membrane.Pipeline.terminate(mixer_pipeline)

まとめ

音声処理をパイプラインで記述し、その流れを図で表すことができました

Livebook による視覚化の好例ですね

3
3
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?