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;
70 foreach( var sampleSource in this._Sounds )
71 sampleSource.Dispose();
80 /// 追加されると同時に、Sound の再生が開始される。
82 public void AddSound( Sound sound )
85 throw new ArgumentNullException();
87 if( ( sound.SampleSource.WaveFormat.Channels != this._WaveFormat.Channels ) ||
88 ( sound.SampleSource.WaveFormat.SampleRate != this._WaveFormat.SampleRate ) ||
89 ( sound.SampleSource.WaveFormat.WaveFormatTag != AudioEncoding.IeeeFloat ) ) // IWaveSource.ToSampleSource() で作成した ISampleSource ならすべて 32bit-float であるはず。
91 // これらの変換は面倒なのでサポートしない。
92 throw new ArgumentException( "ミキサーと同じチャンネル数、サンプルレート、かつ 32bit float 型である必要があります。" );
97 if( !this.Contains( sound ) )
98 this._Sounds.Add( sound );
103 /// Sound をミキサーから除外する。
104 /// 除外されると同時に、Sound の再生は終了する。
106 public void RemoveSound( Sound sound )
108 lock( this._スレッド間同期 )
110 if( this.Contains( sound ) )
111 this._Sounds.Remove( sound );
116 /// Sound がミキサーに登録されているかを調べる。
118 /// <returns>Sound がミキサーに追加済みなら true 。</returns>
119 public bool Contains( Sound sound )
124 return this._Sounds.Contains( sound );
128 /// バッファにサウンドデータを出力する。
130 /// <returns>実際に出力したサンプル数。</returns>
131 public int Read( float[] バッファ, int バッファの出力開始位置, int 出力サンプル数 )
133 // ミキサに登録されている Sound の入力とこのメソッドが出力するデータはいずれも常に 32bit-float であり、
134 // これは this.WaveFormat.WaveFormatTag とは無関係なので注意。(this.WaveFormat は、チャンネル数とサンプルレートしか見てない。)
138 lock( this._スレッド間同期 )
140 // 中間バッファが十分あることを確認する。足りなければ新しく確保して戻ってくる。
141 this._中間バッファ = this._中間バッファ.CheckBuffer( 出力サンプル数 ); // サンプル数であって、フレーム数(サンプル数×チャンネル数)ではない。
144 Array.Clear( バッファ, 0, 出力サンプル数 );
146 // ミキサに登録されているすべての Sound を出力する。
147 if( 0 < this._Sounds.Count )
149 for( int m = this._Sounds.Count - 1; m >= 0; m-- ) // リストから Remove する場合があるので、リストの後ろから進める。
151 var sound = this._Sounds[ m ];
153 // 中間バッファにサウンドデータを受け取る。
154 int 受け取ったサンプル数 = sound.SampleSource.Read( this._中間バッファ, 0, 出力サンプル数 );
156 // 中間バッファから出力バッファへ転送する。
157 for( int i = バッファの出力開始位置, n = 0; n < 受け取ったサンプル数; i++, n++ )
159 float data = this._中間バッファ[ n ] // 原音
160 * sound.Volume // 個別音量
161 * this._Volume; // ミキサ音量
163 // ベースに無音を出力済みなので、上書きじゃなく常に加算。
167 if( 0 == 受け取ったサンプル数 )
170 this.RemoveSound( sound );
180 private float _Volume = 1.0f;
181 private CSCore.WaveFormat _WaveFormat = null;
182 private readonly List<Sound> _Sounds = new List<Sound>();
183 private float[] _中間バッファ = null;
184 private readonly object _スレッド間同期 = new object();