アカベコマイリ

HEAR NOTHING SEE NOTHING SAY NOTHING

C# で音楽再生 5

C# で音楽再生を行ってみるシリーズその 5。

今回は Bass.Net の機能を利用してエフェクターを実装する。エフェクターを実装といっても音響処理に関する専門知識はほとんど不要。Bass.Net に用意されたクラスを利用するだけで簡単に音響効果を楽しめる。

サンプル プログラム

プログラムを実行すると以下のようになる。前面の小さなウィンドウがエフェクター。モードレスで作成しているのでエフェクター表示しながらプレイヤーを操作可能。エフェクタを見分けやすいように背景色を個別にしてある。

サンプル プログラム

プロジェクト一式は以下。ビルドには Visual Studio 2008 SP1 と Bass.Net、プログラムの実行には NET Framework 3.5 SP1 が必要となる。

エフェクターとは?

エフェクターとは音声に様々な音響効果を与える機器や機能である。原音がスピーカーや録音機器に出力されるまでの経路にエフェクターを挿入することで音声データを変質させる。

代表的なものとしては、エコーやディレイなどの空間系、ディストーションやオーバードライブなどの歪み系、前回の記事で作成したイコライザーのような補正系などが挙げられる。

Bass.Net にはエフェクターの種類毎に専用クラスを提供している。これらのインスタンスを音声ストリームに関連付ける事で音響効果を発生させられる。

エフェクターの実装

Bass.Net に用意されたエフェクターは FX 系 と DSP 系に大別される。前者は BASS_DX8_ というクラス名を持ち Bass.BASS_ChannelSetFXBass.BASS_ChannelRemoveFX で音声ストリームとの関連付けを切り替える。後者はクラスのコンストラクタに音声ストリームのハンドルを指定する事で関連付けられ SetBypass メソッドで有効・無効を切り替える。

これらに共通する機能として有効・無効の切り替えがあるので以下のようにインターフェース化しておく。

/// <summary>
/// 音響効果の共通インターフェースです。
/// </summary>
interface IEffect
{
    /// <summary>
    /// 音響効果が有効である事を示す値を取得または設定します。
    /// </summary>
    bool IsEnabled { get; set; }
}

FX 系は以下のように実装。

/// <summary>
/// 音声のコーラス効果を提供します。
/// </summary>
class EffectChorus : IEffect
{
    /// <summary>
    /// インスタンスを初期化します。
    /// </summary>
    /// <param name="stream">ストリームの識別子。</param>
    public EffectChorus( int stream )
    {
        this._effect = new BASS_DX8_CHORUS( EffectChorus.DefaultWetDryMix, EffectChorus.DefaultDepth, EffectChorus.DefaultFeedback, 5f, 1, EffectChorus.DefaultDelay, BASSFXPhase.BASS_FX_PHASE_NEG_90 );
        this._stream   = stream;
    }

    /// <summary>
    /// 音響効果を更新します。
    /// </summary>
    private void UpdateEffect()
    {
        if( this.IsEnabled )
        {
            Bass.BASS_FXSetParameters( this._handle, this._effect );
        }
    }

    /// <summary>
    /// 音声の遅延時間を取得または設定します。
    /// </summary>
    public float Delay
    {
        get
        {
            return this._effect.fDelay;
        }
        set
        {
            this._effect.fDelay = value;
            this.UpdateEffect();
        }
    }

    // ...以下、パラメーター毎にプロパティ定義が続く

    /// <summary>
    /// 音響効果。
    /// </summary>
    private BASS_DX8_CHORUS _effect;

    /// <summary>
    /// 音響効果の識別子。
    /// </summary>
    private int _handle;

    /// <summary>
    /// ストリームの識別子。
    /// </summary>
    private int _stream;

    #region IEffect メンバ

    /// <summary>
    /// 音響効果が有効である事を示す値を取得または設定します。
    /// </summary>
    public bool IsEnabled
    {
        get
        {
            return ( this._handle != 0 );
        }
        set
        {
            if( this._handle == 0 )
            {
                if( value )
                {
                    this._handle = Bass.BASS_ChannelSetFX( this._stream, BASSFXType.BASS_FX_DX8_CHORUS, 1 );
                    Bass.BASS_FXSetParameters( this._handle, this._effect );
                }
            }
            else if( !value )
            {
                Bass.BASS_ChannelRemoveFX( this._stream, this._handle );
                this._handle = 0;
            }
        }
    }

    #endregion
}

エフェクター用オブジェクトをコンストラクタで生成しておき IsEnabled 内で関連付けを切り替える。他の FX 系エフェクターも、プロパティとエフェクター用オブジェクトの型が異なるだけで処理としてはほぼ共通。

DSP 系は以下のように実装。

/// <summary>
/// 音声のディレイ効果を提供します。
/// </summary>
class EffectDelay : IEffect
{
    /// <summary>
    /// インスタンスを初期化します。
    /// </summary>
    /// <param name="stream">ストリームの識別子。</param>
    public EffectDelay( int stream )
    {
        this._effector = new DSP_IIRDelay( stream, 0, 2f );
    }

    /// <summary>
    /// サンプリング数を取得または設定します。
    /// </summary>
    public int Delay
    {
        get
        {
            return this._effector.Delay;
        }
        set
        {
            this._effector.Delay = value;
        }
    }

    // ...以下、パラメーター毎にプロパティ定義が続く

    /// <summary>
    /// 音響効果。
    /// </summary>
    private DSP_IIRDelay _effector;

    #region IEffect メンバ

