2 using System.Collections.Generic;
3 using System.Diagnostics;
7 namespace FDK.メディア.サウンド.WASAPI
11 /// 自身が ISampleSource であり、そのまま AudioClient のレンダリングターゲットに指定する。
13 internal class Mixer : CSCore.ISampleSource
16 /// 音量。0.0(無音)~1.0(原音)。
20 get { return this._Volume; }
23 if( ( 0.0f > value ) || ( 1.0f < value ) )
24 throw new ArgumentOutOfRangeException();
33 public CSCore.WaveFormat WaveFormat
35 get { return _WaveFormat; }
39 /// ミキサーはループするので、Position には 非対応。
44 set { throw new NotSupportedException(); }
50 public bool CanSeek => ( false );
53 /// ミキサーはループするので、長さの概念はない。
55 public long Length => ( 0 );
58 /// 指定したフォーマットを持つミキサーを生成する。
60 public Mixer( CSCore.WaveFormat deviceWaveFormat )
62 this._WaveFormat = deviceWaveFormat;
66 /// ミキサに登録されているサウンドをすべて停止し解放する。
73 foreach( var sampleSource in this._Sounds )
74 sampleSource.Dispose();
83 /// 追加されると同時に、Sound の再生が開始される。
85 public void AddSound( Sound sound )
88 throw new ArgumentNullException();
90 if( ( sound.SampleSource.WaveFormat.Channels != this._WaveFormat.Channels ) || // 同じチャンネル数、
91 ( sound.SampleSource.WaveFormat.SampleRate != this._WaveFormat.SampleRate ) || // 同じ周波数、
92 ( sound.SampleSource.WaveFormat.WaveFormatTag != AudioEncoding.IeeeFloat ) ) // 常に 32bit-float であること。
95 throw new ArgumentException( "ミキサーと同じチャンネル数、サンプルレート、かつ 32bit float 型である必要があります。" );
100 if( !this.Contains( sound ) )
101 this._Sounds.Add( sound );
106 /// Sound をミキサーから除外する。
107 /// 除外されると同時に、Sound の再生は終了する。
109 public void RemoveSound( Sound sound )
111 lock( this._スレッド間同期 )
113 if( this.Contains( sound ) )
114 this._Sounds.Remove( sound );
119 /// Sound がミキサーに登録されているかを調べる。
122 /// Sound がミキサーに追加済みなら true 。
124 public bool Contains( Sound sound )
129 return this._Sounds.Contains( sound );
133 /// バッファにサウンドデータを出力する。
138 public int Read( float[] バッファ, int バッファの出力開始位置, int 出力サンプル数 )
140 // ミキサに登録されている Sound の入力とこのメソッドが出力するデータはいずれも常に 32bit-float であり、
141 // これは this.WaveFormat.WaveFormatTag とは無関係なので注意。(this.WaveFormat は、チャンネル数とサンプルレートしか見てない。)
145 lock( this._スレッド間同期 )
147 // 中間バッファが十分あることを確認する。足りなければ新しく確保して戻ってくる。
148 this._中間バッファ = this._中間バッファ.CheckBuffer( 出力サンプル数 ); // サンプル数であり、フレーム数(サンプル数×チャンネル数)ではない。
151 Array.Clear( バッファ, 0, 出力サンプル数 );
153 // その上に、ミキサに登録されているすべての Sound を加算合成する。
154 if( 0 < this._Sounds.Count )
156 for( int m = this._Sounds.Count - 1; m >= 0; m-- ) // リストから Remove する場合があるので、リストの後ろから進める。
158 var sound = this._Sounds[ m ];
160 // 中間バッファにサウンドデータを受け取る。
161 int 受け取ったサンプル数 = sound.SampleSource.Read( this._中間バッファ, 0, 出力サンプル数 );
163 // 中間バッファから出力バッファへ合成する。
164 for( int i = バッファの出力開始位置, n = 0; n < 受け取ったサンプル数; i++, n++ )
166 float data = this._中間バッファ[ n ] // 原音
167 * sound.Volume // 個別音量(Sound)
168 * this._Volume; // マスタ音量(ミキサ)
170 // 先に無音を出力済みなので、上書きかどうかを気にしないで常に加算。
174 if( 0 == 受け取ったサンプル数 )
177 this.RemoveSound( sound );
187 private float _Volume = 1.0f;
189 private CSCore.WaveFormat _WaveFormat = null;
191 private readonly List<Sound> _Sounds = new List<Sound>();
193 private float[] _中間バッファ = null;
195 private readonly object _スレッド間同期 = new object();