• R/O
  • HTTP
  • SSH
  • HTTPS

Commit

Tags
Keine Tags

Frequently used words (click to add to your profile)

javaandroidc++linuxc#objective-ccocoa誰得qtrubybathyscaphegamewindowspythonphpguic翻訳omegattwitterframeworkbtronarduinovb.net計画中(planning stage)directxpreviewertestゲームエンジンdom

dtxmania 中文


Commit MetaInfo

Revision70b828b409219ad46717cfdc3d3723b420ad40ba (tree)
Zeit2017-05-21 01:10:00
Autoryyagi <yyagi.dtxmania@gmai...>
Commiteryyagi

Log Message

DirectSoundでClone()するとメモリリークする問題修正の途中版。WAVデータ実体への参照カウンタを実装済み。
ただし現在、DirectSoundなSoundDeviceのDispose()に常に失敗し、またメモリリークも引き続き発生している。
引き続き調査が必要。
なおSound管理.tサウンドを生成する()内で、新規作成とClone()の首振りを行うようにしたため、外部からClone()を呼ぶ必要はなくなった。

Ändern Zusammenfassung

  • delete: "FDK/\343\202\263\343\203\274\343\203\211/03.\343\202\265\343\202\246\343\203\263\343\203\211/CSound.cs"

Diff

