diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..90bcf39 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ + +agent/tools/IotmService.py diff --git a/README.md b/README.md index d582b68..d8dd6ed 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ -**12月迟来的报到,Fay数字人 AI Agent版(含智慧农业箱的操作demo代码,如果你需要完整代码可以公众号留言申请获取)第4版正式上传!** +**12月迟来的报到,Fay数字人 AI Agent版(含智慧农业箱的操作demo代码,如果你需要完整代码可以公众号留言申请获取)第5版正式上传!** 如果你需要是一个线上线下的销售员,请移步[`带货完整版`](https://github.com/TheRamU/Fay/tree/fay-sales-edition) @@ -55,9 +55,29 @@ python main.py ### **启动数字人(非必须)** -+ 启动数字人[xszyou/fay-ue5: 可对接fay数字人的ue5工程 (github.com)](https://github.com/xszyou/fay-ue5) ++ 仓库地址:https://github.com/xszyou/fay-ue5 + +### **启动android 连接器(非必须)** + ++ 仓库地址:https://github.com/xszyou/fay-android + +### **更新日志** +2023.12.25: + +*实现agent ReAct与LLM chain自动切换逻辑✓ + +聊天窗区分任务消息✓ + +修复删除日程bug✓ + +优化远程音频逻辑✓ + +等待处理引入加载中效果✓ + +优化prompt以解决日程任务递归调用问题✓ + +修复一次性日程清除的bug✓ - ### **联系** diff --git a/README_EN.md b/README_EN.md index 5e33e37..85457ae 100644 --- a/README_EN.md +++ b/README_EN.md @@ -11,7 +11,7 @@ -**Belated December announcement, the 4th edition of Fay Digital Human AI Agent Version (complete code for smart agriculture box can be requested via our public channel) is officially uploaded!** +**Belated December announcement, the 5th edition of Fay Digital Human AI Agent Version (complete code for smart agriculture box can be requested via our public channel) is officially uploaded!** If you need an online and offline salesperson, please go to [`Complete Retail Version`](https://github.com/TheRamU/Fay/tree/fay-sales-edition) @@ -52,9 +52,29 @@ python main.py ### **Launching the Digital Human (Optional)** -+ Launch the digital human[xszyou/fay-ue5: 可对接fay数字人的ue5工程 (github.com)](https://github.com/xszyou/fay-ue5) +Repository URL:https://github.com/xszyou/fay-ue5 + + +### **Launch of Android Connector (Optional)** +Repository URL: https://github.com/xszyou/fay-android + + +### **Changelog** +2023.12.25: + +Implemented the automatic switching logic between agent ReAct and LLM chain ✓ +Distinguished task messages in the chat window ✓ + +Fixed the bug in deleting schedules ✓ + +Optimized remote audio logic ✓ + +Introduced loading effects for pending processes ✓ + +Optimized prompts to resolve recursive calling issues in schedule tasks ✓ + +Fixed the bug in clearing one-time schedules ✓ - ### **Contact** diff --git a/agent/agent_service.py b/agent/agent_service.py index 3cdf053..d006538 100644 --- a/agent/agent_service.py +++ b/agent/agent_service.py @@ -8,6 +8,7 @@ scheduled_tasks = {} agent_running = False agent = FayAgentCore() + # 数据库初始化 def init_db(): conn = sqlite3.connect('timer.db') @@ -51,8 +52,7 @@ def parse_repeat_rule(rule, task_time): # 执行任务 def execute_task(task_time, id, content): - content = "执行任务->现在" + content - agent.run(content) + agent.run("执行任务->立刻" + content) del scheduled_tasks[id] # 如果不重复,执行后删除记录 conn = sqlite3.connect('timer.db') @@ -90,7 +90,6 @@ def agent_start(): agent_running = True init_db() - # insert_test_data() check_and_execute_thread = threading.Thread(target=check_and_execute) check_and_execute_thread.start() diff --git a/agent/fay_agent.py b/agent/fay_agent.py index 1585088..71b3f2d 100644 --- a/agent/fay_agent.py +++ b/agent/fay_agent.py @@ -6,6 +6,7 @@ from langchain.docstore import InMemoryDocstore from langchain.vectorstores import FAISS from langchain.agents import AgentExecutor, Tool, ZeroShotAgent, initialize_agent, agent_types from langchain.chains import LLMChain +from langchain.prompts import PromptTemplate from agent.tools.MyTimer import MyTimer from agent.tools.QueryTime import QueryTime @@ -19,6 +20,8 @@ from agent.tools.QueryTimerDB import QueryTimerDB from agent.tools.DeleteTimer import DeleteTimer from agent.tools.GetSwitchLog import GetSwitchLog from agent.tools.getOnRunLinkage import getOnRunLinkage +from agent.tools.SetChatStatus import SetChatStatus + import utils.config_util as utils from core.content_db import Content_Db @@ -26,6 +29,7 @@ from core import wsa_server import os + class FayAgentCore(): def __init__(self): @@ -64,6 +68,7 @@ class FayAgentCore(): delete_timer_tool = DeleteTimer() get_switch_log = GetSwitchLog() get_on_run_linkage = getOnRunLinkage() + set_chat_status_tool = SetChatStatus() tools = [ Tool( @@ -126,22 +131,57 @@ class FayAgentCore(): func=get_on_run_linkage.run, description=get_on_run_linkage.description ), + Tool( + name=set_chat_status_tool.name, + func=set_chat_status_tool.run, + description=set_chat_status_tool.description + ), ] - + #agent用于执行任务 self.agent = initialize_agent(agent_types=agent_types.AgentType.CHAT_CONVERSATIONAL_REACT_DESCRIPTION, tools=tools, llm=llm, verbose=True, max_history=5, memory=memory, handle_parsing_errors=True) + + #创建llm chain用于聊天 + tools_prompt = "[" + tool_names = [tool.name for tool in tools if tool.name != set_chat_status_tool.name and tool.name != say_tool.name] + tools_prompt += "、".join(tool_names) + "]" + template = """你是一个智慧农业实验箱里的ai,你的责任是陪伴主人生活、工作,以及协助主人打理好农业种植箱里的农作物.现在主人正在和你聊天,若你在聊天过程中感觉到主人想使用以下工具,请按“agent:'主人刚刚的说话'”这样的格式回复,否则请直接回复我文字内容。工具如下: + """ + tools_prompt +""" + {chat_history} + Human: {human_input} + AI:""" + prompt = PromptTemplate( + input_variables=["chat_history", "human_input"], template=template + ) + self.llm_chain = LLMChain( + llm=llm, + prompt=prompt, + verbose=True, + memory=memory + ) + + self.is_chat = False#聊天状态 + def run(self, input_text): #消息保存 contentdb = Content_Db() contentdb.add_content('member', 'agent', input_text.replace('主人语音说了:', '').replace('主人文字说了:', '')) wsa_server.get_web_instance().add_cmd({"panelReply": {"type":"member","content":input_text.replace('主人语音说了:', '').replace('主人文字说了:', '')}}) - result = None + result = "" try: - result = self.agent.run(input_text) + #判断执行聊天模式还是agent模式,双模式在运行过程中会主动切换 + if self.is_chat: + result = self.llm_chain.predict(human_input=input_text.replace('主人语音说了:', '').replace('主人文字说了:', '')) + if "agent:" in result.lower() or not self.is_chat: + print(result) + print(self.is_chat) + self.is_chat = False + input_text = result if result.lower().replace("agent:", "") else input_text + result = self.agent.run(input_text) except Exception as e: print(e) result = "执行完毕" if result is None or result == "N/A" else result diff --git a/agent/tools/GetSwitchLog.py b/agent/tools/GetSwitchLog.py index 54ee251..8a3b318 100644 --- a/agent/tools/GetSwitchLog.py +++ b/agent/tools/GetSwitchLog.py @@ -2,11 +2,11 @@ import os from typing import Any from langchain.tools import BaseTool -import agent.tools.IotmService as IotmService +import tools.IotmService as IotmService class GetSwitchLog(BaseTool): name = "GetSwitchLog" - description = "此工具用于查询农业箱的设备开关操作历史记录,设备序号:小风扇(1)、电热风扇(2)、制冷风扇(3)、肥料开关(4)、补光设备(5)、植物生长灯(6)、二氧化碳(7)" + description = "此工具用于查询农业箱的设备开关当天的操作历史记录" def __init__(self): super().__init__() @@ -16,10 +16,31 @@ class GetSwitchLog(BaseTool): pass - def _run(self, para: str) -> str: - infos = IotmService.get_switch_log() - - return infos + def _run(self, para: str): + logs = IotmService.get_switch_log() + device_logs = {} + + switch_mapping = { + 1: '小风扇', + 2: '电热风扇', + 3: '制冷风扇', + 4: '水开关', + 5: '肥料开关', + 6: '植物生长灯', + 7: '二氧化碳' + } + + for val in logs: + switch_name = switch_mapping[val['number']] + status = 'on' if val['status'] == 1 else 'off' + info = val['timetText'] + + if switch_name not in device_logs: + device_logs[switch_name] = {'on': [], 'off': []} + + device_logs[switch_name][status].append(info) + + return device_logs if __name__ == "__main__": tool = GetSwitchLog() diff --git a/agent/tools/Knowledge.py b/agent/tools/Knowledge.py index 4308cf5..a59066c 100644 --- a/agent/tools/Knowledge.py +++ b/agent/tools/Knowledge.py @@ -7,7 +7,7 @@ from langchain.tools import BaseTool class Knowledge(BaseTool): name = "Knowledge" - description = """此工具用于查询箱内植物的专业知识,可以作为补充参考,使用时请传入相关问题作为参数,例如:“草梅最适合的生长温度”""" + description = """此工具用于查询箱内植物的专业知识,使用时请传入相关问题作为参数,例如:“草梅最适合的生长温度”""" def __init__(self): super().__init__() diff --git a/agent/tools/Say.py b/agent/tools/Say.py index 43f1aa9..ae59bdf 100644 --- a/agent/tools/Say.py +++ b/agent/tools/Say.py @@ -5,11 +5,12 @@ import requests from langchain.tools import BaseTool import fay_booter from core.interact import Interact +from agent import agent_service class Say(BaseTool): - name = "Say" - description = """此工具用于沟通及提醒,使用时请传入说话内容作为参数,例如:“该下班了,请注意休息”""" + name = "语音输出工具" + description = """此工具用于语音输出,使用时请传入说话内容作为参数,例如:“该下班了,请注意休息”""" def __init__(self): super().__init__() @@ -20,8 +21,9 @@ class Say(BaseTool): def _run(self, para: str) -> str: - interact = Interact("agent", 1, {'user': '', 'msg': para}) + interact = Interact("audio", 1, {'user': '', 'msg': para}) fay_booter.feiFei.on_interact(interact) + agent_service.agent.is_chat = True return "语音输出了:" + para diff --git a/agent/tools/SetChatStatus.py b/agent/tools/SetChatStatus.py new file mode 100644 index 0000000..7f23b59 --- /dev/null +++ b/agent/tools/SetChatStatus.py @@ -0,0 +1,29 @@ +import os +from typing import Any + +from langchain.tools import BaseTool +from agent import agent_service + + +class SetChatStatus(BaseTool): + name = "SetChatStatus" + description = """此工具用于设置聊天状态,当识别到主人想进行交流聊天时使用此工具""" + + def __init__(self): + super().__init__() + + async def _arun(self, *args: Any, **kwargs: Any) -> Any: + # 用例中没有用到 arun 不予具体实现 + pass + + + def _run(self, para: str) -> str: + agent_service.agent.is_chat = True + return "设置聊天状态成功" + + + +if __name__ == "__main__": + tool = SetChatStatus() + info = tool.run("该下班了,请注意休息") + print(info) diff --git a/agent/tools/getOnRunLinkage.py b/agent/tools/getOnRunLinkage.py index 5a2d9ab..1a8a124 100644 --- a/agent/tools/getOnRunLinkage.py +++ b/agent/tools/getOnRunLinkage.py @@ -1,12 +1,13 @@ import os from typing import Any +from itertools import groupby from langchain.tools import BaseTool -import agent.tools.IotmService as IotmService +import tools.IotmService as IotmService class getOnRunLinkage(BaseTool): name = "getOnRunLinkage" - description = "此工具用于查询农业箱当前在运行的联动,设备序号:小风扇(1)、电热风扇(2)、制冷风扇(3)、肥料开关(4)、补光设备(5)、植物生长灯(6)、二氧化碳(7)" + description = "此工具用于查询农业箱当前在运行的联动" def __init__(self): super().__init__() @@ -17,9 +18,45 @@ class getOnRunLinkage(BaseTool): def _run(self, para: str) -> str: - infos = IotmService.get_on_run_linkage() - return infos + logs = IotmService.get_on_run_linkage() + desc_list = { + 'co2_S36': '二氧化碳', + 'light_bh1': '箱内的光照强度', + 'air_S37': '污染气体', + 'nh3_S37': '氨气', + 'temperature_MP14': '箱外温度', + 'temperature_MP21': '箱内温度', + 'humidity_MP14': '箱外湿度', + 'humidity_S34': '箱内土壤的湿度', + } + infos = {} + + logs.sort(key=lambda x: (x['label'], x['port'])) + + for (sensor_type, port), group in groupby(logs, key=lambda x: (x['label'], x['port'])): + + group_infos = [] + for val in group: + + onoff = '开启设备开关' if val['onoff'] == 1 else '关闭设备开关' + + info = { + 'max': val['maxVal'], + 'min': val['minVal'], + 'onoff': onoff, + } + if float(val['keeptime']) > 0: + info["持续时间(若需执行开启设备,持续时间过后执行关闭),单位为分钟"] = val['keeptime'] + if float(val['delaytime']) > 0: + info["执行后下次检查相距时间,单位为分钟"] = val['delaytime'] + + group_infos.append(info) + + key_str = f"{sensor_type}_{port}" + infos[desc_list.get(key_str, 'Unknown')] = group_infos + + return infos if __name__ == "__main__": tool = getOnRunLinkage() info = tool.run("") diff --git a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/.gitignore b/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/.gitignore deleted file mode 100644 index aa724b7..0000000 --- a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/.gitignore +++ /dev/null @@ -1,15 +0,0 @@ -*.iml -.gradle -/local.properties -/.idea/caches -/.idea/libraries -/.idea/modules.xml -/.idea/workspace.xml -/.idea/navEditor.xml -/.idea/assetWizardSettings.xml -.DS_Store -/build -/captures -.externalNativeBuild -.cxx -local.properties diff --git a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/.idea/.gitignore b/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/.idea/.gitignore deleted file mode 100644 index 26d3352..0000000 --- a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/.idea/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml diff --git a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/.idea/compiler.xml b/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/.idea/compiler.xml deleted file mode 100644 index fb7f4a8..0000000 --- a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/.idea/compiler.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/.idea/gradle.xml b/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/.idea/gradle.xml deleted file mode 100644 index a2d7c21..0000000 --- a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/.idea/gradle.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/.idea/misc.xml b/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/.idea/misc.xml deleted file mode 100644 index 1166903..0000000 --- a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/.idea/misc.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/.gitignore b/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/.gitignore deleted file mode 100644 index 42afabf..0000000 --- a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build \ No newline at end of file diff --git a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/build.gradle b/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/build.gradle deleted file mode 100644 index 593724e..0000000 --- a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/build.gradle +++ /dev/null @@ -1,38 +0,0 @@ -plugins { - id 'com.android.application' -} - -android { - compileSdk 32 - - defaultConfig { - applicationId "com.yaheen.fayconnectordemo" - minSdk 29 - targetSdk 32 - versionCode 1 - versionName "1.0" - - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - } - } - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } -} - -dependencies { - - implementation 'androidx.appcompat:appcompat:1.3.0' - implementation 'com.google.android.material:material:1.4.0' - implementation 'androidx.constraintlayout:constraintlayout:2.0.4' - testImplementation 'junit:junit:4.13.2' - androidTestImplementation 'androidx.test.ext:junit:1.1.3' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' -} \ No newline at end of file diff --git a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/proguard-rules.pro b/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/proguard-rules.pro deleted file mode 100644 index 481bb43..0000000 --- a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/androidTest/java/com/yaheen/fayconnectordemo/ExampleInstrumentedTest.java b/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/androidTest/java/com/yaheen/fayconnectordemo/ExampleInstrumentedTest.java deleted file mode 100644 index 3035a79..0000000 --- a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/androidTest/java/com/yaheen/fayconnectordemo/ExampleInstrumentedTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.yaheen.fayconnectordemo; - -import android.content.Context; - -import androidx.test.platform.app.InstrumentationRegistry; -import androidx.test.ext.junit.runners.AndroidJUnit4; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import static org.junit.Assert.*; - -/** - * Instrumented test, which will execute on an Android device. - * - * @see Testing documentation - */ -@RunWith(AndroidJUnit4.class) -public class ExampleInstrumentedTest { - @Test - public void useAppContext() { - // Context of the app under test. - Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); - assertEquals("com.yaheen.fayconnectordemo", appContext.getPackageName()); - } -} \ No newline at end of file diff --git a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/AndroidManifest.xml b/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/AndroidManifest.xml deleted file mode 100644 index 4526265..0000000 --- a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/AndroidManifest.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/java/com/yaheen/fayconnectordemo/FayConnectorService.java b/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/java/com/yaheen/fayconnectordemo/FayConnectorService.java deleted file mode 100644 index b62dd51..0000000 --- a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/java/com/yaheen/fayconnectordemo/FayConnectorService.java +++ /dev/null @@ -1,343 +0,0 @@ -package com.yaheen.fayconnectordemo; - -import android.Manifest; -import android.app.Notification; -import android.app.NotificationChannel; -import android.app.NotificationManager; -import android.app.PendingIntent; -import android.app.Service; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.pm.PackageManager; -import android.graphics.BitmapFactory; -import android.media.AudioFormat; -import android.media.AudioManager; -import android.media.AudioRecord; -import android.media.MediaPlayer; -import android.media.MediaRecorder; -import android.os.Build; -import android.os.IBinder; -import android.util.Log; - -import androidx.annotation.Nullable; -import androidx.core.app.ActivityCompat; -import androidx.core.app.NotificationCompat; -import androidx.core.app.NotificationManagerCompat; -import androidx.core.content.ContextCompat; - -import com.google.android.material.snackbar.Snackbar; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.Socket; -import java.util.Arrays; -import java.util.Date; - -public class FayConnectorService extends Service { - private AudioRecord record; - private int recordBufsize = 0; - private Socket socket = null; - private InputStream in = null; - private OutputStream out = null; - public static boolean running = false; - private File cacheDir = null; - private String channelId = null; - private PendingIntent pendingIntent = null; - private NotificationManagerCompat notificationManager = null; - private long totalrece = 0; - private long totalsend = 0; - private AudioManager mAudioManager = null; - private boolean isPlay = false; - - - //创建通知 - private String createNotificationChannel(String channelID, String channelNAME, int level) { - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { - NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); - NotificationChannel channel = new NotificationChannel(channelID, channelNAME, level); - manager.createNotificationChannel(channel); - return channelID; - } else { - return null; - } - } - - - @Nullable - @Override - public IBinder onBind(Intent intent) { - return null; - } - - @Override - public int onStartCommand(Intent intent, int flags, int startId) { - super.onStartCommand(intent, START_FLAG_REDELIVERY, startId); - return Service.START_STICKY; - - } - - @Override - public void onCreate() { - super.onCreate(); - Log.d("fay", "服务启动"); - - - - //开启蓝牙传输 - mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); - mAudioManager.startBluetoothSco(); - IntentFilter intentFilter = new IntentFilter(); - intentFilter.addAction(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED); - BroadcastReceiver receiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - int state = intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, -1); - if (AudioManager.SCO_AUDIO_STATE_CONNECTED == state) { - Log.d("fay", "蓝牙sco连接成功"); - - - } - } - }; - this.registerReceiver(receiver, intentFilter); - - running = true; - this.cacheDir = getApplicationContext().getFilesDir();//getCacheDir(); - Thread sendThread = new Thread(new Runnable() { - @Override - public void run() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - if (ContextCompat.checkSelfPermission(FayConnectorService.this, Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED) { - if (record == null) { - recordBufsize = AudioRecord - .getMinBufferSize(16000, - AudioFormat.CHANNEL_IN_MONO, - AudioFormat.ENCODING_PCM_16BIT); - record = new AudioRecord(MediaRecorder.AudioSource.MIC, - 16000, - AudioFormat.CHANNEL_IN_MONO, - AudioFormat.ENCODING_PCM_16BIT, - recordBufsize); - - } - try { - socket = new Socket("192.168.1.101", 10001); - in = socket.getInputStream(); - out = socket.getOutputStream(); - Log.d("fay", "fay控制器连接成功"); - } catch (IOException e) { - Log.d("fay", "socket连接失败"); - running = false; - return; - } - byte[] data = new byte[1024]; - record.startRecording(); - Log.d("fay", "麦克风启动成功"); - try { - Log.d("fay", "开始传输音频"); - while (running) { - if (isPlay){ - continue; - } - int size = record.read(data, 0, 1024); - if (size > 0) { - out.write(data); - totalsend += data.length / 1024; - }else{//录音异常,等待60秒重新录取 - try { - Thread.sleep(60000); - record.stop(); - record.startRecording(); - }catch (Exception e){ - } - } - } - } catch (Exception e) { //通过异常关退出循环 - Log.d("fay", "服务端关闭:" + e.toString()); - } finally { - running = false; - record.stop(); - record = null; - ((AudioManager) getSystemService(Context.AUDIO_SERVICE)).stopBluetoothSco(); - try { - socket.close(); - } catch (Exception e) { - } - socket = null; - Log.d("fay", "send线程结束"); - - } - - } - } - - } - }); - - - Thread receThread = new Thread(new Runnable() { - @Override - public void run() { - try { - while (running) { - while (socket != null && !socket.isClosed()) { - byte[] data = new byte[9]; - byte[] wavhead = new byte[]{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};//文件传输开始标记 - in.read(data); - if (Arrays.equals(wavhead, data)) { - Log.d("fay", "开始接收音频文件"); - String filedata = ""; - data = new byte[1024]; - int len = 0; - while ((len = in.read(data)) != -1) { - byte[] temp = new byte[len]; - System.arraycopy(data, 0, temp, 0, len); - filedata += MainActivity.bytesToHexString(temp); - int index = filedata.indexOf("080706050403020100"); - if (filedata.length() > 9 && index > 0) {//wav文件结束标记 - filedata = filedata.substring(0, index).replaceAll("F0F1F2F3F4F5F6F7F8", ""); - File wavFile = new File(cacheDir, String.format("sample-%s.wav", new Date().getTime() + "")); - wavFile.createNewFile(); - FileOutputStream fos = new FileOutputStream(wavFile); - fos.write(MainActivity.decodeHexBytes(filedata.toCharArray())); - fos.close(); - totalrece += filedata.length() / 2 / 1024; - Log.d("fay", "wav文件接收完成:" + wavFile.getAbsolutePath() + "," + filedata.length() / 2); - try { - MediaPlayer player = new MediaPlayer(); - player.setDataSource(wavFile.getAbsolutePath()); - player.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { - @Override - public void onPrepared(MediaPlayer mp) { - Log.d("fay", "开始播放"); - if (mAudioManager.isBluetoothScoOn()){ - mAudioManager.stopBluetoothSco(); - mAudioManager.setBluetoothScoOn(false); - mAudioManager.setMode(mAudioManager.MODE_NORMAL); - } - try { - Thread.sleep(500); - }catch (Exception e){ - - } - isPlay = true; - mp.start(); - } - }); - player.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { - - @Override - public void onCompletion(MediaPlayer mp) { - Log.d("fay", "播放完成"); - isPlay = false; - mp.release(); - mAudioManager.startBluetoothSco(); - mAudioManager.setMode(mAudioManager.MODE_IN_CALL); - mAudioManager.setBluetoothScoOn(true); - - - } - - }); - player.setVolume(1,1); - player.setLooping(false); - player.prepareAsync(); - - } catch (IOException e) { - Log.e("fay", e.toString()); - } - break; - } - - } - try { - Thread.sleep(1000); - } catch (Exception e) { - - } - - } - } - try { - Thread.sleep(1000); - } catch (Exception e) { - - } - - } - } catch (Exception e) {//通过异常判断socket已经关闭,退出循环 - - } finally { - Log.d("fay", "rece线程结束"); - - } - } - }); - sendThread.start(); - receThread.start(); - - //通知栏 - new Thread(new Runnable() { - @Override - public void run() { - try{ - while (running) { - Thread.sleep(3000); - if (totalsend + totalrece > 2048){ - inotify("fay connector demo", "已经连接fay控制器,累计接收/发送:" + String.format("%.2f", (double)totalrece / 1024) + "/" + String.format("%.2f", (double)totalsend / 1024) + "MB"); - } else { - inotify("fay connector demo", "已经连接fay控制器,累计接收/发送:" + totalrece + "/" + totalsend + "KB"); - } - } - inotify("fay connector demo", "已经断开fay控制器"); - }catch (Exception e){ - Log.e("fay", e.toString()); - }finally { - FayConnectorService.this.stopSelf(); - } - } - }).start(); - - - } - - private void inotify(String title, String content){ - Intent intent = new Intent(this, MainActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); - if (pendingIntent == null){ - pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE); - } - if (channelId == null){ - channelId = createNotificationChannel("my_channel_ID", "my_channel_NAME", NotificationManager.IMPORTANCE_HIGH); - } - if (notificationManager == null){ - notificationManager = NotificationManagerCompat.from(this); - } - NotificationCompat.Builder notification2 = new NotificationCompat.Builder(FayConnectorService.this, channelId) - .setContentTitle(title) - .setContentText(content) - .setContentIntent(pendingIntent) - .setSmallIcon(R.drawable.icon) - .setPriority(NotificationCompat.PRIORITY_HIGH) - .setAutoCancel(true); - //notificationManager.notify(100, notification2.build()); - startForeground(100, notification2.build()); - } - - - - - @Override - public void onDestroy() { - Log.d("fay", "服务关闭"); - super.onDestroy(); - mAudioManager.stopBluetoothSco(); - running = false; - stopForeground(true); - } -} diff --git a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/java/com/yaheen/fayconnectordemo/MainActivity.java b/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/java/com/yaheen/fayconnectordemo/MainActivity.java deleted file mode 100644 index 6164ca8..0000000 --- a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/java/com/yaheen/fayconnectordemo/MainActivity.java +++ /dev/null @@ -1,137 +0,0 @@ -package com.yaheen.fayconnectordemo; - -import androidx.appcompat.app.AppCompatActivity; -import androidx.core.app.ActivityCompat; -import androidx.core.content.ContextCompat; - -import android.Manifest; -import android.app.ActivityManager; -import android.app.PendingIntent; -import android.content.BroadcastReceiver; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.pm.PackageManager; -import android.media.AudioFormat; -import android.media.AudioManager; -import android.media.AudioRecord; -import android.media.MediaPlayer; -import android.media.MediaRecorder; -import android.os.Build; -import android.os.Bundle; -import android.util.Log; -import android.view.View; -import android.widget.TextView; - -import com.google.android.material.snackbar.Snackbar; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.Socket; -import java.net.SocketException; -import java.util.Arrays; -import java.util.Date; -import java.util.List; - -public class MainActivity extends AppCompatActivity { - private TextView tv = null; - private boolean running = false; - private Intent serviceIntent = null; - - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_main); - tv = this.findViewById(R.id.tv); - serviceIntent = new Intent(this, FayConnectorService.class); - - //按钮点击 - tv.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - Log.d("fay","onclick"); - running = FayConnectorService.running;//isServiceRunning();//同步service的运行状态,不好使! - if (!running){//运行 - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {//开启 - if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { - if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this, Manifest.permission.RECORD_AUDIO)) { - Log.d("fay", "用户彻底拒绝了权限"); - return; - } else { - // 用户未彻底拒绝授予权限 - ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.RECORD_AUDIO}, 1); - } - } - - if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED) { - Log.d("fay","权限ok"); - - Snackbar.make(view, "正在连接fay控制器", Snackbar.LENGTH_SHORT) - .setAction("Action", null).show(); - startForegroundService(serviceIntent); - running = true; - } - } - } else{//关闭 - stopService(serviceIntent); - Snackbar.make(view, "已经断开fay控制器", Snackbar.LENGTH_SHORT) - .setAction("Action", null).show(); - running = false; - } - - } - }); - } - - - public static String bytesToHexString(byte[] data){ - String result=""; - for (int i = 0; i < data.length; i++) { - result+=Integer.toHexString((data[i] & 0xFF) | 0x100).toUpperCase().substring(1, 3); - } - return result; - } - - - public static byte[] decodeHexBytes(char[] data) { - int len = data.length; - if ((len & 0x01) != 0) { - throw new RuntimeException("未知的字符"); - } - byte[] out = new byte[len >> 1]; - for (int i = 0, j = 0; j < len; i++) { - int f = toDigit(data[j], j) << 4; - j++; - f = f | toDigit(data[j], j); - j++; - out[i] = (byte) (f & 0xFF); - } - return out; - } - - protected static int toDigit(char ch, int index) { - int digit = Character.digit(ch, 16); - if (digit == -1) { - throw new RuntimeException("非法16进制字符 " + ch - + " 在索引 " + index); - } - return digit; - } - - private boolean isServiceRunning() { - ActivityManager activityManager = (ActivityManager) this.getApplicationContext() - .getSystemService(Context.ACTIVITY_SERVICE); - ComponentName serviceName = new ComponentName("com.yaheen.fayconnectordemo", ".FayConnectorService"); - PendingIntent intent = activityManager.getRunningServiceControlPanel(serviceName); - if (intent == null){ - return false; - } - return true; - - } -} \ No newline at end of file diff --git a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/drawable-v24/ic_launcher_foreground.xml deleted file mode 100644 index 2b068d1..0000000 --- a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/drawable-v24/ic_launcher_foreground.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/drawable-v24/icon.png b/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/drawable-v24/icon.png deleted file mode 100644 index c66c4d3..0000000 Binary files a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/drawable-v24/icon.png and /dev/null differ diff --git a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/drawable/ic_launcher_background.xml b/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/drawable/ic_launcher_background.xml deleted file mode 100644 index 07d5da9..0000000 --- a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/drawable/ic_launcher_background.xml +++ /dev/null @@ -1,170 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/layout/activity_main.xml b/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/layout/activity_main.xml deleted file mode 100644 index caf5670..0000000 --- a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/layout/activity_main.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml deleted file mode 100644 index eca70cf..0000000 --- a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml deleted file mode 100644 index eca70cf..0000000 --- a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/mipmap-hdpi/ic_launcher.webp deleted file mode 100644 index c209e78..0000000 Binary files a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/mipmap-hdpi/ic_launcher.webp and /dev/null differ diff --git a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp deleted file mode 100644 index b2dfe3d..0000000 Binary files a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp and /dev/null differ diff --git a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/mipmap-mdpi/ic_launcher.webp deleted file mode 100644 index 4f0f1d6..0000000 Binary files a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/mipmap-mdpi/ic_launcher.webp and /dev/null differ diff --git a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp deleted file mode 100644 index 62b611d..0000000 Binary files a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp and /dev/null differ diff --git a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/mipmap-xhdpi/ic_launcher.webp deleted file mode 100644 index 948a307..0000000 Binary files a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/mipmap-xhdpi/ic_launcher.webp and /dev/null differ diff --git a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp deleted file mode 100644 index 1b9a695..0000000 Binary files a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp and /dev/null differ diff --git a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp deleted file mode 100644 index 28d4b77..0000000 Binary files a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp and /dev/null differ diff --git a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp deleted file mode 100644 index 9287f50..0000000 Binary files a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp and /dev/null differ diff --git a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp deleted file mode 100644 index aa7d642..0000000 Binary files a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp and /dev/null differ diff --git a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp deleted file mode 100644 index 9126ae3..0000000 Binary files a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp and /dev/null differ diff --git a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/values-night/themes.xml b/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/values-night/themes.xml deleted file mode 100644 index 3fe0a9a..0000000 --- a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/values-night/themes.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - \ No newline at end of file diff --git a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/values/colors.xml b/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/values/colors.xml deleted file mode 100644 index f8c6127..0000000 --- a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/values/colors.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - #FFBB86FC - #FF6200EE - #FF3700B3 - #FF03DAC5 - #FF018786 - #FF000000 - #FFFFFFFF - \ No newline at end of file diff --git a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/values/strings.xml b/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/values/strings.xml deleted file mode 100644 index 6fdadf3..0000000 --- a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/values/strings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - fayConnectorDemo - \ No newline at end of file diff --git a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/values/themes.xml b/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/values/themes.xml deleted file mode 100644 index 215bbd4..0000000 --- a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/values/themes.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - \ No newline at end of file diff --git a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/xml/backup_rules.xml b/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/xml/backup_rules.xml deleted file mode 100644 index fa0f996..0000000 --- a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/xml/backup_rules.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - \ No newline at end of file diff --git a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/xml/data_extraction_rules.xml b/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/xml/data_extraction_rules.xml deleted file mode 100644 index 9ee9997..0000000 --- a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/main/res/xml/data_extraction_rules.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/test/java/com/yaheen/fayconnectordemo/ExampleUnitTest.java b/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/test/java/com/yaheen/fayconnectordemo/ExampleUnitTest.java deleted file mode 100644 index 1eee6ff..0000000 --- a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/app/src/test/java/com/yaheen/fayconnectordemo/ExampleUnitTest.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.yaheen.fayconnectordemo; - -import org.junit.Test; - -import static org.junit.Assert.*; - -/** - * Example local unit test, which will execute on the development machine (host). - * - * @see Testing documentation - */ -public class ExampleUnitTest { - @Test - public void addition_isCorrect() { - assertEquals(4, 2 + 2); - } -} \ No newline at end of file diff --git a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/build.gradle b/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/build.gradle deleted file mode 100644 index a550ce3..0000000 --- a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/build.gradle +++ /dev/null @@ -1,9 +0,0 @@ -// Top-level build file where you can add configuration options common to all sub-projects/modules. -plugins { - id 'com.android.application' version '7.2.1' apply false - id 'com.android.library' version '7.2.1' apply false -} - -task clean(type: Delete) { - delete rootProject.buildDir -} \ No newline at end of file diff --git a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/gradle.properties b/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/gradle.properties deleted file mode 100644 index dab7c28..0000000 --- a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/gradle.properties +++ /dev/null @@ -1,21 +0,0 @@ -# Project-wide Gradle settings. -# IDE (e.g. Android Studio) users: -# Gradle settings configured through the IDE *will override* -# any settings specified in this file. -# For more details on how to configure your build environment visit -# http://www.gradle.org/docs/current/userguide/build_environment.html -# Specifies the JVM arguments used for the daemon process. -# The setting is particularly useful for tweaking memory settings. -org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 -# When configured, Gradle will run in incubating parallel mode. -# This option should only be used with decoupled projects. More details, visit -# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects -# org.gradle.parallel=true -# AndroidX package structure to make it clearer which packages are bundled with the -# Android operating system, and which are packaged with your app"s APK -# https://developer.android.com/topic/libraries/support-library/androidx-rn -android.useAndroidX=true -# Enables namespacing of each library's R class so that its R class includes only the -# resources declared in the library itself and none from the library's dependencies, -# thereby reducing the size of the R class for that library -android.nonTransitiveRClass=true \ No newline at end of file diff --git a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/gradle/wrapper/gradle-wrapper.jar b/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index e708b1c..0000000 Binary files a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/gradle/wrapper/gradle-wrapper.jar and /dev/null differ diff --git a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/gradle/wrapper/gradle-wrapper.properties b/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 3d3b352..0000000 --- a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,6 +0,0 @@ -#Fri Jan 20 09:27:45 CST 2023 -distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip -distributionPath=wrapper/dists -zipStorePath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME diff --git a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/gradlew b/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/gradlew deleted file mode 100644 index 4f906e0..0000000 --- a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/gradlew +++ /dev/null @@ -1,185 +0,0 @@ -#!/usr/bin/env sh - -# -# Copyright 2015 the original author or authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -############################################################################## -## -## Gradle start up script for UN*X -## -############################################################################## - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" - -warn () { - echo "$*" -} - -die () { - echo - echo "$*" - echo - exit 1 -} - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=`expr $i + 1` - done - case $i in - 0) set -- ;; - 1) set -- "$args0" ;; - 2) set -- "$args0" "$args1" ;; - 3) set -- "$args0" "$args1" "$args2" ;; - 4) set -- "$args0" "$args1" "$args2" "$args3" ;; - 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac -fi - -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=`save "$@"` - -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" - -exec "$JAVACMD" "$@" diff --git a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/gradlew.bat b/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/gradlew.bat deleted file mode 100644 index 107acd3..0000000 --- a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/gradlew.bat +++ /dev/null @@ -1,89 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/settings.gradle b/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/settings.gradle deleted file mode 100644 index 49e31ef..0000000 --- a/android_connector_demo/fayConnectorDemo-蓝牙service后台运行版/settings.gradle +++ /dev/null @@ -1,16 +0,0 @@ -pluginManagement { - repositories { - gradlePluginPortal() - google() - mavenCentral() - } -} -dependencyResolutionManagement { - repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) - repositories { - google() - mavenCentral() - } -} -rootProject.name = "fayConnectorDemo" -include ':app' diff --git a/core/fay_core.py b/core/fay_core.py index 4be5952..eb75723 100644 --- a/core/fay_core.py +++ b/core/fay_core.py @@ -33,7 +33,6 @@ if platform.system() == "Windows": from test_olipsync import LipSyncGenerator - #文本消息处理(20231211增加:agent操作) def send_for_answer(msg): #记录运行时间 @@ -53,6 +52,11 @@ def send_for_answer(msg): #agent 处理 text = agent_service.agent.run(msg) + #聊天模式语音输入语音输出 + if text and "语音" in msg and agent_service.agent.is_chat: + interact = Interact("audio", 1, {'user': '', 'msg': text}) + fay_booter.feiFei.on_interact(interact) + #推送数字人 if not cfg.config["interact"]["playSound"]: content = {'Topic': 'Unreal', 'Data': {'Key': 'log', 'Value': text}} @@ -240,7 +244,7 @@ class FeiFei: # if audio_length <= config_util.config["interact"]["maxInteractTime"] or say_type == "script": if config_util.config["interact"]["playSound"]: # 展板播放 self.__play_sound(file_url) - else:#发送音频给ue和socket + else:#发送音频给ue #推送ue content = {'Topic': 'Unreal', 'Data': {'Key': 'audio', 'Value': os.path.abspath(file_url), 'Text': self.a_msg, 'Time': audio_length, 'Type': say_type}} #计算lips @@ -254,22 +258,22 @@ class FeiFei: util.log(1, "唇型数字生成失败,无法使用新版ue5工程") wsa_server.get_instance().add_cmd(content) - #推送远程音频 - if self.deviceConnect is not None: - try: - self.deviceConnect.send(b'\x00\x01\x02\x03\x04\x05\x06\x07\x08') # 发送音频开始标志,同时也检查设备是否在线 - wavfile = open(os.path.abspath(file_url),'rb') + #推送远程音频 + if self.deviceConnect is not None: + try: + self.deviceConnect.send(b'\x00\x01\x02\x03\x04\x05\x06\x07\x08') # 发送音频开始标志,同时也检查设备是否在线 + wavfile = open(os.path.abspath(file_url),'rb') + data = wavfile.read(1024) + total = 0 + while data: + total += len(data) + self.deviceConnect.send(data) data = wavfile.read(1024) - total = 0 - while data: - total += len(data) - self.deviceConnect.send(data) - data = wavfile.read(1024) - time.sleep(0.001) - self.deviceConnect.send(b'\x08\x07\x06\x05\x04\x03\x02\x01\x00')# 发送音频结束标志 - util.log(1, "远程音频发送完成:{}".format(total)) - except socket.error as serr: - util.log(1,"远程音频输入输出设备已经断开:{}".format(serr)) + time.sleep(0.001) + self.deviceConnect.send(b'\x08\x07\x06\x05\x04\x03\x02\x01\x00')# 发送音频结束标志 + util.log(1, "远程音频发送完成:{}".format(total)) + except socket.error as serr: + util.log(1,"远程音频输入输出设备已经断开:{}".format(serr)) time.sleep(audio_length + 0.5) wsa_server.get_web_instance().add_cmd({"panelMsg": ""}) diff --git a/core/recorder.py b/core/recorder.py index c28ef7e..b83b10e 100644 --- a/core/recorder.py +++ b/core/recorder.py @@ -155,8 +155,7 @@ class Recorder: if not data: continue - - if cfg.config['source']['record']['enabled']: + if cfg.config['source']['record']['enabled'] and not self.is_remote(): if len(cfg.config['source']['record'])<3: channels = 1 else: @@ -167,7 +166,6 @@ class Recorder: mono = data[:, 0] # taking the first channel data = mono.tobytes() - level = audioop.rms(data, 2) if len(self.__history_data) >= 5: self.__history_data.pop(0) @@ -229,3 +227,8 @@ class Recorder: @abstractmethod def get_stream(self): pass + + #TODO Edit by xszyou on 20231225:子类实现返回是否远程音频 + @abstractmethod + def is_remote(self): + pass diff --git a/fay_booter.py b/fay_booter.py index e236e0d..0fa351e 100644 --- a/fay_booter.py +++ b/fay_booter.py @@ -71,7 +71,9 @@ class RecorderListener(Recorder): except Exception as e: print(e) util.log(1, "请检查设备是否有误,再重新启动!") - + + def is_remote(self): + return False @@ -129,6 +131,9 @@ class DeviceInputListener(Recorder): def start_ngrok(self, clientId): self.ngrok = ngrok_util.NgrokCilent(clientId) self.ngrok.start() + + def is_remote(self): + return True diff --git a/gui/static/css/index.css b/gui/static/css/index.css index 1cc0f39..fd66112 100644 --- a/gui/static/css/index.css +++ b/gui/static/css/index.css @@ -417,4 +417,32 @@ textarea { border-radius: 10px; background: rgba(0, 0, 0, 0); -webkit-box-shadow: inset006pxrgba(0, 0, 0, 0.5); -} \ No newline at end of file +} +/* CSS 加载动画样式 */ +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } + } + + .loading-spinner { + border: 5px solid #f3f3f3; /* 轻颜色 */ + border-top: 5px solid #3498db; /* 蓝色 */ + border-radius: 50%; + width: 50px; + height: 50px; + animation: spin 2s linear infinite; + } + .spinner { + border: 4px solid rgba(0, 0, 0, 0.1); + border-radius: 50%; + border-top: 4px solid #3498db; + width: 10px; + height: 10px; + animation: spin 1s linear infinite; + } + + @keyframes spin { + to { + transform: rotate(360deg); + } + } \ No newline at end of file diff --git a/gui/static/js/index.js b/gui/static/js/index.js index 40b550c..24eebb1 100644 --- a/gui/static/js/index.js +++ b/gui/static/js/index.js @@ -74,7 +74,8 @@ new Vue({ msg_list:[], is_connect: false, wake_word_enabled:false, - wake_word: '' + wake_word: '', + loading: false } }, @@ -535,6 +536,12 @@ new Vue({ 'type' : data['type'] , 'way' : 'send' } + console.log(info) + if (data['type'] == 'fay'){ + _this.loading = false; + }else{ + _this.loading = true; + } _this.msg_list.push(info); this.timer = setTimeout(()=>{ //设置延迟执行 diff --git a/gui/templates/index.html b/gui/templates/index.html index 8a69a34..341f4a3 100644 --- a/gui/templates/index.html +++ b/gui/templates/index.html @@ -157,28 +157,32 @@
- -
+ +
+
[[item.timetext]]
- -
-
-
-
[[item.content]] -
-
-
-
[[item.content]]
-
-
-
-
- - - - -
+
+
+
+
[[item.content]] +
+
+ + +
+
[[item.content]]
+
+
+
+
+
+ +
+ + +
+
diff --git a/python_connector_demo/remote_audio.py b/python_connector_demo/remote_audio.py index 77ae694..a10ec64 100644 --- a/python_connector_demo/remote_audio.py +++ b/python_connector_demo/remote_audio.py @@ -35,7 +35,7 @@ def receive_audio(client): break print("receive audio end:{}".format(len(filedata)), end="") - filename = "sample/recv_{}.mp3".format(time.time()) + filename = "samples/recv_{}.mp3".format(time.time()) with open(filename, "wb") as f: f.write(filedata) f.close()