123
This commit is contained in:
parent
97f852d5e5
commit
631019cf6a
|
|
@ -121,6 +121,11 @@ public class AudioFilesConfig
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string DigitToneFileTemplate { get; set; } = "{0}.mp3";
|
public string DigitToneFileTemplate { get; set; } = "{0}.mp3";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 按键音的最小播放时间(毫秒),即使按键释放也会至少播放这么长时间
|
||||||
|
/// </summary>
|
||||||
|
public int MinKeyTonePlayTimeMs { get; set; } = 200;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取完整的音频文件路径
|
/// 获取完整的音频文件路径
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -214,6 +219,21 @@ public class RecordingConfig
|
||||||
/// 是否上传录音文件到服务器
|
/// 是否上传录音文件到服务器
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool UploadRecordingToServer { get; set; } = false;
|
public bool UploadRecordingToServer { get; set; } = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否在录音时播放背景音乐
|
||||||
|
/// </summary>
|
||||||
|
public bool EnableBackgroundMusic { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 录音背景音乐文件名
|
||||||
|
/// </summary>
|
||||||
|
public string BackgroundMusicFile { get; set; } = "bj.mp3";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 背景音乐音量 (0.0-1.0)
|
||||||
|
/// </summary>
|
||||||
|
public float BackgroundMusicVolume { get; set; } = 0.1f;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,12 @@ public class PhoneBoothService : IPhoneBoothService, IDisposable
|
||||||
private Timer? _silenceTimer = null;
|
private Timer? _silenceTimer = null;
|
||||||
private volatile bool _isHangUpKeyPressed = false;
|
private volatile bool _isHangUpKeyPressed = false;
|
||||||
|
|
||||||
|
// 背景音乐相关
|
||||||
|
private WaveOutEvent? _backgroundMusicDevice = null;
|
||||||
|
private AudioFileReader? _backgroundMusicReader = null;
|
||||||
|
private CancellationTokenSource? _backgroundMusicCts = null;
|
||||||
|
private volatile bool _isBackgroundMusicPlaying = false;
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
private struct KBDLLHOOKSTRUCT
|
private struct KBDLLHOOKSTRUCT
|
||||||
{
|
{
|
||||||
|
|
@ -175,6 +181,12 @@ public class PhoneBoothService : IPhoneBoothService, IDisposable
|
||||||
await Task.Delay(100); // 给一点时间让等待音停止
|
await Task.Delay(100); // 给一点时间让等待音停止
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 停止背景音乐
|
||||||
|
if (_isBackgroundMusicPlaying)
|
||||||
|
{
|
||||||
|
StopBackgroundMusic();
|
||||||
|
}
|
||||||
|
|
||||||
// 停止录音
|
// 停止录音
|
||||||
if (_isRecording)
|
if (_isRecording)
|
||||||
{
|
{
|
||||||
|
|
@ -241,6 +253,7 @@ public class PhoneBoothService : IPhoneBoothService, IDisposable
|
||||||
// 释放取消令牌源
|
// 释放取消令牌源
|
||||||
_programCts.Dispose();
|
_programCts.Dispose();
|
||||||
_waitingToneCts?.Dispose();
|
_waitingToneCts?.Dispose();
|
||||||
|
_backgroundMusicCts?.Dispose();
|
||||||
|
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
|
|
@ -292,6 +305,26 @@ public class PhoneBoothService : IPhoneBoothService, IDisposable
|
||||||
_waitingToneReader = null;
|
_waitingToneReader = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 释放背景音乐设备
|
||||||
|
_backgroundMusicCts?.Cancel();
|
||||||
|
_backgroundMusicCts?.Dispose();
|
||||||
|
_backgroundMusicCts = null;
|
||||||
|
|
||||||
|
if (_backgroundMusicDevice != null)
|
||||||
|
{
|
||||||
|
_backgroundMusicDevice.Stop();
|
||||||
|
_backgroundMusicDevice.Dispose();
|
||||||
|
_backgroundMusicDevice = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_backgroundMusicReader != null)
|
||||||
|
{
|
||||||
|
_backgroundMusicReader.Dispose();
|
||||||
|
_backgroundMusicReader = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isBackgroundMusicPlaying = false;
|
||||||
|
|
||||||
foreach (var (device, reader) in _keyToneDevices.Values)
|
foreach (var (device, reader) in _keyToneDevices.Values)
|
||||||
{
|
{
|
||||||
device.Stop();
|
device.Stop();
|
||||||
|
|
@ -543,12 +576,19 @@ public class PhoneBoothService : IPhoneBoothService, IDisposable
|
||||||
recordingCompletionSource.TrySetResult(true);
|
recordingCompletionSource.TrySetResult(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 启动背景音乐播放(不等待它完成)
|
||||||
|
if (_config.Recording.EnableBackgroundMusic)
|
||||||
|
{
|
||||||
|
_ = StartPlayingBackgroundMusic(linkedCts.Token);
|
||||||
|
}
|
||||||
|
|
||||||
// 等待录音完成
|
// 等待录音完成
|
||||||
await recordingCompletionSource.Task;
|
await recordingCompletionSource.Task;
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
// 捕获所有异常,确保不会中断主流程
|
// 捕获所有异常,确保不会中断主流程
|
||||||
|
Console.WriteLine($"录音任务异常: {ex.Message}");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -565,6 +605,9 @@ public class PhoneBoothService : IPhoneBoothService, IDisposable
|
||||||
// 停止录音
|
// 停止录音
|
||||||
StopAudioRecording();
|
StopAudioRecording();
|
||||||
|
|
||||||
|
// 确保背景音乐停止
|
||||||
|
StopBackgroundMusic();
|
||||||
|
|
||||||
Console.WriteLine("录音结束,重置状态...");
|
Console.WriteLine("录音结束,重置状态...");
|
||||||
|
|
||||||
// 尝试上传录音文件到服务器
|
// 尝试上传录音文件到服务器
|
||||||
|
|
@ -826,6 +869,9 @@ public class PhoneBoothService : IPhoneBoothService, IDisposable
|
||||||
// 确保录音设备被释放
|
// 确保录音设备被释放
|
||||||
StopAudioRecording();
|
StopAudioRecording();
|
||||||
|
|
||||||
|
// 确保背景音乐被停止
|
||||||
|
StopBackgroundMusic();
|
||||||
|
|
||||||
// 清除录音状态
|
// 清除录音状态
|
||||||
_isRecording = false;
|
_isRecording = false;
|
||||||
_isHangUpKeyPressed = false;
|
_isHangUpKeyPressed = false;
|
||||||
|
|
@ -864,6 +910,16 @@ public class PhoneBoothService : IPhoneBoothService, IDisposable
|
||||||
_currentPressedKeys.Clear();
|
_currentPressedKeys.Clear();
|
||||||
_keyToneDevices.Clear();
|
_keyToneDevices.Clear();
|
||||||
|
|
||||||
|
// 尝试停止背景音乐
|
||||||
|
try
|
||||||
|
{
|
||||||
|
StopBackgroundMusic();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// 忽略任何错误
|
||||||
|
}
|
||||||
|
|
||||||
// 尝试重新开始播放等待音
|
// 尝试重新开始播放等待音
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
@ -1084,12 +1140,12 @@ public class PhoneBoothService : IPhoneBoothService, IDisposable
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (_keyToneDevices.TryRemove(digit, out var deviceInfo))
|
// 当按键释放时,我们不做特殊处理
|
||||||
{
|
// 按键音的播放和结束完全由PlayKeyTone中的逻辑控制
|
||||||
deviceInfo.Device.Stop();
|
// 这样确保了即使按键时间很短,声音也能够完整播放
|
||||||
deviceInfo.Device.Dispose();
|
|
||||||
deviceInfo.Reader.Dispose();
|
// 我们只需要确认按键已经从当前按下状态列表中移除即可
|
||||||
}
|
// 实际的音频资源释放由PlayKeyTone中的PlaybackStopped事件处理
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|
@ -1104,18 +1160,68 @@ public class PhoneBoothService : IPhoneBoothService, IDisposable
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
// 如果该按键声音正在播放中,不再重新开始播放
|
||||||
if (_keyToneDevices.ContainsKey(digit))
|
if (_keyToneDevices.ContainsKey(digit))
|
||||||
{
|
{
|
||||||
return; // 已经在播放中
|
// 如果已经在播放中,可以选择重新开始播放,或者保持当前播放
|
||||||
|
// 这里我们选择重新开始播放
|
||||||
|
if (_keyToneDevices.TryRemove(digit, out var oldDeviceInfo))
|
||||||
|
{
|
||||||
|
// 安全地停止并释放旧设备
|
||||||
|
try
|
||||||
|
{
|
||||||
|
oldDeviceInfo.Device.Stop();
|
||||||
|
oldDeviceInfo.Device.Dispose();
|
||||||
|
oldDeviceInfo.Reader.Dispose();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"停止旧按键音 {digit} 失败: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var device = new WaveOutEvent();
|
var device = new WaveOutEvent();
|
||||||
var reader = new AudioFileReader(_config.AudioFiles.GetDigitToneFilePath(digit));
|
var reader = new AudioFileReader(_config.AudioFiles.GetDigitToneFilePath(digit));
|
||||||
device.Init(reader);
|
device.Init(reader);
|
||||||
|
|
||||||
|
// 记录开始播放时间
|
||||||
|
var startTime = DateTime.Now;
|
||||||
|
int minPlayTime = _config.AudioFiles.MinKeyTonePlayTimeMs;
|
||||||
|
|
||||||
if (_keyToneDevices.TryAdd(digit, (device, reader)))
|
if (_keyToneDevices.TryAdd(digit, (device, reader)))
|
||||||
{
|
{
|
||||||
device.Play();
|
device.Play();
|
||||||
|
|
||||||
|
// 确保至少播放最小时长
|
||||||
|
device.PlaybackStopped += async (s, e) =>
|
||||||
|
{
|
||||||
|
// 计算已经播放的时间
|
||||||
|
var playedTime = (int)(DateTime.Now - startTime).TotalMilliseconds;
|
||||||
|
|
||||||
|
// 如果播放时间不足最小时间,延迟处理
|
||||||
|
if (playedTime < minPlayTime)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 如果播放已经停止但时间不够,先不删除设备
|
||||||
|
// 等待剩余的时间后再处理
|
||||||
|
var remainingTime = minPlayTime - playedTime;
|
||||||
|
await Task.Delay(remainingTime);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"按键音 {digit} 最小播放时间延迟失败: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 播放完成后(包括可能的延迟),移除并释放资源
|
||||||
|
if (_keyToneDevices.TryRemove(digit, out var deviceInfo) && deviceInfo.Device == device)
|
||||||
|
{
|
||||||
|
deviceInfo.Device.Dispose();
|
||||||
|
deviceInfo.Reader.Dispose();
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -1229,4 +1335,124 @@ public class PhoneBoothService : IPhoneBoothService, IDisposable
|
||||||
reader?.Dispose();
|
reader?.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 开始播放背景音乐
|
||||||
|
/// </summary>
|
||||||
|
private async Task StartPlayingBackgroundMusic(CancellationToken token)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 如果配置不启用背景音乐,则直接返回
|
||||||
|
if (!_config.Recording.EnableBackgroundMusic)
|
||||||
|
{
|
||||||
|
Console.WriteLine("背景音乐功能已禁用");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取背景音乐文件路径
|
||||||
|
string musicFilePath = _config.AudioFiles.GetFullPath(_config.Recording.BackgroundMusicFile);
|
||||||
|
|
||||||
|
// 检查文件是否存在
|
||||||
|
if (!File.Exists(musicFilePath))
|
||||||
|
{
|
||||||
|
Console.WriteLine($"背景音乐文件不存在: {musicFilePath}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取消可能正在播放的背景音乐
|
||||||
|
_backgroundMusicCts?.Cancel();
|
||||||
|
_backgroundMusicCts?.Dispose();
|
||||||
|
_backgroundMusicCts = CancellationTokenSource.CreateLinkedTokenSource(token);
|
||||||
|
|
||||||
|
// 初始化背景音乐设备
|
||||||
|
_backgroundMusicDevice = new WaveOutEvent();
|
||||||
|
_backgroundMusicReader = new AudioFileReader(musicFilePath);
|
||||||
|
|
||||||
|
// 设置音量(默认为10%)
|
||||||
|
_backgroundMusicReader.Volume = _config.Recording.BackgroundMusicVolume;
|
||||||
|
|
||||||
|
_backgroundMusicDevice.Init(_backgroundMusicReader);
|
||||||
|
|
||||||
|
// 标记背景音乐开始播放
|
||||||
|
_isBackgroundMusicPlaying = true;
|
||||||
|
Console.WriteLine($"开始播放背景音乐,音量: {_backgroundMusicReader.Volume * 100}%");
|
||||||
|
|
||||||
|
// 循环播放背景音乐
|
||||||
|
while (!_backgroundMusicCts.Token.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 检查是否到达文件末尾,是则重置
|
||||||
|
if (_backgroundMusicReader.Position >= _backgroundMusicReader.Length)
|
||||||
|
{
|
||||||
|
_backgroundMusicReader.Position = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果没有在播放,则开始播放
|
||||||
|
if (_backgroundMusicDevice.PlaybackState != PlaybackState.Playing)
|
||||||
|
{
|
||||||
|
_backgroundMusicDevice.Play();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 短暂等待,避免CPU占用过高
|
||||||
|
await Task.Delay(100, _backgroundMusicCts.Token);
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
// 正常取消,退出循环
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"背景音乐播放出错: {ex.Message}");
|
||||||
|
// 短暂等待后继续尝试
|
||||||
|
await Task.Delay(1000, _backgroundMusicCts.Token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"启动背景音乐失败: {ex.Message}");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
// 停止并释放音乐设备
|
||||||
|
StopBackgroundMusic();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 停止背景音乐
|
||||||
|
/// </summary>
|
||||||
|
private void StopBackgroundMusic()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 取消播放任务
|
||||||
|
_backgroundMusicCts?.Cancel();
|
||||||
|
|
||||||
|
// 停止并释放设备
|
||||||
|
if (_backgroundMusicDevice != null)
|
||||||
|
{
|
||||||
|
_backgroundMusicDevice.Stop();
|
||||||
|
_backgroundMusicDevice.Dispose();
|
||||||
|
_backgroundMusicDevice = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_backgroundMusicReader != null)
|
||||||
|
{
|
||||||
|
_backgroundMusicReader.Dispose();
|
||||||
|
_backgroundMusicReader = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 标记背景音乐已停止
|
||||||
|
_isBackgroundMusicPlaying = false;
|
||||||
|
Console.WriteLine("背景音乐已停止");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"停止背景音乐失败: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -6,7 +6,8 @@
|
||||||
"PhonePickupFile": "\u7535\u8BDD\u63A5\u8D77.mp3",
|
"PhonePickupFile": "\u7535\u8BDD\u63A5\u8D77.mp3",
|
||||||
"PromptUserRecordFile": "\u63D0\u793A\u7528\u6237\u5F55\u97F3.mp3",
|
"PromptUserRecordFile": "\u63D0\u793A\u7528\u6237\u5F55\u97F3.mp3",
|
||||||
"BeepPromptFile": "\u6EF4\u63D0\u793A\u97F3.wav",
|
"BeepPromptFile": "\u6EF4\u63D0\u793A\u97F3.wav",
|
||||||
"DigitToneFileTemplate": "{0}.mp3"
|
"DigitToneFileTemplate": "{0}.mp3",
|
||||||
|
"MinKeyTonePlayTimeMs": 200
|
||||||
},
|
},
|
||||||
"Dial": {
|
"Dial": {
|
||||||
"MinDigitsToDialOut": 8,
|
"MinDigitsToDialOut": 8,
|
||||||
|
|
@ -22,7 +23,10 @@
|
||||||
"SilenceThreshold": 0.02,
|
"SilenceThreshold": 0.02,
|
||||||
"SilenceTimeoutSeconds": 30,
|
"SilenceTimeoutSeconds": 30,
|
||||||
"AllowUserHangup": true,
|
"AllowUserHangup": true,
|
||||||
"UploadRecordingToServer": false
|
"UploadRecordingToServer": false,
|
||||||
|
"EnableBackgroundMusic": true,
|
||||||
|
"BackgroundMusicFile": "bj.mp3",
|
||||||
|
"BackgroundMusicVolume": 0.1
|
||||||
},
|
},
|
||||||
"CallFlow": {
|
"CallFlow": {
|
||||||
"WaitForPickupMinSeconds": 3,
|
"WaitForPickupMinSeconds": 3,
|
||||||
|
|
|
||||||
BIN
ShengShengBuXi.ConsoleApp/mp3/bj.mp3
Normal file
BIN
ShengShengBuXi.ConsoleApp/mp3/bj.mp3
Normal file
Binary file not shown.
|
|
@ -386,7 +386,7 @@ namespace ShengShengBuXi.Services
|
||||||
// 检查会话是否存在
|
// 检查会话是否存在
|
||||||
if (!_webSockets.TryGetValue(sessionId, out var webSocket))
|
if (!_webSockets.TryGetValue(sessionId, out var webSocket))
|
||||||
{
|
{
|
||||||
_logger.LogWarning($"会话不存在: {sessionId}");
|
_logger.LogWarning($"ProcessAudioAsync->会话不存在: {sessionId}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user