.NET MAUI我基本都是參考Android代碼改造來的,官方文檔 = 廢品。
參考:https://blog.csdn.net/wxzjn1027/article/details/82345604
需要注意的是,流的加載過程是一個將音頻解壓爲原始16位PCM數據的過程,由一個後臺線程來進行處理異步,所以初始化後不能立即播放,需要等待一點時間。
有以下幾個函數可用於控制播放:
final int play(int soundID, float leftVolume, float rightVolume, int priority, int loop, float rate)
播放指定音頻的音效,並返回一個streamID 。
priority —— 流的優先級,值越大優先級高,影響當同時播放數量超出了最大支持數時SoundPool對該流的處理;
loop —— 循環播放的次數,0爲值播放一次,-1爲無限循環,其他值爲播放loop+1次(例如,3爲一共播放4次).
rate —— 播放的速率,範圍0.5-2.0(0.5爲一半速率,1.0爲正常速率,2.0爲兩倍速率)
final void pause(int streamID)
暫停指定播放流的音效(streamID 應通過play()返回)。
final void resume(int streamID)
繼續播放指定播放流的音效(streamID 應通過play()返回)。
final void stop(int streamID)
終止指定播放流的音效(streamID 應通過play()返回)。
這裏需要注意的是,
1.play()函數傳遞的是一個load()返回的soundID——指向一個被記載的音頻資源 ,如果播放成功則返回一個非0的streamID——指向一個成功播放的流 ;同一個soundID 可以通過多次調用play()而獲得多個不同的streamID (只要不超出同時播放的最大數量);
2.pause()、resume()和stop()是針對播放流操作的,傳遞的是play()返回的streamID ;
3.play()中的priority參數,只在同時播放的流的數量超過了預先設定的最大數量時起作用,管理器將自動終止優先級低的播放流。如果存在多個同樣優先級的流,再進一步根據其創建時間來處理,新創建的流的年齡是最小的,將被終止;
4.無論如何,程序退出時,手動終止播放並釋放資源是必要的。
using Android.Annotation; using Android.Media;using Stream = Android.Media.Stream; namespace NetAppTest.Utils { public class PlaySoundUtil : IDisposable { //音頻池 public static SoundPool soundPool = null; //預加載的音頻文件 public static Dictionary<int, int> mSoundCtrl = new Dictionary<int, int>(); /// <summary> /// 加載音頻文件 /// </summary> /// <param name="soundId">自定義音頻資源id</param> /// <param name="rawPath">音頻資源名(1.mp3)</param> /// <exception cref="System.Exception"></exception> public static void LoadSoundResource(int soundId, string rawPath) { //已經加載過,返回。 if (mSoundCtrl.ContainsKey(soundId)) { return; } //初始化音頻資源池 if (soundPool == null) { //音頻資源池建造者 SoundPool.Builder builder = new SoundPool.Builder(); //同時播放的流的最大數量 builder.SetMaxStreams(1); //AudioAttributes是一個封裝音頻各種屬性的方法 AudioAttributes.Builder attrBuilder = new AudioAttributes.Builder(); //設置音頻流的合適的屬性 attrBuilder.SetLegacyStreamType(Stream.Music); //加載一個AudioAttributes builder.SetAudioAttributes(attrBuilder.Build()); //建造對象 soundPool = builder.Build(); } //拼接完整音頻文件路徑,需要先把音頻文件copy到這個目錄中 string targetFile = System.IO.Path.Combine(FileSystem.Current.AppDataDirectory, rawPath); //加載進音頻資源池,返回一個非0的音頻id。 int ret = soundPool.Load(targetFile, 1); if (ret < 1) { throw new System.Exception("裝載聲音資源時發生異常:ID=" + soundId); } //保存進字典變量 mSoundCtrl.Add(soundId, ret); } /// <summary> /// 初始化音頻資源 /// </summary> public static void loadData() { try { string path1 = "1.mp3"; LoadSoundResource(1, path1); } catch (System.Exception ex) { } } /// <summary> /// 播放音頻資源 /// </summary> /// <param name="soundId">資源id</param> public static void play(int soundId) { try { if (!mSoundCtrl.ContainsKey(soundId)) {return; } //獲取、播放音頻 int r = mSoundCtrl[soundId]; int ret = soundPool.Play(r, 1.0f, 1.0f, 0, 0, 1); if (ret < 1) {return; } } catch (System.Exception ex) { } } /// <summary> /// 釋放資源 /// </summary> public void Dispose() { if (soundPool == null) { return; } soundPool.Release(); } } }
複製到新目錄中
public async static void CopyConfigFile() { //1.mp3存放於/Resources/Raw/ string fileName1 = "1.mp3"; string fileTargetPath1 = System.IO.Path.Combine(FileSystem.Current.AppDataDirectory, fileName1); if (File.Exists(fileTargetPath1)) { File.Delete(fileTargetPath1); } bool v1 = await FileSystem.Current.AppPackageFileExistsAsync(fileName1); if (v1) { using System.IO.Stream fileStream = await FileSystem.Current.OpenAppPackageFileAsync(fileName1); // 緩衝區爲10k byte[] buffer = new Byte[10000]; // 文件長度 int length; using System.IO.FileStream fs = new System.IO.FileStream(fileTargetPath1, FileMode.OpenOrCreate); do { length = fileStream.Read(buffer, 0, 10000); fs.Write(buffer, 0, length); buffer = new Byte[10000]; } while (length > 0); fs.Flush(); } }