You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
278 lines
6.8 KiB
278 lines
6.8 KiB
using UnityEngine; |
|
using System; |
|
using System.IO; |
|
using AX.AudioSystem; |
|
|
|
[RequireComponent(typeof(AudioSource))] |
|
public class AudioPlayer : MonoBehaviour |
|
{ |
|
[Header("音频采样率")] |
|
public int SamplingRate = 8000; |
|
[Header("音频声道数,默认为单声道")] |
|
public int Channels = 1; |
|
[Header("音频录制最大时长,按秒计")] |
|
public int RecordMaxTime = 60; |
|
|
|
/// <summary> |
|
/// 表示当前播放器是否正在播放。 |
|
/// </summary> |
|
public bool IsPlaying { get { return !stopped && audioSource.isPlaying; } } |
|
|
|
/// <summary> |
|
/// 获得播放列表。 |
|
/// </summary> |
|
public PlayList PlayList { get { return playList; } } |
|
|
|
/// <summary> |
|
/// 获得当前已经播放到播放列表中哪一条曲目了。 |
|
/// </summary> |
|
public int PlayIndex |
|
{ |
|
get { return playIndex; } |
|
set |
|
{ |
|
if (value < -1 || value >= playList.Count) |
|
throw new InvalidOperationException("value"); |
|
playIndex = value; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// 表示要开始播放某曲目前的事件。 |
|
/// </summary> |
|
public event Action<int, Track> Playing; |
|
/// <summary> |
|
/// 表示播放某曲目结束后的事件。 |
|
/// </summary> |
|
public event Action<int, Track> Played; |
|
|
|
private AudioSource audioSource; |
|
private OpusDecoder opusDecoder; |
|
private BetterList<float> audioBuffer; |
|
private BetterList<byte> opusBuffer; |
|
private float[] packageBuffer; |
|
private byte[] frameBuffer; |
|
|
|
private PlayList playList; |
|
private int playIndex; |
|
private bool stopped; |
|
|
|
private const int BufferSize = 8192; |
|
private byte[] buffer = new byte[BufferSize]; |
|
|
|
void Awake() |
|
{ |
|
Initialize(); |
|
} |
|
|
|
void OnDisable() |
|
{ |
|
Stop(); |
|
} |
|
|
|
void OnDestroy() |
|
{ |
|
if (opusDecoder != null) |
|
{ |
|
opusDecoder.Dispose(); |
|
opusDecoder = null; |
|
} |
|
} |
|
|
|
void Update() |
|
{ |
|
// 自动播放列表中的语音消息 |
|
if (!stopped && playList.Count > 0) |
|
{ |
|
// 已经全部播放过 |
|
if (playIndex != -1 && playIndex == playList.Count - 1 && playList[playIndex].Played && !playList[playIndex].Playing) |
|
return; |
|
|
|
// 正在播放 |
|
if (IsPlaying) |
|
return; |
|
|
|
// 当前项播放完毕 |
|
var curTrack = playList[playIndex]; |
|
curTrack.Playing = false; |
|
playList[playIndex] = curTrack; |
|
|
|
OnPlayed(playIndex, curTrack); |
|
|
|
// 准备播放下一条 |
|
var nextIndex = playIndex + 1; |
|
|
|
if (nextIndex != playList.Count) |
|
{ |
|
var track = playList[nextIndex]; |
|
|
|
if (!track.Played) |
|
{ |
|
playIndex = nextIndex; |
|
track.Played = true; |
|
track.Playing = true; |
|
|
|
playList[nextIndex] = track; |
|
|
|
PlayAudio(playList[nextIndex].Filename); |
|
} |
|
} |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// 立即播放指定的音频文件,即使该播放器正在播放其它音频文件。 |
|
/// </summary> |
|
public void Play(string audioFile) |
|
{ |
|
var index = playList.IndexOf(audioFile); |
|
|
|
if (index == -1) |
|
{ |
|
playList.Add(audioFile, true, true); |
|
playIndex = playList.Count - 1; |
|
} |
|
else |
|
{ |
|
playIndex = index; |
|
|
|
var track = playList[playIndex]; |
|
track.Played = true; |
|
track.Playing = true; |
|
playList[playIndex] = track; |
|
} |
|
|
|
if (IsPlaying) |
|
Stop(); |
|
|
|
stopped = false; |
|
|
|
PlayAudio(audioFile); |
|
} |
|
|
|
/// <summary> |
|
/// 排队播放音频文件。 |
|
/// </summary> |
|
public void PlayScheduled(string audioFile) |
|
{ |
|
var index = playList.IndexOf(audioFile); |
|
|
|
if (index == -1) |
|
playList.Add(audioFile, false, false); |
|
|
|
stopped = false; |
|
} |
|
|
|
/// <summary> |
|
/// 播放音频。 |
|
/// </summary> |
|
public void Play() |
|
{ |
|
stopped = false; |
|
} |
|
|
|
public void Stop() |
|
{ |
|
if (opusDecoder != null) |
|
{ |
|
audioSource.Stop(); |
|
|
|
if (audioSource.clip != null) |
|
{ |
|
Destroy(audioSource.clip); |
|
audioSource.clip = null; |
|
} |
|
|
|
if (playIndex != -1) |
|
{ |
|
var track = playList[playIndex]; |
|
track.Playing = false; |
|
playList[playIndex] = track; |
|
} |
|
} |
|
|
|
stopped = true; |
|
} |
|
|
|
private void Initialize() |
|
{ |
|
if (opusDecoder == null) |
|
{ |
|
audioSource = GetComponent<AudioSource>(); |
|
|
|
audioBuffer = new BetterList<float>(RecordMaxTime * SamplingRate * Channels + PromptTone.Data.Length); |
|
opusBuffer = new BetterList<byte>(200 * 1024); |
|
packageBuffer = new float[OpusDecoder.FrameMaxSize * Channels]; |
|
opusDecoder = new OpusDecoder((SamplingRate)SamplingRate, (Channel)Channels); |
|
|
|
playList = new PlayList(); |
|
playIndex = -1; |
|
} |
|
} |
|
|
|
private void PlayAudio(string audioFile) |
|
{ |
|
OnPlaying(playIndex, playList[playIndex]); |
|
|
|
// 先从文件读取数据 |
|
using (var stream = new FileStream(audioFile, FileMode.Open, FileAccess.Read, FileShare.Read, BufferSize)) |
|
{ |
|
var readLength = 0; |
|
|
|
while ((readLength = stream.Read(buffer, 0, BufferSize)) > 0) |
|
opusBuffer.AddRange(buffer, 0, readLength); |
|
} |
|
|
|
// 解析数据并解码音频 |
|
var length = 0; |
|
|
|
for (var i = 0; i < opusBuffer.Count; ++i) |
|
{ |
|
length = opusBuffer[i]; |
|
|
|
if (length == 0) |
|
continue; |
|
|
|
if (frameBuffer == null || length != frameBuffer.Length) |
|
frameBuffer = new byte[length]; |
|
|
|
opusBuffer.CopyTo(i + 1, frameBuffer, 0, length); |
|
|
|
var decoded = opusDecoder.Decode(frameBuffer, packageBuffer); |
|
|
|
audioBuffer.AddRange(packageBuffer, 0, decoded); |
|
|
|
i += length; |
|
} |
|
|
|
var data = audioBuffer.ToArray(); |
|
|
|
audioBuffer.Clear(); |
|
opusBuffer.Clear(); |
|
|
|
var clip = AudioClip.Create("VoicePlayback", data.Length, Channels, SamplingRate, false); |
|
clip.SetData(data, 0); |
|
|
|
audioSource.clip = clip; |
|
audioSource.Play(); |
|
} |
|
|
|
public void Clear() |
|
{ |
|
Stop(); |
|
PlayList.Clear(); |
|
playIndex = -1; |
|
} |
|
|
|
protected virtual void OnPlaying(int index, Track track) |
|
{ |
|
if (Playing != null) |
|
Playing(index, track); |
|
} |
|
|
|
protected virtual void OnPlayed(int index, Track track) |
|
{ |
|
if (Played != null) |
|
Played(index, track); |
|
} |
|
} |