using AxibugEmuOnline.Client.ClientCore; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using UnityEngine; namespace AxibugEmuOnline.Client { public class AudioMgr : MonoBehaviour { /// /// 手动设置AudioCfg 主要用于模拟器各核心采样率对齐 /// /// public void SetAudioConfig(AudioConfiguration config) { // 应用新的音频配置 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."); } } /// /// 切换设备(蓝牙)后重新播放背景音乐 /// /// void OnAudioConfigurationChanged(bool deviceWasChanged) { //函数仅处理设备变化的情况,非设备变化不再本函数处理,避免核心采样率变化和本处循环调用 if (deviceWasChanged) { AudioConfiguration config = AudioSettings.GetConfiguration(); AudioSettings.Reset(config); //TODO 重新播放音效,但是DSP不用,若有UI BGM,后续 这里加重播 } } #region 录音功能实现 WaveHeader waveHeader; FormatChunk formatChunk; DataChunk dataChunk; public bool IsRecording { get; private set; } public void BeginRecordGameAudio() { App.emu.Core.GetAudioParams(out int frequency, out int channels); BeginRecording(frequency, channels); } public void StopRecordGameAudio() { SaveRecording(GetSavePath()); } string GetSavePath() { string dir = $"{App.PersistentDataPath(App.emu.Core.Platform)}/AxiSoundRecord"; if (!Directory.Exists(dir)) { Directory.CreateDirectory(dir); } return $"{dir}/{App.tickLoop.GetDateTimeStr()}"; } void BeginRecording(int frequency, int channels) { if (IsRecording) { OverlayManager.PopTip("已有正在进行的录音……"); return; } waveHeader = new WaveHeader(); formatChunk = new FormatChunk(frequency, channels); dataChunk = new DataChunk(); waveHeader.FileLength += formatChunk.Length(); IsRecording = true; OverlayManager.PopTip("录音开始"); } void SaveRecording(string filename) { if (!IsRecording) { OverlayManager.PopTip("没有录音数据"); return; } 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("录音结束"); } public unsafe void WriteToRecord(short* stereoBuffer, int lenght) { if (!IsRecording) return; dataChunk.AddSampleData(stereoBuffer, lenght); waveHeader.FileLength += (uint)lenght; } #endregion } 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(); chunkData.AddRange(Encoding.ASCII.GetBytes(FileTypeId)); chunkData.AddRange(BitConverter.GetBytes(FileLength)); chunkData.AddRange(Encoding.ASCII.GetBytes(MediaTypeId)); return chunkData.ToArray(); } public uint Length() { return (uint)GetBytes().Length; } } 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; ChunkSize = 16; FormatTag = 1; /* MS PCM (Uncompressed wave file) */ Channels = 2; /* Default to stereo */ Frequency = 44100; /* Default to 44100hz */ 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(); chunkBytes.AddRange(Encoding.ASCII.GetBytes(ChunkId)); chunkBytes.AddRange(BitConverter.GetBytes(ChunkSize)); chunkBytes.AddRange(BitConverter.GetBytes(FormatTag)); chunkBytes.AddRange(BitConverter.GetBytes(Channels)); chunkBytes.AddRange(BitConverter.GetBytes(Frequency)); chunkBytes.AddRange(BitConverter.GetBytes(AverageBytesPerSec)); chunkBytes.AddRange(BitConverter.GetBytes(BlockAlign)); chunkBytes.AddRange(BitConverter.GetBytes(BitsPerSample)); return chunkBytes.ToArray(); } public uint Length() { return (uint)GetBytes().Length; } } class DataChunk { const string chunkId = "data"; public string ChunkId { get; private set; } public uint ChunkSize { get; set; } public List WaveData { get; private set; } public DataChunk() { ChunkId = chunkId; ChunkSize = 0; WaveData = new List(); } public byte[] GetBytes() { //待优化 List chunkBytes = new List(); chunkBytes.AddRange(Encoding.ASCII.GetBytes(ChunkId)); chunkBytes.AddRange(BitConverter.GetBytes(ChunkSize)); byte[] bufferBytes = new byte[WaveData.Count * 2]; Buffer.BlockCopy(WaveData.ToArray(), 0, bufferBytes, 0, bufferBytes.Length); chunkBytes.AddRange(bufferBytes.ToList()); return chunkBytes.ToArray(); } public uint Length() { return (uint)GetBytes().Length; } public unsafe void AddSampleData(short* stereoBuffer, int lenght) { for (int i = 0; i < lenght; i++) { WaveData.Add(stereoBuffer[i]); } ChunkSize += (uint)(lenght * 2); } } }