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
4 years ago
|
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);
|
||
|
}
|
||
|
}
|