2 using System.Collections.Generic;
4 using System.Runtime.InteropServices;
6 using System.Diagnostics;
8 using System.Security.Cryptography;
9 using System.Reflection;
10 using System.Globalization;
11 using System.Threading;
17 /// CDTX 内で用いる入れ子型を partial にし、ここで定義します。
19 public partial class CDTX : CActivity
28 public string BACKGROUND;
29 public string BACKGROUND_GR;
30 public double BASEBPM;
31 public bool BLACKCOLORKEY;
33 public STチップがある bチップがある;
34 public string COMMENT;
38 public bool HIDDENLEVEL;
39 public STDGBSValue<int> LEVEL;
40 public Dictionary<int, CAVI> listAVI;
41 public Dictionary<int, CAVIPAN> listAVIPAN;
42 public Dictionary<int, CBGA> listBGA;
43 public Dictionary<int, CBGAPAN> listBGAPAN;
44 public Dictionary<int, CBMP> listBMP;
45 public Dictionary<int, CBMPTEX> listBMPTEX;
46 public Dictionary<int, CBPM> listBPM;
47 public List<CChip> listChip;
48 public Dictionary<int, CWAV> listWAV;
49 public string MIDIFILE;
52 public STDGBSValue<int> n可視チップ数;
53 public const int n最大音数 = 4;
54 public const int n小節の解像度 = 384;
56 public string PATH_WAV;
57 public string PREIMAGE;
58 public string PREMOVIE;
59 public string PREVIEW;
60 public STRESULT RESULTIMAGE;
61 public STRESULT RESULTMOVIE;
62 public STRESULT RESULTSOUND;
63 public string SOUND_AUDIENCE;
64 public string SOUND_FULLCOMBO;
65 public string SOUND_NOWLOADING;
66 public string SOUND_STAGEFAILED;
67 public string STAGEFILE;
68 public string strハッシュofDTXファイル;
69 public string strファイル名;
70 public string strファイル名の絶対パス;
71 public string strフォルダ名;
73 public double dbDTXVPlaySpeed;
74 public bool bMovieをFullscreen再生する;
75 public bool bUse556x710BGAAVI;
76 public STDGBSValue<List<int>> listAutoGhostLag;
77 public STDGBSValue<List<int>> listTargetGhsotLag;
78 public STDGBSValue<EUseLanes> n使用レーン数;
81 public STLANEVALUE<bool> b演奏で直前の音を消音する;
82 // public bool bHH演奏で直前のHHを消音する;
83 // public bool bGUITAR演奏で直前のGUITARを消音する;
84 // public bool bBASS演奏で直前のBASSを消音する;
100 this.BACKGROUND_GR = "";
103 this.SOUND_STAGEFAILED = "";
104 this.SOUND_FULLCOMBO = "";
105 this.SOUND_NOWLOADING = "";
106 this.SOUND_AUDIENCE = "";
108 this.BLACKCOLORKEY = true;
109 STDGBSValue<int> stdgbvalue = new STDGBSValue<int>();
110 stdgbvalue.Drums = 0;
111 stdgbvalue.Guitar = 0;
113 this.LEVEL = stdgbvalue;
114 for (int i = 0; i < 7; i++)
116 this.RESULTIMAGE[i] = "";
117 this.RESULTMOVIE[i] = "";
118 this.RESULTSOUND[i] = "";
121 this.strハッシュofDTXファイル = "";
122 this.bチップがある = new STチップがある();
123 this.bチップがある.Drums = false;
124 this.bチップがある.Guitar = false;
125 this.bチップがある.Bass = false;
126 this.bチップがある.HHOpen = false;
127 this.bチップがある.Ride = false;
128 this.bチップがある.LeftCymbal = false;
129 this.bチップがある.OpenGuitar = false;
130 this.bチップがある.OpenBass = false;
131 this.bチップがある.BGA = false;
132 this.bチップがある.Movie = false;
133 this.bチップがある.LeftPedal = false;
134 this.bチップがある.LeftBassDrum = false;
135 this.bMovieをFullscreen再生する = false;
138 this.strファイル名の絶対パス = "";
139 this.n無限管理WAV = new int[36 * 36];
140 this.n無限管理BPM = new int[36 * 36];
141 this.n無限管理VOL = new int[36 * 36];
142 this.n無限管理PAN = new int[36 * 36];
143 this.n無限管理SIZE = new int[36 * 36];
144 this.nRESULTIMAGE用優先順位 = new int[7];
145 this.nRESULTMOVIE用優先順位 = new int[7];
146 this.nRESULTSOUND用優先順位 = new int[7];
147 this.listAutoGhostLag = new STDGBSValue<List<int>>();
148 this.listTargetGhsotLag = new STDGBSValue<List<int>>();
150 #region [ 2011.1.1 yyagi GDA->DTX変換テーブル リファクタ後 ]
151 STGDAPARAM[] stgdaparamArray = new STGDAPARAM[] { // GDA->DTX conversion table
152 new STGDAPARAM("TC", EChannel.BPM),
153 new STGDAPARAM("BL", EChannel.BarLength),
154 new STGDAPARAM("GS", EChannel.flowspeed_gt_nouse),
155 new STGDAPARAM("DS", EChannel.flowspeed_dr_nouse),
156 new STGDAPARAM("FI", EChannel.FillIn),
157 new STGDAPARAM("HH", EChannel.HiHatClose),
158 new STGDAPARAM("SD", EChannel.Snare),
159 new STGDAPARAM("BD", EChannel.BassDrum),
160 new STGDAPARAM("HT", EChannel.HighTom),
161 new STGDAPARAM("LT", EChannel.LowTom),
162 new STGDAPARAM("CY", EChannel.Cymbal),
163 new STGDAPARAM("G1", EChannel.Guitar_xxB),
164 new STGDAPARAM("G2", EChannel.Guitar_xGx),
165 new STGDAPARAM("G3", EChannel.Guitar_xGB),
166 new STGDAPARAM("G4", EChannel.Guitar_Rxx),
167 new STGDAPARAM("G5", EChannel.Guitar_RxB),
168 new STGDAPARAM("G6", EChannel.Guitar_RGx),
169 new STGDAPARAM("G7", EChannel.Guitar_RGB),
170 new STGDAPARAM("GW", EChannel.Guitar_Wailing),
171 new STGDAPARAM("01", EChannel.SE01),
172 new STGDAPARAM("02", EChannel.SE02),
173 new STGDAPARAM("03", EChannel.SE03),
174 new STGDAPARAM("04", EChannel.SE04),
175 new STGDAPARAM("05", EChannel.SE05),
176 new STGDAPARAM("06", EChannel.SE06),
177 new STGDAPARAM("07", EChannel.SE07),
178 new STGDAPARAM("08", EChannel.SE08),
179 new STGDAPARAM("09", EChannel.SE09),
180 new STGDAPARAM("0A", EChannel.SE10),
181 new STGDAPARAM("0B", EChannel.SE11),
182 new STGDAPARAM("0C", EChannel.SE12),
183 new STGDAPARAM("0D", EChannel.SE13),
184 new STGDAPARAM("0E", EChannel.SE14),
185 new STGDAPARAM("0F", EChannel.SE15),
186 new STGDAPARAM("10", EChannel.SE16),
187 new STGDAPARAM("11", EChannel.SE17),
188 new STGDAPARAM("12", EChannel.SE18),
189 new STGDAPARAM("13", EChannel.SE19),
190 new STGDAPARAM("14", EChannel.SE20),
191 new STGDAPARAM("15", EChannel.SE21),
192 new STGDAPARAM("16", EChannel.SE22),
193 new STGDAPARAM("17", EChannel.SE23),
194 new STGDAPARAM("18", EChannel.SE24),
195 new STGDAPARAM("19", EChannel.SE25),
196 new STGDAPARAM("1A", EChannel.SE26),
197 new STGDAPARAM("1B", EChannel.SE27),
198 new STGDAPARAM("1C", EChannel.SE28),
199 new STGDAPARAM("1D", EChannel.SE29),
200 new STGDAPARAM("1E", EChannel.SE30),
201 new STGDAPARAM("1F", EChannel.SE31),
202 new STGDAPARAM("20", EChannel.SE32),
203 new STGDAPARAM("B1", EChannel.Bass_xxB),
204 new STGDAPARAM("B2", EChannel.Bass_xGx),
205 new STGDAPARAM("B3", EChannel.Bass_xGB),
206 new STGDAPARAM("B4", EChannel.Bass_Rxx),
207 new STGDAPARAM("B5", EChannel.Bass_RxB),
208 new STGDAPARAM("B6", EChannel.Bass_RGx),
209 new STGDAPARAM("B7", EChannel.Bass_RGB),
210 new STGDAPARAM("BW", EChannel.Bass_Wailing),
211 new STGDAPARAM("G0", EChannel.Guitar_Open),
212 new STGDAPARAM("B0", EChannel.Bass_Open)
214 this.stGDAParam = stgdaparamArray;
217 this.nPolyphonicSounds = CDTXMania.Instance.ConfigIni.nPolyphonicSounds;
218 this.dbDTXVPlaySpeed = 1.0f;
219 this.bUse556x710BGAAVI = false;
220 this.n使用レーン数 = new STDGBSValue<EUseLanes>();
223 this.bHH演奏で直前のHHを消音する = true;
224 this.bGUITAR演奏で直前のGUITARを消音する = true;
225 this.bBASS演奏で直前のBASSを消音する = true;
229 private CDTX(string str全入力文字列)
233 this.t入力_全入力文字列から(str全入力文字列);
235 public CDTX(string strファイル名, bool bヘッダのみ)
239 this.t入力(strファイル名, bヘッダのみ);
241 private CDTX(string str全入力文字列, double db再生速度, int nBGMAdjust)
245 this.t入力_全入力文字列から(str全入力文字列, db再生速度, nBGMAdjust);
247 public CDTX(string strファイル名, bool bヘッダのみ, double db再生速度, int nBGMAdjust)
251 this.t入力(strファイル名, bヘッダのみ, db再生速度, nBGMAdjust);
258 public int nモニタを考慮した音量(EPart part)
263 if (CDTXMania.Instance.ConfigIni.bEmphasizePlaySound.Drums)
265 return CDTXMania.Instance.ConfigIni.nAutoVolume;
267 return CDTXMania.Instance.ConfigIni.nChipVolume;
270 if (CDTXMania.Instance.ConfigIni.bEmphasizePlaySound.Guitar)
272 return CDTXMania.Instance.ConfigIni.nAutoVolume;
274 return CDTXMania.Instance.ConfigIni.nChipVolume;
277 if (CDTXMania.Instance.ConfigIni.bEmphasizePlaySound.Bass)
279 return CDTXMania.Instance.ConfigIni.nAutoVolume;
281 return CDTXMania.Instance.ConfigIni.nChipVolume;
283 if ((!CDTXMania.Instance.ConfigIni.bEmphasizePlaySound.Drums && !CDTXMania.Instance.ConfigIni.bEmphasizePlaySound.Guitar) && !CDTXMania.Instance.ConfigIni.bEmphasizePlaySound.Bass)
285 return CDTXMania.Instance.ConfigIni.nChipVolume;
287 return CDTXMania.Instance.ConfigIni.nAutoVolume;
289 public void tAVIの読み込み()
291 if (this.listAVI != null)
293 foreach (CAVI cavi in this.listAVI.Values)
295 cavi.OnDeviceCreated();
300 foreach (CChip chip in this.listChip)
302 chip.ApplyAVI(listAVI, listAVIPAN);
306 #region [ BMP/BMPTEXの並列読み込み・デコード用メソッド ]
307 delegate void BackgroundBMPLoadAll(Dictionary<int, CBMP> listB);
308 static BackgroundBMPLoadAll backgroundBMPLoadAll = new BackgroundBMPLoadAll(BMPLoadAll);
309 delegate void BackgroundBMPTEXLoadAll(Dictionary<int, CBMPTEX> listB);
310 static BackgroundBMPTEXLoadAll backgroundBMPTEXLoadAll = new BackgroundBMPTEXLoadAll(BMPTEXLoadAll);
311 private static void LoadTexture(CBMPbase cbmp) // バックグラウンドスレッドで動作する、ファイル読み込み部
313 string filename = cbmp.GetFullPathname;
314 if (!File.Exists(filename))
316 Trace.TraceWarning("ファイルが存在しません。({0})", filename);
322 cbmp.bitmap = new Bitmap(filename);
324 catch (ArgumentException)
326 Trace.TraceWarning("引数が不正です。ファイルが破損している可能性があります。({0})", filename);
331 private static void BMPLoadAll(Dictionary<int, CBMP> listB) // バックグラウンドスレッドで、テクスチャファイルをひたすら読み込んではキューに追加する
333 //Trace.TraceInformation( "Back: ThreadID(BMPLoad)=" + Thread.CurrentThread.ManagedThreadId + ", listCount=" + listB.Count );
334 foreach (CBMPbase cbmp in listB.Values)
339 queueCBMPbaseDone.Enqueue(cbmp);
340 // Trace.TraceInformation( "Back: Enqueued(" + queueCBMPbaseDone.Count + "): " + cbmp.strファイル名 );
342 if (queueCBMPbaseDone.Count > 8)
348 private static void BMPTEXLoadAll(Dictionary<int, CBMPTEX> listB) // ダサい実装だが、Dictionary<>の中には手を出せず、妥協した
350 //Trace.TraceInformation( "Back: ThreadID(BMPLoad)=" + Thread.CurrentThread.ManagedThreadId + ", listCount=" + listB.Count );
351 foreach (CBMPbase cbmp in listB.Values)
356 queueCBMPbaseDone.Enqueue(cbmp);
357 // Trace.TraceInformation( "Back: Enqueued(" + queueCBMPbaseDone.Count + "): " + cbmp.strファイル名 );
359 if (queueCBMPbaseDone.Count > 8)
366 private static Queue<CBMPbase> queueCBMPbaseDone = new Queue<CBMPbase>();
367 private static object lockQueue = new object();
368 private static int nLoadDone;
371 public void tBMP_BMPTEXの読み込み()
373 #region [ CPUコア数の取得 ]
374 CWin32.SYSTEM_INFO sysInfo = new CWin32.SYSTEM_INFO();
375 CWin32.GetSystemInfo(ref sysInfo);
376 int nCPUCores = (int)sysInfo.dwNumberOfProcessors;
379 if (this.listBMP != null)
383 #region [ シングルスレッドで逐次読み出し・デコード・テクスチャ定義 ]
384 foreach (CBMP cbmp in this.listBMP.Values)
386 cbmp.OnDeviceCreated();
392 #region [ メインスレッド(テクスチャ定義)とバックグラウンドスレッド(読み出し・デコード)を並列動作させ高速化 ]
393 //Trace.TraceInformation( "Main: ThreadID(Main)=" + Thread.CurrentThread.ManagedThreadId + ", listCount=" + this.listBMP.Count );
395 backgroundBMPLoadAll.BeginInvoke(listBMP, null, null);
397 // t.Priority = ThreadPriority.Lowest;
398 // t.Start( listBMP );
399 int c = listBMP.Count;
400 while (nLoadDone < c)
402 if (queueCBMPbaseDone.Count > 0)
405 //Trace.TraceInformation( "Main: Lock Begin for dequeue1." );
410 cbmp = (CBMP)queueCBMPbaseDone.Dequeue();
411 // Trace.TraceInformation( "Main: Dequeued(" + queueCBMPbaseDone.Count + "): " + cbmp.strファイル名 );
413 cbmp.OnDeviceCreated(cbmp.bitmap, cbmp.GetFullPathname);
415 catch (InvalidCastException) // bmp読み込み失敗時は、キャストに失敗する
422 //Trace.TraceInformation( "Main: OnDeviceCreated: " + cbmp.strファイル名 );
426 //Trace.TraceInformation( "Main: Sleeped.");
427 Thread.Sleep(5); // WaitOneのイベント待ちにすると、メインスレッド処理中に2個以上イベント完了したときにそれを正しく検出できなくなるので、
428 } // ポーリングに逃げてしまいました。
434 #region [ BMPTEX読み込み ]
435 if (this.listBMPTEX != null)
439 #region [ シングルスレッドで逐次読み出し・デコード・テクスチャ定義 ]
440 foreach (CBMPTEX cbmptex in this.listBMPTEX.Values)
442 cbmptex.OnDeviceCreated();
448 #region [ メインスレッド(テクスチャ定義)とバックグラウンドスレッド(読み出し・デコード)を並列動作させ高速化 ]
449 //Trace.TraceInformation( "Main: ThreadID(Main)=" + Thread.CurrentThread.ManagedThreadId + ", listCount=" + this.listBMP.Count );
451 backgroundBMPTEXLoadAll.BeginInvoke(listBMPTEX, null, null);
452 int c = listBMPTEX.Count;
453 while (nLoadDone < c)
455 if (queueCBMPbaseDone.Count > 0)
458 //Trace.TraceInformation( "Main: Lock Begin for dequeue1." );
463 cbmptex = (CBMPTEX)queueCBMPbaseDone.Dequeue();
464 // Trace.TraceInformation( "Main: Dequeued(" + queueCBMPbaseDone.Count + "): " + cbmp.strファイル名 );
466 cbmptex.OnDeviceCreated(cbmptex.bitmap, cbmptex.GetFullPathname);
468 catch (InvalidCastException)
475 //Trace.TraceInformation( "Main: OnDeviceCreated: " + cbmp.strファイル名 );
479 //Trace.TraceInformation( "Main: Sleeped.");
480 Thread.Sleep(5); // WaitOneのイベント待ちにすると、メインスレッド処理中に2個以上イベント完了したときにそれを正しく検出できなくなるので、
481 } // ポーリングに逃げてしまいました。
489 foreach (CChip chip in this.listChip)
491 chip.ApplyBMP_BMPTEX(listBGA, listBGAPAN, listBMP, listBMPTEX);
495 public void tWave再生位置自動補正()
497 foreach (CWAV cwav in this.listWAV.Values)
499 this.tWave再生位置自動補正(cwav);
502 public void tWave再生位置自動補正(CWAV wc)
504 if (wc.rSound[0] != null && wc.rSound[0].n総演奏時間ms >= 5000)
506 for (int i = 0; i < nPolyphonicSounds; i++)
508 if ((wc.rSound[i] != null) && (wc.rSound[i].b再生中))
510 long nCurrentTime = CSound管理.rc演奏用タイマ.nシステム時刻ms;
511 if (nCurrentTime > wc.n再生開始時刻[i])
513 long nAbsTimeFromStartPlaying = nCurrentTime - wc.n再生開始時刻[i];
514 //Trace.TraceInformation( "再生位置自動補正: {0}, 実タイマ値={1}, seek先={2}ms, 全音長={3}ms",
515 // Path.GetFileName( wc.rSound[ 0 ].strファイル名 ),
517 // nAbsTimeFromStartPlaying,
518 // wc.rSound[ 0 ].n総演奏時間ms
520 // wc.rSound[ i ].t再生位置を変更する( wc.rSound[ i ].t時刻から位置を返す( nAbsTimeFromStartPlaying ) );
521 wc.rSound[i].t再生位置を変更する(nAbsTimeFromStartPlaying); // WASAPI/ASIO用
522 //Debug.WriteLine( "再生位置を変更: " + Path.GetFileName( wc.strファイル名 ) + nAbsTimeFromStartPlaying + "ms");
532 public void tWaveBGM再生位置表示()
534 foreach (CWAV wc in this.listWAV.Values)
536 if (wc.rSound[0] != null && wc.rSound[0].n総演奏時間ms >= 5000)
538 for (int i = 0; i < nPolyphonicSounds; i++)
540 if ((wc.rSound[i] != null) && (wc.rSound[i].b再生中))
544 wc.rSound[i].t再生位置を取得する(out n位置byte, out db位置ms);
545 Trace.TraceInformation("再生位置: {0}, seek先={1}ms / {2}byte, 全音長={3}ms",
546 Path.GetFileName(wc.rSound[0].strファイル名),
548 wc.rSound[0].n総演奏時間ms
556 public void tWavの再生停止(int nWaveの内部番号)
558 tWavの再生停止(nWaveの内部番号, false);
560 public void tWavの再生停止(int nWaveの内部番号, bool bミキサーからも削除する)
562 if (this.listWAV.ContainsKey(nWaveの内部番号))
564 CWAV cwav = this.listWAV[nWaveの内部番号];
565 for (int i = 0; i < nPolyphonicSounds; i++)
567 if (cwav.rSound[i] != null && cwav.rSound[i].b再生中)
571 cwav.rSound[i].tサウンドを停止してMixerからも削除する();
575 cwav.rSound[i].t再生を停止する();
581 public void tWAVの読み込み(CWAV cwav)
583 // Trace.TraceInformation("WAV files={0}", this.listWAV.Count);
585 // foreach (CWAV cwav in this.listWAV.Values)
587 // string strCount = count.ToString() + " / " + this.listWAV.Count.ToString();
588 // Debug.WriteLine(strCount);
589 // CDTXMania.Instance.app.act文字コンソール.tPrint(0, 0, C文字コンソール.Eフォント種別.白, strCount);
592 string str = string.IsNullOrEmpty(this.PATH_WAV) ? this.strフォルダ名 : this.PATH_WAV;
593 str = str + cwav.strファイル名;
594 bool bIsDirectSound = (CDTXMania.Instance.Sound管理.GetCurrentSoundDeviceType() == "DirectSound");
599 // cwav.rSound[ 0 ] = CDTXMania.Instance.app.Sound管理.tサウンドを生成する( str );
600 // cwav.rSound[ 0 ].n音量 = 100;
601 // if ( CDTXMania.Instance.app.ConfigIni.bLog作成解放ログ出力 )
603 // Trace.TraceInformation( "サウンドを作成しました。({3})({0})({1})({2}bytes)", cwav.strコメント文, str, cwav.rSound[ 0 ].nサウンドバッファサイズ, cwav.rSound[ 0 ].bストリーム再生する ? "Stream" : "OnMemory" );
608 // cwav.rSound[ 0 ] = null;
609 // Trace.TraceError( "サウンドの作成に失敗しました。({0})({1})", cwav.strコメント文, str );
611 //if ( cwav.rSound[ 0 ] == null ) // #xxxxx 2012.5.3 yyagi rSound[1-3]もClone()するようにし、これらのストリーム再生がおかしくなる問題を修正
613 // for ( int j = 1; j < nPolyphonicSounds; j++ )
615 // cwav.rSound[ j ] = null;
620 // for ( int j = 1; j < nPolyphonicSounds; j++ )
622 // cwav.rSound[ j ] = (CSound) cwav.rSound[ 0 ].Clone(); // #24007 2011.9.5 yyagi add: to accelerate loading chip sounds
623 // CDTXMania.Instance.app.Sound管理.tサウンドを登録する( cwav.rSound[ j ] );
630 cwav.rSound[0] = CDTXMania.Instance.Sound管理.tサウンドを生成する(str);
631 cwav.rSound[0].n音量 = 100;
632 if (!CDTXMania.Instance.ConfigIni.bDynamicBassMixerManagement)
634 cwav.rSound[0].tBASSサウンドをミキサーに追加する();
636 if (CDTXMania.Instance.ConfigIni.bLogCreateRelease)
638 Trace.TraceInformation("サウンドを作成しました。({3})({0})({1})({2}bytes)", cwav.strコメント文, str, cwav.rSound[0].nサウンドバッファサイズ, cwav.rSound[0].bストリーム再生する ? "Stream" : "OnMemory");
643 cwav.rSound[0] = null;
644 Trace.TraceError("サウンドの作成に失敗しました。({0})({1})", cwav.strコメント文, str);
645 Trace.TraceError("例外: " + e.Message);
648 #region [ 同時発音数を、チャンネルによって変える ]
649 int nPoly = nPolyphonicSounds;
650 if (!bIsDirectSound) // DShowでの再生の場合はミキシング負荷が高くないため、
651 { // チップのライフタイム管理を行わない
652 if (cwav.bIsBassSound) nPoly = (nPolyphonicSounds >= 2) ? 2 : 1;
653 else if (cwav.bIsGuitarSound) nPoly = (nPolyphonicSounds >= 2) ? 2 : 1;
654 else if (cwav.bIsSESound) nPoly = 1;
655 else if (cwav.bIsBGMSound) nPoly = 1;
657 if (cwav.bIsBGMSound) nPoly = 1;
661 if (bIsDirectSound) // DirectSoundでの再生の場合はCloneする
663 for (int i = 1; i < nPoly; i++)
665 cwav.rSound[i] = (CSound)cwav.rSound[0].Clone(); // #24007 2011.9.5 yyagi add: to accelerate loading chip sounds
666 // CDTXMania.Instance.app.Sound管理.tサウンドを登録する( cwav.rSound[ j ] );
668 for (int i = nPoly; i < nPolyphonicSounds; i++)
670 cwav.rSound[i] = null;
673 else // WASAPI/ASIO時は通常通り登録
675 for (int i = 1; i < nPoly; i++)
679 cwav.rSound[i] = CDTXMania.Instance.Sound管理.tサウンドを生成する(str);
680 cwav.rSound[i].n音量 = 100;
681 if (!CDTXMania.Instance.ConfigIni.bDynamicBassMixerManagement)
683 cwav.rSound[i].tBASSサウンドをミキサーに追加する();
685 if (CDTXMania.Instance.ConfigIni.bLogCreateRelease)
687 Trace.TraceInformation("サウンドを作成しました。({3})({0})({1})({2}bytes)", cwav.strコメント文, str, cwav.rSound[0].nサウンドバッファサイズ, cwav.rSound[0].bストリーム再生する ? "Stream" : "OnMemory");
692 cwav.rSound[i] = null;
693 Trace.TraceError("サウンドの作成に失敗しました。({0})({1})", cwav.strコメント文, str);
694 Trace.TraceError("例外: " + e.Message);
699 catch (Exception exception)
701 Trace.TraceError("サウンドの生成に失敗しました。({0})({1})({2})", exception.Message, cwav.strコメント文, str);
702 for (int j = 0; j < nPolyphonicSounds; j++)
704 cwav.rSound[j] = null;
710 public static string tZZ(int n)
712 if (n < 0 || n >= 36 * 36)
713 return "!!"; // オーバー/アンダーフロー。
715 // n を36進数2桁の文字列にして返す。
717 string str = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
718 return new string(new char[] { str[n / 36], str[n % 36] });
720 public void tギターとベースのランダム化(EPart part, ERandom eRandom)
722 if (((part == EPart.Guitar) || (part == EPart.Bass)) && (eRandom != ERandom.Off))
725 foreach (CChip chip in this.listChip)
727 bool bOpenChip = (chip.bGuitar可視チップ && this.bチップがある.OpenGuitar) || ((chip.bBass可視チップ) && this.bチップがある.OpenBass);
728 if (chip[EChannel.BarLine]) // 小節が変化したら
730 rndVal = CDTXMania.Instance.Random.Next(6);
733 chip.RandomizeRGB(eRandom, rndVal, bOpenChip);// #23546 2010.10.28 yyagi fixed (bチップがある.Bass→bチップがある.OpenBass)
738 #region [ チップの再生と停止 ]
739 public void tチップの再生(CChip rChip, long n再生開始システム時刻ms)
741 this.tチップの再生(rChip, n再生開始システム時刻ms, CDTXMania.Instance.ConfigIni.nAutoVolume, false, false);
743 public void tチップの再生(CChip rChip, long n再生開始システム時刻ms, int nVol)
745 this.tチップの再生(rChip, n再生開始システム時刻ms, nVol, false, false);
747 public void tチップの再生(CChip rChip, long n再生開始システム時刻ms, int nVol, bool bMIDIMonitor)
749 this.tチップの再生(rChip, n再生開始システム時刻ms, nVol, bMIDIMonitor, false);
751 public void tチップの再生(CChip pChip, long n再生開始システム時刻ms, int nVol, bool bMIDIMonitor, bool bBad)
753 if (pChip.n整数値_内部番号 >= 0)
755 if (this.listWAV.ContainsKey(pChip.n整数値_内部番号))
757 CWAV wc = this.listWAV[pChip.n整数値_内部番号];
758 int index = wc.n現在再生中のサウンド番号 = (wc.n現在再生中のサウンド番号 + 1) % nPolyphonicSounds;
759 if ((wc.rSound[0] != null) &&
760 (wc.rSound[0].bストリーム再生する || wc.rSound[index] == null))
762 index = wc.n現在再生中のサウンド番号 = 0;
764 CSound sound = wc.rSound[index];
769 sound.db周波数倍率 = ((float)(100 + (((CDTXMania.Instance.Random.Next(3) + 1) * 7) * (1 - (CDTXMania.Instance.Random.Next(2) * 2))))) / 100f;
775 sound.db再生速度 = ((double)CDTXMania.Instance.ConfigIni.nPlaySpeed) / 20.0;
776 // 再生速度によって、WASAPI/ASIOで使う使用mixerが決まるため、付随情報の設定(音量/PAN)は、再生速度の設定後に行う
777 sound.n音量 = (int)(((double)(nVol * wc.n音量)) / 100.0);
781 wc.n再生開始時刻[wc.n現在再生中のサウンド番号] = n再生開始システム時刻ms;
782 this.tWave再生位置自動補正(wc);
786 public void t各自動再生音チップの再生時刻を変更する(int nBGMAdjustの増減値)
788 this.nBGMAdjust += nBGMAdjustの増減値;
789 foreach (CChip chip in listChip)
791 chip.AddPlayPositionMsForSE(nBGMAdjustの増減値);
793 foreach (CWAV cwav in this.listWAV.Values)
795 for (int j = 0; j < nPolyphonicSounds; j++)
797 if ((cwav.rSound[j] != null) && cwav.rSound[j].b再生中)
799 cwav.n再生開始時刻[j] += nBGMAdjustの増減値;
804 public void t全チップの再生一時停止()
806 foreach (CWAV cwav in this.listWAV.Values)
808 for (int i = 0; i < nPolyphonicSounds; i++)
810 if ((cwav.rSound[i] != null) && cwav.rSound[i].b再生中)
812 cwav.n一時停止時刻[i] = CSound管理.rc演奏用タイマ.nシステム時刻ms;
813 cwav.rSound[i].t再生を一時停止する();
819 /// 全チップの再生を再開する。しかし一時停止と再開を繰り返すと、再生位置が徐々にずれる問題あり。
820 /// 泥臭い回避方法は、CStage演奏画面共通.cs の tキー入力()を参照のこと。
822 public void t全チップの再生再開()
824 foreach (CWAV cwav in this.listWAV.Values)
826 for (int i = 0; i < nPolyphonicSounds; i++)
828 if ((cwav.rSound[i] != null) && cwav.rSound[i].b一時停止中)
830 cwav.n再生開始時刻[i] += CSound管理.rc演奏用タイマ.nシステム時刻ms - cwav.n一時停止時刻[i];
835 public void t全チップの再生停止()
837 foreach (CWAV cwav in this.listWAV.Values)
839 this.tWavの再生停止(cwav.n内部番号);
842 public void t全チップの再生停止とミキサーからの削除()
844 foreach (CWAV cwav in this.listWAV.Values)
846 this.tWavの再生停止(cwav.n内部番号, true);
852 /// サウンドミキサーにサウンドを登録・削除する時刻を事前に算出する
854 public void PlanToAddMixerChannel()
856 if (CDTXMania.Instance.Sound管理.GetCurrentSoundDeviceType() == "DirectSound") // DShowでの再生の場合はミキシング負荷が高くないため、
857 { // チップのライフタイム管理を行わない
861 List<CChip> listAddMixerChannel = new List<CChip>(128); ;
862 List<CChip> listRemoveMixerChannel = new List<CChip>(128);
863 List<CChip> listRemoveTiming = new List<CChip>(128);
865 //foreach ( CChip pChip in listChip )
866 for (int i = 0; i < listChip.Count; i++)
868 CChip pChip = listChip[i];
869 if (pChip.bWAVを使うチャンネルである)
871 #region [ 発音1秒前のタイミングを記録 ]
872 int n発音前余裕ms = 1000, n発音後余裕ms = 800; // Drums
875 if (pChip.e楽器パート == EPart.Guitar || pChip.e楽器パート == EPart.Bass)
881 if (pChip.ESoundChipTypeを得る == ESoundChipType.SE)
888 #region [ BGMチップならば即ミキサーに追加・・・はしない (全て注釈化) ]
889 //if ( pChip.nチャンネル番号 == 0x01 ) // BGMチップは即ミキサーに追加
891 // if ( listWAV.ContainsKey( pChip.n整数値・内部番号 ) )
893 // CDTX.CWAV wc = CDTXMania.Instance.app.DTX.listWAV[ pChip.n整数値・内部番号 ];
894 // if ( wc.rSound[ 0 ] != null )
896 // CDTXMania.Instance.app.Sound管理.AddMixer( wc.rSound[ 0 ] ); // BGMは多重再生しない仕様としているので、1個目だけミキサーに登録すればよい
901 #region [ 発音1秒前のタイミングを算出 ]
902 int nAddMixer時刻ms, nAddMixer位置 = 0;
903 //Debug.WriteLine("==================================================================");
904 //Debug.WriteLine( "Start: ch=" + pChip.nチャンネル番号.ToString("x2") + ", nWAV番号=" + pChip.n整数値 + ", time=" + pChip.n発声時刻ms + ", lasttime=" + listChip[ listChip.Count - 1 ].n発声時刻ms );
905 t発声時刻msと発声位置を取得する(pChip.n発声時刻ms - n発音前余裕ms, out nAddMixer時刻ms, out nAddMixer位置);
906 //Debug.WriteLine( "nAddMixer時刻ms=" + nAddMixer時刻ms + ",nAddMixer位置=" + nAddMixer位置 );
908 CChip c_AddMixer = new CChip(nAddMixer位置, pChip.n整数値, pChip.n整数値_内部番号, EChannel.MixerAdd, nAddMixer時刻ms, false);
909 listAddMixerChannel.Add(c_AddMixer);
910 //Debug.WriteLine("listAddMixerChannel:" );
911 //DebugOut_CChipList( listAddMixerChannel );
914 #region [ そのチップ音のfullduration(チップ音wavの最大再生時間)を取得 ]
915 int fullduration = 0;
916 if (listWAV.ContainsKey(pChip.n整数値_内部番号))
918 CDTX.CWAV wc = CDTXMania.Instance.DTX.listWAV[pChip.n整数値_内部番号];
919 double _db再生速度 = (CDTXMania.Instance.DTXVmode.Enabled) ? this.dbDTXVPlaySpeed : this.db再生速度;
920 fullduration = (wc.rSound[0] == null) ? 0 : (int)(wc.rSound[0].n総演奏時間ms / _db再生速度); // #23664 durationに再生速度が加味されておらず、低速再生でBGMが途切れる問題を修正 (発声時刻msは、DTX読み込み時に再生速度加味済)
922 //Debug.WriteLine("fullduration=" + fullduration );
925 #region [ そのチップのduration (GtBsが次の音にかき消されることを加味した再生時間) を取得・・・のコードは未使用。mixing抑制の効果が薄いため。]
928 // int ch = ( pChip.nチャンネル番号 >> 4 );
929 // bool bGtBs = ( ch == 0x02 || ch == 0x0A );
930 // if ( bGtBs ) // Guitar/Bassの場合
936 // if ( ++p >= listChip.Count )
940 // chNext = ( listChip[ p ].nチャンネル番号 >> 4 );
941 // duration = listChip[ p ].n発声時刻ms - pChip.n発声時刻ms;
942 // if ( ch == chNext )
947 // while ( duration < fullduration );
951 // duration = fullduration;
955 //Debug.WriteLine( i + ": duration diff= " + (fullduration - duration ) );
956 duration = fullduration;
957 int n新RemoveMixer時刻ms, n新RemoveMixer位置;
958 t発声時刻msと発声位置を取得する(pChip.n発声時刻ms + duration + n発音後余裕ms, out n新RemoveMixer時刻ms, out n新RemoveMixer位置);
959 //Debug.WriteLine( "n新RemoveMixer時刻ms=" + n新RemoveMixer時刻ms + ",n新RemoveMixer位置=" + n新RemoveMixer位置 );
960 if (n新RemoveMixer時刻ms < pChip.n発声時刻ms + duration) // 曲の最後でサウンドが切れるような場合は
962 CChip c_AddMixer_noremove = c_AddMixer;
963 c_AddMixer_noremove.SetSoundAfterPlayEnd(true);
964 listAddMixerChannel[listAddMixerChannel.Count - 1] = c_AddMixer_noremove;
965 continue; // 発声位置の計算ができないので、Mixer削除をあきらめる・・・のではなく
966 // #32248 2013.10.15 yyagi 演奏終了後も再生を続けるチップであるというフラグをpChip内に立てる
969 //if ( n新RemoveMixer時刻ms < pChip.n発声時刻ms + duration ) // 曲の最後でサウンドが切れるような場合
971 // n新RemoveMixer時刻ms = pChip.n発声時刻ms + duration;
972 // // 「位置」は比例計算で求めてお茶を濁す...このやり方だと誤動作したため対応中止
973 // n新RemoveMixer位置 = listChip[ listChip.Count - 1 ].n発声位置 * n新RemoveMixer時刻ms / listChip[ listChip.Count - 1 ].n発声時刻ms;
977 #region [ 発音終了2秒後にmixerから削除するが、その前に再発音することになるのかを確認(再発音ならmixer削除タイミングを延期) ]
978 int n整数値 = pChip.n整数値;
979 int index = listRemoveTiming.FindIndex(
980 delegate (CChip cchip) { return cchip.n整数値 == n整数値; }
982 //Debug.WriteLine( "index=" + index );
983 if (index >= 0) // 過去に同じチップで発音中のものが見つかった場合
984 { // 過去の発音のmixer削除を確定させるか、延期するかの2択。
985 int n旧RemoveMixer時刻ms = listRemoveTiming[index].n発声時刻ms;
986 int n旧RemoveMixer位置 = listRemoveTiming[index].n発声位置;
988 //Debug.WriteLine( "n旧RemoveMixer時刻ms=" + n旧RemoveMixer時刻ms + ",n旧RemoveMixer位置=" + n旧RemoveMixer位置 );
989 if (pChip.n発声時刻ms - n発音前余裕ms <= n旧RemoveMixer時刻ms) // mixer削除前に、同じ音の再発音がある場合は、
990 { // mixer削除時刻を遅延させる(if-else後に行う)
991 //Debug.WriteLine( "remove TAIL of listAddMixerChannel. TAIL INDEX=" + listAddMixerChannel.Count );
992 //DebugOut_CChipList( listAddMixerChannel );
993 listAddMixerChannel.RemoveAt(listAddMixerChannel.Count - 1); // また、同じチップ音の「mixerへの再追加」は削除する
994 //Debug.WriteLine( "removed result:" );
995 //DebugOut_CChipList( listAddMixerChannel );
997 else // 逆に、時間軸上、mixer削除後に再発音するような流れの場合は
999 //Debug.WriteLine( "Publish the value(listRemoveTiming[index] to listRemoveMixerChannel." );
1000 listRemoveMixerChannel.Add(listRemoveTiming[index]); // mixer削除を確定させる
1001 //Debug.WriteLine( "listRemoveMixerChannel:" );
1002 //DebugOut_CChipList( listRemoveMixerChannel );
1003 //listRemoveTiming.RemoveAt( index );
1005 CChip c = new CChip(n新RemoveMixer位置, listRemoveTiming[index].n整数値, listRemoveTiming[index].n整数値_内部番号, EChannel.MixerRemove, n新RemoveMixer時刻ms, false);// mixer削除時刻を更新(遅延)する
1006 listRemoveTiming[index] = c;
1007 //listRemoveTiming[ index ].n発声時刻ms = n新RemoveMixer時刻ms; // mixer削除時刻を更新(遅延)する
1008 //listRemoveTiming[ index ].n発声位置 = n新RemoveMixer位置;
1009 //Debug.WriteLine( "listRemoveTiming: modified" );
1010 //DebugOut_CChipList( listRemoveTiming );
1012 else // 過去に同じチップを発音していないor
1013 { // 発音していたが既にmixer削除確定していたなら
1014 CChip c = new CChip(n新RemoveMixer位置, pChip.n整数値, pChip.n整数値_内部番号, EChannel.MixerRemove, n新RemoveMixer時刻ms, false);// 新しくmixer削除候補として追加する
1015 //Debug.WriteLine( "Add new chip to listRemoveMixerTiming: " );
1016 //Debug.WriteLine( "ch=" + c.nチャンネル番号.ToString( "x2" ) + ", nWAV番号=" + c.n整数値 + ", time=" + c.n発声時刻ms + ", lasttime=" + listChip[ listChip.Count - 1 ].n発声時刻ms );
1017 listRemoveTiming.Add(c);
1018 //Debug.WriteLine( "listRemoveTiming:" );
1019 //DebugOut_CChipList( listRemoveTiming );
1024 //Debug.WriteLine("==================================================================");
1025 //Debug.WriteLine( "Result:" );
1026 //Debug.WriteLine( "listAddMixerChannel:" );
1027 //DebugOut_CChipList( listAddMixerChannel );
1028 //Debug.WriteLine( "listRemoveMixerChannel:" );
1029 //DebugOut_CChipList( listRemoveMixerChannel );
1030 //Debug.WriteLine( "listRemoveTiming:" );
1031 //DebugOut_CChipList( listRemoveTiming );
1032 //Debug.WriteLine( "==================================================================" );
1034 listChip.AddRange(listAddMixerChannel);
1035 listChip.AddRange(listRemoveMixerChannel);
1036 listChip.AddRange(listRemoveTiming);
1039 private void DebugOut_CChipList(List<CChip> c)
1041 //Debug.WriteLine( "Count=" + c.Count );
1042 for (int i = 0; i < c.Count; i++)
1044 Debug.WriteLine(i + ": ch=" + c[i].eチャンネル番号.ToString("x2") + ", WAV番号=" + c[i].n整数値 + ", time=" + c[i].n発声時刻ms);
1049 /// 発声時刻msから発声位置を逆算することはできないため、近似計算する。
1050 /// 具体的には、希望発声位置前後の2つのチップの発声位置の中間を取る。
1052 /// <param name="n希望発声時刻ms"></param>
1053 /// <param name="n新発声時刻ms"></param>
1054 /// <param name="n新発声位置"></param>
1055 /// <returns></returns>
1056 private bool t発声時刻msと発声位置を取得する(int n希望発声時刻ms, out int n新発声時刻ms, out int n新発声位置)
1058 // 発声時刻msから発声位置を逆算することはできないため、近似計算する。
1059 // 具体的には、希望発声位置前後の2つのチップの発声位置の中間を取る。
1065 //else if ( n希望発声時刻ms > listChip[ listChip.Count - 1 ].n発声時刻ms ) // BGMの最後の余韻を殺してしまうので、この条件は外す
1067 // n希望発声時刻ms = listChip[ listChip.Count - 1 ].n発声時刻ms;
1070 int index_min = -1, index_max = -1;
1071 for (int i = 0; i < listChip.Count; i++) // 希望発声位置前後の「前」の方のチップを検索
1073 if (listChip[i].n発声時刻ms >= n希望発声時刻ms)
1079 if (index_min < 0) // 希望発声時刻に至らずに曲が終了してしまう場合
1081 // listの最終項目の時刻をそのまま使用する
1082 //・・・のではダメ。BGMが尻切れになる。
1083 // そこで、listの最終項目の発声時刻msと発生位置から、希望発声時刻に相当する希望発声位置を比例計算して求める。
1084 //n新発声時刻ms = n希望発声時刻ms;
1085 //n新発声位置 = listChip[ listChip.Count - 1 ].n発声位置 * n希望発声時刻ms / listChip[ listChip.Count - 1 ].n発声時刻ms;
1086 n新発声時刻ms = listChip[listChip.Count - 1].n発声時刻ms;
1087 n新発声位置 = listChip[listChip.Count - 1].n発声位置;
1090 index_max = index_min + 1;
1091 if (index_max >= listChip.Count)
1093 index_max = index_min;
1095 n新発声時刻ms = (listChip[index_max].n発声時刻ms + listChip[index_min].n発声時刻ms) / 2;
1096 n新発声位置 = (listChip[index_max].n発声位置 + listChip[index_min].n発声位置) / 2;
1103 /// Swap infos between Guitar and Bass (notes, level, n可視チップ数, bチップがある)
1105 public void SwapGuitarBassInfos() // #24063 2011.1.24 yyagi ギターとベースの譜面情報入替
1107 foreach (CChip chip in listChip)
1111 int t = this.LEVEL.Bass;
1112 this.LEVEL.Bass = this.LEVEL.Guitar;
1113 this.LEVEL.Guitar = t;
1115 t = this.n可視チップ数.Bass;
1116 this.n可視チップ数.Bass = this.n可視チップ数.Guitar;
1117 this.n可視チップ数.Guitar = t;
1119 bool ts = this.bチップがある.Bass;
1120 this.bチップがある.Bass = this.bチップがある.Guitar;
1121 this.bチップがある.Guitar = ts;
1123 // SwapGuitarBassInfos_AutoFlags();
1126 // SwapGuitarBassInfos_AutoFlags()は、CDTXからCConfigIniに移動。
1130 public override void On活性化()
1132 this.listWAV = new Dictionary<int, CWAV>();
1133 this.listBMP = new Dictionary<int, CBMP>();
1134 this.listBMPTEX = new Dictionary<int, CBMPTEX>();
1135 this.listBPM = new Dictionary<int, CBPM>();
1136 this.listBGAPAN = new Dictionary<int, CBGAPAN>();
1137 this.listBGA = new Dictionary<int, CBGA>();
1138 this.listAVIPAN = new Dictionary<int, CAVIPAN>();
1139 this.listAVI = new Dictionary<int, CAVI>();
1140 this.listChip = new List<CChip>();
1143 public override void On非活性化()
1145 if (this.listWAV != null)
1147 foreach (CWAV cwav in this.listWAV.Values)
1151 this.listWAV = null;
1153 if (this.listBMP != null)
1155 foreach (CBMP cbmp in this.listBMP.Values)
1159 this.listBMP = null;
1161 if (this.listBMPTEX != null)
1163 foreach (CBMPTEX cbmptex in this.listBMPTEX.Values)
1167 this.listBMPTEX = null;
1169 if (this.listAVI != null)
1171 foreach (CAVI cavi in this.listAVI.Values)
1175 this.listAVI = null;
1177 if (this.listBPM != null)
1179 this.listBPM.Clear();
1180 this.listBPM = null;
1182 if (this.listBGAPAN != null)
1184 this.listBGAPAN.Clear();
1185 this.listBGAPAN = null;
1187 if (this.listBGA != null)
1189 this.listBGA.Clear();
1190 this.listBGA = null;
1192 if (this.listAVIPAN != null)
1194 this.listAVIPAN.Clear();
1195 this.listAVIPAN = null;
1197 if (this.listChip != null)
1199 this.listChip.Clear();
1203 public override void OnManagedリソースの作成()
1207 this.tBMP_BMPTEXの読み込み();
1209 base.OnManagedリソースの作成();
1212 public override void OnManagedリソースの解放()
1216 if (this.listBMP != null)
1218 foreach (CBMP cbmp in this.listBMP.Values)
1223 if (this.listBMPTEX != null)
1225 foreach (CBMPTEX cbmptex in this.listBMPTEX.Values)
1230 if (this.listAVI != null)
1232 foreach (CAVI cavi in this.listAVI.Values)
1237 base.OnManagedリソースの解放();
1247 /// <para>GDAチャンネル番号に対応するDTXチャンネル番号。</para>
1249 [StructLayout(LayoutKind.Sequential)]
1250 private struct STGDAPARAM
1252 public string strGDAのチャンネル文字列;
1253 public EChannel eDTXのチャンネル番号;
1255 public STGDAPARAM(string strGDAのチャンネル文字列, EChannel eDTXのチャンネル番号) // 2011.1.1 yyagi 構造体のコンストラクタ追加(初期化簡易化のため)
1257 this.strGDAのチャンネル文字列 = strGDAのチャンネル文字列;
1258 this.eDTXのチャンネル番号 = eDTXのチャンネル番号;
1262 private readonly STGDAPARAM[] stGDAParam;
1263 private bool bヘッダのみ;
1264 private Stack<bool> bstackIFからENDIFをスキップする;
1269 private int nPolyphonicSounds = 4; // #28228 2012.5.1 yyagi
1271 private int n内部番号BPM1to;
1272 private int n内部番号WAV1to;
1273 private int[] n無限管理BPM;
1274 private int[] n無限管理PAN;
1275 private int[] n無限管理SIZE;
1276 private int[] n無限管理VOL;
1277 private int[] n無限管理WAV;
1278 private int[] nRESULTIMAGE用優先順位;
1279 private int[] nRESULTMOVIE用優先順位;
1280 private int[] nRESULTSOUND用優先順位;
1282 #region [#23880 2010.12.30 yyagi: コンマとスペースの両方を小数点として扱うTryParse]
1284 /// 小数点としてコンマとピリオドの両方を受け付けるTryParse()
1286 /// <param name="s">strings convert to double</param>
1287 /// <param name="result">parsed double value</param>
1288 /// <returns>s が正常に変換された場合は true。それ以外の場合は false。</returns>
1289 /// <exception cref="ArgumentException">style が NumberStyles 値でないか、style に NumberStyles.AllowHexSpecifier 値が含まれている</exception>
1290 private bool TryParse(string s, out double result)
1291 { // #23880 2010.12.30 yyagi: alternative TryParse to permit both '.' and ',' for decimal point
1292 // EU諸国での #BPM 123,45 のような記述に対応するため、
1293 // 小数点の最終位置を検出して、それをlocaleにあった
1294 // 文字に置き換えてからTryParse()する
1297 const string DecimalSeparators = ".,"; // 小数点文字
1298 const string GroupSeparators = ".,' "; // 桁区切り文字
1299 const string NumberSymbols = "0123456789"; // 数値文字
1301 int len = s.Length; // 文字列長
1302 int decimalPosition = len; // 真の小数点の位置 最初は文字列終端位置に仮置きする
1304 for (int i = 0; i < len; i++)
1305 { // まず、真の小数点(一番最後に現れる小数点)の位置を求める
1307 if (NumberSymbols.IndexOf(c) >= 0)
1311 else if (DecimalSeparators.IndexOf(c) >= 0)
1312 { // 小数点文字だったら、その都度位置を上書き記憶
1313 decimalPosition = i;
1315 else if (GroupSeparators.IndexOf(c) >= 0)
1320 { // 数値・小数点・区切り文字以外がきたらループ終了
1325 StringBuilder decimalStr = new StringBuilder(16);
1326 for (int i = 0; i < len; i++)
1327 { // 次に、localeにあった数値文字列を生成する
1329 if (NumberSymbols.IndexOf(c) >= 0)
1331 decimalStr.Append(c); // そのままコピー
1333 else if (DecimalSeparators.IndexOf(c) >= 0)
1335 if (i == decimalPosition)
1336 { // 最後に出現した小数点文字なら、localeに合った小数点を出力する
1337 decimalStr.Append(CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator);
1340 else if (GroupSeparators.IndexOf(c) >= 0)
1342 continue; // 何もしない(スキップ)
1349 return double.TryParse(decimalStr.ToString(), out result); // 最後に、自分のlocale向けの文字列に対してTryParse実行
1355 internal void t全AVIの一時停止()
1358 foreach (var avi in listAVI)
1360 if (avi.Value.avi != null && avi.Value.avi.b再生中)
1362 avi.Value.avi.Pause();
1367 foreach (var avi in listAVIPAN)
1369 //if ( avi.Value.avi != null && avi.Value.avi.b再生中 )
1371 // avi.Value.avi.ToggleRun();
1375 internal void t全AVIの再生再開()
1378 foreach (var avi in listAVI)
1380 if (avi.Value.avi != null && avi.Value.avi.b一時停止中)
1382 avi.Value.avi.ToggleRun();
1386 foreach (var avi in listAVIPAN)
1388 //if ( avi.Value.avi != null && avi.Value.avi.b一時停止中 )
1390 // avi.Value.avi.ToggleRun();