OSDN Git Service

排他モードと共有モードの両方の実相を完了。
[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                 public void Dispose()
66                 {
67                         lock( this._スレッド間同期 )
68                         {
69                                 // すべての Sound を解放する。
70                                 foreach( var sampleSource in this._Sounds )
71                                         sampleSource.Dispose();
72
73                                 // Sound リストをクリアする。
74                                 this._Sounds.Clear();
75                         }
76                 }
77
78                 /// <summary>
79                 ///             Sound をミキサーに追加する。
80                 ///             追加されると同時に、Sound の再生が開始される。
81                 /// </summary>
82                 public void AddSound( Sound sound )
83                 {
84                         if( null == sound )
85                                 throw new ArgumentNullException();
86
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 であるはず。
90                         {
91                                 // これらの変換は面倒なのでサポートしない。
92                                 throw new ArgumentException( "ミキサーと同じチャンネル数、サンプルレート、かつ 32bit float 型である必要があります。" );
93                         }
94
95                         lock( this._スレッド間同期 )
96                         {
97                                 if( !this.Contains( sound ) )
98                                         this._Sounds.Add( sound );
99                         }
100                 }
101
102                 /// <summary>
103                 ///             Sound をミキサーから除外する。
104                 ///             除外されると同時に、Sound の再生は終了する。
105                 /// </summary>
106                 public void RemoveSound( Sound sound )
107                 {
108                         lock( this._スレッド間同期 )
109                         {
110                                 if( this.Contains( sound ) )
111                                         this._Sounds.Remove( sound );
112                         }
113                 }
114
115                 /// <summary>
116                 ///             Sound がミキサーに登録されているかを調べる。
117                 /// </summary>
118                 /// <returns>Sound がミキサーに追加済みなら true 。</returns>
119                 public bool Contains( Sound sound )
120                 {
121                         if( null == sound )
122                                 return false;
123
124                         return this._Sounds.Contains( sound );
125                 }
126
127                 /// <summary>
128                 ///             バッファにサウンドデータを出力する。
129                 /// </summary>
130                 /// <returns>実際に出力したサンプル数。</returns>
131                 public int Read( float[] バッファ, int バッファの出力開始位置, int 出力サンプル数 )
132                 {
133                         // ミキサに登録されている Sound の入力とこのメソッドが出力するデータはいずれも常に 32bit-float であり、
134                         // これは this.WaveFormat.WaveFormatTag とは無関係なので注意。(this.WaveFormat は、チャンネル数とサンプルレートしか見てない。)
135
136                         if( 0 < 出力サンプル数 )
137                         {
138                                 lock( this._スレッド間同期 )
139                                 {
140                                         // 中間バッファが十分あることを確認する。足りなければ新しく確保して戻ってくる。
141                                         this._中間バッファ = this._中間バッファ.CheckBuffer( 出力サンプル数 ); // サンプル数であって、フレーム数(サンプル数×チャンネル数)ではない。
142
143                                         // 無音を出力する。
144                                         Array.Clear( バッファ, 0, 出力サンプル数 );
145
146                                         // ミキサに登録されているすべての Sound を出力する。
147                                         if( 0 < this._Sounds.Count )
148                                         {
149                                                 for( int m = this._Sounds.Count - 1; m >= 0; m-- ) // リストから Remove する場合があるので、リストの後ろから進める。
150                                                 {
151                                                         var sound = this._Sounds[ m ];
152
153                                                         // 中間バッファにサウンドデータを受け取る。
154                                                         int 受け取ったサンプル数 = sound.SampleSource.Read( this._中間バッファ, 0, 出力サンプル数 );
155
156                                                         // 中間バッファから出力バッファへ転送する。
157                                                         for( int i = バッファの出力開始位置, n = 0; n < 受け取ったサンプル数; i++, n++ )
158                                                         {
159                                                                 float data = this._中間バッファ[ n ] // 原音
160                                                                         * sound.Volume                  // 個別音量
161                                                                         * this._Volume;                 // ミキサ音量
162
163                                                                 // ベースに無音を出力済みなので、上書きじゃなく常に加算。
164                                                                 バッファ[ i ] += data;
165                                                         }
166
167                                                         if( 0 == 受け取ったサンプル数 )
168                                                         {
169                                                                 // 再生終了。リストから削除。
170                                                                 this.RemoveSound( sound );
171                                                         }
172                                                 }
173                                         }
174                                 }
175                         }
176
177                         return 出力サンプル数;
178                 }
179
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();
185         }
186 }