    /// <summary>
    /// 音響効果が有効である事を示す値を取得または設定します。
    /// </summary>
    public bool IsEnabled
    {
        get
        {
            return !this._effector.IsBypassed;

        }
        set
        {
            this._effector.SetBypass( !value );
        }
    }

    #endregion
}

FX 系と同様に IEffect インタフェースを実装しているが内容は異なる。FX 系は音声ストリームにエフェクトを抜き差しするイメージ。DSP 系は繋いだままエフェクターの ON/OFF スイッチを切り替えるような感じとなる。IsBypassedSetBypass はすり抜けるという意味になるので注意が必要。真偽値としては true ですり抜け false で経由となる。エフェクトを有効にする場合は SetBypassfalse を指定しなければならない。

エフェクターの各種パラメーターには適正な範囲が厳密に定められている。Bass.Net のヘルプに詳しく解説されているので、それにならいプロパティのセッターなどで範囲チェックすること。サンプル プロジェクトでは入力範囲を MVVM の View の XAML に定義しているが、これは以下のようにした方がよいかも。

  • Model に上限・下限の定数を持たせる
  • ViewModel に上限・下限取得用プロパティを定義して Model の定数を返す
  • View の Slider.Maximum などは ViewModel の上限・下限プロパティにバインドする

けっこう冗長な箇所が残っており、まだまだ最適化の余地 ( 例えばパラメータ自体を ViewModel 化してエフェクタの UI はそのコレクションとして表現する、など ) はあるが、今回はここまで。

楽曲紹介

サンプル プログラムのスクリーンショットに映っている楽曲は Otis Redding の The Immortal Otis Redding から。

このアルバムは彼の作品で初めて聴いたものなので思い入れが深い。Otis が飛行機事故で急逝した後、MG's が録り貯められた音源から完成させたので The Immortal と名付けられている。Queen の Made in Heaven を彷彿とさせるエピソードだ。

以下は在りし日の Otis。

Otis をリスペクトしているアーティストは多い。日本では忌野清志郎が有名だ。彼の歌唱スタイルには Otis の影響が色濃いし歌詞に登場する事もある。20 年ぐらい前の月刊カドカワで Otis ゆかりの地であるメンフィスを訪れた時の事を嬉しそうに語っていたのを覚えている。確か「エスカレーターにまでオーティスって書いてあった」とか言っていた。Otis Elevator Company と引っかけたジョークだろう。

MAGMA の Christian Vander も、John Coltrane と共に Otis への憧憬を表している。死と再生をテーマにした Merci というアルバムの中に Otis と Otis (Ending) という曲がワンセットになっているのも The Immortal Otis Redding と関係していそうである。以下は MAGMA の Otis。Christian Vander はドラムや作曲だけでなくボーカルも素晴らしい。

https://www.youtube.com/watch?v=hVvOhRI2f6E

Comments from WordPress

  • kentyabi 2010-04-23T08:49:27Z

    初めまして。

    この記事のソースコードがとてもきれいで感動したので、勉強のついでに勝手に機能を追加してみました。

    アカベコさんと同じようにブログで解説してみたので、何かあったら言ってください。

  • akabeko akabeko 2010-04-23T11:44:31Z

    解説を読ませていただきました。このシリーズでは「あるていど使いものになるプレーヤーを作る」という点を重視しているので、こういう反応はとても嬉しく、ブログを書いていてよかったと思いました。

  • TAKA TAKA 2018-04-30T11:30:27Z

    初めまして。

    8年ほど前の記事ですがコメントさせていただきます。

    僕は今音楽プレイヤーを作っているのですが、なかなかうまくいかず色々調べた結果アカベコさんのブログにたどり着きました。

    そこで第3回の記事を見て、Bassをダウンロードしてインストールしようと思ったのですが、バージョンが変わっているようでsetup.exeがなく、自分なりにいろいろ試しましたがうまくいきませんでした...

    なので、お時間ございましたら最新バージョンのインストール方法を教えていただきたいです。

    よろしくお願いします。

  • アカベコ アカベコ 2018-05-01T05:07:14Z

    http://bass.radio42.com/ の Download から入手した ZIP を展開するとあらわれる readme.txt によると、自身の必要な環境に応じたバージョンの DLL を組み込めばよいようです。

    Windows の Visual Studio で開発しているのなら .NET Framework のバージョンにあったもの、例えば v4.5 フォルダー内の Bass.Net.dll をプロジェクトから参照するように設定することになるでしょう。

    方法: 参照マネージャーを使用して参照を追加または削除する

    https://msdn.microsoft.com/ja-jp/library/hh708954.aspx

  • TAKA TAKA 2018-06-12T12:25:08Z

    ありがとうございます。

    あと、初歩的なことで申し訳ないですが、ボタンをクリックして曲を再生したりするにはどうすればよいのでしょうか...?

    第3回の記事をくまなく呼んだのですがよくわかりませんでした...

    方法を教えていただけると幸いです。

  • アカベコ アカベコ 2018-06-14T12:17:52Z

    ボタンの処理については GUI 実装によります。考え方としては

    1. ボタンがクリックされた時に呼び出されるイベントを実装
    2. イベント (メソッド、関数) 内で音楽再生を実行

    となります。一般的には GUI と内部処理をなるべく疎結合となるように設計します。この記事のサンプルではいわゆる MVVM で設計しています。

    View (画面、GUI) が操作されると関連づいている ViewModel (View と Model を仲介する層) のイベントが実行され、その中から Model (アプリの本質的な処理、サンプルでは音楽再生など) にイベントの内容を伝えて処理が実行される、という流れです。

    文章やコードだけでは理解しにくいので、Visual Studio などの開発環境でコードの要所にブレーク ポイントをしかけてステップ実行しながら処理を追ってみることをオススメします。