Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions PCL.Neo.Core/PCL.Neo.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,13 @@
<PackageReference Include="System.Reactive.Linq" Version="6.0.1" />
</ItemGroup>


<ItemGroup>
<Folder Include="Utils\" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="System.Runtime.InteropServices" Version="4.3.0" />
</ItemGroup>

</Project>
135 changes: 135 additions & 0 deletions PCL.Neo.Core/Service/Audio/AudioDemo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
using System;
using System.IO;
using System.Threading.Tasks;

namespace PCL.Neo.Core.Service.Audio;

/// <summary>
/// 音频服务演示类,包含使用音频服务API的示例代码
/// </summary>
public static class AudioDemo
{
/// <summary>
/// 演示基本音频播放功能
/// </summary>
/// <param name="audioFilePath">音频文件路径</param>
/// <returns>演示任务</returns>
public static async Task BasicPlaybackDemo(string audioFilePath)
{
// 创建音频服务
IAudioService audioService = AudioServiceFactory.CreateForCurrentPlatform();

Console.WriteLine("开始播放音频...");
bool result = await audioService.PlayAsync(audioFilePath);

if (!result)
{
Console.WriteLine("播放失败,可能文件不存在或格式不支持");
return;
}

// 等待3秒后暂停
await Task.Delay(3000);
Console.WriteLine("暂停播放...");
await audioService.PauseAsync();

// 等待2秒后恢复播放
await Task.Delay(2000);
Console.WriteLine("恢复播放...");
await audioService.ResumeAsync();

// 等待3秒后设置音量
await Task.Delay(3000);
Console.WriteLine("设置音量为50%...");
await audioService.SetVolumeAsync(0.5f);

// 等待3秒后停止播放
await Task.Delay(3000);
Console.WriteLine("停止播放...");
await audioService.StopAsync();
}

/// <summary>
/// 演示使用扩展方法
/// </summary>
/// <param name="audioFilePath">音频文件路径</param>
/// <returns>演示任务</returns>
public static async Task ExtensionsDemo(string audioFilePath)
{
// 创建音频服务
IAudioService audioService = AudioServiceFactory.CreateForCurrentPlatform();

// 播放音效并等待完成
Console.WriteLine("播放音效并等待完成...");
bool result = await audioService.PlaySoundAndWaitAsync(audioFilePath, 10000); // 10秒超时

if (result)
{
Console.WriteLine("音效已完成播放");
}
else
{
Console.WriteLine("音效播放失败或超时");
}

// 音量渐变示例
Console.WriteLine("开始播放并渐入音量...");
await audioService.PlayAsync(audioFilePath);
await audioService.SetVolumeAsync(0.0f); // 开始时音量为0
await audioService.FadeVolumeAsync(1.0f, 3000); // 3秒内渐入

await Task.Delay(3000);

Console.WriteLine("渐出音量...");
await audioService.FadeVolumeAsync(0.0f, 3000); // 3秒内渐出

await Task.Delay(3000);
await audioService.StopAsync();
}

/// <summary>
/// 演示从流播放音频
/// </summary>
/// <param name="audioFilePath">用于演示的音频文件路径</param>
/// <returns>演示任务</returns>
public static async Task StreamPlaybackDemo(string audioFilePath)
{
// 创建音频服务
IAudioService audioService = AudioServiceFactory.CreateForCurrentPlatform();

// 从文件加载到流中(在实际应用中,这可能来自网络或嵌入资源)
Console.WriteLine("从流播放音频...");
using (FileStream stream = File.OpenRead(audioFilePath))
{
// 从流播放
string extension = Path.GetExtension(audioFilePath);
bool result = await audioService.PlayAsync(stream, extension);

if (!result)
{
Console.WriteLine("从流播放失败");
return;
}

// 等待10秒或播放完成
Console.WriteLine("等待播放完成...");
var completionSource = new TaskCompletionSource<bool>();

EventHandler? handler = null;
handler = (s, e) =>
{
audioService.PlaybackFinished -= handler;
completionSource.SetResult(true);
};

audioService.PlaybackFinished += handler;

// 10秒超时或等待播放完成
var timeoutTask = Task.Delay(10000);
await Task.WhenAny(completionSource.Task, timeoutTask);

audioService.PlaybackFinished -= handler;
await audioService.StopAsync();
}
}
}
42 changes: 42 additions & 0 deletions PCL.Neo.Core/Service/Audio/AudioOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
namespace PCL.Neo.Core.Service.Audio;

