Skip to content

Commit

Permalink
updated to v2.10.3
Browse files Browse the repository at this point in the history
  • Loading branch information
michael811125 committed Apr 1, 2024
1 parent c58e046 commit f56faee
Show file tree
Hide file tree
Showing 6 changed files with 212 additions and 35 deletions.
5 changes: 5 additions & 0 deletions Assets/OxGFrame/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# CHANGELOG

## [2.10.3] -2024-04-01
- Added CacheType to AudioManager, which will be used for caching when AudioBase is using the request method.
- Modified AudioBase to not rely on the methods of the OxGKit.Utilities' Requester.
- Modified Acax encoding type (use UTF-8).

## [2.10.2] - 2024-03-19
- Fixed When scene load with suspend (activateOnLoad = false) cannot return BundlePack correctly .
```C#
Expand Down
57 changes: 30 additions & 27 deletions Assets/OxGFrame/MediaFrame/Scripts/Editor/AudioBaseEditor.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using OxGFrame.MediaFrame.AudioFrame;
using Cysharp.Threading.Tasks;
using OxGFrame.MediaFrame.AudioFrame;
using UnityEditor;
using UnityEngine;

Expand Down Expand Up @@ -36,39 +37,41 @@ protected virtual async void DrawAudioLengthView()
GUI.backgroundColor = Color.cyan;

serializedObject.Update();

EditorGUILayout.PropertyField(this._audioLength);
if (GUILayout.Button("Preload"))
{
// must set dirty (save will be success)
EditorUtility.SetDirty(this._target);

AudioClip audioClip = null;
switch (this._target.sourceType)
UniTask.Void(async () =>
{
case SourceType.Audio:
audioClip = this._target.audioClip;
if (audioClip != null) this._audioLength.floatValue = this._target.audioLength = this._target.audioClip.length;
else Debug.LogError("Cannot found AudioClip");
break;

case SourceType.StreamingAssets:
audioClip = await this._target.GetAudioFromStreamingAssets(false);
if (audioClip != null) this._audioLength.floatValue = this._target.audioLength = audioClip.length;
break;

case SourceType.Url:
audioClip = await this._target.GetAudioFromURL(false);
if (audioClip != null) this._audioLength.floatValue = this._target.audioLength = audioClip.length;
break;
}

if (audioClip != null)
{
Debug.Log($"<color=#FFE700>AudioClip Info => Channel: {audioClip.channels}, Frequency: {audioClip.frequency}, Sample: {audioClip.samples}, Length: {audioClip.length}, State: {audioClip.loadState}</color>");
}

serializedObject.ApplyModifiedProperties();
AudioClip audioClip = null;
switch (this._target.sourceType)
{
case SourceType.Audio:
audioClip = this._target.audioClip;
if (audioClip != null) this._audioLength.floatValue = this._target.audioLength = this._target.audioClip.length;
else Debug.LogError("Cannot found AudioClip");
break;
case SourceType.StreamingAssets:
audioClip = await this._target.GetAudioFromStreamingAssets(false);
if (audioClip != null) this._audioLength.floatValue = this._target.audioLength = audioClip.length;
break;
case SourceType.Url:
audioClip = await this._target.GetAudioFromURL(false);
if (audioClip != null) this._audioLength.floatValue = this._target.audioLength = audioClip.length;
break;
}
if (audioClip != null)
Debug.Log($"<color=#ffe700>AudioClip Info => Channel: {audioClip.channels}, Frequency: {audioClip.frequency}, Sample: {audioClip.samples}, Length: {audioClip.length}, State: {audioClip.loadState}</color>");
else
Debug.Log($"<color=#ff0000>AudioClip request failed!!!</color>");
serializedObject.ApplyModifiedProperties();
});
}
else
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using Cysharp.Threading.Tasks;
using MyBox;
using OxGKit.LoggingSystem;
using OxGKit.Utilities.Request;
using UnityEngine;
using UnityEngine.Audio;

Expand All @@ -19,7 +18,7 @@ public class AudioBase : MediaBase
[Tooltip("Drag audio clip. This is not supports [WebGL]"), ConditionalField(nameof(sourceType), false, SourceType.Audio)]
public AudioClip audioClip = null;
// SourceType => StreamingAssets, Url
[Tooltip("Depends on Requester.InitCacheCapacityForAudio"), ConditionalField(nameof(sourceType), true, SourceType.Audio)]
[Tooltip("Can select the \"CacheType\" from the AudioManager's inspector."), ConditionalField(nameof(sourceType), true, SourceType.Audio)]
public bool requestCached = true;
// SourceType => StreamingAssets
[Tooltip("Default path is [StreamingAssets]. Just set that inside path and file name, Don't forget file name must with extension, ex: Audio/example.mp3"), ConditionalField(nameof(sourceType), false, SourceType.StreamingAssets)]
Expand Down Expand Up @@ -99,7 +98,7 @@ private async UniTask _InitAudio()
public async UniTask<AudioClip> GetAudioFromStreamingAssets(bool cached)
{
string pathName = System.IO.Path.Combine(GetRequestStreamingAssetsPath(), this.fullPathName);
var audioClip = await Requester.RequestAudio(pathName, this.audioFileType, null, null, null, cached);
var audioClip = await AudioManager.GetInstance().RequestAudio(pathName, this.audioFileType, null, null, null, cached);
return audioClip;
}

Expand All @@ -108,7 +107,7 @@ public async UniTask<AudioClip> GetAudioFromURL(bool cached)
string urlCfg = await this.urlSet.urlCfg.GetFileText();
string urlSet = this.urlSet.getUrlPathFromCfg ? GetValueFromUrlCfg(urlCfg, AUDIO_URLSET) : string.Empty;
string url = (!string.IsNullOrEmpty(urlSet)) ? $"{urlSet.Trim()}{this.urlSet.url.Trim()}" : this.urlSet.url.Trim();
var audioClip = await Requester.RequestAudio(url, this.audioFileType, null, null, null, cached);
var audioClip = await AudioManager.GetInstance().RequestAudio(url, this.audioFileType, null, null, null, cached);
return audioClip;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,39 @@
using Cysharp.Threading.Tasks;
using MyBox;
using OxGKit.LoggingSystem;
using OxGKit.Utilities.Cacher;
using System;
using System.Collections.Generic;
using System.Threading;
using UnityEngine;
using UnityEngine.Audio;
using UnityEngine.Networking;

namespace OxGFrame.MediaFrame.AudioFrame
{
internal class AudioManager : MediaManager<AudioBase>
{
public enum CacheType
{
None,
LRU,
ARC
}

[Separator("Audio Mixer")]
[SerializeField, Tooltip("Setup AudioMixer in list")]
private List<AudioMixer> _listMixer = new List<AudioMixer>(); // 中控混音器
private Dictionary<string, float> _dictMixerExpParams = new Dictionary<string, float>(); // 用於記錄 Exposed Parameters 參數
private Dictionary<string, GameObject> _dictNodes = new Dictionary<string, GameObject>(); // 節點物件

[Separator("Audio Cacher")]
[SerializeField, Tooltip("The caching method requires ensuring that \"RequestCached\" is checked on AudioBase.")]
private CacheType _cacheType = CacheType.LRU;
[SerializeField, Tooltip("The capacity of the cache."), ConditionalField(nameof(_cacheType), true, CacheType.None)]
private int _cacheCapacity = 20;
private ARCCache<string, AudioClip> _arcAudios = null;
private LRUCache<string, AudioClip> _lruAudios = null;

private static readonly object _locker = new object();
private static AudioManager _instance = null;
public static AudioManager GetInstance()
Expand Down Expand Up @@ -51,6 +69,18 @@ private void Awake()
this._dictNodes.Add(nodeName, this.CreateNode(nodeName, this.transform));
}
}

switch (this._cacheType)
{
case CacheType.None:
break;
case CacheType.LRU:
this._InitLRUCacheCapacityForAudio(this._cacheCapacity);
break;
case CacheType.ARC:
this._InitARCCacheCapacityForAudio(this._cacheCapacity);
break; ;
}
}

protected override void SetParent(AudioBase audBase, Transform parent)
Expand All @@ -62,6 +92,147 @@ protected override void SetParent(AudioBase audBase, Transform parent)
}
}

#region Cacher
#region ARC Audio
private void _InitARCCacheCapacityForAudio(int capacity)
{
if (this._arcAudios == null)
this._arcAudios = new ARCCache<string, AudioClip>(capacity);

// Only allow one cache type (Clear LRU)
this._ClearLRUCacheCapacityForAudio();
this._lruAudios = null;
}

private void _ClearARCCacheCapacityForAudio()
{
if (this._arcAudios != null)
this._arcAudios.Clear();
}
#endregion

#region LRU Audio
private void _InitLRUCacheCapacityForAudio(int capacity)
{
if (this._lruAudios == null)
this._lruAudios = new LRUCache<string, AudioClip>(capacity);

// Only allow one cache type (Clear ARC)
this._ClearARCCacheCapacityForAudio();
_arcAudios = null;
}

private void _ClearLRUCacheCapacityForAudio()
{
if (this._lruAudios != null)
this._lruAudios.Clear();
}
#endregion

/// <summary>
/// Audio request
/// </summary>
/// <param name="url"></param>
/// <param name="audioType"></param>
/// <param name="successAction"></param>
/// <param name="errorAction"></param>
/// <param name="cts"></param>
/// <returns></returns>
public async UniTask<AudioClip> RequestAudio(string url, UnityEngine.AudioType audioType = UnityEngine.AudioType.MPEG, Action<AudioClip> successAction = null, Action errorAction = null, CancellationTokenSource cts = null, bool cached = true)
{
if (string.IsNullOrEmpty(url))
{
Logging.Print<Logger>($"<color=#FF0000>Request failed, URL is null or empty.</color>");
return null;
}

if (cached)
{
// ARCCache
if (this._arcAudios != null)
{
AudioClip audioClip = this._arcAudios.Get(url);
if (audioClip != null) return audioClip;
}
// LRUCache
else if (this._lruAudios != null)
{
AudioClip audioClip = this._lruAudios.Get(url);
if (audioClip != null) return audioClip;
}
}

UnityWebRequest request = null;
try
{
request = UnityWebRequestMultimedia.GetAudioClip(url, audioType);
((DownloadHandlerAudioClip)request.downloadHandler).streamAudio = true;

if (cts != null) await request.SendWebRequest().WithCancellation(cts.Token);
else await request.SendWebRequest();

if (request.result == UnityWebRequest.Result.ProtocolError ||
request.result == UnityWebRequest.Result.ConnectionError)
{
request.Dispose();
errorAction?.Invoke();
Logging.Print<Logger>($"<color=#FF0000>Request failed, URL: {url}</color>");
return null;
}

AudioClip audioClip = ((DownloadHandlerAudioClip)request.downloadHandler).audioClip;
if (cached)
{
// ARCCache
if (this._arcAudios != null)
{
this._arcAudios.Add(url, audioClip);
audioClip = this._arcAudios.Get(url);
}
// LRUCache
else if (this._lruAudios != null)
{
this._lruAudios.Add(url, audioClip);
audioClip = this._lruAudios.Get(url);
}
}
successAction?.Invoke(audioClip);

#if UNITY_EDITOR
string GetBytesToString(ulong bytes)
{
if (bytes < (1024 * 1024 * 1f))
{
return (bytes / 1024f).ToString("f2") + "KB";
}
else if (bytes >= (1024 * 1024 * 1f) && bytes < (1024 * 1024 * 1024 * 1f))
{
return (bytes / (1024 * 1024 * 1f)).ToString("f2") + "MB";
}
else
{
return (bytes / (1024 * 1024 * 1024 * 1f)).ToString("f2") + "GB";
}
}

ulong sizeBytes = (ulong)request.downloadHandler.data.Length;
Logging.Print<Logger>($"<color=#90ff67>Request Audio => Channel: {audioClip.channels}, Frequency: {audioClip.frequency}, Sample: {audioClip.samples}, Length: {audioClip.length}, State: {audioClip.loadState}, Size: {GetBytesToString(sizeBytes)}</color>");
#endif

request.Dispose();
return audioClip;
}
catch (Exception ex)
{
request?.Dispose();
errorAction?.Invoke();
Logging.Print<Logger>($"<color=#FF0000>Request failed, URL: {url}</color>");
Logging.PrintException<Logger>(ex);
return null;
}
}
#endregion

#region 中控 Mixer
/// <summary>
/// 依照 Mixer 的名稱與其中的 ExposedParam 合併成雙 key, 執行自動記錄
Expand Down Expand Up @@ -209,7 +380,7 @@ private void _Play(AudioBase audBase, int loops, float volume)
// 處理長期沒有被 Unload 的 Audio
if (!audBase.onDestroyAndUnload)
this.TryLRUCache<AudioBase>(audBase.assetName);

this.LoadAndPlay(audBase, loops, volume);

Logging.Print<Logger>(string.Format("Play Audio: {0}, Current Length: {1} (s)", audBase?.mediaName, audBase?.CurrentLength()));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using Cysharp.Threading.Tasks;
using MyBox;
using OxGKit.LoggingSystem;
using OxGKit.Utilities.Request;
using System;
using System.Collections.Generic;
using UnityEngine;
Expand Down Expand Up @@ -54,7 +53,7 @@ public async UniTask<string> GetFileText()

case RequestType.StreamingAssets:
string pathName = System.IO.Path.Combine(GetRequestStreamingAssetsPath(), this.fullPathName);
if (string.IsNullOrEmpty(_urlCfgContent)) _urlCfgContent = await Requester.RequestText(pathName, null, null, null, false);
if (string.IsNullOrEmpty(_urlCfgContent)) _urlCfgContent = await OxGKit.Utilities.Request.Requester.RequestText(pathName, null, null, null, false);
return _urlCfgContent;
}

Expand Down
2 changes: 1 addition & 1 deletion Assets/OxGFrame/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "com.michaelo.oxgframe",
"displayName": "OxGFrame",
"description": "The OxGFrame is a framework based on Unity for accelerating game development. Supports multi-platform Win, OSX, Android, iOS, WebGL.",
"version": "2.10.2",
"version": "2.10.3",
"unity": "2021.3",
"license": "MIT",
"samples": [
Expand Down

0 comments on commit f56faee

Please sign in to comment.