-
Notifications
You must be signed in to change notification settings - Fork 296
/
Copy pathWhisperStream.swift
98 lines (80 loc) · 2.46 KB
/
WhisperStream.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
import AVFoundation
public struct Segment {
let text: String
let t0: Int64
let t1: Int64
}
public typealias OrderedSegments = [Segment]
public extension OrderedSegments {
var text: any StringProtocol {
map { $0.text }.joined()
}
}
public class WhisperStream: Thread {
let waiter = DispatchGroup()
@Published public private(set) var segments = OrderedSegments()
@Published public private(set) var alive = true
let model: URL
let device: CaptureDevice?
let window: TimeInterval
public init(model: URL, device: CaptureDevice? = nil, window: TimeInterval = 300) {
self.model = model
self.device = device
self.window = window
super.init()
}
public override func start() {
waiter.enter()
super.start()
}
public override func main() {
task()
waiter.leave()
}
public func join() {
waiter.wait()
}
func task() {
model.path.withCString { modelCStr in
var params = stream_default_params()
params.model = modelCStr
if let device = device {
params.capture_id = device.id
}
let ctx = stream_init(params)
if ctx == nil {
return
}
while !self.isCancelled {
let errno = stream_run(ctx, Unmanaged.passUnretained(self).toOpaque()) {
return Unmanaged<WhisperStream>.fromOpaque($3!).takeUnretainedValue().callback(
text: $0 != nil ? String(cString: $0!) : nil,
t0: $1,
t1: $2
)
}
if errno != 0 {
break
}
}
stream_free(ctx)
alive = false
}
}
func callback(text: String?, t0: Int64, t1: Int64) -> Int32 {
if segments.isEmpty || text == nil {
segments.append(Segment(text: "", t0: -1, t1: -1))
}
if let text = text {
segments[segments.count - 1] = Segment(text: text, t0: t0, t1: t1)
}
var k = 0
for segment in segments {
if let last = segments.last, last.t0 - segment.t0 > Int64(window * 1000) {
k += 1
}
}
segments.removeFirst(k)
return 0
}
}