在C#中有許多音頻播放的方案,例如WinForm里調用系統自帶MediaPlayer的COM組件和WPF的MediaPlayer(實質上還是WindowsMediaPlayer) 以及一堆API播放和DirectX (SDK一大堆) 於是我找到了適用於全平臺、高效、小巧的音頻解碼器--Bass (主 ...
在C#中有許多音頻播放的方案,例如WinForm里調用系統自帶MediaPlayer的COM組件和WPF的MediaPlayer(實質上還是WindowsMediaPlayer)
以及一堆API播放和DirectX (SDK一大堆)
於是我找到了適用於全平臺、高效、小巧的音頻解碼器--Bass (主程式基於C++ C#可通過官方庫Bass.Net調用)
一、開始
首先需要到官網下載bass.dll 主程式文件(大約 257kb): http://www.un4seen.com/
以及類庫(.Net平臺調用) :你可以在http://www.bass.radio42.com/bass_register.html 中使用你的郵箱即可註冊到一個key 和下載Bass.Net.dll(大約520kb)
官方文檔:http://www.bass.radio42.com/help/
P.S:bass.dll需要放在程式主目錄下 Bass.Net.dll隨意(添加到程式集引用)
二、編碼
在一切開始之前,你需要先註冊程式和初始化Bass解碼器:
using Un4seen.Bass;//添加引用 ... BassNet.Registration("你的郵箱", "你註冊到的Key"); Bass.BASS_Init(-1, 44100, BASSInit.BASS_DEVICE_CPSPEAKERS, 視窗句柄 沒有的話就IntPtr.Zero);
1.實現簡單的mp3播放
播放mp3有2種方式 :從文件中載入、從URL中載入
例1:從文件中載入:
//---------調用到的方法------------ public static int BASS_StreamCreateFile( string file,//文件路徑 long offset,//偏移量,一般不怎麼使用 long length,//如果你使用了偏移量,定義一個偏移量之後的讀取長度 BASSFlag flags//以什麼方式創建流 ) //幫助鏈接和其他信息:http://www.bass.radio42.com/help/html/e49e005c-52c0-fc33-e5f9-f27f2e0b1c1f.htm //---------------------------------- //創建流的id,建議作為全局變數加入(如果是播放單文件) private int stream = -1024;//可以自己定義一個初始值 ... //從文件中創建一個簡單的FLOAT音頻流,返迴流的id 便於控制和查詢 stream = Bass.BASS_StreamCreateFile(你的文件完整路徑, 0L, 0L, BASSFlag.BASS_SAMPLE_FLOAT); ... 開始播放 //參數:int 要播放的流的id,bool 是否在播放完成之後再重新播放 Bass.BASS_ChannelPlay(stream, false); ... 暫停播放 Bass.BASS_ChannelPause(stream);
例2:從URL中載入:
stream = Bass.BASS_StreamCreateURL(url, 0, BASSFlag.BASS_SAMPLE_FLOAT, null, IntPrt.Zero);
只是載入音頻流的方式改變了,其他的一致
如果你需要一些其他的功能(請求Url時的標頭和下載回調),請參閱以下:
1.添加URL請求標頭:
很簡單:在url參數里添加即可,url與每一條標頭之間用"\r\n"換行,例如:
stream = Bass.BASS_StreamCreateURL(url+"\r\n"+"一條標頭,Header:Content"+"\r\n"+"再一條標頭",...);
2.下載回調: 多用於緩存
//必須是全局變數,否則會被GC回收! private DOWNLOADPROC _myDownloadProc; private FileStream _fs = null;//寫入文件的流 private byte[] _data; // 本地緩存 ... //添加調用 _myDownloadProc = new DOWNLOADPROC(DownloadCallBack); ... //下載回調,由Bass調用 private void DownloadCallBack(IntPtr buffer, int length, IntPtr user) { // file length long len = Bass.BASS_StreamGetFilePosition(stream, BASSStreamFilePosition.BASS_FILEPOS_END); // download progress long down = Bass.BASS_StreamGetFilePosition(stream, BASSStreamFilePosition.BASS_FILEPOS_DOWNLOAD); //可在此處添加下載進度的Callback if (_fs == null) { // 開始下載,打開文件流 //坑:當你切歌的時候,Bass並不會繼續下載而且會向你發送已經下載完成的標識,此時你得到的文件是不完整的!所以此處先作為.cache下載 _fs = File.OpenWrite(保存的路徑 + ".cache"); } if (buffer == IntPtr.Zero) { // 下載完成 _fs.Flush(); _fs.Close(); _fs = null; FileInfo fi = new FileInfo(DLPath + ".cache"); //如果下載不完整的話就刪除.cache if (fi.Length != len) { fi.Delete(); } else { //如果下載完整的話就移動到緩存(下載)目錄 fi.MoveTo(你的路徑, true); //這裡可以做下載完成的回調 } } else { //接受到下載數據,實質上是Bass傳來數據的指針,C#根據指針從記憶體中複製數據 // increase the data buffer as needed if (_data == null || _data.Length < length) _data = new byte[length]; // copy from managed to unmanaged memory Marshal.Copy(buffer, _data, 0, length); // write to file _fs.Write(_data, 0, length); } }
2.獲得和設置一些常規數據(播放進度、聲音):
1.設置、獲取音量:
坑:音量的設置是暫時性的,僅對於你輸入的stream,當你再次新建音頻流時(例如切歌),音量會恢復預設!你可能需要記錄下設置的音量併在下一次載入流時將音量設置Set到stream中!
//設置音量 值在0~1之間 預設值為1 Bass.BASS_ChannelSetAttribute(stream, BASSAttribute.BASS_ATTRIB_VOL, value); ... //獲取當前音量 ref value float value=0; Bass.BASS_ChannelGetAttribute(stream, BASSAttribute.BASS_ATTRIB_VOL,ref value);
2.播放進度和總長(時間):
//長度 public TimeSpan GetLength { get { double seconds = Bass.BASS_ChannelBytes2Seconds(stream, Bass.BASS_ChannelGetLength(stream)); return TimeSpan.FromSeconds(seconds); } } //當前播放位置 public TimeSpan Position { get { double seconds = Bass.BASS_ChannelBytes2Seconds(stream, Bass.BASS_ChannelGetPosition(stream)); return TimeSpan.FromSeconds(seconds); } set => Bass.BASS_ChannelSetPosition(stream, value.TotalSeconds); }
獲取FFT數據,你可以用這個數據來做頻譜:
//獲取256位的FFT數據,當然你可以選擇更大的,但是也足夠了 //坑:暫停播放時獲得的FFT數據仍是沒有暫停前的數據(停留在此位置的FFT)如果你需要做頻譜圖,在暫停時應該手動設置為0,因為Bass並不會在暫停時返回0 float[] fft = new float[256]; Bass.BASS_ChannelGetData(stream, fft, (int)BASSData.BASS_DATA_FFT256);
可惜的是,Bass並沒有提供播放完成的回調,你需要設置一個Timer來判斷是否播放完成,理論上噹噹前位置=播放總長時算播放完成...
3.更新設備和結束時
1.系統會將當前的預設設備放在集合的[0]位,但是Bass並不會自動更新設備也沒有更新設備的事件回調(或是我沒有找到?),所以你需要自己檢查下有沒有新的設備插入,你需要手動更新設備(如果需要)
public void UpdataDevice() { var data = Bass.BASS_GetDeviceInfos(); int index = -1; for (int i = 0; i < data.Length; i++) if (data[i].IsDefault) { index = i; break; } if (!data[index].IsInitialized) Bass.BASS_Init(index, 44100, BASSInit.BASS_DEVICE_CPSPEAKERS, wind); var a = Bass.BASS_ChannelGetDevice(stream); if (a != index) { Bass.BASS_ChannelSetDevice(stream, index); Bass.BASS_SetDevice(index); } }
2.結束時,需要釋放流和Bass解碼器:
Bass.BASS_ChannelStop(stream);
Bass.BASS_StreamFree(stream);
Bass.BASS_Stop();
Bass.BASS_Free();
4.倍速播放和其他FX效果
為什麼這裡需要重新起個標題呢?
這個方法推翻了之前用到的stream創建形式,但是控制播放暫停等方法不變。
首先你需要引入幾個Bass的概念:
音頻流:stream,用於播放音頻,就是你控制播放暫停時傳入的stream
解碼流 decode,用於解碼音頻,然後傳給Fx效果器,Fx效果器會返回給你一個音頻流
實現此方法,你需要下載FX擴展(就在bass.dll的地方下載 選擇add-on 即可),大約85kb
添加using引用:
using Un4seen.Bass.AddOn.Fx;
創建流:
//全局變數,解碼流 private int decode; decode = Bass.BASS_StreamCreateFile(你的文件, 0L, 0L, BASSFlag.BASS_STREAM_DECODE); //相對於前面的你只需要把標簽換成BASS_STREAM_DECODE即可,CreateURL亦是如此 //將解碼流傳入Fx效果器中,你將得到一個音頻流 stream = BassFx.BASS_FX_TempoCreate(decode, BASSFlag.BASS_FX_FREESOURCE );
設置Fx效果:
value值在-90~0~5000之間 以百分制計算 例如 加速/減速 播放10%那麼value=10,不用倍速播放value=0 減速 value=-10
Bass.BASS_ChannelSetAttribute(stream, BASSAttribute.BASS_ATTRIB_TEMPO, value);
獲取Fx效果的方法和音量一致,更換標簽為BASSAttribute.BASS_ATTRIB_TEMPO即可.
更多的FX效果可以參閱:http://www.bass.radio42.com/help/html/90d034c4-b426-7f7c-4f32-28210a5e6bfb.htm 原理一樣 修改標簽和value值即可
完結..Thanks!
有啥問題,QQ俺:
QQ:2728578956 (Twilight./Lemon)