OSDN Git Service

#32661 Beat Detection機能と、それに付随するBPM自動設定機能を追加。使い方はチケット参照のこと。
[dtxmania/dtxmania.git] / FDK17プロジェクト / コード / 06.Tempo / CBeatDetect.cs
1 using System;\r
2 using System.Collections.Generic;\r
3 using System.Text;\r
4 using System.Diagnostics;\r
5 using System.Threading;\r
6 using Un4seen.Bass;\r
7 //using Un4seen.BassAsio;\r
8 //using Un4seen.BassWasapi;\r
9 //using Un4seen.Bass.AddOn.Mix;\r
10 using Un4seen.Bass.AddOn.Fx;\r
11 \r
12 namespace FDK\r
13 {\r
14         public class CBeatDetect : IDisposable\r
15         {\r
16                 public struct stBeatPos\r
17                 {\r
18                         public float fBeatTime;\r
19                         public int n小節番号;\r
20                         public int nGrid;\r
21                         public int n小節内Grid;\r
22                         public bool b無効;                                    // \r
23                         public bool bレーン表示する;             // 未使用\r
24 \r
25                         public stBeatPos( float _fBeatTime, int _n小節番号, int _nGrid, int _n小節内Grid, bool _b無効, bool _bレーン表示する )\r
26                         {\r
27                                 fBeatTime = _fBeatTime;\r
28                                 n小節番号 = _n小節番号;\r
29                                 nGrid = _nGrid;\r
30                                 n小節内Grid = _n小節内Grid;\r
31                                 b無効 = _b無効;\r
32                                 bレーン表示する= _bレーン表示する;\r
33                         }\r
34                 }\r
35 \r
36                 #region [ コンストラクタ ]\r
37                 public CBeatDetect()\r
38                 {\r
39                         Initialize();\r
40                 }\r
41                 public CBeatDetect( string _filename )\r
42                 {\r
43                         this.filename = _filename;\r
44                         Initialize();\r
45                 }\r
46                 #endregion\r
47                 #region [ 初期化(コンストラクタから呼び出される) ]\r
48                 private void Initialize()\r
49                 {\r
50                         if ( this.listBeatPositions == null )\r
51                         {\r
52                                 this.listBeatPositions = new List<stBeatPos>();\r
53                         }\r
54 \r
55                         #region [ BASS registration ]\r
56                         // BASS.NET ユーザ登録(BASSスプラッシュが非表示になる)。\r
57 \r
58                         BassNet.Registration( "dtx2013@gmail.com", "2X9181017152222" );\r
59                         #endregion\r
60                         #region [ BASS Version Check ]\r
61                         // BASS のバージョンチェック。\r
62                         int nBASSVersion = Utils.HighWord( Bass.BASS_GetVersion() );\r
63                         if ( nBASSVersion != Bass.BASSVERSION )\r
64                                 throw new DllNotFoundException( string.Format( "bass.dll のバージョンが異なります({0})。このプログラムはバージョン{1}で動作します。", nBASSVersion, Bass.BASSVERSION ) );\r
65 \r
66                         int nBASSFXVersion = Utils.HighWord( BassFx.BASS_FX_GetVersion() );\r
67                         if ( nBASSFXVersion != BassFx.BASSFXVERSION )\r
68                                 throw new DllNotFoundException( string.Format( "bass_fx.dll のバージョンが異なります({0})。このプログラムはバージョン{1}で動作します。", nBASSFXVersion, BassFx.BASSFXVERSION ) );\r
69                         #endregion\r
70 \r
71                         #region [ BASS の設定。]\r
72                         //this.bIsBASSFree = true;\r
73                         //Debug.Assert( Bass.BASS_SetConfig( BASSConfig.BASS_CONFIG_UPDATEPERIOD, 0 ),          // 0:BASSストリームの自動更新を行わない。(サウンド出力しないため)\r
74                         //    string.Format( "BASS_SetConfig() に失敗しました。[{0}", Bass.BASS_ErrorGetCode() ) );\r
75                         #endregion\r
76                         #region [ BASS の初期化。]\r
77                         int nデバイス = 0;          // 0:"no sound" … BASS からはデバイスへアクセスさせない。\r
78                         int n周波数 = 44100; // 仮決め。lデバイス(≠ドライバ)がネイティブに対応している周波数であれば何でもいい?ようだ。いずれにしろBASSMXで自動的にリサンプリングされる。\r
79                         if ( !Bass.BASS_Init( nデバイス, n周波数, BASSInit.BASS_DEVICE_DEFAULT, IntPtr.Zero ) )\r
80                                 throw new Exception( string.Format( "BASS の初期化に失敗しました。(BASS_Init)[{0}]", Bass.BASS_ErrorGetCode().ToString() ) );\r
81                         #endregion\r
82 \r
83                         #region [ 指定されたサウンドファイルをBASSでオープンし、必要最小限の情報を取得する。]\r
84                         //this.hBassStream = Bass.BASS_StreamCreateFile( this.filename, 0, 0, BASSFlag.BASS_STREAM_PRESCAN | BASSFlag.BASS_STREAM_DECODE );\r
85                         this.hBassStream = Bass.BASS_StreamCreateFile( this.filename, 0, 0, BASSFlag.BASS_STREAM_DECODE );\r
86                         if ( this.hBassStream == 0 )\r
87                                 throw new Exception( string.Format( "{0}: サウンドストリームの生成に失敗しました。(BASS_StreamCreateFile)[{1}]", filename, Bass.BASS_ErrorGetCode().ToString() ) );\r
88 \r
89                         this.nTotalBytes = Bass.BASS_ChannelGetLength( this.hBassStream );\r
90 \r
91                         this.nTotalSeconds = Bass.BASS_ChannelBytes2Seconds( this.hBassStream, nTotalBytes );\r
92                         if ( !Bass.BASS_ChannelGetAttribute( this.hBassStream, BASSAttribute.BASS_ATTRIB_FREQ, ref fFreq ) )\r
93                         {\r
94                                 string errmes = string.Format( "サウンドストリームの周波数取得に失敗しました。(BASS_ChannelGetAttribute)[{0}]", Bass.BASS_ErrorGetCode().ToString() );\r
95                                 Bass.BASS_Free();\r
96                                 throw new Exception( errmes );\r
97                         }\r
98                         #endregion\r
99                 }\r
100                 #endregion\r
101 \r
102                 /// <summary>\r
103                 /// 曲全体のテンポを取得する\r
104                 /// </summary>\r
105                 /// <returns>テンポ値</returns>\r
106                 /// <remarks>テンポ値の範囲は70-300</remarks>\r
107                 public float GetTempo()\r
108                 {\r
109                         fTempo = BassFx.BASS_FX_BPM_DecodeGet(\r
110                                 this.hBassStream,\r
111                                 0,\r
112                                 nTotalSeconds,\r
113                                 ( 300 << 16 ) + 70,             // MAX BPM=320, MIN BPM=70\r
114                                 //0,\r
115                                 BASSFXBpm.BASS_FX_BPM_DEFAULT,          //BASSFXBpm.BASS_FX_BPM_MULT2,\r
116                                 null,\r
117                                 IntPtr.Zero );\r
118                         return fTempo;\r
119                 }\r
120                 /// <summary>\r
121                 /// 曲の一部分のテンポを取得する\r
122                 /// </summary>\r
123                 /// <param name="startSec">開始位置</param>\r
124                 /// <param name="endSec">終了位置</param>\r
125                 /// <returns>テンポ値</returns>\r
126                 /// <remarks>テンポ値の範囲は70-300</remarks>\r
127                 public float GetTempo( double startSec, double endSec )\r
128                 {\r
129                         fTempo = BassFx.BASS_FX_BPM_DecodeGet(\r
130                                 this.hBassStream,\r
131                                 startSec,\r
132                                 endSec,\r
133                                 ( 300 << 16 ) + 70,             // MAX BPM=320, MIN BPM=70\r
134                                 //0,\r
135                                 BASSFXBpm.BASS_FX_BPM_DEFAULT,          //BASSFXBpm.BASS_FX_BPM_MULT2,\r
136                                 null,\r
137                                 IntPtr.Zero );\r
138                         return fTempo;\r
139                 }\r
140 \r
141 \r
142                 /// <summary>\r
143                 /// Beatの検出位置をListで返す\r
144                 /// </summary>\r
145                 /// <returns>Beat検出位置群</returns>\r
146                 public List<stBeatPos> GetBeatPositions()\r
147                 {\r
148                         #region [  BeatPosition格納リストの初期化 ]\r
149                         if ( this.listBeatPositions != null )\r
150                         {\r
151                                 this.listBeatPositions.Clear();\r
152                         }\r
153                         else\r
154                         {\r
155                                 this.listBeatPositions = new List<stBeatPos>();\r
156                         }\r
157                         #endregion\r
158 \r
159                         BPMBEATPROC _beatProc = new BPMBEATPROC( GetBeat_ProgressCallback );\r
160 \r
161                         bool ret = BassFx.BASS_FX_BPM_BeatDecodeGet(\r
162                                 this.hBassStream,\r
163                                 0,\r
164                                 nTotalSeconds,\r
165                                 //0,\r
166                                 BASSFXBpm.BASS_FX_BPM_DEFAULT,          //BASSFXBpm.BASS_FX_BPM_MULT2,\r
167                                 _beatProc,\r
168                                 IntPtr.Zero );\r
169 \r
170                         return this.listBeatPositions;\r
171                 }\r
172 \r
173                 private void GetBeat_ProgressCallback( int channel, double beatpos, IntPtr user )\r
174                 {\r
175                         stBeatPos sbp = new stBeatPos(\r
176                                 (float) beatpos,\r
177                                 0,\r
178                                 0,\r
179                                 0,\r
180                                 false,\r
181                                 true\r
182                         );                              \r
183                                 \r
184 \r
185                         listBeatPositions.Add( sbp );\r
186 //                      Debug.WriteLine( "Beat at: " + beatpos.ToString() );\r
187                 }\r
188 \r
189 \r
190         \r
191                 \r
192                 public void Dispose()   // 使い終わったら必ずDispose()すること。BASSのリソースを握りっぱなしにすると、他の再生に不都合が生じるため。\r
193                 {\r
194                         BassFx.BASS_FX_BPM_Free( this.hBassStream );\r
195                         Bass.BASS_StreamFree( this.hBassStream );\r
196                         this.hBassStream = -1;\r
197                         Bass.BASS_Free();\r
198                 }\r
199         \r
200                 // =============\r
201                 private string filename = "";\r
202                 private int hBassStream = -1;\r
203                 private long nTotalBytes = 0;\r
204                 private double nTotalSeconds = 0.0f;\r
205                 private float fFreq = 0.0f;\r
206                 private float fTempo;\r
207                 private List<stBeatPos> listBeatPositions = null;\r
208         }\r
209 }\r