--- "a/FDK/\343\202\263\343\203\274\343\203\211/03.\343\202\265\343\202\246\343\203\263\343\203\211/CSound.cs"
+++ "b/FDK/\343\202\263\343\203\274\343\203\211/03.\343\202\265\343\202\246\343\203\263\343\203\211/CSound.cs"
@@ -116,6 +116,105 @@ namespace FDK
116116 {
117117 return nStreams;
118118 }
119+
120+ /// <summary>
121+ /// サウンドファイルの実体への参照数。
122+ /// </summary>
123+ /// <remarks>0の時にCSound()を作ると実体を生成し、1以上の時はClone()する。開放の時も同様で、2以上の時は実体を開放せず、1の時に実体を開放する。</remarks>
124+ /// <remarks>DirectSound時のみ使用する。</remarks>
125+ private static Dictionary<string, int> dicWavFileRefCounter;
126+ private static Dictionary<string, CSound> dicCSoundWavInstance;
127+
128+ public CSound tGetCSoundWavInstance(string fullPathName)
129+ {
130+ if (dicCSoundWavInstance.ContainsKey(fullPathName))
131+ {
132+//Debug.WriteLine("dic instance hit" + Path.GetFileName(fullPathName));
133+ return dicCSoundWavInstance[fullPathName];
134+ }
135+ else
136+ {
137+//Debug.WriteLine("dic instance fail" + Path.GetFileName(fullPathName));
138+ return null;
139+ }
140+ }
141+ public bool tSetCSoundWavInstance(string fullPathName, CSound cs)
142+ {
143+ if (dicCSoundWavInstance.ContainsKey(fullPathName))
144+ {
145+//Debug.WriteLine("dic instance already set" + Path.GetFileName(fullPathName));
146+
147+ throw new ArgumentException("tSetCSoundWavInstance(" + fullPathName + "): 既に登録されています。");
148+ return false;
149+ }
150+ else
151+ {
152+//Debug.WriteLine("dic instance add" + Path.GetFileName(fullPathName));
153+ dicCSoundWavInstance.Add(fullPathName, cs);
154+ return true;
155+ }
156+ }
157+
158+ public static int tGetWavFileRefCounter(string fullPathName)
159+ {
160+ if (dicWavFileRefCounter.ContainsKey(fullPathName))
161+ {
162+//Debug.WriteLine("dic ref counter = " +dicWavFileRefCounter[fullPathName] + " (" + Path.GetFileName(fullPathName) + ")" );
163+ return dicWavFileRefCounter[fullPathName];
164+ }
165+ else
166+ {
167+//Debug.WriteLine("dic ref counter = 0 (" + Path.GetFileName(fullPathName) + ")" );
168+ return 0;
169+ }
170+ }
171+
172+ public static int tIncrementWavFileRefCounter(string fullPathName)
173+ {
174+ if (dicWavFileRefCounter.ContainsKey(fullPathName))
175+ {
176+//Debug.WriteLine("dic incremented counter = " + tGetWavFileRefCounter(fullPathName) + "+1 (" + Path.GetFileName(fullPathName) + ")" );
177+ return ++dicWavFileRefCounter[fullPathName];
178+ }
179+ else
180+ {
181+//Debug.WriteLine("dic incremented counter = 1 (" + Path.GetFileName(fullPathName) + ")" );
182+ dicWavFileRefCounter.Add(fullPathName, 1);
183+ return 1;
184+ }
185+ }
186+
187+ public static int tDecrementWavFileRefCounter(string fullPathName)
188+ {
189+ if (dicWavFileRefCounter.ContainsKey(fullPathName))
190+ {
191+//Debug.WriteLine("dic decremented counter = " + tGetWavFileRefCounter(fullPathName) + "-1 (" + Path.GetFileName(fullPathName) + ")" );
192+ int value = --dicWavFileRefCounter[fullPathName];
193+ if (value == 0)
194+ {
195+ dicWavFileRefCounter.Remove(fullPathName);
196+ dicCSoundWavInstance.Remove(fullPathName);
197+ }
198+ else if (value < 0)
199+ {
200+ throw new Exception("tDecrementWavFileRefCounter(" + fullPathName + "): カウンタが負の値です。(" + value.ToString() + ")" );
201+ }
202+ return value;
203+ }
204+ else
205+ {
206+ //Debug.WriteLine("dic dec counter = null (" + Path.GetFileName(fullPathName) + ")" );
207+ //throw new ArgumentException("tDecrementWavFileRefCounter(" + fullPathName + "): 引数に相当するサウンドファイルが登録されていません。");
208+ return 0;
209+ }
210+ }
211+
212+ public static void tClearWavFileRefCounter()
213+ {
214+ dicWavFileRefCounter.Clear();
215+ dicCSoundWavInstance.Clear();
216+ }
217+
119218 #region [ WASAPI/ASIO/DirectSound設定値 ]
120219 /// <summary>
121220 /// <para>WASAPI 排他モード出力における再生遅延[ms](の希望値)。最終的にはこの数値を基にドライバが決定する)。</para>
@@ -259,6 +358,7 @@ namespace FDK
259358 rc演奏用タイマ = null; // Global.Bass 依存(つまりユーザ依存)
260359 nMixing = 0;
261360
361+Debug.WriteLine("t初期化() 開始");
262362 SoundDelayExclusiveWASAPI = _nSoundDelayExclusiveWASAPI;
263363 SoundDelayASIO = _nSoundDelayASIO;
264364 ASIODevice = _nASIODevice;
@@ -293,11 +393,14 @@ namespace FDK
293393 n初期デバイス = 4;
294394 break;
295395 }
396+Debug.WriteLine("t初期化() ループ開始");
296397 for ( SoundDeviceType = ESoundDeviceTypes[ n初期デバイス ]; ; SoundDeviceType = ESoundDeviceTypes[ ++n初期デバイス ] )
297398 {
298399 try
299400 {
401+Debug.WriteLine("再構築 開始");
300402 t現在のユーザConfigに従ってサウンドデバイスとすべての既存サウンドを再構築する();
403+Debug.WriteLine("再構築 終了");
301404 break;
302405 }
303406 catch ( Exception e )
@@ -314,10 +417,15 @@ namespace FDK
314417 {
315418 //Bass.BASS_SetConfig( BASSConfig.BASS_CONFIG_UPDATETHREADS, 4 );
316419 //Bass.BASS_SetConfig( BASSConfig.BASS_CONFIG_UPDATEPERIOD, 0 );
420+ Bass.BASS_SetConfig( BASSConfig.BASS_CONFIG_ASYNCFILE_BUFFER, 65536 * 4 );
317421
318422 Trace.TraceInformation( "BASS_CONFIG_UpdatePeriod=" + Bass.BASS_GetConfig( BASSConfig.BASS_CONFIG_UPDATEPERIOD ) );
319423 Trace.TraceInformation( "BASS_CONFIG_UpdateThreads=" + Bass.BASS_GetConfig( BASSConfig.BASS_CONFIG_UPDATETHREADS ) );
424+ Trace.TraceInformation( "BASS_CONFIG_ASYNCFILE_BUFFER=" + Bass.BASS_GetConfig(BASSConfig.BASS_CONFIG_ASYNCFILE_BUFFER ) );
320425 }
426+
427+ dicWavFileRefCounter = new Dictionary<string, int>();
428+ dicCSoundWavInstance = new Dictionary<string, CSound>();
321429 }
322430
323431 public void tDisableUpdateBufferAutomatically()
@@ -332,29 +440,49 @@ namespace FDK
332440
333441 public static void t終了()
334442 {
335- C共通.tDisposeする( SoundDevice ); SoundDevice = null;
336- C共通.tDisposeする( ref rc演奏用タイマ ); // Global.Bass を解放した後に解放すること。(Global.Bass で参照されているため)
443+Debug.WriteLine("t終了()の開始");
444+ try
445+ {
446+ C共通.tDisposeする(SoundDevice); SoundDevice = null;
447+ }
448+ catch (Exception e)
449+ {
450+ Trace.TraceError(e.Message);
451+ }
452+Debug.WriteLine("SoundDeviceのDispose終了");
453+ try
454+ {
455+ C共通.tDisposeする(ref rc演奏用タイマ); // Global.Bass を解放した後に解放すること。(Global.Bass で参照されているため)
456+ }
457+ catch (Exception e)
458+ {
459+ Trace.TraceError(e.Message);
460+ }
461+Debug.WriteLine("t終了()の終了");
337462 }
338463
339464
340465 public static void t現在のユーザConfigに従ってサウンドデバイスとすべての既存サウンドを再構築する()
341466 {
342467 #region [ すでにサウンドデバイスと演奏タイマが構築されていれば解放する。]
468+Debug.WriteLine("開放チェック");
343469 //-----------------
344470 if ( SoundDevice != null )
345471 {
346472 // すでに生成済みのサウンドがあれば初期状態に戻す。
347473
348- CSound.tすべてのサウンドを初期状態に戻す(); // リソースは解放するが、CSoundのインスタンスは残す。
474+Debug.WriteLine("SoundDevice: すべてのサウンドを初期状態に戻す 開始");
475+ CSound.tすべてのサウンドを初期状態に戻す(); // リソースは解放するが、CSoundのインスタンスは残す。
349476
350477
351478 // サウンドデバイスと演奏タイマを解放する。
352-
479+Debug.WriteLine("SoundDevice: Disposing");
353480 C共通.tDisposeする( SoundDevice ); SoundDevice = null;
354481 C共通.tDisposeする( ref rc演奏用タイマ ); // Global.SoundDevice を解放した後に解放すること。(Global.SoundDevice で参照されているため)
355482 }
356483 //-----------------
357484 #endregion
485+Debug.WriteLine("SoundDevice: Start構築");
358486
359487 #region [ 新しいサウンドデバイスを構築する。]
360488 //-----------------
@@ -379,6 +507,7 @@ namespace FDK
379507 default:
380508 throw new Exception( string.Format( "未対応の SoundDeviceType です。[{0}]", SoundDeviceType.ToString() ) );
381509 }
510+Debug.WriteLine("SoundDevice: Start構築完了");
382511 //-----------------
383512 #endregion
384513 #region [ 新しい演奏タイマを構築する。]
@@ -386,10 +515,12 @@ namespace FDK
386515 rc演奏用タイマ = new CSoundTimer( SoundDevice );
387516 //-----------------
388517 #endregion
518+Debug.WriteLine("SoundDevice: 演奏タイマ構築完了");
389519
390520 SoundDevice.nMasterVolume = _nMasterVolume; // サウンドデバイスに対して、マスターボリュームを再設定する
391521
392522 CSound.tすべてのサウンドを再構築する( SoundDevice ); // すでに生成済みのサウンドがあれば作り直す。
523+Debug.WriteLine("SoundDevice: すべてのサウンドを再構築完了");
393524 }
394525 public CSound tサウンドを生成する( string filename )
395526 {
@@ -397,7 +528,26 @@ namespace FDK
397528 {
398529 throw new Exception( string.Format( "未対応の SoundDeviceType です。[{0}]", SoundDeviceType.ToString() ) );
399530 }
400- return SoundDevice.tサウンドを作成する( filename );
531+ if (SoundDeviceType == ESoundDeviceType.DirectSound)
532+ {
533+ if (tIncrementWavFileRefCounter(filename) <= 1)
534+ {
535+ CSound cs = SoundDevice.tサウンドを作成する(filename);
536+ tSetCSoundWavInstance(filename, cs);
537+ return cs;
538+ }
539+ else
540+ {
541+ CSound cs = (CSound)tGetCSoundWavInstance(filename).Clone();
542+ CSound.listインスタンス.Add(cs);
543+//Debug.WriteLine("listインスタンスに追加3: " + Path.GetFileName(cs.strファイル名));
544+ return cs;
545+ }
546+ }
547+ else
548+ {
549+ return SoundDevice.tサウンドを作成する(filename);
550+ }
401551 }
402552
403553 private static DateTime lastUpdateTime = DateTime.MinValue;
@@ -740,6 +890,11 @@ namespace FDK
740890 this._hTempoStream = 0;
741891 }
742892
893+ /// <summary>
894+ ///
895+ /// </summary>
896+ /// <returns></returns>
897+ /// <remarks>CSoundの新規作成時に、新規作成かClone()するかを自動で判別するため、外部からは呼び出さないこと。</remarks>
743898 public object Clone()
744899 {
745900 if ( !bDirectSoundである )
@@ -777,12 +932,13 @@ namespace FDK
777932 public void tDirectSoundサウンドを作成する( string strファイル名, DirectSound DirectSound )
778933 {
779934 this.e作成方法 = E作成方法.ファイルから;
780- this.strファイル名 = strファイル名;
781- if ( String.Compare( Path.GetExtension( strファイル名 ), ".xa", true ) == 0 ||
782- String.Compare( Path.GetExtension( strファイル名 ), ".mp3", true ) == 0 ||
783- String.Compare( Path.GetExtension( strファイル名 ), ".ogg", true ) == 0 ) // caselessで文字列比較
935+ this.strファイル名 = Path.GetFullPath( strファイル名 );
936+
937+ if (String.Compare(Path.GetExtension(strファイル名), ".xa", true) == 0 ||
938+ String.Compare(Path.GetExtension(strファイル名), ".mp3", true) == 0 ||
939+ String.Compare(Path.GetExtension(strファイル名), ".ogg", true) == 0) // caselessで文字列比較
784940 {
785- tDirectSoundサウンドを作成するXaOggMp3( strファイル名, DirectSound );
941+ tDirectSoundサウンドを作成するXaOggMp3(strファイル名, DirectSound);
786942 return;
787943 }
788944
@@ -793,55 +949,55 @@ namespace FDK
793949
794950 {
795951 #region [ ファイルがWAVかつPCMフォーマットか否か調べる。]
796- //-----------------
797- try
798- {
799- using( var ws = new SoundStream( new FileStream( strファイル名, FileMode.Open ) ) )
952+ //-----------------
953+ try
800954 {
801- if( ws.Format.Encoding != WaveFormatEncoding.Pcm )
802- bファイルがWAVかつPCMフォーマットである = false;
955+ using (var ws = new SoundStream(new FileStream(strファイル名, FileMode.Open)))
956+ {
957+ if (ws.Format.Encoding != WaveFormatEncoding.Pcm)
958+ bファイルがWAVかつPCMフォーマットである = false;
959+ }
803960 }
804- }
805- catch
806- {
807- bファイルがWAVかつPCMフォーマットである = false;
808- }
809- //-----------------
810- #endregion
961+ catch
962+ {
963+ bファイルがWAVかつPCMフォーマットである = false;
964+ }
965+ //-----------------
966+ #endregion
811967
812- if ( bファイルがWAVかつPCMフォーマットである )
968+ if (bファイルがWAVかつPCMフォーマットである)
813969 {
814970 #region [ ファイルを読み込んで byArrWAVファイルイメージへ格納。]
815- //-----------------
816- var fs = File.Open( strファイル名, FileMode.Open, FileAccess.Read );
817- var br = new BinaryReader( fs );
971+ //-----------------
972+ var fs = File.Open(strファイル名, FileMode.Open, FileAccess.Read);
973+ var br = new BinaryReader(fs);
818974
819- byArrWAVファイルイメージ = new byte[ fs.Length ];
820- br.Read( byArrWAVファイルイメージ, 0, (int) fs.Length );
975+ byArrWAVファイルイメージ = new byte[fs.Length];
976+ br.Read(byArrWAVファイルイメージ, 0, (int)fs.Length);
821977
822- br.Close();
823- fs.Close();
824- //-----------------
825- #endregion
978+ br.Close();
979+ fs.Close();
980+ //-----------------
981+ #endregion
826982 }
827983 else
828984 {
829985 #region [ DirectShow でデコード変換し、 byArrWAVファイルイメージへ格納。]
830- //-----------------
831- CDStoWAVFileImage.t変換( strファイル名, out byArrWAVファイルイメージ );
832- //-----------------
833- #endregion
986+ //-----------------
987+ CDStoWAVFileImage.t変換(strファイル名, out byArrWAVファイルイメージ);
988+ //-----------------
989+ #endregion
834990 }
835991 }
836992
837993 // あとはあちらで。
838994
839- this.tDirectSoundサウンドを作成する( byArrWAVファイルイメージ, DirectSound );
995+ this.tDirectSoundサウンドを作成する(byArrWAVファイルイメージ, DirectSound);
840996 }
841997 public void tDirectSoundサウンドを作成するXaOggMp3( string strファイル名, DirectSound DirectSound )
842998 {
843999 this.e作成方法 = E作成方法.ファイルから;
844- this.strファイル名 = strファイル名;
1000+ this.strファイル名 = Path.GetFullPath( strファイル名 );
8451001
8461002
8471003 int nPCMデータの先頭インデックス = 0;
@@ -1002,6 +1158,7 @@ namespace FDK
10021158
10031159 // インスタンスリストに登録。
10041160 CSound.listインスタンス.Add( this );
1161+ //Debug.WriteLine("listインスタンスに追加2: " + Path.GetFileName(this.strファイル名));
10051162 }
10061163
10071164 #region [ DTXMania用の変換 ]
@@ -1284,6 +1441,7 @@ Debug.WriteLine("更に再生に失敗: " + Path.GetFileName(this.strファイ
12841441
12851442 public static void tすべてのサウンドを初期状態に戻す()
12861443 {
1444+Debug.WriteLine("SoundDevice: すべてのサウンドを初期状態に戻すメイン");
12871445 foreach ( var sound in CSound.listインスタンス )
12881446 {
12891447 sound.t解放する( false );
@@ -1299,6 +1457,7 @@ Debug.WriteLine("更に再生に失敗: " + Path.GetFileName(this.strファイ
12991457
13001458 var sounds = CSound.listインスタンス.ToArray();
13011459 CSound.listインスタンス.Clear();
1460+ CSound管理.tClearWavFileRefCounter();
13021461
13031462
13041463 // 配列に基づいて個々のサウンドを作成する。
@@ -1389,7 +1548,10 @@ Debug.WriteLine("更に再生に失敗: " + Path.GetFileName(this.strファイ
13891548 {
13901549 // 演奏終了後、長時間解放しないでいると、たまに AccessViolationException が発生することがある。
13911550 }
1392- C共通.tDisposeする( ref this.Buffer );
1551+ if ( CSound管理.tDecrementWavFileRefCounter(this.strファイル名) > 0)
1552+ {
1553+ this.Buffer = null;
1554+ }
13931555 }
13941556 //-----------------
13951557 #endregion
@@ -1409,7 +1571,7 @@ Debug.WriteLine("更に再生に失敗: " + Path.GetFileName(this.strファイ
14091571 this.byArrWAVファイルイメージ = null;
14101572 }
14111573
1412- if ( bインスタンス削除 )
1574+ if (bインスタンス削除)
14131575 {
14141576 //try
14151577 //{
@@ -1419,14 +1581,40 @@ Debug.WriteLine("更に再生に失敗: " + Path.GetFileName(this.strファイ
14191581 //{
14201582 // Debug.WriteLine( "FAILED to remove CSound.listインスタンス: Count=" + CSound.listインスタンス.Count + ", filename=" + Path.GetFileName( this.strファイル名 ) );
14211583 //}
1422- bool b = CSound.listインスタンス.Remove( this ); // これだと、Clone()したサウンドのremoveに失敗する
1423- if ( !b )
1584+
1585+ if (this.eデバイス種別 == ESoundDeviceType.DirectSound && CSound管理.tGetWavFileRefCounter(this.strファイル名) <= 0)
1586+ {
1587+//Debug.WriteLine("実体をDisposeしました: " + Path.GetFileName(this.strファイル名));
1588+ C共通.tDisposeする(ref this.Buffer);
1589+ }
1590+ else
14241591 {
1425- Debug.WriteLine( "FAILED to remove CSound.listインスタンス: Count=" + CSound.listインスタンス.Count + ", filename=" + Path.GetFileName( this.strファイル名 ) );
1592+//Debug.WriteLine("実体をDisposeしません: " + Path.GetFileName(this.strファイル名));
1593+ }
1594+ if (!CSound.listインスタンス.Contains(this))
1595+ {
1596+//Debug.WriteLine("Removeするitemが CSound.listインスタンスにありません: " + Path.GetFileName(this.strファイル名));
14261597 }
14271598
1599+//Debug.WriteLine("Remove前のインスタンス数=" + CSound.listインスタンス.Count);
1600+ bool b = CSound.listインスタンス.Remove(this); // これだと、Clone()したサウンドのremoveに失敗する
1601+//Debug.WriteLine("Remove後のインスタンス数=" + CSound.listインスタンス.Count);
1602+ if (!b)
1603+ {
1604+ Debug.WriteLine("FAILED to remove CSound.listインスタンス: Count=" + CSound.listインスタンス.Count + ", filename=" + Path.GetFileName(this.strファイル名));
1605+ }
1606+//Debug.WriteLine("インスタンス一覧:");
1607+//ShowAllCSoundFiles();
1608+ }
1609+ else
1610+ {
1611+ //Debug.WriteLine("bインスタンス削除 == false for " + Path.GetFileName(this.strファイル名));
14281612 }
14291613 }
1614+ else
1615+ {
1616+ //Debug.WriteLine("bManagedも開放する == false for " + Path.GetFileName(this.strファイル名));
1617+ }
14301618 }
14311619 ~CSound()
14321620 {
@@ -1523,7 +1711,7 @@ Debug.WriteLine("更に再生に失敗: " + Path.GetFileName(this.strファイ
15231711 #endregion
15241712
15251713 this.e作成方法 = E作成方法.ファイルから;
1526- this.strファイル名 = strファイル名;
1714+ this.strファイル名 = Path.GetFullPath( strファイル名 );
15271715
15281716
15291717 // BASSファイルストリームを作成。
@@ -1614,7 +1802,7 @@ Debug.WriteLine("更に再生に失敗: " + Path.GetFileName(this.strファイ
16141802 nBytes = totalPCMSize;
16151803
16161804 this.e作成方法 = E作成方法.WAVファイルイメージから; //.ファイルから; // 再構築時はデコード後のイメージを流用する&Dispose時にhGCを解放する
1617- this.strファイル名 = strファイル名;
1805+ this.strファイル名 = Path.GetFullPath( strファイル名 );
16181806 this.hGC = GCHandle.Alloc( this.byArrWAVファイルイメージ, GCHandleType.Pinned ); // byte[] をピン留め
16191807
16201808 //_cbStreamXA = new STREAMPROC( CallbackPlayingXA );
@@ -1675,6 +1863,7 @@ Debug.WriteLine("更に再生に失敗: " + Path.GetFileName(this.strファイ
16751863 // インスタンスリストに登録。
16761864
16771865 CSound.listインスタンス.Add( this );
1866+//Debug.WriteLine("listインスタンスに追加1: " + Path.GetFileName(this.strファイル名));
16781867
16791868 // n総演奏時間の取得; DTXMania用に追加。
16801869 double seconds = Bass.BASS_ChannelBytes2Seconds( this._hBassStream, nBytes );