From da7944d5216ed7f3c3c7d88106ab88142f2d38e3 Mon Sep 17 00:00:00 2001 From: sin365 <353374337@qq.com> Date: Tue, 23 Sep 2025 19:36:48 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=92=8C=E7=BB=9F=E4=B8=80?= =?UTF-8?q?=E9=9F=B3=E9=A2=91=E5=A4=84=E7=90=86=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../UEssgeeInterface/UEGSoundPlayer.cs | 70 ++++-- .../UniInterface/UniSoundPlayer.cs | 85 ++++++- .../Emulator/NesEmulator/AudioProvider.cs | 79 ++++++- .../StoicGooseInterface/SGSoundPlayer.cs | 86 +++++-- .../Assets/Script/AppMain/MonoCom/AudioMgr.cs | 221 +++++++++++++----- 5 files changed, 423 insertions(+), 118 deletions(-) diff --git a/AxibugEmuOnline.Client/Assets/Script/AppMain/Emulator/EssgeeEmulator/UEssgeeInterface/UEGSoundPlayer.cs b/AxibugEmuOnline.Client/Assets/Script/AppMain/Emulator/EssgeeEmulator/UEssgeeInterface/UEGSoundPlayer.cs index ac555e6f..449f660d 100644 --- a/AxibugEmuOnline.Client/Assets/Script/AppMain/Emulator/EssgeeEmulator/UEssgeeInterface/UEGSoundPlayer.cs +++ b/AxibugEmuOnline.Client/Assets/Script/AppMain/Emulator/EssgeeEmulator/UEssgeeInterface/UEGSoundPlayer.cs @@ -1,9 +1,10 @@ +using AxibugEmuOnline.Client; using AxibugEmuOnline.Client.ClientCore; using System; using System.Collections.Generic; using UnityEngine; -public class UEGSoundPlayer : MonoBehaviour +public class UEGSoundPlayer : MonoBehaviour, AxiAudioPull { [SerializeField] private AudioSource m_as; @@ -18,32 +19,72 @@ public class UEGSoundPlayer : MonoBehaviour void Awake() { - // 获取当前音频配置 - AudioConfiguration config = AudioSettings.GetConfiguration(); - // 设置目标音频配置 - config.sampleRate = sampleRate; // 采样率为 44100Hz - config.numRealVoices = 32; // 设置最大音频源数量(可选) - config.numVirtualVoices = 512; // 设置虚拟音频源数量(可选) - config.dspBufferSize = 1024; // 设置 DSP 缓冲区大小(可选) - config.speakerMode = AudioSpeakerMode.Stereo; // 设置为立体声(2 声道) - App.audioMgr.SetAudioConfig(config); + return; + //// 获取当前音频配置 + //AudioConfiguration config = AudioSettings.GetConfiguration(); + //// 设置目标音频配置 + //config.sampleRate = sampleRate; // 采样率为 44100Hz + //config.numRealVoices = 32; // 设置最大音频源数量(可选) + //config.numVirtualVoices = 512; // 设置虚拟音频源数量(可选) + //config.dspBufferSize = 1024; // 设置 DSP 缓冲区大小(可选) + //config.speakerMode = AudioSpeakerMode.Stereo; // 设置为立体声(2 声道) + //App.audioMgr.SetAudioConfig(config); + } + + + private void OnEnable() + { + App.audioMgr.RegisterStream(nameof(UEssgee), AudioSettings.outputSampleRate, this); + } + + void OnDisable() + { + App.audioMgr.ClearAudioData(nameof(UEssgee)); } private Queue sampleQueue = new Queue(); - // Unity 音频线程回调 - void OnAudioFilterRead(float[] data, int channels) + public unsafe void PullAudio(float[] data, int channels) { + fixed (float* pData = data) + { + float* outputPtr = pData; // 指向数组起始位置的指针 + int dataLength = data.Length; + for (int i = 0; i < dataLength; i++) + { + float rawData; + if (_buffer.TryRead(out rawData)) + *outputPtr = rawData; + else + *outputPtr = 0; // 无数据时静音 + + outputPtr++; // 指针移动到下一个位置 + } + } + + /* 非指针版本,代码保留 for (int i = 0; i < data.Length; i++) { if (_buffer.TryRead(out float rawData)) data[i] = rawData; else data[i] = 0; // 无数据时静音 - } + }*/ } + //// Unity 音频线程回调 + //void OnAudioFilterRead(float[] data, int channels) + //{ + // for (int i = 0; i < data.Length; i++) + // { + // if (_buffer.TryRead(out float rawData)) + // data[i] = rawData; + // else + // data[i] = 0; // 无数据时静音 + // } + //} + public void Initialize() { @@ -72,7 +113,7 @@ public class UEGSoundPlayer : MonoBehaviour { _buffer.Write(buffer[i] / 32767.0f); } - App.audioMgr.WriteToRecord(buffer, samples_a); + //App.audioMgr.WriteToRecord(buffer, samples_a); } public void BufferWirte(int Off, byte[] Data) { @@ -91,4 +132,5 @@ public class UEGSoundPlayer : MonoBehaviour return; m_as.volume = Vol; } + } diff --git a/AxibugEmuOnline.Client/Assets/Script/AppMain/Emulator/MameEmulator/UniInterface/UniSoundPlayer.cs b/AxibugEmuOnline.Client/Assets/Script/AppMain/Emulator/MameEmulator/UniInterface/UniSoundPlayer.cs index d9678c83..6312cd4a 100644 --- a/AxibugEmuOnline.Client/Assets/Script/AppMain/Emulator/MameEmulator/UniInterface/UniSoundPlayer.cs +++ b/AxibugEmuOnline.Client/Assets/Script/AppMain/Emulator/MameEmulator/UniInterface/UniSoundPlayer.cs @@ -1,8 +1,10 @@ +using AxibugEmuOnline.Client; +using AxibugEmuOnline.Client.ClientCore; using MAME.Core; using System; using UnityEngine; -public class UniSoundPlayer : MonoBehaviour, ISoundPlayer +public class UniSoundPlayer : MonoBehaviour, ISoundPlayer, AxiAudioPull { [SerializeField] private AudioSource m_as; @@ -13,14 +15,24 @@ public class UniSoundPlayer : MonoBehaviour, ISoundPlayer void Awake() { - //TODO 采样率需要更准确,而且和clip并没有关系 - var dummy = AudioClip.Create("dummy", 1, 1, AudioSettings.outputSampleRate, false); - dummy.SetData(new float[] { 1 }, 0); - m_as.clip = dummy; //just to let unity play the audiosource - m_as.loop = true; - m_as.spatialBlend = 1; - m_as.Play(); + ////TODO 采样率需要更准确,而且和clip并没有关系 + //var dummy = AudioClip.Create("dummy", 1, 1, AudioSettings.outputSampleRate, false); + //dummy.SetData(new float[] { 1 }, 0); + //m_as.clip = dummy; //just to let unity play the audiosource + //m_as.loop = true; + //m_as.spatialBlend = 1; + //m_as.Play(); } + private void OnEnable() + { + App.audioMgr.RegisterStream(nameof(UMAME), AudioSettings.outputSampleRate, this); + } + + void OnDisable() + { + App.audioMgr.ClearAudioData(nameof(UMAME)); + } + public void GetAudioParams(out int frequency, out int channels) { frequency = m_as.clip.samples; @@ -38,12 +50,62 @@ public class UniSoundPlayer : MonoBehaviour, ISoundPlayer public void StopPlay() { if (m_as.isPlaying) - { + { m_as.Stop(); } } - void OnAudioFilterRead(float[] data, int channels) + public unsafe void PullAudio(float[] data, int channels) + { + if (!UMAME.bInGame) return; + + fixed (float* pData = data) + { + float* outputPtr = pData; // 指向数组起始位置的指针 + int dataLength = data.Length; + + for (int i = 0; i < dataLength; i += channels) + { + float rawFloat = lastData; + float rawData; + + if (_buffer.TryRead(out rawData)) + { + rawFloat = rawData; + } + + *outputPtr = rawFloat; + outputPtr++; // 指针移动到下一个位置 + + // 填充剩余声道(模拟立体声或多声道) + for (int fill = 1; fill < channels; fill++) + { + *outputPtr = rawFloat; + outputPtr++; // 指针移动到下一个位置 + } + + lastData = rawFloat; + } + } + + /* 非指针版本,代码保留 + int step = channels; + for (int i = 0; i < data.Length; i += step) + { + float rawFloat = lastData; + float rawData; + if (_buffer.TryRead(out rawData)) + { + rawFloat = rawData; + } + + data[i] = rawFloat; + for (int fill = 1; fill < step; fill++) + data[i + fill] = rawFloat; + lastData = rawFloat; + }*/ + } + /*void OnAudioFilterRead(float[] data, int channels) { if (!UMAME.bInGame) return; int step = channels; @@ -61,7 +123,7 @@ public class UniSoundPlayer : MonoBehaviour, ISoundPlayer data[i + fill] = rawFloat; lastData = rawFloat; } - } + }*/ public void SubmitSamples(byte[] buffer, int samples_a) { @@ -96,4 +158,5 @@ public class UniSoundPlayer : MonoBehaviour, ISoundPlayer return; m_as.volume = Vol; } + } diff --git a/AxibugEmuOnline.Client/Assets/Script/AppMain/Emulator/NesEmulator/AudioProvider.cs b/AxibugEmuOnline.Client/Assets/Script/AppMain/Emulator/NesEmulator/AudioProvider.cs index d241679a..e4b95a87 100644 --- a/AxibugEmuOnline.Client/Assets/Script/AppMain/Emulator/NesEmulator/AudioProvider.cs +++ b/AxibugEmuOnline.Client/Assets/Script/AppMain/Emulator/NesEmulator/AudioProvider.cs @@ -4,7 +4,7 @@ using VirtualNes.Core; namespace AxibugEmuOnline.Client { - public class AudioProvider : MonoBehaviour + public class AudioProvider : MonoBehaviour, AxiAudioPull { public NesEmulator NesEmu { get; set; } @@ -14,7 +14,7 @@ namespace AxibugEmuOnline.Client private SoundBuffer _buffer = new SoundBuffer(4096); public void Start() { - + return; //// 鑾峰彇褰撳墠闊抽閰嶇疆 //AudioConfiguration config = AudioSettings.GetConfiguration(); //// 璁剧疆鐩爣闊抽閰嶇疆 @@ -25,22 +25,33 @@ namespace AxibugEmuOnline.Client //config.speakerMode = AudioSpeakerMode.Stereo; // 璁剧疆涓虹珛浣撳0锛2 澹伴亾锛 //App.audioMgr.SetAudioConfig(new AudioConfiguration()); - //TODO 閲囨牱鐜囬渶瑕佹洿鍑嗙‘锛岃屼笖鍜宑lip骞舵病鏈夊叧绯 - var dummy = AudioClip.Create("dummy", 1, 1, AudioSettings.outputSampleRate, false); - dummy.SetData(new float[] { 1 }, 0); - m_as.clip = dummy; //just to let unity play the audiosource - m_as.loop = true; - m_as.spatialBlend = 1; - m_as.Play(); + ////TODO 閲囨牱鐜囬渶瑕佹洿鍑嗙‘锛岃屼笖鍜宑lip骞舵病鏈夊叧绯 + //var dummy = AudioClip.Create("dummy", 1, 1, AudioSettings.outputSampleRate, false); + //dummy.SetData(new float[] { 1 }, 0); + //m_as.clip = dummy; //just to let unity play the audiosource + //m_as.loop = true; + //m_as.spatialBlend = 1; + //m_as.Play(); } + + private void OnEnable() + { + App.audioMgr.RegisterStream(nameof(NesEmulator), AudioSettings.outputSampleRate, this); + } + + void OnDisable() + { + App.audioMgr.ClearAudioData(nameof(NesEmulator)); + } + public void GetAudioParams(out int frequency, out int channels) { frequency = m_as.clip.samples; channels = m_as.clip.channels; } - void OnAudioFilterRead(float[] data, int channels) + public unsafe void PullAudio(float[] data, int channels) { int step = channels; @@ -49,6 +60,29 @@ namespace AxibugEmuOnline.Client ProcessSound(NesEmu.NesCore, (uint)(data.Length / channels)); + fixed (float* pData = data) + { + float* outputPtr = pData; // 鎸囧悜 data 鏁扮粍璧峰浣嶇疆鐨勬寚閽 + int dataLength = data.Length; + for (int i = 0; i < dataLength; i += step) + { + byte rawData; + float rawFloat = 0f; + if (_buffer.TryRead(out rawData)) + rawFloat = rawData / 255f; + + *outputPtr = rawFloat; + outputPtr++; //鎸囬拡绉诲姩鍒颁笅涓涓綅缃 + + for (int fill = 1; fill < step; fill++) + { + *outputPtr = rawFloat; + outputPtr++; //鎸囬拡绉诲姩鍒颁笅涓涓綅缃 + } + } + } + + /* 闈炴寚閽堢増鏈紝浠g爜淇濈暀 for (int i = 0; i < data.Length; i += step) { float rawFloat = 0; @@ -59,13 +93,36 @@ namespace AxibugEmuOnline.Client data[i] = rawFloat; for (int fill = 1; fill < step; fill++) data[i + fill] = rawFloat; - } + }*/ } + //void OnAudioFilterRead(float[] data, int channels) + //{ + // int step = channels; + + // if (NesEmu == null || NesEmu.NesCore == null) return; + // if (NesEmu.IsPause) return; + + // ProcessSound(NesEmu.NesCore, (uint)(data.Length / channels)); + + // for (int i = 0; i < data.Length; i += step) + // { + // float rawFloat = 0; + // byte rawData; + // if (_buffer.TryRead(out rawData)) + // rawFloat = rawData / 255f; + + // data[i] = rawFloat; + // for (int fill = 1; fill < step; fill++) + // data[i + fill] = rawFloat; + // } + //} + void ProcessSound(NES nes, uint feedCount) { nes.apu.Process(_buffer, feedCount); } + } } \ No newline at end of file diff --git a/AxibugEmuOnline.Client/Assets/Script/AppMain/Emulator/StoicGooseEmulator/StoicGooseInterface/SGSoundPlayer.cs b/AxibugEmuOnline.Client/Assets/Script/AppMain/Emulator/StoicGooseEmulator/StoicGooseInterface/SGSoundPlayer.cs index c2b359b2..1445b1e9 100644 --- a/AxibugEmuOnline.Client/Assets/Script/AppMain/Emulator/StoicGooseEmulator/StoicGooseInterface/SGSoundPlayer.cs +++ b/AxibugEmuOnline.Client/Assets/Script/AppMain/Emulator/StoicGooseEmulator/StoicGooseInterface/SGSoundPlayer.cs @@ -1,8 +1,10 @@ +using AxibugEmuOnline.Client; +using AxibugEmuOnline.Client.ClientCore; using System; using System.Collections.Generic; using UnityEngine; -public class SGSoundPlayer : MonoBehaviour//, ISoundPlayer +public class SGSoundPlayer : MonoBehaviour, AxiAudioPull { [SerializeField] private AudioSource m_as; @@ -18,44 +20,82 @@ public class SGSoundPlayer : MonoBehaviour//, ISoundPlayer void Awake() { - // 获取当前音频配置 - AudioConfiguration config = AudioSettings.GetConfiguration(); + return; + //// 获取当前音频配置 + //AudioConfiguration config = AudioSettings.GetConfiguration(); - // 设置目标音频配置 - config.sampleRate = 44100; // 采样率为 44100Hz - config.numRealVoices = 32; // 设置最大音频源数量(可选) - config.numVirtualVoices = 512; // 设置虚拟音频源数量(可选) - config.dspBufferSize = 1024; // 设置 DSP 缓冲区大小(可选) - config.speakerMode = AudioSpeakerMode.Stereo; // 设置为立体声(2 声道) + //// 设置目标音频配置 + //config.sampleRate = 44100; // 采样率为 44100Hz + //config.numRealVoices = 32; // 设置最大音频源数量(可选) + //config.numVirtualVoices = 512; // 设置虚拟音频源数量(可选) + //config.dspBufferSize = 1024; // 设置 DSP 缓冲区大小(可选) + //config.speakerMode = AudioSpeakerMode.Stereo; // 设置为立体声(2 声道) - // 应用新的音频配置 - if (AudioSettings.Reset(config)) - { - Debug.Log("Audio settings updated successfully."); - Debug.Log("Sample Rate: " + config.sampleRate + "Hz"); - Debug.Log("Speaker Mode: " + config.speakerMode); - } - else - { - Debug.LogError("Failed to update audio settings."); - } + //// 应用新的音频配置 + //if (AudioSettings.Reset(config)) + //{ + // Debug.Log("Audio settings updated successfully."); + // Debug.Log("Sample Rate: " + config.sampleRate + "Hz"); + // Debug.Log("Speaker Mode: " + config.speakerMode); + //} + //else + //{ + // Debug.LogError("Failed to update audio settings."); + //} } + private void OnEnable() + { + App.audioMgr.RegisterStream(nameof(UStoicGoose), AudioSettings.outputSampleRate, this); + } + + void OnDisable() + { + App.audioMgr.ClearAudioData(nameof(UStoicGoose)); + } + private Queue sampleQueue = new Queue(); - // Unity 音频线程回调 - void OnAudioFilterRead(float[] data, int channels) + public unsafe void PullAudio(float[] data, int channels) { + fixed (float* pData = data) + { + float* outputPtr = pData; // 指向数组起始位置的指针 + int dataLength = data.Length; + for (int i = 0; i < dataLength; i++) + { + float rawData; + if (_buffer.TryRead(out rawData)) + *outputPtr = rawData; + else + *outputPtr = 0; // 无数据时静音 + + outputPtr++; // 指针移动到下一个位置 + } + } + + /* 非指针版本,代码保留 for (int i = 0; i < data.Length; i++) { if (_buffer.TryRead(out float rawData)) data[i] = rawData; else data[i] = 0; // 无数据时静音 - } + }*/ } + //// Unity 音频线程回调 + //void OnAudioFilterRead(float[] data, int channels) + //{ + // for (int i = 0; i < data.Length; i++) + // { + // if (_buffer.TryRead(out float rawData)) + // data[i] = rawData; + // else + // data[i] = 0; // 无数据时静音 + // } + //} public void Initialize() diff --git a/AxibugEmuOnline.Client/Assets/Script/AppMain/MonoCom/AudioMgr.cs b/AxibugEmuOnline.Client/Assets/Script/AppMain/MonoCom/AudioMgr.cs index 2a9a83f5..c0eef243 100644 --- a/AxibugEmuOnline.Client/Assets/Script/AppMain/MonoCom/AudioMgr.cs +++ b/AxibugEmuOnline.Client/Assets/Script/AppMain/MonoCom/AudioMgr.cs @@ -4,9 +4,15 @@ using System.Collections.Generic; using System.Linq; using System.Text; using UnityEngine; +using UnityEngine.Audio; namespace AxibugEmuOnline.Client { + public interface AxiAudioPull + { + public void PullAudio(float[] data, int channels); + } + public class AudioMgr : MonoBehaviour { public enum E_SFXTYPE @@ -19,33 +25,48 @@ namespace AxibugEmuOnline.Client system_ok } - public Dictionary dictAudioClip = new Dictionary(); - - private AudioSource mSource; - private void Awake() + void Awake() { - mSource = this.gameObject.AddComponent(); - LoadAudioClip(); - PlaySFX(E_SFXTYPE.Launch); + DontDestroyOnLoad(gameObject); + InitializeAudioSystem(); } - /// - /// 鎵嬪姩璁剧疆AudioCfg 涓昏鐢ㄤ簬妯℃嫙鍣ㄥ悇鏍稿績閲囨牱鐜囧榻 - /// - /// - public void SetAudioConfig(AudioConfiguration config) + #region 闊抽璧勬簮 + Dictionary dictAudioClip = new Dictionary(); + void LoadAudioClip() { - // 搴旂敤鏂扮殑闊抽閰嶇疆 - if (AudioSettings.Reset(config)) + dictAudioClip[E_SFXTYPE.Cancel] = Resources.Load("Sound/XMBSFX/cancel"); + dictAudioClip[E_SFXTYPE.Cursor] = Resources.Load("Sound/XMBSFX/cursor"); + dictAudioClip[E_SFXTYPE.Option] = Resources.Load("Sound/XMBSFX/option"); + dictAudioClip[E_SFXTYPE.Launch] = Resources.Load("Sound/XMBSFX/StartPSP"); + dictAudioClip[E_SFXTYPE.system_ng] = Resources.Load("Sound/XMBSFX/system_ng"); + dictAudioClip[E_SFXTYPE.system_ok] = Resources.Load("Sound/XMBSFX/system_ok"); + } + #endregion + + [SerializeField] private AudioMixerGroup _staticGroup; // 闈欐侀煶鏁堬紙UI绛夛級杈撳嚭缁 + [Header("闈欐侀煶鏁")] + [SerializeField] private AudioSource _staticAudioSource; // 鐢ㄤ簬鎾斁闈欐侀煶鏁堢殑婧 + AudioStreamData _audioStreams; + private int _targetOutputSampleRate; // Unity闊抽绯荤粺鐨勮緭鍑洪噰鏍风巼 + + /// + /// 鍒濆鍖栭煶棰戠郴缁 + /// + private void InitializeAudioSystem() + { + AudioSettings.OnAudioConfigurationChanged += OnAudioConfigurationChanged; + _targetOutputSampleRate = AudioSettings.outputSampleRate; + if (_staticAudioSource == null) { - Debug.Log("Audio settings updated successfully."); - Debug.Log("Sample Rate: " + config.sampleRate + "Hz"); - Debug.Log("Speaker Mode: " + config.speakerMode); - } - else - { - Debug.LogError("Failed to update audio settings."); + _staticAudioSource = this.gameObject.AddComponent(); + _staticAudioSource.outputAudioMixerGroup = _staticGroup; } + + // 璁剧疆鍒濆闊抽噺 + SetStaticVolume(0.9f); + Debug.Log($"Audio System Initialized. Output Sample Rate: {_targetOutputSampleRate}Hz"); + LoadAudioClip(); } /// @@ -57,28 +78,117 @@ namespace AxibugEmuOnline.Client //鍑芥暟浠呭鐞嗚澶囧彉鍖栫殑鎯呭喌锛岄潪璁惧鍙樺寲涓嶅啀鏈嚱鏁板鐞嗭紝閬垮厤鏍稿績閲囨牱鐜囧彉鍖栧拰鏈寰幆璋冪敤 if (deviceWasChanged) { - AudioConfiguration config = AudioSettings.GetConfiguration(); - AudioSettings.Reset(config); + ResetAudioCfg(); + //AudioConfiguration config = AudioSettings.GetConfiguration(); + //AudioSettings.Reset(config); //TODO 閲嶆柊鎾斁闊虫晥锛屼絾鏄疍SP涓嶇敤锛岃嫢鏈塙I BGM锛屽悗缁 杩欓噷鍔犻噸鎾 } } - public void LoadAudioClip() - { - dictAudioClip[E_SFXTYPE.Cancel] = Resources.Load("Sound/XMBSFX/cancel"); - dictAudioClip[E_SFXTYPE.Cursor] = Resources.Load("Sound/XMBSFX/cursor"); - dictAudioClip[E_SFXTYPE.Option] = Resources.Load("Sound/XMBSFX/option"); - dictAudioClip[E_SFXTYPE.Launch] = Resources.Load("Sound/XMBSFX/StartPSP"); - dictAudioClip[E_SFXTYPE.system_ng] = Resources.Load("Sound/XMBSFX/system_ng"); - dictAudioClip[E_SFXTYPE.system_ok] = Resources.Load("Sound/XMBSFX/system_ok"); - } - + #region 闈欐侀煶婧 public void PlaySFX(E_SFXTYPE type, bool isLoop = false) { - mSource.clip = dictAudioClip[type]; - mSource.loop = isLoop; - mSource.Play(); + PlayStaticSound(dictAudioClip[type], 1, 1); } + /// + /// 鎾斁闈欐侀煶棰戝壀杈戯紙UI闊虫晥绛夛級 + /// + void PlayStaticSound(AudioClip clip, float volume = 1.0f, float pitch = 1.0f) + { + if (clip == null) return; + + _staticAudioSource.pitch = Mathf.Clamp(pitch, 0.5f, 2.0f); + _staticAudioSource.PlayOneShot(clip, Mathf.Clamp01(volume)); + } + + /// + /// 璁剧疆闈欐侀煶棰戦煶閲忥紙绾挎0.0-1.0锛 + /// + public void SetStaticVolume(float volumeLinear) + { + if (_staticGroup != null && _staticGroup.audioMixer != null) + { + float volumeDB = ConvertLinearToDecibel(Mathf.Clamp01(volumeLinear)); + _staticGroup.audioMixer.SetFloat("StaticVolume", volumeDB); + } + } + #endregion + + #region 鍔ㄦ侀煶婧愶紙妯℃嫙鍣級 + /// + /// 娉ㄥ唽涓涓姩鎬侀煶棰戞祦閫氶亾锛堟ā鎷熷櫒锛 + /// + /// 閫氶亾鏍囪瘑绗 (e.g., "NES", "MAME") + /// 璇ラ氶亾鐨勫師濮嬮噰鏍风巼 + public void RegisterStream(string channelId, int inputSampleRate, AxiAudioPull audioPullHandle) + { + _audioStreams = null; + _audioStreams = new AudioStreamData(channelId, inputSampleRate, audioPullHandle); + ResetAudioCfg(); + } + + private void ResetAudioCfg() + { + // 鑾峰彇褰撳墠闊抽閰嶇疆 + AudioConfiguration config = AudioSettings.GetConfiguration(); + + // 璁剧疆鐩爣闊抽閰嶇疆 + config.sampleRate = 48000; // 閲囨牱鐜囦负 44100Hz + config.numRealVoices = 32; // 璁剧疆鏈澶ч煶棰戞簮鏁伴噺锛堝彲閫夛級 + config.numVirtualVoices = 512; // 璁剧疆铏氭嫙闊抽婧愭暟閲忥紙鍙夛級 + config.dspBufferSize = 1024; // 璁剧疆 DSP 缂撳啿鍖哄ぇ灏忥紙鍙夛級 + config.speakerMode = AudioSpeakerMode.Stereo; // 璁剧疆涓虹珛浣撳0锛2 澹伴亾锛 + + // 搴旂敤鏂扮殑闊抽閰嶇疆 + if (AudioSettings.Reset(config)) + { + Debug.Log("Audio settings updated successfully."); + Debug.Log("Sample Rate: " + config.sampleRate + "Hz"); + Debug.Log("Speaker Mode: " + config.speakerMode); + } + else + { + Debug.LogError("Failed to update audio settings."); + } + _staticAudioSource.Play();//鍙负璁〥SP缁х画 + } + + /// + /// 娓呯┖鎸囧畾閫氶亾鐨勯煶棰戞暟鎹 + /// + public void ClearAudioData(string channelId) + { + if (_audioStreams == null || _audioStreams.channelid != channelId) + return; + _audioStreams = null; + } + #endregion + + #region Core Audio Processing (Called automatically by Unity) + /// + /// Unity闊抽绾跨▼鍥炶皟锛氬湪杩欓噷澶勭悊鍜屾贩鍚堟墍鏈夊姩鎬侀煶棰戞祦[1](@ref) + /// + void OnAudioFilterRead(float[] data, int channels) + { + if (_audioStreams == null) return; + _audioStreams.AxiAudioPullHandle.PullAudio(data, channels); + + //TODO 濡傛灉瑕佸鐞嗛噰鏍风巼宸紓 + if (_audioStreams.NeedsResampling){ } + } + #endregion + + #region Helper Methods + /// + /// 绾挎ч煶閲忓艰浆鎹负鍒嗚礉鍊 (dB)[4](@ref) + /// + private float ConvertLinearToDecibel(float linear) + { + if (linear <= 0.0001f) return -80.0f; // 閬垮厤log10(0) + return Mathf.Log10(linear) * 20.0f; + } + + #endregion #region 褰曢煶鍔熻兘瀹炵幇 @@ -138,14 +248,6 @@ namespace AxibugEmuOnline.Client ms.Write(dataChunk.GetBytes(), 0, (int)dataChunk.Length()); AxiIO.File.WriteAllBytesFromStream(filename, ms); } - - //using (FileStream file = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.ReadWrite)) - //{ - // file.Write(waveHeader.GetBytes(), 0, (int)waveHeader.Length()); - // file.Write(formatChunk.GetBytes(), 0, (int)formatChunk.Length()); - // file.Write(dataChunk.GetBytes(), 0, (int)dataChunk.Length()); - //} - IsRecording = false; OverlayManager.PopTip("褰曢煶缁撴潫"); } @@ -159,22 +261,35 @@ namespace AxibugEmuOnline.Client #endregion } + // 鐢ㄤ簬鎻忚堪涓涓姩鎬侀煶棰戞祦鐨勬暟鎹粨鏋 + public class AudioStreamData + { + public string channelid; + public int SourceSampleRate; + public bool NeedsResampling; + public AxiAudioPull AxiAudioPullHandle; + public AudioStreamData(string channelid, int SourceSampleRate, AxiAudioPull audiohandle) + { + this.channelid = channelid; + this.SourceSampleRate = SourceSampleRate; + this.AxiAudioPullHandle = audiohandle; + NeedsResampling = SourceSampleRate != AudioSettings.outputSampleRate; + AudioSettings.GetDSPBufferSize(out int bufferLength, out int numBuffers); + } + } class WaveHeader { const string fileTypeId = "RIFF"; const string mediaTypeId = "WAVE"; - public string FileTypeId { get; private set; } public uint FileLength { get; set; } public string MediaTypeId { get; private set; } - public WaveHeader() { FileTypeId = fileTypeId; MediaTypeId = mediaTypeId; FileLength = 4; /* Minimum size is always 4 bytes */ } - public byte[] GetBytes() { List chunkData = new List(); @@ -185,7 +300,6 @@ namespace AxibugEmuOnline.Client return chunkData.ToArray(); } - public uint Length() { return (uint)GetBytes().Length; @@ -195,35 +309,28 @@ namespace AxibugEmuOnline.Client class FormatChunk { const string chunkId = "fmt "; - ushort bitsPerSample, channels; uint frequency; - public string ChunkId { get; private set; } public uint ChunkSize { get; private set; } public ushort FormatTag { get; private set; } - public ushort Channels { get { return channels; } set { channels = value; RecalcBlockSizes(); } } - public uint Frequency { get { return frequency; } set { frequency = value; RecalcBlockSizes(); } } - public uint AverageBytesPerSec { get; private set; } public ushort BlockAlign { get; private set; } - public ushort BitsPerSample { get { return bitsPerSample; } set { bitsPerSample = value; RecalcBlockSizes(); } } - public FormatChunk() { ChunkId = chunkId; @@ -234,20 +341,17 @@ namespace AxibugEmuOnline.Client BitsPerSample = 16; /* Default to 16bits */ RecalcBlockSizes(); } - public FormatChunk(int frequency, int channels) : this() { Channels = (ushort)channels; Frequency = (ushort)frequency; RecalcBlockSizes(); } - private void RecalcBlockSizes() { BlockAlign = (ushort)(channels * (bitsPerSample / 8)); AverageBytesPerSec = frequency * BlockAlign; } - public byte[] GetBytes() { List chunkBytes = new List(); @@ -263,7 +367,6 @@ namespace AxibugEmuOnline.Client return chunkBytes.ToArray(); } - public uint Length() { return (uint)GetBytes().Length;