using System; using System.Collections.Generic; using System.Text; using System.Diagnostics; using System.Threading; using Un4seen.Bass; //using Un4seen.BassAsio; //using Un4seen.BassWasapi; //using Un4seen.Bass.AddOn.Mix; using Un4seen.Bass.AddOn.Fx; namespace FDK { public class CBeatDetect : IDisposable { public struct stBeatPos { public float fBeatTime; public int n小節番号; public int nGrid; public int n小節内Grid; public bool b無効; // public bool bレーン表示する; // 未使用 public stBeatPos( float _fBeatTime, int _n小節番号, int _nGrid, int _n小節内Grid, bool _b無効, bool _bレーン表示する ) { fBeatTime = _fBeatTime; n小節番号 = _n小節番号; nGrid = _nGrid; n小節内Grid = _n小節内Grid; b無効 = _b無効; bレーン表示する= _bレーン表示する; } } #region [ コンストラクタ ] public CBeatDetect() { Initialize(); } public CBeatDetect( string _filename ) { this.filename = _filename; Initialize(); } #endregion #region [ 初期化(コンストラクタから呼び出される) ] private void Initialize() { if ( this.listBeatPositions == null ) { this.listBeatPositions = new List(); } #region [ BASS registration ] // BASS.NET ユーザ登録(BASSスプラッシュが非表示になる)。 BassNet.Registration( "dtx2013@gmail.com", "2X9181017152222" ); #endregion #region [ BASS Version Check ] // BASS のバージョンチェック。 int nBASSVersion = Utils.HighWord( Bass.BASS_GetVersion() ); if ( nBASSVersion != Bass.BASSVERSION ) throw new DllNotFoundException( string.Format( "bass.dll のバージョンが異なります({0})。このプログラムはバージョン{1}で動作します。", nBASSVersion, Bass.BASSVERSION ) ); int nBASSFXVersion = Utils.HighWord( BassFx.BASS_FX_GetVersion() ); if ( nBASSFXVersion != BassFx.BASSFXVERSION ) throw new DllNotFoundException( string.Format( "bass_fx.dll のバージョンが異なります({0})。このプログラムはバージョン{1}で動作します。", nBASSFXVersion, BassFx.BASSFXVERSION ) ); #endregion #region [ BASS の設定。] //this.bIsBASSFree = true; //Debug.Assert( Bass.BASS_SetConfig( BASSConfig.BASS_CONFIG_UPDATEPERIOD, 0 ), // 0:BASSストリームの自動更新を行わない。(サウンド出力しないため) // string.Format( "BASS_SetConfig() に失敗しました。[{0}", Bass.BASS_ErrorGetCode() ) ); #endregion #region [ BASS の初期化。] int nデバイス = 0; // 0:"no sound" … BASS からはデバイスへアクセスさせない。 int n周波数 = 44100; // 仮決め。lデバイス(≠ドライバ)がネイティブに対応している周波数であれば何でもいい?ようだ。いずれにしろBASSMXで自動的にリサンプリングされる。 if ( !Bass.BASS_Init( nデバイス, n周波数, BASSInit.BASS_DEVICE_DEFAULT, IntPtr.Zero ) ) throw new Exception( string.Format( "BASS の初期化に失敗しました。(BASS_Init)[{0}]", Bass.BASS_ErrorGetCode().ToString() ) ); #endregion #region [ 指定されたサウンドファイルをBASSでオープンし、必要最小限の情報を取得する。] //this.hBassStream = Bass.BASS_StreamCreateFile( this.filename, 0, 0, BASSFlag.BASS_STREAM_PRESCAN | BASSFlag.BASS_STREAM_DECODE ); this.hBassStream = Bass.BASS_StreamCreateFile( this.filename, 0, 0, BASSFlag.BASS_STREAM_DECODE ); if ( this.hBassStream == 0 ) throw new Exception( string.Format( "{0}: サウンドストリームの生成に失敗しました。(BASS_StreamCreateFile)[{1}]", filename, Bass.BASS_ErrorGetCode().ToString() ) ); this.nTotalBytes = Bass.BASS_ChannelGetLength( this.hBassStream ); this.nTotalSeconds = Bass.BASS_ChannelBytes2Seconds( this.hBassStream, nTotalBytes ); if ( !Bass.BASS_ChannelGetAttribute( this.hBassStream, BASSAttribute.BASS_ATTRIB_FREQ, ref fFreq ) ) { string errmes = string.Format( "サウンドストリームの周波数取得に失敗しました。(BASS_ChannelGetAttribute)[{0}]", Bass.BASS_ErrorGetCode().ToString() ); Bass.BASS_Free(); throw new Exception( errmes ); } #endregion } #endregion /// /// 曲全体のテンポを取得する /// /// テンポ値 /// テンポ値の範囲は70-300 public float GetTempo() { fTempo = BassFx.BASS_FX_BPM_DecodeGet( this.hBassStream, 0, nTotalSeconds, ( 300 << 16 ) + 70, // MAX BPM=320, MIN BPM=70 //0, BASSFXBpm.BASS_FX_BPM_DEFAULT, //BASSFXBpm.BASS_FX_BPM_MULT2, null, IntPtr.Zero ); return fTempo; } /// /// 曲の一部分のテンポを取得する /// /// 開始位置 /// 終了位置 /// テンポ値 /// テンポ値の範囲は70-300 public float GetTempo( double startSec, double endSec ) { fTempo = BassFx.BASS_FX_BPM_DecodeGet( this.hBassStream, startSec, endSec, ( 300 << 16 ) + 70, // MAX BPM=320, MIN BPM=70 //0, BASSFXBpm.BASS_FX_BPM_DEFAULT, //BASSFXBpm.BASS_FX_BPM_MULT2, null, IntPtr.Zero ); return fTempo; } /// /// Beatの検出位置をListで返す /// /// Beat検出位置群 public List GetBeatPositions() { #region [ BeatPosition格納リストの初期化 ] if ( this.listBeatPositions != null ) { this.listBeatPositions.Clear(); } else { this.listBeatPositions = new List(); } #endregion BPMBEATPROC _beatProc = new BPMBEATPROC( GetBeat_ProgressCallback ); bool ret = BassFx.BASS_FX_BPM_BeatDecodeGet( this.hBassStream, 0, nTotalSeconds, //0, BASSFXBpm.BASS_FX_BPM_DEFAULT, //BASSFXBpm.BASS_FX_BPM_MULT2, _beatProc, IntPtr.Zero ); return this.listBeatPositions; } private void GetBeat_ProgressCallback( int channel, double beatpos, IntPtr user ) { stBeatPos sbp = new stBeatPos( (float) beatpos, 0, 0, 0, false, true ); listBeatPositions.Add( sbp ); // Debug.WriteLine( "Beat at: " + beatpos.ToString() ); } public void Dispose() // 使い終わったら必ずDispose()すること。BASSのリソースを握りっぱなしにすると、他の再生に不都合が生じるため。 { BassFx.BASS_FX_BPM_Free( this.hBassStream ); Bass.BASS_StreamFree( this.hBassStream ); this.hBassStream = -1; Bass.BASS_Free(); } // ============= private string filename = ""; private int hBassStream = -1; private long nTotalBytes = 0; private double nTotalSeconds = 0.0f; private float fFreq = 0.0f; private float fTempo; private List listBeatPositions = null; } }