From 7eb6ff01f6f339f85ca600b428767a3bde6d8d20 Mon Sep 17 00:00:00 2001
From: tanlongsheng <252620078@qq.com>
Date: Thu, 27 Mar 2025 09:28:21 +0800
Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=90=8D=E7=A7=B0=E3=80=81?=
=?UTF-8?q?=E5=8C=85=E5=90=8D?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
ios/Runner/BluetoothMediaButtonHelper.swift | 4 +-
ios/Runner/Info.plist | 2 +-
.../speech_impl/volcano_asr_api_service.dart | 285 +++++++++---------
lib/main.dart | 2 +-
lib/modules/chat/views/chat_view.dart | 2 -
macos/Runner.xcodeproj/project.pbxproj | 6 +-
macos/Runner/Configs/AppInfo.xcconfig | 2 +-
7 files changed, 153 insertions(+), 150 deletions(-)
diff --git a/ios/Runner/BluetoothMediaButtonHelper.swift b/ios/Runner/BluetoothMediaButtonHelper.swift
index 3d203b74d..1a8369172 100644
--- a/ios/Runner/BluetoothMediaButtonHelper.swift
+++ b/ios/Runner/BluetoothMediaButtonHelper.swift
@@ -230,8 +230,8 @@ class BluetoothMediaButtonHelper: NSObject, AVAudioPlayerDelegate {
// 创建音频专辑图片
let albumArt = UIImage(named: "AppIcon") // 使用应用图标作为专辑图片
var nowPlayingInfo: [String: Any] = [
- MPMediaItemPropertyTitle: "DeepVoice 语音助手",
- MPMediaItemPropertyArtist: "DeepVoice",
+ MPMediaItemPropertyTitle: "DeepSound 语音助手",
+ MPMediaItemPropertyArtist: "DeepSound",
MPNowPlayingInfoPropertyPlaybackRate: isPlaying ? 1.0 : 0.0, // 1.0表示正在播放
MPNowPlayingInfoPropertyElapsedPlaybackTime: currentPlaybackTime,
MPMediaItemPropertyPlaybackDuration: totalDuration
diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist
index e55e60462..aea2275af 100644
--- a/ios/Runner/Info.plist
+++ b/ios/Runner/Info.plist
@@ -7,7 +7,7 @@
CFBundleDevelopmentRegion
$(DEVELOPMENT_LANGUAGE)
CFBundleDisplayName
- Deep Voice
+ DeepSound
CFBundleExecutable
$(EXECUTABLE_NAME)
CFBundleIdentifier
diff --git a/lib/data/services/speech_impl/volcano_asr_api_service.dart b/lib/data/services/speech_impl/volcano_asr_api_service.dart
index 7a2d6f457..bab3790ef 100644
--- a/lib/data/services/speech_impl/volcano_asr_api_service.dart
+++ b/lib/data/services/speech_impl/volcano_asr_api_service.dart
@@ -15,61 +15,61 @@ import 'package:record/record.dart';
import '../asr_service.dart';
/// Volcano语音识别服务
-///
+///
/// 负责与火山引擎实时语音识别服务的通信
class VolcanoAsrApiService implements AsrService {
static final VolcanoAsrApiService to = Get.find();
-
+
// API配置
late final String _appKey;
late final String _secretKey;
late final String _serviceId;
late final String _hostUrl;
final String _path = '/ws/v1/asr';
-
+
// 请求相关参数
final String _algorithm = 'hmac-sha1';
final Uuid _uuid = const Uuid();
-
+
// 录音配置
final int _sampleRate = 16000;
final int _bitsPerSample = 16;
final int _channels = 1;
final String _encoding = 'pcm';
-
+
// 连接相关
WebSocketChannel? _channel;
Completer? _connectionCompleter;
StreamSubscription? _connectionSubscription;
Timer? _heartbeatTimer;
-
+
// 语音录音
final Record? _recorder = null; // TODO: 实现正确的Record实例化
Timer? _audioTimer;
bool _isRecording = false;
-
+
// 状态控制
bool _isInitialized = false;
bool _isConnecting = false;
bool _isContinuousRecognitionActive = false;
-
+
// 识别结果控制
bool _isRecognitionEndRequested = false;
String _currentSessionId = '';
final List _supportedLanguages = ['zh-CN', 'en-US'];
String _selectedLanguage = 'zh-CN';
-
+
// 结果流
StreamController? _recognitionController;
-
+
// 事件计数器(用于调试)
int _sendPacketCount = 0;
int _receivePacketCount = 0;
-
+
// AsrService接口实现
@override
List get supportedLanguages => _supportedLanguages;
-
+
@override
Future initialize({
List? supportedLanguages,
@@ -79,21 +79,21 @@ class VolcanoAsrApiService implements AsrService {
if (_isInitialized) {
await dispose();
}
-
+
_isInitialized = false;
-
+
// 从环境变量加载配置
_loadConfig();
-
+
// 设置支持的语言
if (supportedLanguages != null && supportedLanguages.isNotEmpty) {
_supportedLanguages.clear();
_supportedLanguages.addAll(supportedLanguages);
}
-
+
// 初始化录音机
await _initRecorder();
-
+
_isInitialized = true;
Logger.info('火山语音API已初始化');
return true;
@@ -102,19 +102,19 @@ class VolcanoAsrApiService implements AsrService {
return false;
}
}
-
+
@override
Future recognizeOnce() async {
try {
if (!_isInitialized) {
await initialize();
}
-
+
final resultCompleter = Completer();
-
+
// 开始连续识别流程
final stream = await startContinuousRecognition();
-
+
// 监听结果
StreamSubscription? subscription;
subscription = stream.listen((event) {
@@ -138,7 +138,7 @@ class VolcanoAsrApiService implements AsrService {
resultCompleter.complete(RecognitionEvent.error('未收到识别结果'));
}
});
-
+
// 设置超时
Timer(const Duration(seconds: 30), () {
if (!resultCompleter.isCompleted) {
@@ -147,104 +147,104 @@ class VolcanoAsrApiService implements AsrService {
stopContinuousRecognition();
}
});
-
+
return resultCompleter.future;
} catch (e) {
Logger.error('单次语音识别失败: $e');
return RecognitionEvent.error(e.toString());
}
}
-
+
@override
Future> startContinuousRecognition() async {
if (!_isInitialized) {
await initialize();
}
-
+
if (_isContinuousRecognitionActive) {
await stopContinuousRecognition();
}
-
+
try {
// 创建结果控制器
_recognitionController = StreamController.broadcast();
-
+
// 连接WebSocket
await _connectWebSocket();
-
+
// 开始录音
await _startRecording();
-
+
_isContinuousRecognitionActive = true;
_isRecognitionEndRequested = false;
-
+
Logger.info('连续语音识别已开始');
-
+
return _recognitionController!.stream;
} catch (e) {
Logger.error('开始连续语音识别失败: $e');
await _cleanupResources();
-
+
// 返回一个包含错误的流
final errorController = StreamController.broadcast();
errorController.add(RecognitionEvent.error(e.toString()));
errorController.close();
-
+
return errorController.stream;
}
}
-
+
@override
Future stopContinuousRecognition() async {
if (!_isContinuousRecognitionActive) {
return true;
}
-
+
try {
_isRecognitionEndRequested = true;
-
+
// 先停止录音,以确保不会再发送新的音频数据
await _stopRecording();
-
+
// 发送结束指令
await _sendEndSignal();
-
+
// 等待服务器处理完所有已发送的数据
await Future.delayed(const Duration(milliseconds: 500));
-
+
// 清理资源
await _cleanupResources();
-
+
_isContinuousRecognitionActive = false;
Logger.info('连续语音识别已停止');
-
+
return true;
} catch (e) {
Logger.error('停止连续语音识别失败: $e');
-
+
// 确保资源被清理
await _cleanupResources();
_isContinuousRecognitionActive = false;
-
+
return false;
}
}
-
+
@override
bool isContinuousRecognitionActive() {
return _isContinuousRecognitionActive;
}
-
+
@override
Future dispose() async {
try {
if (_isContinuousRecognitionActive) {
await stopContinuousRecognition();
}
-
+
// 确保资源被清理
await _cleanupResources();
-
+
_isInitialized = false;
Logger.info('火山语音API资源已释放');
} catch (e) {
@@ -253,31 +253,32 @@ class VolcanoAsrApiService implements AsrService {
_isInitialized = false;
}
}
-
+
/// 从环境变量加载配置
void _loadConfig() {
_appKey = dotenv.env['VOLCANO_APP_KEY'] ?? '';
_secretKey = dotenv.env['VOLCANO_SECRET_KEY'] ?? '';
_serviceId = dotenv.env['VOLCANO_SERVICE_ID'] ?? '';
_hostUrl = dotenv.env['VOLCANO_HOST_URL'] ?? 'ws-api-asr.volces.com';
-
+
if (_appKey.isEmpty || _secretKey.isEmpty || _serviceId.isEmpty) {
- throw Exception('未找到火山语音API配置,请在.env文件中设置VOLCANO_APP_KEY, VOLCANO_SECRET_KEY和VOLCANO_SERVICE_ID');
+ throw Exception(
+ '未找到火山语音API配置,请在.env文件中设置VOLCANO_APP_KEY, VOLCANO_SECRET_KEY和VOLCANO_SERVICE_ID');
}
}
-
+
/// 初始化录音机
Future _initRecorder() async {
try {
// TODO: 正确实现录音机初始化
// _recorder = Record();
-
+
// 检查和请求权限
// final hasPermission = await _recorder!.hasPermission();
// if (!hasPermission) {
// throw Exception('没有录音权限');
// }
-
+
Logger.info('录音机初始化成功');
} catch (e) {
// _recorder = null;
@@ -285,7 +286,7 @@ class VolcanoAsrApiService implements AsrService {
rethrow;
}
}
-
+
/// 连接WebSocket
Future _connectWebSocket() async {
if (_isConnecting) {
@@ -294,20 +295,20 @@ class VolcanoAsrApiService implements AsrService {
return _connectionCompleter!.future;
}
}
-
+
_connectionCompleter = Completer();
_isConnecting = true;
-
+
try {
// 关闭现有连接
await _closeWebSocket();
-
+
// 生成新的会话ID
_currentSessionId = _uuid.v4();
-
+
// 构建URL和请求头
final (url, headers) = _buildConnectionParams();
-
+
// 创建WebSocket连接
if (kIsWeb) {
_channel = WebSocketChannel.connect(Uri.parse(url));
@@ -317,42 +318,42 @@ class VolcanoAsrApiService implements AsrService {
headers: headers,
);
}
-
+
// 处理WebSocket消息
_connectionSubscription = _channel!.stream.listen(
_handleWebSocketMessage,
onError: _handleWebSocketError,
onDone: _handleWebSocketDone,
);
-
+
// 开始心跳
_startHeartbeat();
-
+
// 发送开始识别指令
await _sendStartCommand();
-
+
_isConnecting = false;
_connectionCompleter!.complete();
-
+
Logger.info('WebSocket连接成功');
} catch (e) {
_isConnecting = false;
if (_connectionCompleter != null && !_connectionCompleter!.isCompleted) {
_connectionCompleter!.completeError(e);
}
-
+
Logger.error('连接WebSocket失败: $e');
rethrow;
}
-
+
return _connectionCompleter!.future;
}
-
+
/// 构建连接参数
(String, Map?) _buildConnectionParams() {
final timestamp = (DateTime.now().millisecondsSinceEpoch / 1000).floor();
final nonce = _generateNonce();
-
+
// 构建表单(用于签名和查询参数)
final form = {
'algorithm': _algorithm,
@@ -362,54 +363,54 @@ class VolcanoAsrApiService implements AsrService {
'session_id': _currentSessionId,
'timestamp': timestamp.toString(),
};
-
+
// 计算签名
final signature = _calculateSignature(form);
-
+
// 添加签名到表单
form['signature'] = signature;
-
+
// 构建URL查询参数
final queryString = form.entries
.map((e) => '${e.key}=${Uri.encodeComponent(e.value)}')
.join('&');
-
+
// 构建完整URL
final url = 'wss://$_hostUrl$_path?$queryString';
-
+
// 构建请求头
final headers = {
- 'User-Agent': 'Deep Voice App/1.0.0',
+ 'User-Agent': 'DeepSound App/1.0.0',
};
-
+
return (url, kIsWeb ? null : headers);
}
-
+
/// 生成随机数
String _generateNonce() {
final random = Random();
final randoms = List.generate(16, (_) => random.nextInt(256));
return base64Url.encode(randoms).substring(0, 16);
}
-
+
/// 计算签名
String _calculateSignature(Map form) {
// 按键排序
final sortedKeys = form.keys.toList()..sort();
-
+
// 构建待签名字符串
final signString = sortedKeys.map((key) => '$key=${form[key]}').join('');
-
+
// 使用HMAC-SHA1进行签名
final key = utf8.encode(_secretKey);
final bytes = utf8.encode(signString);
final hmacSha1 = Hmac(sha1, key);
final digest = hmacSha1.convert(bytes);
-
+
// 返回Base64编码的签名
return base64.encode(digest.bytes);
}
-
+
/// 开始心跳定时
void _startHeartbeat() {
_heartbeatTimer?.cancel();
@@ -417,7 +418,7 @@ class VolcanoAsrApiService implements AsrService {
_sendHeartbeat();
});
}
-
+
/// 发送心跳包
void _sendHeartbeat() {
if (_channel != null && _isContinuousRecognitionActive) {
@@ -426,18 +427,18 @@ class VolcanoAsrApiService implements AsrService {
'type': 'heartbeat',
'sequence': _sendPacketCount++,
};
-
+
_channel!.sink.add(jsonEncode(heartbeatMsg));
} catch (e) {
Logger.error('发送心跳包失败: $e');
}
}
}
-
+
/// 发送开始识别指令
Future _sendStartCommand() async {
if (_channel == null) return;
-
+
try {
final startCommand = {
'type': 'start',
@@ -454,7 +455,7 @@ class VolcanoAsrApiService implements AsrService {
'max_seconds': 60,
},
};
-
+
_channel!.sink.add(jsonEncode(startCommand));
Logger.info('已发送开始识别指令');
} catch (e) {
@@ -462,7 +463,7 @@ class VolcanoAsrApiService implements AsrService {
rethrow;
}
}
-
+
/// 开始录音
Future _startRecording() async {
// TODO: 实现正确的录音开始逻辑
@@ -497,12 +498,12 @@ class VolcanoAsrApiService implements AsrService {
rethrow;
}
*/
-
+
// 临时实现,防止错误
_isRecording = true;
Logger.info('录音已开始(模拟)');
}
-
+
/// 捕获并发送音频数据
Future _captureAndSendAudio() async {
// TODO: 实现正确的音频捕获逻辑
@@ -545,46 +546,46 @@ class VolcanoAsrApiService implements AsrService {
}
*/
}
-
+
/// 发送音频数据
Future _sendAudioData(Uint8List data) async {
if (_channel == null || data.isEmpty) return;
-
+
try {
final audioMessage = {
'type': 'audio',
'sequence': _sendPacketCount++,
'audio': base64.encode(data),
};
-
+
_channel!.sink.add(jsonEncode(audioMessage));
} catch (e) {
Logger.error('发送音频数据失败: $e');
}
}
-
+
/// 发送结束信号
Future _sendEndSignal() async {
if (_channel == null) return;
-
+
try {
final endCommand = {
'type': 'end',
'sequence': _sendPacketCount++,
};
-
+
_channel!.sink.add(jsonEncode(endCommand));
Logger.info('已发送结束指令');
} catch (e) {
Logger.error('发送结束指令失败: $e');
}
}
-
+
/// 停止录音
Future _stopRecording() async {
_audioTimer?.cancel();
_audioTimer = null;
-
+
// TODO: 实现正确的录音停止逻辑
/*
if (_isRecording && _recorder != null) {
@@ -597,64 +598,65 @@ class VolcanoAsrApiService implements AsrService {
}
}
*/
-
+
// 临时实现,防止错误
_isRecording = false;
Logger.info('录音已停止(模拟)');
}
-
+
/// 处理WebSocket消息
void _handleWebSocketMessage(dynamic message) {
try {
_receivePacketCount++;
-
+
if (message is String) {
final Map data = jsonDecode(message);
final String type = data['type'] as String? ?? '';
-
+
switch (type) {
case 'result':
_handleResultMessage(data);
break;
-
+
case 'start_result':
_handleStartResultMessage(data);
break;
-
+
case 'end_result':
_handleEndResultMessage(data);
break;
-
+
case 'error':
_handleErrorMessage(data);
break;
-
+
case 'heartbeat_result':
// 心跳响应,不需要特殊处理
break;
-
+
default:
Logger.info('收到未知类型的消息: $type');
}
}
} catch (e) {
Logger.error('处理WebSocket消息失败: $e');
-
+
if (_recognitionController != null && !_recognitionController!.isClosed) {
_recognitionController!.addError('处理消息失败: $e');
}
}
}
-
+
/// 处理结果消息
void _handleResultMessage(Map data) {
try {
- final Map? result = data['result'] as Map?;
+ final Map? result =
+ data['result'] as Map?;
if (result == null) return;
-
+
final bool isEnd = result['is_end'] as bool? ?? false;
final String text = result['text'] as String? ?? '';
-
+
if (_recognitionController != null && !_recognitionController!.isClosed) {
if (isEnd) {
// 最终结果
@@ -674,11 +676,11 @@ class VolcanoAsrApiService implements AsrService {
Logger.error('处理结果消息失败: $e');
}
}
-
+
/// 处理开始结果消息
void _handleStartResultMessage(Map data) {
final int code = data['code'] as int? ?? -1;
-
+
if (code == 0) {
// 开始成功
if (_recognitionController != null && !_recognitionController!.isClosed) {
@@ -689,19 +691,20 @@ class VolcanoAsrApiService implements AsrService {
} else {
// 开始失败
final String message = data['message'] as String? ?? '未知错误';
-
+
Logger.error('语音识别开始失败: [$code] $message');
-
+
if (_recognitionController != null && !_recognitionController!.isClosed) {
- _recognitionController!.add(RecognitionEvent.error('开始失败: [$code] $message'));
+ _recognitionController!
+ .add(RecognitionEvent.error('开始失败: [$code] $message'));
}
}
}
-
+
/// 处理结束结果消息
void _handleEndResultMessage(Map data) {
final int code = data['code'] as int? ?? -1;
-
+
if (code == 0) {
// 结束成功
Logger.info('语音识别成功结束');
@@ -710,73 +713,75 @@ class VolcanoAsrApiService implements AsrService {
final String message = data['message'] as String? ?? '未知错误';
Logger.error('语音识别结束失败: [$code] $message');
}
-
+
// 无论成功还是失败,通知会话已结束
if (_recognitionController != null && !_recognitionController!.isClosed) {
_recognitionController!.add(RecognitionEvent(
type: RecognitionEventType.sessionStopped,
));
}
-
+
// 清理资源
_cleanupResources();
}
-
+
/// 处理错误消息
void _handleErrorMessage(Map data) {
final int code = data['code'] as int? ?? -1;
final String message = data['message'] as String? ?? '未知错误';
-
+
Logger.error('收到错误消息: [$code] $message');
-
+
if (_recognitionController != null && !_recognitionController!.isClosed) {
_recognitionController!.add(RecognitionEvent.error('[$code] $message'));
}
}
-
+
/// 处理WebSocket错误
void _handleWebSocketError(dynamic error) {
Logger.error('WebSocket错误: $error');
-
+
if (_connectionCompleter != null && !_connectionCompleter!.isCompleted) {
_connectionCompleter!.completeError(error);
}
-
+
if (_recognitionController != null && !_recognitionController!.isClosed) {
_recognitionController!.addError(error);
}
-
+
// 清理资源
_cleanupResources();
}
-
+
/// 处理WebSocket连接关闭
void _handleWebSocketDone() {
Logger.info('WebSocket连接已关闭');
-
+
if (_connectionCompleter != null && !_connectionCompleter!.isCompleted) {
_connectionCompleter!.completeError('WebSocket连接已关闭');
}
-
+
// 通知会话已结束
- if (_recognitionController != null && !_recognitionController!.isClosed && !_isRecognitionEndRequested) {
+ if (_recognitionController != null &&
+ !_recognitionController!.isClosed &&
+ !_isRecognitionEndRequested) {
_recognitionController!.add(RecognitionEvent(
type: RecognitionEventType.sessionStopped,
));
}
-
+
// 清理资源
_cleanupResources();
}
-
+
/// 关闭WebSocket连接
Future _closeWebSocket() async {
_connectionSubscription?.cancel();
_connectionSubscription = null;
-
+
_heartbeatTimer?.cancel();
_heartbeatTimer = null;
-
+
if (_channel != null) {
try {
_channel!.sink.close();
@@ -786,22 +791,22 @@ class VolcanoAsrApiService implements AsrService {
}
}
}
-
+
/// 清理所有资源
Future _cleanupResources() async {
// 停止录音
await _stopRecording();
-
+
// 关闭WebSocket
await _closeWebSocket();
-
+
// 关闭结果控制器
if (_recognitionController != null && !_recognitionController!.isClosed) {
await _recognitionController!.close();
_recognitionController = null;
}
-
+
_isContinuousRecognitionActive = false;
_isRecognitionEndRequested = false;
}
-}
\ No newline at end of file
+}
diff --git a/lib/main.dart b/lib/main.dart
index e08049e9d..47dbe1b08 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -30,7 +30,7 @@ void main() async {
const double width = 375.0;
const double height = 812.0;
- setWindowTitle('Deep Voice');
+ setWindowTitle('DeepSound');
setWindowMinSize(const Size(width, height));
setWindowMaxSize(const Size(width, height));
diff --git a/lib/modules/chat/views/chat_view.dart b/lib/modules/chat/views/chat_view.dart
index cddb49257..86d77e3a1 100644
--- a/lib/modules/chat/views/chat_view.dart
+++ b/lib/modules/chat/views/chat_view.dart
@@ -2,11 +2,9 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'dart:math';
-import 'package:audio_waveforms/audio_waveforms.dart';
import '../controllers/chat_controller.dart';
import '../models/message_model.dart';
import '../../../core/theme/app_colors.dart';
-import '../../../core/widgets/message_bubble.dart';
class ChatView extends GetView {
const ChatView({Key? key}) : super(key: key);
diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj
index a5036d1d0..4658cacbc 100644
--- a/macos/Runner.xcodeproj/project.pbxproj
+++ b/macos/Runner.xcodeproj/project.pbxproj
@@ -479,7 +479,7 @@
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
- PRODUCT_BUNDLE_IDENTIFIER = com.example.deepVoice.RunnerTests;
+ PRODUCT_BUNDLE_IDENTIFIER = com.yunqiinnovation.deepsound.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/deep_voice.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/deep_voice";
@@ -494,7 +494,7 @@
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
- PRODUCT_BUNDLE_IDENTIFIER = com.example.deepVoice.RunnerTests;
+ PRODUCT_BUNDLE_IDENTIFIER = com.yunqiinnovation.deepsound.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/deep_voice.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/deep_voice";
@@ -509,7 +509,7 @@
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
- PRODUCT_BUNDLE_IDENTIFIER = com.example.deepVoice.RunnerTests;
+ PRODUCT_BUNDLE_IDENTIFIER = com.yunqiinnovation.deepsound.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/deep_voice.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/deep_voice";
diff --git a/macos/Runner/Configs/AppInfo.xcconfig b/macos/Runner/Configs/AppInfo.xcconfig
index 99aa41de4..34460e77c 100644
--- a/macos/Runner/Configs/AppInfo.xcconfig
+++ b/macos/Runner/Configs/AppInfo.xcconfig
@@ -8,7 +8,7 @@
PRODUCT_NAME = deep_voice
// The application's bundle identifier
-PRODUCT_BUNDLE_IDENTIFIER = com.example.deepVoice
+PRODUCT_BUNDLE_IDENTIFIER = com.yunqiinnovation.deepsound
// The copyright displayed in application information
PRODUCT_COPYRIGHT = Copyright © 2025 com.example. All rights reserved.