More attempts at getting this usb dectalk working.
This commit is contained in:
@@ -521,6 +521,7 @@ class SettingsManager:
|
||||
"speechdDriver",
|
||||
"genericDriver",
|
||||
"dectalkDriver",
|
||||
"dectalkUsbDriver",
|
||||
"doubletalkDriver",
|
||||
"litetalkDriver",
|
||||
"tripletalkDriver",
|
||||
|
||||
@@ -0,0 +1,202 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Fenrir TTY screen reader
|
||||
# By Chrys, Storm Dragon, and contributors.
|
||||
|
||||
import threading
|
||||
|
||||
from fenrirscreenreader.core import debug
|
||||
from fenrirscreenreader.speechDriver import dectalkDriver
|
||||
|
||||
|
||||
class driver(dectalkDriver.driver):
|
||||
vendor_id = 0x0DD0
|
||||
product_id = 0x2001
|
||||
interface = 0
|
||||
speech_endpoint = 0x02
|
||||
immediate_endpoint = 0x04
|
||||
packet_size = 64
|
||||
write_timeout = 1000
|
||||
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
self._is_initialized = False
|
||||
self.usb_device = None
|
||||
self.usb_core = None
|
||||
self.usb_util = None
|
||||
self.immediate_lock = threading.Lock()
|
||||
self._debug(
|
||||
"DECtalk USB initialize: requested_device=0dd0:2001",
|
||||
debug.DebugLevel.INFO,
|
||||
on_any_level=True,
|
||||
)
|
||||
|
||||
try:
|
||||
self.usb_core, self.usb_util = self._load_usb_modules()
|
||||
except ImportError as error:
|
||||
self._debug(
|
||||
f"DECtalk USB requires PyUSB: {error}",
|
||||
debug.DebugLevel.ERROR,
|
||||
on_any_level=True,
|
||||
)
|
||||
raise RuntimeError("DECtalk USB requires PyUSB") from error
|
||||
|
||||
try:
|
||||
usb_device = self.usb_core.find(
|
||||
idVendor=self.vendor_id,
|
||||
idProduct=self.product_id,
|
||||
)
|
||||
except (
|
||||
self.usb_core.NoBackendError,
|
||||
self.usb_core.USBError,
|
||||
OSError,
|
||||
) as error:
|
||||
self._debug(
|
||||
f"DECtalk USB discovery failed: {error}",
|
||||
debug.DebugLevel.ERROR,
|
||||
on_any_level=True,
|
||||
)
|
||||
raise RuntimeError(
|
||||
f"DECtalk USB discovery failed: {error}"
|
||||
) from error
|
||||
if usb_device is None:
|
||||
self._debug(
|
||||
"DECtalk USB device 0dd0:2001 was not found",
|
||||
debug.DebugLevel.ERROR,
|
||||
on_any_level=True,
|
||||
)
|
||||
raise RuntimeError("DECtalk USB device 0dd0:2001 was not found")
|
||||
|
||||
try:
|
||||
try:
|
||||
usb_device.get_active_configuration()
|
||||
except self.usb_core.USBError:
|
||||
usb_device.set_configuration()
|
||||
self.usb_util.claim_interface(usb_device, self.interface)
|
||||
except (self.usb_core.USBError, OSError) as error:
|
||||
self._dispose_usb_device(usb_device)
|
||||
self._debug(
|
||||
f"DECtalk USB open failed: {error}",
|
||||
debug.DebugLevel.ERROR,
|
||||
on_any_level=True,
|
||||
)
|
||||
raise RuntimeError(f"DECtalk USB open failed: {error}") from error
|
||||
|
||||
self.usb_device = usb_device
|
||||
self._is_initialized = True
|
||||
self._stop_worker = False
|
||||
self.worker_thread = threading.Thread(
|
||||
target=self._worker,
|
||||
daemon=True,
|
||||
)
|
||||
self.worker_thread.start()
|
||||
self._debug(
|
||||
"DECtalk USB device opened: 0dd0:2001",
|
||||
debug.DebugLevel.INFO,
|
||||
on_any_level=True,
|
||||
)
|
||||
|
||||
def shutdown(self):
|
||||
if not self._is_initialized:
|
||||
return
|
||||
self._stop_worker = True
|
||||
self.clear_buffer()
|
||||
self.text_queue.put(None)
|
||||
if self.worker_thread:
|
||||
self.worker_thread.join(timeout=0.5)
|
||||
|
||||
try:
|
||||
with self.lock, self.immediate_lock:
|
||||
usb_device = self.usb_device
|
||||
self.usb_device = None
|
||||
if usb_device is None:
|
||||
return
|
||||
try:
|
||||
self.usb_util.release_interface(
|
||||
usb_device,
|
||||
self.interface,
|
||||
)
|
||||
except (self.usb_core.USBError, OSError) as error:
|
||||
self._debug(
|
||||
f"DECtalk USB interface release failed: {error}",
|
||||
debug.DebugLevel.WARNING,
|
||||
)
|
||||
self._dispose_usb_device(usb_device)
|
||||
finally:
|
||||
self._is_initialized = False
|
||||
|
||||
def cancel(self):
|
||||
if not self._is_initialized:
|
||||
return
|
||||
self.clear_buffer()
|
||||
self._write_endpoint(
|
||||
self.immediate_endpoint,
|
||||
b"\x06",
|
||||
"cancel",
|
||||
write_lock=self.immediate_lock,
|
||||
)
|
||||
|
||||
def _write_bytes(self, data, description="data"):
|
||||
self._write_endpoint(self.speech_endpoint, data, description)
|
||||
|
||||
def _write_endpoint(
|
||||
self,
|
||||
endpoint,
|
||||
data,
|
||||
description,
|
||||
write_lock=None,
|
||||
):
|
||||
if not data:
|
||||
return
|
||||
if write_lock is None:
|
||||
write_lock = self.lock
|
||||
with write_lock:
|
||||
if self.usb_device is None:
|
||||
return
|
||||
try:
|
||||
total_written = 0
|
||||
while total_written < len(data):
|
||||
chunk = data[
|
||||
total_written : total_written + self.packet_size
|
||||
]
|
||||
bytes_written = self.usb_device.write(
|
||||
endpoint,
|
||||
chunk,
|
||||
timeout=self.write_timeout,
|
||||
)
|
||||
if bytes_written != len(chunk):
|
||||
raise OSError(
|
||||
"USB write returned "
|
||||
f"{bytes_written} of {len(chunk)} bytes"
|
||||
)
|
||||
total_written += bytes_written
|
||||
preview = self._format_bytes_preview(data)
|
||||
self._debug(
|
||||
"DECtalk USB wrote "
|
||||
f"{total_written} {description} bytes "
|
||||
f"to endpoint 0x{endpoint:02x}: {preview}",
|
||||
debug.DebugLevel.INFO,
|
||||
on_any_level=True,
|
||||
)
|
||||
except (self.usb_core.USBError, OSError) as error:
|
||||
self._debug(
|
||||
f"DECtalk USB write failed: {error}",
|
||||
debug.DebugLevel.ERROR,
|
||||
on_any_level=True,
|
||||
)
|
||||
|
||||
def _dispose_usb_device(self, usb_device):
|
||||
try:
|
||||
self.usb_util.dispose_resources(usb_device)
|
||||
except Exception as error:
|
||||
self._debug(
|
||||
f"DECtalk USB resource disposal failed: {error}",
|
||||
debug.DebugLevel.WARNING,
|
||||
)
|
||||
|
||||
def _load_usb_modules(self):
|
||||
import usb.core
|
||||
import usb.util
|
||||
|
||||
return usb.core, usb.util
|
||||
Reference in New Issue
Block a user