Преглед изворни кода

Add cshape websocket client (#846)

* add cshape client

* delete

* Add wss-client
GlocKieHuan пре 2 година
родитељ
комит
55732c9397

+ 31 - 0
funasr/runtime/wss-client/FunASRClient_CShape.sln

@@ -0,0 +1,31 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.6.33829.357
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FunASRWSClient_Offline", "FunASRWSClient_Offline\FunASRWSClient_Offline.csproj", "{E0986CC4-D443-44E2-96E8-F6E4B691CA57}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FunASRWSClient_Online", "FunASRWSClient_Online\FunASRWSClient_Online.csproj", "{11E80B4F-A838-4DFB-A0C8-9BAE6726BAC0}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{E0986CC4-D443-44E2-96E8-F6E4B691CA57}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{E0986CC4-D443-44E2-96E8-F6E4B691CA57}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{E0986CC4-D443-44E2-96E8-F6E4B691CA57}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{E0986CC4-D443-44E2-96E8-F6E4B691CA57}.Release|Any CPU.Build.0 = Release|Any CPU
+		{11E80B4F-A838-4DFB-A0C8-9BAE6726BAC0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{11E80B4F-A838-4DFB-A0C8-9BAE6726BAC0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{11E80B4F-A838-4DFB-A0C8-9BAE6726BAC0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{11E80B4F-A838-4DFB-A0C8-9BAE6726BAC0}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {E8483245-31D3-4C42-AAF9-B3195EAB97C2}
+	EndGlobalSection
+EndGlobal

BIN
funasr/runtime/wss-client/FunASRClient_CShape.suo


+ 14 - 0
funasr/runtime/wss-client/FunASRWSClient_Offline/FunASRWSClient_Offline.csproj

@@ -0,0 +1,14 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <TargetFramework>net6.0</TargetFramework>
+    <ImplicitUsings>enable</ImplicitUsings>
+    <Nullable>enable</Nullable>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="Websocket.Client" Version="4.6.1" />
+  </ItemGroup>
+
+</Project>

+ 85 - 0
funasr/runtime/wss-client/FunASRWSClient_Offline/Program.cs

@@ -0,0 +1,85 @@
+using System.Collections.Specialized;
+using WebSocketSpace;
+
+namespace FunASRWSClient_Offline
+{
+    /// <summary>
+    /// /主程序入口
+    /// </summary>
+    public class Program
+    {
+        private static void Main()
+        {
+            WSClient_Offline m_funasrclient = new WSClient_Offline();
+            m_funasrclient.FunASR_Main();
+        }
+    }
+
+    public class WSClient_Offline
+    {
+        public static string host = "0.0.0.0";
+        public static string port = "10095";
+        private static CWebSocketClient m_websocketclient = new CWebSocketClient();
+        [STAThread]
+        public async void FunASR_Main()
+        {
+            loadconfig();
+            //初始化通信连接
+            string errorStatus = string.Empty;
+            string commstatus = ClientConnTest();
+            if (commstatus != "通信连接成功")
+                errorStatus = commstatus;
+            //程序初始监测异常--报错、退出
+            if (errorStatus != string.Empty)
+            {
+                //报错方式待加
+                Environment.Exit(0);
+            }
+
+            //循环输入推理文件
+            while (true)
+            {
+                Console.WriteLine("请输入转录文件路径:");
+                string filepath = Console.ReadLine();
+                if (filepath != string.Empty && filepath != null)
+                {
+                     await m_websocketclient.ClientSendFileFunc(filepath);
+                }
+            }
+        }
+        private void loadconfig()
+        {
+            string filePath = "config.ini";
+            NameValueCollection settings = new NameValueCollection();
+            using (StreamReader reader = new StreamReader(filePath))
+            {
+                string line;
+                while ((line = reader.ReadLine()) != null)
+                {
+                    // 忽略空行和注释
+                    if (string.IsNullOrEmpty(line) || line.StartsWith(";") || line.StartsWith("#"))
+                        continue;
+                    // 解析键值对
+                    int equalsIndex = line.IndexOf('=');
+                    if (equalsIndex > 0)
+                    {
+                        string key = line.Substring(0, equalsIndex).Trim();
+                        string value = line.Substring(equalsIndex + 1).Trim();
+                        if (key == "host")
+                            host = value;
+                        else if (key == "port")
+                            port = value;
+                    }
+                }
+            }
+        }
+        private static string ClientConnTest()
+        {
+            //WebSocket连接状态监测
+            Task<string> websocketstatus = m_websocketclient.ClientConnTest();
+            if (websocketstatus != null && websocketstatus.Result.IndexOf("成功") == -1)
+                return websocketstatus.Result;
+            return "通信连接成功";
+        }
+    }
+}

+ 9 - 0
funasr/runtime/wss-client/FunASRWSClient_Offline/README.md

@@ -0,0 +1,9 @@
+# cshape-client-offline
+
+这是一个基于FunASR-Websocket服务器的CShape客户端,用于转录本地音频文件。
+
+将配置文件放在与程序相同目录下的config文件夹中,并在config.ini中配置服务器ip地址和端口号。
+
+配置好服务端ip和端口号,在vs中打开需添加Websocket.Client的Nuget程序包后,可直接进行测试,按照控制台提示操作即可。
+
+注:本客户端暂支持wav文件,在win11下完成测试,编译环境VS2022。

+ 120 - 0
funasr/runtime/wss-client/FunASRWSClient_Offline/WebScoketClient.cs

@@ -0,0 +1,120 @@
+using Websocket.Client;
+using System.Text.Json;
+using System.Reactive.Linq;
+using FunASRWSClient_Offline;
+
+namespace WebSocketSpace
+{
+    internal class  CWebSocketClient
+    {
+        private static readonly Uri serverUri = new Uri($"ws://{WSClient_Offline.host}:{WSClient_Offline.port}"); // 你要连接的WebSocket服务器地址
+        private static WebsocketClient client = new WebsocketClient(serverUri);
+        public async Task<string> ClientConnTest()
+        {
+            string commstatus = "WebSocket通信连接失败";
+            try
+            {
+                client.Name = "funasr";
+                client.ReconnectTimeout = null;
+                client.ReconnectionHappened.Subscribe(info =>
+                    Console.WriteLine($"Reconnection happened, type: {info.Type}, url: {client.Url}"));
+                client.DisconnectionHappened.Subscribe(info =>
+                    Console.WriteLine($"Disconnection happened, type: {info.Type}"));
+
+                client
+                    .MessageReceived
+                    .Where(msg => msg.Text != null)
+                    .Subscribe(msg =>
+                    {
+                        recmessage(msg.Text);
+                    });
+
+                await client.Start();
+
+                if (client.IsRunning)
+                    commstatus = "WebSocket通信连接成功";
+            }
+            catch (Exception ex)
+            {
+                Console.WriteLine(ex.ToString());
+                client.Dispose();
+            }
+
+            return commstatus;
+        }
+
+        public async Task<Task> ClientSendFileFunc(string file_name)//文件转录
+        {
+            try
+            {
+                if (client.IsRunning)
+                {
+                    var exitEvent = new ManualResetEvent(false);
+                    string path = Path.GetFileName(file_name);
+                    string firstbuff = string.Format("{{\"mode\": \"offline\", \"wav_name\": \"{0}\", \"is_speaking\": true}}", Path.GetFileName(file_name));
+                    client.Send(firstbuff);
+                    showWAVForm(client, file_name);
+                }
+            }
+            catch (Exception ex)
+            {
+                Console.WriteLine(ex.ToString());
+            }
+            return Task.CompletedTask;
+        }
+
+        public void recmessage(string message)
+        {
+            if (message != null)
+            {
+                try
+                {
+                    JsonDocument jsonDoc = JsonDocument.Parse(message);
+                    JsonElement root = jsonDoc.RootElement;
+                    string mode = root.GetProperty("mode").GetString();
+                    string text = root.GetProperty("text").GetString();
+                    string name = root.GetProperty("wav_name").GetString();
+                    if(name == "asr_stream")
+                        Console.WriteLine($"实时识别内容: {text}");
+                    else
+                        Console.WriteLine($"文件名称:{name} 文件转录内容: {text}");
+                }
+                catch (JsonException ex)
+                {
+                    Console.WriteLine("JSON 解析错误: " + ex.Message);
+                }
+            }
+        }
+
+        private void showWAVForm(WebsocketClient client, string file_name)
+        {
+            byte[] getbyte = FileToByte(file_name).Skip(44).ToArray();
+
+            for (int i = 0; i < getbyte.Length; i += 1024000)
+            {
+                byte[] send = getbyte.Skip(i).Take(1024000).ToArray();
+                client.Send(send);
+                Thread.Sleep(5);
+            }
+            Thread.Sleep(10);
+            client.Send("{\"is_speaking\": false}");
+        }
+
+        public byte[] FileToByte(string fileUrl)
+        {
+            try
+            {
+                using (FileStream fs = new FileStream(fileUrl, FileMode.Open, FileAccess.Read))
+                {
+                    byte[] byteArray = new byte[fs.Length];
+                    fs.Read(byteArray, 0, byteArray.Length);
+                    return byteArray;
+                }
+            }
+            catch
+            {
+                return null;
+            }
+        }
+    }
+}

+ 15 - 0
funasr/runtime/wss-client/FunASRWSClient_Online/FunASRWSClient_Online.csproj

@@ -0,0 +1,15 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <TargetFramework>net6.0</TargetFramework>
+    <ImplicitUsings>enable</ImplicitUsings>
+    <Nullable>enable</Nullable>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="NAudio" Version="2.1.0" />
+    <PackageReference Include="Websocket.Client" Version="4.6.1" />
+  </ItemGroup>
+
+</Project>

+ 255 - 0
funasr/runtime/wss-client/FunASRWSClient_Online/Program.cs

@@ -0,0 +1,255 @@
+using AliFsmnVadSharp;
+using NAudio.Wave;
+using System.Collections.Concurrent;
+using WebSocketSpace;
+using NAudio.CoreAudioApi;
+using System.IO;
+using System.Collections.Specialized;
+
+namespace FunASRWSClient_Online
+{
+    /// <summary>
+    /// /主程序入口
+    /// </summary>
+    public class Program
+    {
+        private static void Main()
+        {
+            WSClient_Online m_funasrclient = new WSClient_Online();
+            m_funasrclient.FunASR_Main();
+        }
+    }
+    /// <summary>
+    /// /主线程入口,初始化后读取数据
+    /// </summary>
+    public class WSClient_Online
+    {
+        /// <summary>
+        /// FunASR客户端软件运行状态
+        /// </summary>
+        /// 
+        public static string host = "0.0.0.0";
+        public static string port = "10095";
+        public static string onlineasrmode = string.Empty;
+        private static WaveCollect m_wavecollect = new WaveCollect();
+        private static CWebSocketClient m_websocketclient = new CWebSocketClient();
+        public static readonly ConcurrentQueue<byte[]> ActiveAudioSet = new ConcurrentQueue<byte[]>();
+        public static readonly ConcurrentQueue<string> AudioFileQueue = new ConcurrentQueue<string>();
+        [STAThread]
+        public void FunASR_Main()
+        {
+            loadconfig();
+            //麦克风状态监测
+            string errorStatus = string.Empty;
+            if (GetCurrentMicVolume() == -2)
+                errorStatus = "注意:麦克风被设置为静音!";
+            else if (GetCurrentMicVolume() == -1)
+                errorStatus = "注意:麦克风未连接!";
+            else if (GetCurrentMicVolume() == 0)
+                errorStatus = "注意:麦克风声音设置为0!";
+
+            //初始化通信连接
+            string commstatus = ClientConnTest();
+            if (commstatus != "通信连接成功")
+                errorStatus = commstatus;
+            //程序初始监测异常--报错、退出
+            if (errorStatus != string.Empty)
+            {
+                Environment.Exit(0);//报错方式待加
+            }
+
+            //启动客户端向服务端发送音频数据线程
+            Thread SendAudioThread = new Thread(SendAudioToSeverAsync);
+            SendAudioThread.Start();
+
+            //启动音频文件转录线程
+            Thread AudioFileThread = new Thread(SendAudioFileToSeverAsync);
+            AudioFileThread.Start();
+
+            while (true)
+            {
+                Console.WriteLine("请选择语音识别方式:1.离线文件转写;2.实时语音识别");
+                string str = Console.ReadLine();
+                if (str != string.Empty)
+                {
+                    if (str == "1")//离线文件转写
+                    {
+                        onlineasrmode = "offline";
+                        Console.WriteLine("请输入转录文件路径");
+                        str = Console.ReadLine();
+                        if (!string.IsNullOrEmpty(str))
+                            AudioFileQueue.Enqueue(str);
+                    }
+                    else if (str == "2")//实时语音识别
+                    {
+                        Console.WriteLine("请输入实时语音识别模式:1.online;2.2pass");
+                        str = Console.ReadLine();
+                        OnlineASR(str);
+                    }
+                }
+            }
+        }
+        private void loadconfig()
+        {
+            string filePath = "config.ini";
+            NameValueCollection settings = new NameValueCollection();
+            using (StreamReader reader = new StreamReader(filePath))
+            {
+                string line;
+                while ((line = reader.ReadLine()) != null)
+                {
+                    // 忽略空行和注释
+                    if (string.IsNullOrEmpty(line) || line.StartsWith(";") || line.StartsWith("#"))
+                        continue;
+                    // 解析键值对
+                    int equalsIndex = line.IndexOf('=');
+                    if (equalsIndex > 0)
+                    {
+                        string key = line.Substring(0, equalsIndex).Trim();
+                        string value = line.Substring(equalsIndex + 1).Trim();
+                        if (key == "host")
+                            host = value;
+                        else if (key == "port")
+                            port = value;
+                    }
+                }
+            }
+        }
+        private void OnlineASR(string str)
+        {
+            if (!string.IsNullOrEmpty(str))
+            {
+                if (str == "1")//实时语音识别
+                    onlineasrmode = "online";
+                else if (str == "2")//实时语音识别-动态修正
+                    onlineasrmode = "2pass";
+            }
+            //开始录制声音、发送识别
+            if (onlineasrmode != string.Empty)
+            {
+                m_wavecollect.StartRec();
+                m_websocketclient.ClientFirstConnOnline(onlineasrmode);
+                try
+                {
+                    while (true)
+                    {
+                        if (!WaveCollect.voicebuff.IsEmpty)
+                        {
+                            byte[] buff;
+                            int buffcnt = WaveCollect.voicebuff.Count;
+                            WaveCollect.voicebuff.TryDequeue(out buff);
+                            if (buff != null)
+                                ActiveAudioSet.Enqueue(buff);
+                        }
+                        else
+                        {
+                            if (Console.KeyAvailable)
+                            {
+                                var key = Console.ReadKey(true);
+
+                                // 检测到按下Ctrl+C
+                                if ((key.Modifiers & ConsoleModifiers.Control) != 0 && key.Key == ConsoleKey.C)
+                                {
+                                    // 执行相应的操作
+                                    Console.WriteLine("Ctrl+C Pressed!");
+                                    // 退出循环或执行其他操作
+                                    break;
+                                }
+                            }
+                            else
+                            {
+                                Thread.Sleep(10);
+                            }
+                        }
+                    }
+                }
+                catch
+                {
+                    Console.WriteLine("实时识别出现异常!");
+                }
+                finally
+                {
+                    m_wavecollect.StopRec();
+                    m_websocketclient.ClientLastConnOnline();
+                }
+            }
+        }
+
+        private string ClientConnTest()
+        {
+            //WebSocket连接状态监测
+            Task<string> websocketstatus = m_websocketclient.ClientConnTest();
+            if (websocketstatus != null && websocketstatus.Result.IndexOf("成功") == -1)
+                return websocketstatus.Result;
+            return "通信连接成功";
+        }
+        private void SendAudioFileToSeverAsync()
+        {
+            while (true)
+            {
+                Thread.Sleep(1000);
+                if (AudioFileQueue.Count > 0)
+                {
+                    string filepath = string.Empty;
+                    AudioFileQueue.TryDequeue(out filepath);
+                    if (filepath != string.Empty && filepath != null)
+                    {
+                        m_websocketclient.ClientSendFileFunc(filepath);
+                    }
+                }
+                else
+                {
+                    Thread.Sleep(100);
+                }
+            }
+        }
+        private void SendAudioToSeverAsync()
+        {
+            while (true)
+            {
+                if (ActiveAudioSet.Count > 0)
+                {
+                    byte[] audio;
+                    ActiveAudioSet.TryDequeue(out audio);
+                    if (audio == null)
+                        continue;
+
+                    byte[] mArray = new byte[audio.Length];
+                    Array.Copy(audio, 0, mArray, 0, audio.Length);
+                    if (mArray != null)
+                        m_websocketclient.ClientSendAudioFunc(mArray);
+                }
+                else
+                {
+                    Thread.Sleep(10);
+                }
+            }
+        }
+
+        private void SaveAsWav(byte[] pcmData, string fileName, int sampleRate, int bitsPerSample, int channels)
+        {
+            using (var writer = new WaveFileWriter(fileName, new WaveFormat(sampleRate, bitsPerSample, channels)))
+            {
+                writer.Write(pcmData, 0, pcmData.Length);
+            }
+        }
+
+        private int GetCurrentMicVolume()                       //获取麦克风设置
+        {
+            int volume = -1;
+            var enumerator = new MMDeviceEnumerator();
+
+            //获取音频输入设备
+            IEnumerable<MMDevice> captureDevices = enumerator.EnumerateAudioEndPoints(DataFlow.Capture, DeviceState.Active).ToArray();
+            if (captureDevices.Count() > 0)
+            {
+                MMDevice mMDevice = captureDevices.ToList()[0];
+                if (mMDevice.AudioEndpointVolume.Mute)
+                    return -2;
+                volume = (int)(mMDevice.AudioEndpointVolume.MasterVolumeLevelScalar * 100);
+
+            }
+            return volume;
+        }
+    }
+}

+ 9 - 0
funasr/runtime/wss-client/FunASRWSClient_Online/README.md

@@ -0,0 +1,9 @@
+# cshape-client-online
+
+这是一个基于FunASR-Websocket服务器的CShape客户端,用于实时语音识别和转录本地音频文件。
+
+将配置文件放在与程序相同目录下的config文件夹中,并在config.ini中配置服务器ip地址和端口号。
+
+配置好服务端ip和端口号,在vs中打开需添加NAudio和Websocket.Client的Nuget程序包后,可直接进行测试,按照控制台提示操作即可。
+
+注:实时语音识别使用online或2pass,转录文件默认使用offline,在win11下完成测试,编译环境VS2022。

+ 106 - 0
funasr/runtime/wss-client/FunASRWSClient_Online/WaveCollect.cs

@@ -0,0 +1,106 @@
+using System.Collections.Concurrent;
+using NAudio.Wave;
+using NAudio.CoreAudioApi;
+
+namespace AliFsmnVadSharp
+{
+    class WaveCollect
+    {
+        private string fileName = string.Empty;
+        private WaveInEvent? waveSource = null;
+        private WaveFileWriter? waveFile = null;
+        public static int wave_buffer_milliseconds = 600;
+        public static int wave_buffer_collectbits = 16;
+        public static int wave_buffer_collectchannels = 1;
+        public static int wave_buffer_collectfrequency = 16000;
+        public static readonly ConcurrentQueue<byte[]> voicebuff = new ConcurrentQueue<byte[]>();
+
+        public void StartRec()
+        {
+            // 获取麦克风设备
+            var captureDevices = new MMDeviceEnumerator().EnumerateAudioEndPoints(DataFlow.Capture, DeviceState.Active);
+            foreach (var device in captureDevices)
+            {
+                Console.WriteLine("Device Name: " + device.FriendlyName);
+                using (var capture = new WasapiLoopbackCapture(device))
+                {
+                    // 获取支持的采样率列表
+                    Console.WriteLine("Device Channels:" + capture.WaveFormat.Channels);
+                    Console.WriteLine("Device SampleRate:" + capture.WaveFormat.SampleRate);
+                    Console.WriteLine("Device BitsPerSample:" + capture.WaveFormat.BitsPerSample);
+                }
+            }
+            //清空缓存数据
+            int buffnum = voicebuff.Count;
+            for (int i = 0; i < buffnum; i++)
+                voicebuff.TryDequeue(out byte[] buff);
+
+            waveSource = new WaveInEvent();
+            waveSource.BufferMilliseconds = wave_buffer_milliseconds;
+            waveSource.WaveFormat = new WaveFormat(wave_buffer_collectfrequency, wave_buffer_collectbits, wave_buffer_collectchannels); // 16bit,16KHz,Mono的录音格式
+            waveSource.DataAvailable += new EventHandler<WaveInEventArgs>(waveSource_DataAvailable);
+            SetFileName(AppDomain.CurrentDomain.BaseDirectory + "tmp.wav");
+            waveFile = new WaveFileWriter(fileName, waveSource.WaveFormat);
+            waveSource.StartRecording();
+        }
+
+        public void StopRec()
+        {
+            if (waveSource != null)
+            {
+                waveSource.StopRecording();
+                if (waveSource != null)
+                {
+                    waveSource.Dispose();
+                    waveSource = null;
+                }
+                if (waveFile != null)
+                {
+                    waveFile.Dispose();
+                    waveFile = null;
+                }
+            }
+        }
+
+        public void SetFileName(string fileName)
+        {
+            this.fileName = fileName;
+        }
+
+        private void waveSource_DataAvailable(object sender, WaveInEventArgs e)
+        {
+            if (waveFile != null)
+            {
+                if (e.Buffer != null && e.BytesRecorded > 0)
+                {
+                    voicebuff.Enqueue(e.Buffer);
+                    //waveFile.Write(e.Buffer, 0, e.BytesRecorded);
+                    waveFile.Flush();
+                }
+
+            }
+        }
+
+        public static byte[] Wavedata_Dequeue()
+        {
+            byte[] datas;
+            voicebuff.TryDequeue(out datas);
+            return datas;
+        }
+
+        private void waveSource_RecordingStopped(object sender, StoppedEventArgs e)
+        {
+            if (waveSource != null)
+            {
+                waveSource.Dispose();
+                waveSource = null;
+            }
+
+            if (waveFile != null)
+            {
+                waveFile.Dispose();
+                waveFile = null;
+            }
+        }
+    }
+}

+ 219 - 0
funasr/runtime/wss-client/FunASRWSClient_Online/WebScoketClient.cs

@@ -0,0 +1,219 @@
+using System.Net.WebSockets;
+using Websocket.Client;
+using System.Text.Json;
+using NAudio.Wave;
+using AliFsmnVadSharp;
+using System.Reactive.Linq;
+using FunASRWSClient_Online;
+
+namespace WebSocketSpace
+{
+    internal class  CWebSocketClient
+    {
+        private static int chunk_interval = 10;
+        private static int[] chunk_size = new int[] { 5, 10, 5 };
+        private static readonly Uri serverUri = new Uri($"ws://{WSClient_Online.host}:{WSClient_Online.port}"); // 你要连接的WebSocket服务器地址
+        private static WebsocketClient client = new WebsocketClient(serverUri);
+        public async Task<string> ClientConnTest()
+        {
+            string commstatus = "WebSocket通信连接失败";
+            try
+            {
+                client.Name = "funasr";
+                client.ReconnectTimeout = null;
+                client.ReconnectionHappened.Subscribe(info =>
+                   Console.WriteLine($"Reconnection happened, type: {info.Type}, url: {client.Url}"));
+                client.DisconnectionHappened.Subscribe(info =>
+                    Console.WriteLine($"Disconnection happened, type: {info.Type}"));
+
+                client
+                    .MessageReceived
+                    .Where(msg => msg.Text != null)
+                    .Subscribe(msg =>
+                {
+                    rec_message(msg.Text, client);
+                });
+
+                await client.Start();
+
+                if (client.IsRunning)
+                    commstatus = "WebSocket通信连接成功";
+            }
+            catch (Exception ex)
+            {
+                Console.WriteLine(ex.ToString());
+                client.Dispose();
+            }
+
+            return commstatus;
+        }
+
+        public bool ClientFirstConnOnline(string asrmode)
+        {
+            if (client.IsRunning)
+            {
+                string firstbuff = string.Format("{{\"mode\": \"{0}\", \"chunk_size\": [{1},{2},{3}], \"chunk_interval\": {4}, \"wav_name\": \"microphone\", \"is_speaking\": true}}"
+                       , asrmode, chunk_size[0], chunk_size[1], chunk_size[2], chunk_interval);
+                Task.Run(() => client.Send(firstbuff));
+            }
+            else
+            {
+                client.Reconnect();
+                return false;
+            }
+
+            return true;
+        }
+        public bool ClientSendAudioFunc(byte[] buff)    //实时识别
+        {
+            if (client.IsRunning)
+            {
+                ////发送音频数据
+                int CHUNK = WaveCollect.wave_buffer_collectfrequency / 1000 * 60 * chunk_size[1] / chunk_interval;
+                for (int i = 0; i < buff.Length; i += CHUNK)
+                {
+                    byte[] send = buff.Skip(i).Take(CHUNK).ToArray();
+                    Task.Run(() => client.Send(send));
+                    Thread.Sleep(1);
+                }
+            }
+            else
+            {
+                client.Reconnect();
+                return false;
+            }
+
+            return true;
+        }
+        public void ClientLastConnOnline()
+        {
+            Task.Run(() => client.Send("{\"is_speaking\": false}"));
+        }
+
+        public int ClientSendFileFunc(string file_name)//文件转录 0:发送成功 ret -1:文件类型不支持 -2:通信断开
+        {
+            string fileExtension = Path.GetExtension(file_name);
+            fileExtension = fileExtension.Replace(".", "");
+            if (!(fileExtension == "mp3" || fileExtension == "mp4" || fileExtension == "wav" || fileExtension == "pcm"))
+                return -1;
+
+            if (client.IsRunning)
+            {
+                if (fileExtension == "wav" || fileExtension == "pcm")
+                {
+                    string firstbuff = string.Format("{{\"mode\": \"office\", \"chunk_size\": [{0},{1},{2}], \"chunk_interval\": {3}, \"wav_name\": \"{4}\", \"is_speaking\": true, \"wav_format\":\"pcm\"}}"
+                        , chunk_size[0], chunk_size[1], chunk_size[2], chunk_interval, Path.GetFileName(file_name));
+                    Task.Run(() => client.Send(firstbuff));
+                    if (fileExtension == "wav")
+                        showWAVForm(file_name);
+                    else if (fileExtension == "pcm")
+                        showWAVForm_All(file_name);
+                }
+                else if (fileExtension == "mp3" || fileExtension == "mp4")
+                {
+                    string firstbuff = string.Format("{{\"mode\": \"offline\", \"chunk_size\": \"{0},{1},{2}\", \"chunk_interval\": {3}, \"wav_name\": \"{4}\", \"is_speaking\": true, \"wav_format\":\"{5}\"}}"
+                        , chunk_size[0], chunk_size[1], chunk_size[2], chunk_interval, Path.GetFileName(file_name), fileExtension);
+                    Task.Run(() => client.Send(firstbuff));
+                    showWAVForm_All(file_name);
+                }
+            }
+            else
+            {
+                client.Reconnect();
+                return -2;
+            }
+
+            return 0;
+        }
+        private string recbuff = string.Empty;//接收累计缓存内容
+        private string onlinebuff = string.Empty;//接收累计在线缓存内容
+        public void rec_message(string message, WebsocketClient client)
+        {
+            if (message != null)
+            {
+                try
+                {
+                    string name = string.Empty;
+                    JsonDocument jsonDoc = JsonDocument.Parse(message);
+                    JsonElement root = jsonDoc.RootElement;
+                    string mode = root.GetProperty("mode").GetString();
+                    string text = root.GetProperty("text").GetString();
+                    bool isfinal = root.GetProperty("is_final").GetBoolean();
+                    if (message.IndexOf("wav_name  ") != -1)
+                        name = root.GetProperty("wav_name").GetString();
+
+                    //if (name == "microphone")
+                    //    Console.WriteLine($"实时识别内容: {text}");
+                    //else
+                    //    Console.WriteLine($"文件名称:{name} 文件转录内容: {text}");
+
+                    if (mode == "2pass-online" && WSClient_Online.onlineasrmode != "offline")
+                    {
+                        onlinebuff += text;
+                        Console.WriteLine(recbuff + onlinebuff);
+                    }
+                    else if (mode == "2pass-offline")
+                    {
+                        recbuff += text;
+                        onlinebuff = string.Empty;
+                        Console.WriteLine(recbuff);
+                    }
+
+                    if (isfinal && WSClient_Online.onlineasrmode != "offline")//未结束当前识别
+                    {
+                        recbuff = string.Empty;
+                    }
+                }
+                catch (JsonException ex)
+                {
+                    Console.WriteLine("JSON 解析错误: " + ex.Message);
+                }
+            }
+        }
+
+        private void showWAVForm(string file_name)
+        {
+            byte[] getbyte = FileToByte(file_name).Skip(44).ToArray();
+
+            for (int i = 0; i < getbyte.Length; i += 102400)
+            {
+                byte[] send = getbyte.Skip(i).Take(102400).ToArray();
+                Task.Run(() => client.Send(send));
+                Thread.Sleep(5);
+            }
+            Thread.Sleep(100);
+            Task.Run(() => client.Send("{\"is_speaking\": false}"));
+        }
+
+        private void showWAVForm_All(string file_name)
+        {
+            byte[] getbyte = FileToByte(file_name).ToArray();
+
+            for (int i = 0; i < getbyte.Length; i += 1024000)
+            {
+                byte[] send = getbyte.Skip(i).Take(1024000).ToArray();
+                Task.Run(() => client.Send(send));
+                Thread.Sleep(5);
+            }
+            Thread.Sleep(10);
+            Task.Run(() => client.Send("{\"is_speaking\": false}"));
+        }
+
+        public byte[] FileToByte(string fileUrl)
+        {
+            try
+            {
+                using (FileStream fs = new FileStream(fileUrl, FileMode.Open, FileAccess.Read))
+                {
+                    byte[] byteArray = new byte[fs.Length];
+                    fs.Read(byteArray, 0, byteArray.Length);
+                    return byteArray;
+                }
+            }
+            catch
+            {
+                return null;
+            }
+        }
+    }
+}

+ 2 - 0
funasr/runtime/wss-client/confg/config.ini

@@ -0,0 +1,2 @@
+host=127.0.0.1
+port=10095

BIN
funasr/runtime/wss-client/confg/tmp.wav