diff --git a/ModuleWeather/Module.cs b/ModuleWeather/Module.cs
index 430ebef..fd9160f 100644
--- a/ModuleWeather/Module.cs
+++ b/ModuleWeather/Module.cs
@@ -8,26 +8,31 @@ public class WeatherModule : IModule
public string[] GetCommands() => new[] { "Погода", "Пинг" };
- public object Execute(string command, object[] args)
+ public void Settings(object[] args)
+ {
+ // args[0] — это родительское окно из Ядра
+ var parentWindow = args != null && args.Length > 0 ? args[0] as Window : null;
+
+ var win = new Window
+ {
+ Title = "Окно Погоды",
+ Content = new WeatherView(), // Вставляем наш контрол
+ Width = 350,
+ Height = 250,
+ WindowStartupLocation = WindowStartupLocation.CenterOwner
+ };
+
+ if (parentWindow != null) win.Show(parentWindow);
+ else win.Show();
+
+ }
+
+ public object Execute(string command)
{
switch (command)
{
case "Погода":
- // args[0] — это родительское окно из Ядра
- var parentWindow = args != null && args.Length > 0 ? args[0] as Window : null;
-
- var win = new Window
- {
- Title = "Окно Погоды",
- Content = new WeatherView(), // Вставляем наш контрол
- Width = 350,
- Height = 250,
- WindowStartupLocation = WindowStartupLocation.CenterOwner
- };
-
- if (parentWindow != null) win.Show(parentWindow);
- else win.Show();
-
+
return "Окно открыто успешно";
case "Пинг":
diff --git a/ModuleWeather/ModuleWeather.csproj b/ModuleWeather/ModuleWeather.csproj
index d404b85..763c2f4 100644
--- a/ModuleWeather/ModuleWeather.csproj
+++ b/ModuleWeather/ModuleWeather.csproj
@@ -22,7 +22,7 @@
diff --git a/VisionAsist.SDK/IModule.cs b/VisionAsist.SDK/IModule.cs
index 45d5ed0..5a61986 100644
--- a/VisionAsist.SDK/IModule.cs
+++ b/VisionAsist.SDK/IModule.cs
@@ -4,5 +4,6 @@ public interface IModule
{
string Name { get; }
string[] GetCommands();
- object Execute(string command, object[] args);
+ void Settings(object[] args);
+ object Execute(string command);
}
\ No newline at end of file
diff --git a/VisionAsist.sln.DotSettings.user b/VisionAsist.sln.DotSettings.user
index c79a889..56408d8 100644
--- a/VisionAsist.sln.DotSettings.user
+++ b/VisionAsist.sln.DotSettings.user
@@ -1,2 +1,4 @@
- ForceIncluded
\ No newline at end of file
+ ForceIncluded
+ ForceIncluded
+ ForceIncluded
\ No newline at end of file
diff --git a/VisionAsist/Models/Core.cs b/VisionAsist/Models/Core.cs
index c0732ae..4e0be9c 100644
--- a/VisionAsist/Models/Core.cs
+++ b/VisionAsist/Models/Core.cs
@@ -12,7 +12,15 @@ namespace VisionAsist.Models;
public class Core
{
- public static Dictionary _loadedModules = new();
+
+ public class Modules
+ {
+ public string Name { get; set; }
+ public IModule Module { get; set; }
+ public string[] commands { get; set; }
+ }
+
+ public static List modulelist = new();
public static TrigerCore triger = new();
public static string TextAsist;
static string Plugin = Path.Combine(AppContext.BaseDirectory, "Modules");
@@ -35,7 +43,7 @@ public class Core
if (type != null)
{
var module = (IModule)Activator.CreateInstance(type)!;
- _loadedModules.Add(module.Name, module);
+ modulelist.Add(new Modules{Name = folderName, Module = module, commands = module.GetCommands()});
foreach (var cmd in module.GetCommands())
{
Console.WriteLine($"- {cmd}");
@@ -53,8 +61,10 @@ public class Core
triger.OnRecognized += word =>
{
- Console.WriteLine(word); // печатаем сразу, как распознано
+
TextAsist = triger.RecognizedText;
+ Selector.selector(triger.RecognizedText);
+
};
// Запускаем запись
diff --git a/VisionAsist/Models/Selector.cs b/VisionAsist/Models/Selector.cs
index a0c1651..2b96407 100644
--- a/VisionAsist/Models/Selector.cs
+++ b/VisionAsist/Models/Selector.cs
@@ -1,6 +1,14 @@
-namespace VisionAsist.Models;
+using System;
+
+namespace VisionAsist.Models;
public class Selector
{
-
+ public static void selector(string text)
+ {
+ if (text.Contains("вижен"))
+ {
+ Console.WriteLine("dddddd");
+ }
+ }
}
\ No newline at end of file
diff --git a/VisionAsist/Models/TrigerCore.cs b/VisionAsist/Models/TrigerCore.cs
index 58da054..e6ca858 100644
--- a/VisionAsist/Models/TrigerCore.cs
+++ b/VisionAsist/Models/TrigerCore.cs
@@ -1,76 +1,207 @@
using System;
using System.IO;
-using Vosk;
using System.Text.Json;
-using NAudio.Wave;
-using Avalonia.Controls;
+using System.Threading;
+using System.Collections.Concurrent;
+using Vosk;
+using SoundIOSharp;
using Avalonia.Threading;
namespace VisionAsist.Models;
-public class TrigerCore
+public class TrigerCore : IDisposable
{
+ private SoundIO? _soundIO;
+ private SoundIODevice? _device;
+ private SoundIOInStream? _inStream;
- private WaveInEvent? _waveIn;
- private Model? _model;
- private VoskRecognizer? _rec;
- private readonly object _voskLock = new();
- public string RecognizedText { get; private set; } = "";
+ private Thread? _eventThread;
+ private Thread? _processingThread;
+ private bool _isRecording;
+
+ private readonly Model _model;
+ private readonly VoskRecognizer _rec;
+
+ private readonly BlockingCollection _audioQueue = new(100);
+
+ public string RecognizedText { get; private set; } = "";
+ public event Action? OnRecognized;
+
+ // Сохраняем ссылки в полях класса, чтобы GC не удалил их в Debug-режиме
+ private Action? _readCallback;
+ private Action? _overflowCallback;
+ private Action? _errorCallback;
-
public TrigerCore()
{
- string VoskPath = Path.Combine(AppContext.BaseDirectory, "models/Vosk/");
- if (!Directory.Exists(VoskPath))
- throw new DirectoryNotFoundException($"Модель не найдена по пути: {VoskPath}");
+ string voskPath = Path.Combine(AppContext.BaseDirectory, "models", "Vosk");
+ if (!Directory.Exists(voskPath))
+ throw new DirectoryNotFoundException($"Модель Vosk не найдена: {voskPath}");
- _model = new Model(VoskPath);
+ _model = new Model(voskPath);
_rec = new VoskRecognizer(_model, 16000.0f);
-
}
public void StartRecording()
{
- if (_waveIn != null || _rec == null) return;
+ if (_isRecording) return;
- _waveIn = new WaveInEvent { WaveFormat = new WaveFormat(16000, 1) };
- _waveIn.DataAvailable += OnDataAvailable;
- _waveIn.StartRecording();
- }
+ _soundIO = new SoundIO();
+ _soundIO.Connect();
+ _soundIO.FlushEvents();
-
- public void StopRecording()
- {
- _waveIn?.StopRecording();
- _waveIn?.Dispose();
- _waveIn = null;
- }
- public event Action? OnRecognized;
+ int deviceIndex = _soundIO.DefaultInputDeviceIndex;
+ if (deviceIndex < 0) throw new Exception("Микрофон не найден.");
- private void OnDataAvailable(object? sender, WaveInEventArgs e)
- {
- lock (_voskLock)
- {
- if (_rec != null && _rec.AcceptWaveform(e.Buffer, e.BytesRecorded))
+ _device = _soundIO.GetInputDevice(deviceIndex);
+ _inStream = _device.CreateInStream();
+
+ _inStream.Format = SoundIOFormat.S16LE;
+ _inStream.SampleRate = 16000;
+ _inStream.Layout = SoundIOChannelLayout.GetDefault(1);
+
+ // Привязываем методы к полям
+ _readCallback = OnDataAvailable;
+ _overflowCallback = () => Console.WriteLine("Audio Buffer Overflow");
+ _errorCallback = () => Console.WriteLine("Audio Stream Error occurred");
+
+ _inStream.ReadCallback = _readCallback;
+ _inStream.OverflowCallback = _overflowCallback;
+ _inStream.ErrorCallback = _errorCallback;
+
+ _inStream.Open();
+ _inStream.Start();
+
+ _isRecording = true;
+
+ // Поток обработки событий SoundIO
+ _eventThread = new Thread(() => {
+ while (_isRecording && _soundIO != null)
{
- var json = _rec.Result();
- using var doc = JsonDocument.Parse(json);
- var result = doc.RootElement.GetProperty("text").GetString();
+ try { _soundIO.WaitEvents(); } catch { break; }
+ }
+ }) { IsBackground = true, Name = "SoundIO_Wait" };
+ _eventThread.Start();
- if (!string.IsNullOrWhiteSpace(result))
+ // Поток обработки Vosk
+ _processingThread = new Thread(ProcessQueue) {
+ IsBackground = true,
+ Name = "Vosk_Worker"
+ };
+ _processingThread.Start();
+ }
+
+ private unsafe void OnDataAvailable(int frameCountMin, int frameCountMax)
+ {
+ if (!_isRecording || _inStream == null) return;
+
+ int frameCount = frameCountMax;
+ // Работаем с результатом BeginRead как с объектом SoundIOChannelAreas
+ var areas = _inStream.BeginRead(ref frameCount);
+
+ if (frameCount <= 0) return;
+
+ try
+ {
+ var area = areas.GetArea(0);
+ if (area.Pointer == IntPtr.Zero) return;
+
+ int bytesNeeded = frameCount * 2; // 2 байта на семпл для S16LE
+ byte[] data = new byte[bytesNeeded];
+
+ fixed (byte* pDst = data)
+ {
+ short* pSrc = (short*)area.Pointer;
+ short* pDstShort = (short*)pDst;
+ int stepInShorts = area.Step / 2;
+
+ for (int i = 0; i < frameCount; i++)
{
- RecognizedText += result + " ";
- OnRecognized?.Invoke(result); // уведомляем подписчиков
+ pDstShort[i] = pSrc[i * stepInShorts];
}
}
+
+ // Отправляем в очередь и мгновенно освобождаем аудио-поток
+ _audioQueue.TryAdd(data);
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine("Error copying audio data: " + ex.Message);
+ }
+ finally
+ {
+ _inStream.EndRead();
+ }
+ }
+
+ private void ProcessQueue()
+ {
+ // GetConsumingEnumerable будет ждать появления данных в очереди
+ foreach (var data in _audioQueue.GetConsumingEnumerable())
+ {
+ if (!_isRecording) break;
+
+ try
+ {
+ bool isFinal;
+ lock (_rec)
+ {
+ isFinal = _rec.AcceptWaveform(data, data.Length);
+ }
+
+ if (isFinal)
+ {
+ ParseAndSend(_rec.Result());
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine("Vosk processing error: " + ex.Message);
+ }
}
}
-
- protected void Stop()
+
+ private void ParseAndSend(string json)
+ {
+ if (string.IsNullOrWhiteSpace(json)) return;
+ try
+ {
+ using var doc = JsonDocument.Parse(json);
+ if (doc.RootElement.TryGetProperty("text", out var el))
+ {
+ string text = el.GetString() ?? "";
+ if (!string.IsNullOrWhiteSpace(text))
+ {
+ RecognizedText += text + " ";
+ // Безопасный проброс в UI поток Avalonia
+ Dispatcher.UIThread.Post(() => OnRecognized?.Invoke(text));
+ }
+ }
+ }
+ catch { /* JSON Parse Error */ }
+ }
+
+ public void StopRecording()
+ {
+ if (!_isRecording) return;
+ _isRecording = false;
+
+ _audioQueue.CompleteAdding();
+ _soundIO?.Wakeup();
+
+ _inStream?.Dispose();
+ _device?.RemoveReference();
+ _soundIO?.Dispose();
+
+ _inStream = null;
+ _device = null;
+ _soundIO = null;
+ }
+
+ public void Dispose()
{
StopRecording();
_rec?.Dispose();
_model?.Dispose();
-
}
}
\ No newline at end of file
diff --git a/VisionAsist/ViewModels/SettingsViewModel.cs b/VisionAsist/ViewModels/SettingsViewModel.cs
index cf18439..d88080e 100644
--- a/VisionAsist/ViewModels/SettingsViewModel.cs
+++ b/VisionAsist/ViewModels/SettingsViewModel.cs
@@ -23,11 +23,11 @@ public class SettingsViewModel : ViewModelBase
public SettingsViewModel()
{
- foreach (string Name in Core._loadedModules.Keys)
+ foreach (var module in Core.modulelist)
{
- AddModule(Name);
+ AddModule(module.Name);
}
}
@@ -44,10 +44,10 @@ public class SettingsViewModel : ViewModelBase
private void OpenSettings(string moduleName)
{
- var module = Core._loadedModules.Values.FirstOrDefault(m => m.Name == moduleName);
+ var module = Core.modulelist.FirstOrDefault(m => m.Name == moduleName);
if (module != null)
{
- module.Execute("ShowWeather", new object[] { this });
+ module.Module.Settings(new object[] { this });
}
diff --git a/VisionAsist/VisionAsist.csproj b/VisionAsist/VisionAsist.csproj
index d3498af..5a0c5e9 100644
--- a/VisionAsist/VisionAsist.csproj
+++ b/VisionAsist/VisionAsist.csproj
@@ -5,6 +5,8 @@
enable
app.manifest
true
+ LD_LIBRARY_PATH=/usr/lib $(RunCommand)
+ true
@@ -22,6 +24,7 @@
All
+
@@ -29,4 +32,5 @@
+