Files
2026-07-13 12:45:58 +08:00

181 lines
6.7 KiB
Ruby

# frozen_string_literal: true
require "ffi"
module Vosk
module C # :nodoc: all
extend FFI::Library
# FIXME: Load same way as in Python, test on Windows
# This second option, 'vosk', allows system-wide installed library to be loaded.
# I see you search /usr/share/vosk, so I guess it's supported somehow.
# It'll allow the gem to be used on systems not supported in pre-compiled releases.
# (in fact, we only need the first in a pre-compiled release and the second otherwise,
# but not worth the effort to put more configuration in the build stage - not possible without hacks)
# But when we load a lib not shipped with the gem itself, we (might) need to ensure it's a compatible version
# Note: options in the array are alternatives, only the first found is loaded
# Note: probably needs RubyInstaller::Runtime.add_dll_directory on Windows - if FFI::Platform.windows?
ffi_lib [File.join(__dir__, FFI.map_library_name("vosk")), "vosk"]
class VoskModel < FFI::AutoPointer
def self.from_native(ptr, _ctx)
raise Error, "Failed to create a model" if ptr.null?
super
end
def self.release(ptr)
C.vosk_model_free(ptr)
end
end
class VoskSpkModel < FFI::AutoPointer
def self.from_native(ptr, _ctx)
raise Error, "Failed to create a speaker model" if ptr.null?
super
end
def self.release(ptr)
C.vosk_spk_model_free(ptr)
end
end
VoskEndpointerMode = enum(
:VoskEndpointerMode,
[
:default, 0,
:short, 1,
:long, 2,
:very_long, 3,
],
)
class VoskRecognizer < FFI::AutoPointer
def self.from_native(ptr, _ctx)
raise Error, "Failed to create a recognizer" if ptr.null?
super
end
def self.release(ptr)
C.vosk_recognizer_free(ptr)
end
end
class VoskBatchModel < FFI::AutoPointer
def self.from_native(ptr, _ctx)
raise Error, "Failed to create a model" if ptr.null?
super
end
def self.release(ptr)
C.vosk_batch_model_free(ptr)
end
end
class VoskBatchRecognizer < FFI::AutoPointer
def self.from_native(ptr, _ctx)
raise Error, "Failed to create a recognizer" if ptr.null?
super
end
def self.release(ptr)
C.vosk_batch_recognizer_free(ptr)
end
end
class VoskTextProcessor < FFI::AutoPointer
def self.from_native(ptr, _ctx)
raise Error, "Failed to create processor" if ptr.null?
super
end
def self.release(ptr)
C.vosk_text_processor_free(ptr)
end
end
attach_function :vosk_model_new, [:string], VoskModel
attach_function :vosk_model_free, [VoskModel], :void
attach_function :vosk_model_find_word, [VoskModel, :string], :int
attach_function :vosk_spk_model_new, [:string], VoskSpkModel
attach_function :vosk_spk_model_free, [VoskSpkModel], :void
attach_function :vosk_recognizer_new, [VoskModel, :float], VoskRecognizer
attach_function :vosk_recognizer_new_spk, [VoskModel, :float, VoskSpkModel], VoskRecognizer
attach_function :vosk_recognizer_new_grm, [VoskModel, :float, :string], VoskRecognizer
attach_function :vosk_recognizer_set_spk_model, [VoskRecognizer, VoskSpkModel], :void
attach_function :vosk_recognizer_set_grm, [VoskRecognizer, :string], :void
attach_function :vosk_recognizer_set_max_alternatives, [VoskRecognizer, :int], :void
attach_function :vosk_recognizer_set_words, [VoskRecognizer, :int], :void
attach_function :vosk_recognizer_set_partial_words, [VoskRecognizer, :int], :void
attach_function :vosk_recognizer_set_nlsml, [VoskRecognizer, :int], :void
# TODO: remove this, it was needed only because I used libvosk from Python package
if Gem::Version.new(VERSION) >= Gem::Version.new("0.3.46")
attach_function :vosk_recognizer_set_endpointer_mode, [VoskRecognizer, VoskEndpointerMode], :void
attach_function :vosk_recognizer_set_endpointer_delays, [VoskRecognizer, :float, :float, :float], :void
end
attach_function :vosk_recognizer_accept_waveform, [VoskRecognizer, :buffer_in, :int], :int
# vosk_recognizer_accept_waveform_s; vosk_recognizer_accept_waveform_f - skipped
attach_function :vosk_recognizer_result, [VoskRecognizer], :string
attach_function :vosk_recognizer_partial_result, [VoskRecognizer], :string
attach_function :vosk_recognizer_final_result, [VoskRecognizer], :string
attach_function :vosk_recognizer_reset, [VoskRecognizer], :void
attach_function :vosk_recognizer_free, [VoskRecognizer], :void
attach_function :vosk_set_log_level, [:int], :void
attach_function :vosk_gpu_init, [], :void
attach_function :vosk_gpu_thread_init, [], :void
attach_function :vosk_batch_model_new, [:string], VoskBatchModel
attach_function :vosk_batch_model_free, [VoskBatchModel], :void
attach_function :vosk_batch_model_wait, [VoskBatchModel], :void
attach_function :vosk_batch_recognizer_new, [VoskBatchModel, :float], VoskBatchRecognizer
attach_function :vosk_batch_recognizer_free, [VoskBatchRecognizer], :void
attach_function :vosk_batch_recognizer_accept_waveform, [VoskBatchRecognizer, :buffer_in, :int], :void
# vosk_batch_recognizer_set_nlsml - skipped
attach_function :vosk_batch_recognizer_finish_stream, [VoskBatchRecognizer], :void
attach_function :vosk_batch_recognizer_front_result, [VoskBatchRecognizer], :string
attach_function :vosk_batch_recognizer_pop, [VoskBatchRecognizer], :void
attach_function :vosk_batch_recognizer_get_pending_chunks, [VoskBatchRecognizer], :int
# https://github.com/ffi/ffi/issues/467
class OwnedString
extend FFI::DataConverter
native_type :strptr
def self.to_native(_value, _context)
raise TypeError, "owned_string can't be used for input"
end
def self.from_native((str, ptr), _context)
C.free(ptr)
str
end
end
typedef OwnedString, :owned_string
attach_function :free, [:pointer], :void
# TODO: remove this, it was needed only because I used libvosk from Python package
if Gem::Version.new(VERSION) >= Gem::Version.new("0.3.48")
attach_function :vosk_text_processor_new, [:string, :string], VoskTextProcessor
attach_function :vosk_text_processor_free, [VoskTextProcessor], :void
# NOTE: you have a memory leak here in your python version, needs to be
# ptr = _c.vosk_text_processor_itn(self._handle, text.encode('utf-8'))
# str = _ffi.string(ptr).decode('utf-8')
# _ffi.gc(ptr, _c.free) # or call libc free directly
attach_function :vosk_text_processor_itn, [VoskTextProcessor, :string], :owned_string
end
end
private_constant :C
end