OSDN Git Service

演奏ステージを実装。
[strokestylet/CsWin10Desktop3.git] / StrokeStyleT / ステージ / 演奏 / ドラムサウンド.cs
1 using System;
2 using System.Collections.Generic;
3 using System.Diagnostics;
4 using System.IO;
5 using System.Linq;
6 using System.Xml.Linq;
7 using FDK.メディア;
8
9 namespace SST.ステージ.演奏
10 {
11         class ドラムサウンド : FDK.Activity
12         {
13                 public ドラムサウンド()
14                 {
15                 }
16                 protected override void On活性化( デバイスリソース dr )
17                 {
18                         this.KitXmlを読み込む();
19                 }
20                 protected override void On非活性化( デバイスリソース dr )
21                 {
22                         this.すべてのコンテキストを初期化する();
23                 }
24                 public void 発声する( SSTFormat.チップ種別 chipType, float 音量0to1 )
25                 {
26                         if( false == this.チップtoコンテキスト.ContainsKey( chipType ) )
27                                 return; // コンテキスト未登録のチップなら何もしない。
28                         var context = this.チップtoコンテキスト[ chipType ];
29
30                         // 現在発声中のサウンドを全部止めるチップ種別の場合は止める。
31                         if( 0 != chipType.排他発声グループID() ) // ID = 0 は対象外。
32                         {
33                                 // 同じ排他発声グループIDを持つコンテキストの Sounds[] を select する。
34                                 var 停止するサウンド群 =
35                                         from kvp in this.チップtoコンテキスト
36                                         where ( kvp.Key.排他発声グループID() == chipType.排他発声グループID() )
37                                         select kvp.Value.Sounds;
38
39                                 // 集めた Sounds[] をすべて停止する。
40                                 foreach( var sounds in 停止するサウンド群 )
41                                         foreach( var sound in sounds )
42                                                 sound.再生を停止する();
43                         }
44
45                         // 再生する。
46                         if( null != context.Sounds[ context.次に再生するSound番号 ] )
47                         {
48                                 context.Sounds[ context.次に再生するSound番号 ].音量 = 音量0to1;
49                                 context.Sounds[ context.次に再生するSound番号 ].再生を開始する();
50                         }
51
52                         // サウンドローテーション。
53                         context.次に再生するSound番号++;
54                         if( context.次に再生するSound番号 >= ドラムサウンド.多重度 )
55                                 context.次に再生するSound番号 = 0;
56                 }
57
58                 protected const int 多重度 = 2;
59                 protected readonly string KitXmlファイルパス = @"$(Static)\sounds\Kit.xml";
60                 protected class Cコンテキスト : IDisposable
61                 {
62                         public FDK.メディア.サウンド.WASAPI排他.Sound[] Sounds = new FDK.メディア.サウンド.WASAPI排他.Sound[ ドラムサウンド.多重度 ];
63                         public int 次に再生するSound番号 = 0;
64
65                         public void Dispose()
66                         {
67                                 if( null != this.Sounds )
68                                 {
69                                         for( int i = 0; i < this.Sounds.Length; i++ )
70                                         {
71                                                 StrokeStyleT.Wasapiデバイス.サウンドをミキサーから削除する( this.Sounds[ i ] );
72                                                 FDK.Utilities.解放する( ref this.Sounds[ i ] );
73                                         }
74                                 }
75                         }
76                 };
77                 protected Dictionary<SSTFormat.チップ種別, Cコンテキスト> チップtoコンテキスト = null;
78
79                 private void すべてのコンテキストを初期化する()
80                 {
81                         // すでに存在しているなら解放する。
82                         if( null != this.チップtoコンテキスト )
83                         {
84                                 foreach( var kvp in this.チップtoコンテキスト )
85                                         ( (Cコンテキスト) kvp.Value ).Dispose();
86                         }
87
88                         // 生成する。
89                         this.チップtoコンテキスト = new Dictionary<SSTFormat.チップ種別, Cコンテキスト>();
90                 }
91                 private void KitXmlを読み込む()
92                 {
93                         this.すべてのコンテキストを初期化する();
94
95                         string ファイルパス = FDK.フォルダ.絶対パスに含まれるフォルダ変数を展開して返す( this.KitXmlファイルパス );
96
97                         if( false == File.Exists( ファイルパス ) )
98                         {
99                                 FDK.Log.WARNING( $"Kit ファイルが存在しません。ドラムサウンドは生成されません。[{this.KitXmlファイルパス}]" );
100                                 return;
101                         }
102
103                         try
104                         {
105                                 var xml文書 = XDocument.Load( ファイルパス );
106
107                                 // <Root>
108                                 var Root要素 = xml文書.Element( nameof( XML.Root ) );
109                                 {
110                                         // <DrumKit>
111                                         var DrumKit要素 = Root要素.Element( nameof( XML.DrumKit ) );    // 最初の1つのみサポート。
112                                         var DrumKit要素のVersion属性 = DrumKit要素.Attribute( nameof( XML.Version ) );
113                                         var DrumKit要素のName属性 = DrumKit要素.Attribute( nameof( XML.Name ) );   // 現在は未使用。
114                                         if( "1.2" == DrumKit要素のVersion属性.Value )
115                                         {
116                                                 #region " DrumKit ver1.2 "
117                                                 //----------------
118                                                 foreach( var DrumKitの子要素 in DrumKit要素.Elements() )
119                                                 {
120                                                         switch( DrumKitの子要素.Name.LocalName )
121                                                         {
122                                                                 // <Sound>
123                                                                 case nameof( XML.Sound ):
124                                                                         // Name="..." 属性の値は、SSTFormat.チップ種別 の各メンバの名前に等しいものとする。
125                                                                         var Sound要素のName属性 = DrumKitの子要素.Attribute( nameof( XML.Name ) );
126                                                                         var チップ種別 = SSTFormat.チップ種別.Unknown;
127                                                                         if( Enum.TryParse( Sound要素のName属性.Value, out チップ種別 ) )
128                                                                         {
129                                                                                 string サウンドファイルパス = Path.Combine( StrokeStyleT.フォルダ.StaticFolder + @"\sounds\", DrumKitの子要素.Value );
130                                                                                 if( File.Exists( サウンドファイルパス ) )
131                                                                                 {
132                                                                                         // すでに辞書に存在してるなら、解放して削除する。
133                                                                                         if( this.チップtoコンテキスト.ContainsKey( チップ種別 ) )
134                                                                                         {
135                                                                                                 this.チップtoコンテキスト[ チップ種別 ]?.Dispose();
136                                                                                                 this.チップtoコンテキスト.Remove( チップ種別 );
137                                                                                         }
138
139                                                                                         // コンテキストを作成する。
140                                                                                         var context = new Cコンテキスト() {
141                                                                                                 Sounds = new FDK.メディア.サウンド.WASAPI排他.Sound[ ドラムサウンド.多重度 ],
142                                                                                                 次に再生するSound番号 = 0,
143                                                                                         };
144                                                                                         for( int i = 0; i < context.Sounds.Length; i++ )
145                                                                                         {
146                                                                                                 // 多重度分のサウンドを生成しつつ、ミキサーにも登録。
147                                                                                                 context.Sounds[ i ] = new FDK.メディア.サウンド.WASAPI排他.Sound( サウンドファイルパス );
148                                                                                                 StrokeStyleT.Wasapiデバイス.サウンドをミキサーに追加する( context.Sounds[ i ] );
149                                                                                         }
150
151                                                                                         // コンテキストを辞書に追加する。
152                                                                                         this.チップtoコンテキスト.Add( チップ種別, context );
153                                                                                 }
154                                                                                 else
155                                                                                 {
156                                                                                         FDK.Log.WARNING( $"サウンドファイル {FDK.フォルダ.絶対パスをフォルダ変数付き絶対パスに変換して返す( サウンドファイルパス )} が存在しません。[{this.KitXmlファイルパス}]" );
157                                                                                 }
158                                                                         }
159                                                                         else
160                                                                         {
161                                                                                 FDK.Log.WARNING( $"未知の要素 {Sound要素のName属性.Value} をスキップします。[{this.KitXmlファイルパス}]" );
162                                                                         }
163                                                                         break;
164                                                         }
165                                                 }
166                                                 //----------------
167                                                 #endregion
168                                         }
169                                 }
170                         }
171                         catch( Exception e )
172                         {
173                                 FDK.Log.ERROR( $"Kitファイルの読み込みに失敗しました。{e.Message}[{this.KitXmlファイルパス}]" );
174                         }
175                 }
176         }
177 }