OSDN Git Service

ef2e802e45e536f3b0e2bf56c1929612b9e310e9
[strokestylet/CsWin10Desktop3.git] / FDK24 / メディア / サウンド / WASAPI / Mixer.cs
1 using System;
2 using System.Collections.Generic;
3 using System.Diagnostics;
4 using System.Linq;
5 using CSCore;
6
7 namespace FDK.メディア.サウンド.WASAPI
8 {
9         /// <summary>
10         ///             オーディオミキサー。
11         ///             自身が ISampleSource であり、そのまま AudioClient のレンダリングターゲットに指定する。
12         /// </summary>
13         internal class Mixer : CSCore.ISampleSource
14         {
15                 /// <summary>
16                 ///             音量。0.0(無音)~1.0(原音)。
17                 /// </summary>
18                 public float Volume
19                 {
20                         get { return this._Volume; }
21                         set
22                         {
23                                 if( ( 0.0f > value ) || ( 1.0f < value ) )
24                                         throw new ArgumentOutOfRangeException();
25
26                                 this._Volume = value;
27                         }
28                 }
29
30                 /// <summary>
31                 ///             ミキサーのフォーマット。
32                 /// </summary>
33                 public CSCore.WaveFormat WaveFormat
34                 {
35                         get { return _WaveFormat; }
36                 }
37
38                 /// <summary>
39                 ///             ミキサーはループするので、Position には 非対応。
40                 /// </summary>
41                 public long Position
42                 {
43                         get { return 0; }
44                         set { throw new NotSupportedException(); }
45                 }
46
47                 /// <summary>
48                 ///             ミキサーはシークできない。
49                 /// </summary>
50                 public bool CanSeek => ( false );
51
52                 /// <summary>
53                 ///             ミキサーはループするので、長さの概念はない。
54                 /// </summary>
55                 public long Length => ( 0 );
56
57                 /// <summary>
58                 ///             指定したフォーマットを持つミキサーを生成する。
59                 /// </summary>
60                 public Mixer( CSCore.WaveFormat deviceWaveFormat )
61                 {
62                         this._WaveFormat = deviceWaveFormat;
63                 }
64
65                 /// <summary>
66                 ///             ミキサに登録されているサウンドをすべて停止し解放する。
67                 /// </summary>
68                 public void Dispose()
69                 {
70                         lock( this._スレッド間同期 )
71                         {
72                                 // すべての Sound を解放する。
73                                 foreach( var sampleSource in this._Sounds )
74                                         sampleSource.Dispose();
75
76                                 // Sound リストをクリアする。
77                                 this._Sounds.Clear();
78                         }
79                 }
80
81                 /// <summary>
82                 ///             Sound をミキサーに追加する。
83                 ///             追加されると同時に、Sound の再生が開始される。
84                 /// </summary>
85                 public void AddSound( Sound sound )
86                 {
87                         if( null == sound )
88                                 throw new ArgumentNullException();
89
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 であること。
93                         {
94                                 // 違った場合の変換はサポートしない。
95                                 throw new ArgumentException( "ミキサーと同じチャンネル数、サンプルレート、かつ 32bit float 型である必要があります。" );
96                         }
97
98                         lock( this._スレッド間同期 )
99                         {
100                                 if( !this.Contains( sound ) )
101                                         this._Sounds.Add( sound );
102                         }
103                 }
104
105                 /// <summary>
106                 ///             Sound をミキサーから除外する。
107                 ///             除外されると同時に、Sound の再生は終了する。
108                 /// </summary>
109                 public void RemoveSound( Sound sound )
110                 {
111                         lock( this._スレッド間同期 )
112                         {
113                                 if( this.Contains( sound ) )
114                                         this._Sounds.Remove( sound );
115                         }
116                 }
117
118                 /// <summary>
119                 ///             Sound がミキサーに登録されているかを調べる。
120                 /// </summary>
121                 /// <returns>
122                 ///             Sound がミキサーに追加済みなら true 。
123                 ///     </returns>
124                 public bool Contains( Sound sound )
125                 {
126                         if( null == sound )
127                                 return false;
128
129                         return this._Sounds.Contains( sound );
130                 }
131
132                 /// <summary>
133                 ///             バッファにサウンドデータを出力する。
134                 /// </summary>
135                 /// <returns>
136                 ///             実際に出力したサンプル数。
137                 ///     </returns>
138                 public int Read( float[] バッファ, int バッファの出力開始位置, int 出力サンプル数 )
139                 {
140                         // ミキサに登録されている Sound の入力とこのメソッドが出力するデータはいずれも常に 32bit-float であり、
141                         // これは this.WaveFormat.WaveFormatTag とは無関係なので注意。(this.WaveFormat は、チャンネル数とサンプルレートしか見てない。)
142
143                         if( 0 < 出力サンプル数 )
144                         {
145                                 lock( this._スレッド間同期 )
146                                 {
147                                         // 中間バッファが十分あることを確認する。足りなければ新しく確保して戻ってくる。
148                                         this._中間バッファ = this._中間バッファ.CheckBuffer( 出力サンプル数 ); // サンプル数であり、フレーム数(サンプル数×チャンネル数)ではない。
149
150                                         // まずは無音で埋める。
151                                         Array.Clear( バッファ, 0, 出力サンプル数 );
152
153                                         // その上に、ミキサに登録されているすべての Sound を加算合成する。
154                                         if( 0 < this._Sounds.Count )
155                                         {
156                                                 for( int m = this._Sounds.Count - 1; m >= 0; m-- ) // リストから Remove する場合があるので、リストの後ろから進める。
157                                                 {
158                                                         var sound = this._Sounds[ m ];
159
160                                                         // 中間バッファにサウンドデータを受け取る。
161                                                         int 受け取ったサンプル数 = sound.SampleSource.Read( this._中間バッファ, 0, 出力サンプル数 );
162
163                                                         // 中間バッファから出力バッファへ合成する。
164                                                         for( int i = バッファの出力開始位置, n = 0; n < 受け取ったサンプル数; i++, n++ )
165                                                         {
166                                                                 float data = this._中間バッファ[ n ] // 原音
167                                                                         * sound.Volume                  // 個別音量(Sound)
168                                                                         * this._Volume;                 // マスタ音量(ミキサ)
169
170                                                                 // 先に無音を出力済みなので、上書きかどうかを気にしないで常に加算。
171                                                                 バッファ[ i ] += data;
172                                                         }
173
174                                                         if( 0 == 受け取ったサンプル数 )
175                                                         {
176                                                                 // 再生終了。リストから削除。
177                                                                 this.RemoveSound( sound );
178                                                         }
179                                                 }
180                                         }
181                                 }
182                         }
183
184                         return 出力サンプル数;
185                 }
186
187                 private float _Volume = 1.0f;
188
189                 private CSCore.WaveFormat _WaveFormat = null;
190
191                 private readonly List<Sound> _Sounds = new List<Sound>();
192
193                 private float[] _中間バッファ = null;
194
195                 private readonly object _スレッド間同期 = new object();
196         }
197 }