using System;
using System.IO;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using UnityEngine;
using UnityEngine.Events;
// Chinese:
// dmvop --output=tcp --port=5117 --model=small --lang=zh --instant --post="+pinyin" --device=auto
//
// English
// dmvop --output=tcp --port=5117 --model=small.en --lang=en --instant --device=auto
[Serializable]
public struct DMVOPEvent
{
public float volume;
public string sentence;
public DMVOPEvent(float volume, string sentence)
{
this.volume = volume;
this.sentence = sentence;
}
}
///
/// Connects to dmvop via TCP and receives voice input in real-time.
/// Attach to any GameObject. Wire up OnVoiceInput in the Inspector.
///
public class DMVOPListener : MonoBehaviour
{
[Header("DMVOP Connection")]
public string host = "127.0.0.1";
public int port = 5117;
public bool connectOnStart = true;
[Header("Events")]
public UnityEvent onVoiceInput;
private TcpClient _client;
private Thread _readerThread;
private volatile bool _running;
private readonly object _lockObj = new();
private DMVOPEvent _pendingEvent;
private bool _hasPending;
private void Start()
{
if (connectOnStart) Connect();
}
private void Connect()
{
if (_running) return;
_running = true;
try
{
_client = new TcpClient();
_client.Connect(host, port);
Debug.Log($"[DMVOP] Connected to {host}:{port}");
_readerThread = new Thread(ReadLoop);
_readerThread.IsBackground = true;
_readerThread.Start();
}
catch (Exception e)
{
Debug.LogError($"[DMVOP] Failed to connect: {e.Message}");
_running = false;
}
}
private void Disconnect()
{
_running = false;
if (_client != null)
{
_client.Close();
_client = null;
}
}
private void OnDestroy()
{
Disconnect();
}
private void Update()
{
if (!_hasPending) return;
DMVOPEvent evt;
lock (_lockObj)
{
evt = _pendingEvent;
_hasPending = false;
}
Debug.Log($"[DMVOP] Received event: volume={evt.volume}, sentence='{evt.sentence}'");
onVoiceInput?.Invoke(evt);
}
private void ReadLoop()
{
try
{
using var stream = _client.GetStream();
using var reader = new StreamReader(stream, Encoding.UTF8);
while (_running)
{
string line = reader.ReadLine();
if (line == null) break;
var evt = ParseLine(line);
lock (_lockObj)
{
_pendingEvent = evt;
_hasPending = true;
}
}
}
catch (Exception e) when (_running)
{
Debug.LogError($"[DMVOP] Connection lost: {e.Message}");
}
finally
{
_running = false;
Debug.Log("[DMVOP] Disconnected");
}
}
private static DMVOPEvent ParseLine(string line)
{
int comma = line.IndexOf(',');
if (comma > 0 && float.TryParse(line.AsSpan(0, comma), out float vol))
{
return new DMVOPEvent(vol, line.Substring(comma + 1));
}
return new DMVOPEvent(0, line);
}
}