/// <summary>
/// 音频选项配置类
/// </summary>
public class AudioOptions
{
/// <summary>
/// 默认音量 (0.0 - 1.0)
/// </summary>
public float DefaultVolume { get; set; } = 0.7f;

/// <summary>
/// 是否启用音频系统
/// </summary>
public bool EnableAudio { get; set; } = true;

/// <summary>
/// 音频临时文件目录
/// </summary>
public string? TempDirectory { get; set; }

/// <summary>
/// 音频缓冲区大小(字节)
/// </summary>
public int BufferSize { get; set; } = 16384;

/// <summary>
/// 是否记录日志
/// </summary>
public bool EnableLogging { get; set; } = false;

/// <summary>
/// 音频错误重试次数
/// </summary>
public int RetryCount { get; set; } = 3;

/// <summary>
/// 音频错误重试延迟(毫秒)
/// </summary>
public int RetryDelayMs { get; set; } = 500;
}
133 changes: 133 additions & 0 deletions PCL.Neo.Core/Service/Audio/AudioPlayerExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
using System;
using System.IO;
using System.Threading.Tasks;

namespace PCL.Neo.Core.Service.Audio;

/// <summary>
/// 音频播放器扩展方法,提供更便捷的API
/// </summary>
public static class AudioPlayerExtensions
{
/// <summary>
/// 播放简短音效
/// </summary>
/// <param name="audioService">音频服务</param>
/// <param name="filePath">音频文件路径</param>
/// <returns>是否成功开始播放</returns>
public static async Task<bool> PlaySoundEffectAsync(this IAudioService audioService, string filePath)
{
if (audioService == null || string.IsNullOrEmpty(filePath))
return false;

return await audioService.PlayAsync(filePath);
}

/// <summary>
/// 播放嵌入资源音效
/// </summary>
/// <param name="audioService">音频服务</param>
/// <param name="resourceStream">资源流</param>
/// <param name="fileExtension">文件扩展名</param>
/// <returns>是否成功开始播放</returns>
public static async Task<bool> PlayEmbeddedSoundAsync(
this IAudioService audioService,
Stream resourceStream,
string fileExtension = ".mp3")
{
if (audioService == null || resourceStream == null)
return false;

return await audioService.PlayAsync(resourceStream, fileExtension);
}

/// <summary>
/// 同步播放音效(会等待完成)
/// </summary>
/// <param name="audioService">音频服务</param>
/// <param name="filePath">音频文件路径</param>
/// <param name="timeoutMs">超时时间(毫秒),超过此时间将返回,-1表示无超时</param>
/// <returns>是否成功完成播放</returns>
public static async Task<bool> PlaySoundAndWaitAsync(
this IAudioService audioService,
string filePath,
int timeoutMs = -1)
{
if (audioService == null || string.IsNullOrEmpty(filePath))
return false;

var completionSource = new TaskCompletionSource<bool>();
EventHandler? handler = null;

handler = (s, e) =>
{
audioService.PlaybackFinished -= handler;
completionSource.SetResult(true);
};

audioService.PlaybackFinished += handler;

if (!await audioService.PlayAsync(filePath))
{
audioService.PlaybackFinished -= handler;
return false;
}

if (timeoutMs > 0)
{
// 创建超时任务
var timeoutTask = Task.Delay(timeoutMs);

// 等待完成播放或超时
if (await Task.WhenAny(completionSource.Task, timeoutTask) == timeoutTask)
{
// 超时,停止播放并返回false
audioService.PlaybackFinished -= handler;
await audioService.StopAsync();
return false;
}
}
else
{
// 无超时,等待完成
await completionSource.Task;
}

return true;
}

/// <summary>
/// 渐变音量
/// </summary>
/// <param name="audioService">音频服务</param>
/// <param name="targetVolume">目标音量</param>
/// <param name="durationMs">渐变持续时间(毫秒)</param>
/// <returns>操作任务</returns>
public static async Task FadeVolumeAsync(
this IAudioService audioService,
float targetVolume,
int durationMs = 1000)
{
if (audioService == null || durationMs <= 0)
return;

// 获取当前音量的方法不直接支持,所以我们从0开始渐变
float currentVolume = 0.0f;
float startVolume = currentVolume;
float volumeDiff = targetVolume - startVolume;

// 至少进行10次调整,确保渐变平滑
int steps = Math.Max(10, durationMs / 50);
int stepDelayMs = durationMs / steps;

for (int i = 0; i <= steps; i++)
{
float progress = (float)i / steps;
float newVolume = startVolume + (volumeDiff * progress);
await audioService.SetVolumeAsync(newVolume);

if (i < steps)
await Task.Delay(stepDelayMs);
}
}
}
Loading
Loading