@@ -3,113 +3,91 @@ import ReactiveSwift
3
3
import Speech
4
4
5
5
extension SpeechClient {
6
- static let live = SpeechClient (
7
- finishTask: { id in
8
- . fireAndForget {
9
- dependencies [ id] ? . finish ( )
10
- dependencies [ id] ? . subscriber. sendCompleted ( )
11
- dependencies [ id] = nil
12
- }
13
- } ,
14
- recognitionTask: { id, request in
15
- Effect { subscriber, lifetime in
16
- lifetime += AnyDisposable {
17
- dependencies [ id] ? . cancel ( )
18
- dependencies [ id] = nil
6
+ static var live : Self {
7
+ var audioEngine : AVAudioEngine ?
8
+ var inputNode : AVAudioInputNode ?
9
+ var recognitionTask : SFSpeechRecognitionTask ?
10
+
11
+ return Self (
12
+ finishTask: {
13
+ . fireAndForget {
14
+ audioEngine? . stop ( )
15
+ inputNode? . removeTap ( onBus: 0 )
16
+ recognitionTask? . finish ( )
19
17
}
18
+ } ,
19
+ recognitionTask: { request in
20
+ Effect { subscriber, lifetime in
21
+ let speechRecognizer = SFSpeechRecognizer ( locale: Locale ( identifier: " en-US " ) ) !
22
+ let speechRecognizerDelegate = SpeechRecognizerDelegate (
23
+ availabilityDidChange: { available in
24
+ subscriber. send ( value: . availabilityDidChange( isAvailable: available) )
25
+ }
26
+ )
27
+ speechRecognizer. delegate = speechRecognizerDelegate
20
28
21
- let speechRecognizer = SFSpeechRecognizer ( locale: Locale ( identifier: " en-US " ) ) !
22
- let speechRecognizerDelegate = SpeechRecognizerDelegate (
23
- availabilityDidChange: { available in
24
- subscriber. send ( value: . availabilityDidChange( isAvailable: available) )
29
+ let cancellable = AnyDisposable {
30
+ audioEngine? . stop ( )
31
+ inputNode? . removeTap ( onBus: 0 )
32
+ recognitionTask? . cancel ( )
33
+ _ = speechRecognizer
34
+ _ = speechRecognizerDelegate
25
35
}
26
- )
27
- speechRecognizer. delegate = speechRecognizerDelegate
28
36
29
- let audioEngine = AVAudioEngine ( )
30
- let audioSession = AVAudioSession . sharedInstance ( )
31
- do {
32
- try audioSession. setCategory ( . record, mode: . measurement, options: . duckOthers)
33
- try audioSession. setActive ( true , options: . notifyOthersOnDeactivation)
34
- } catch {
35
- subscriber. send ( error: . couldntConfigureAudioSession)
36
- return
37
- }
38
- let inputNode = audioEngine. inputNode
37
+ lifetime += cancellable
39
38
40
- let recognitionTask = speechRecognizer . recognitionTask ( with : request ) { result , error in
41
- switch ( result , error ) {
42
- case let ( . some ( result ) , _ ) :
43
- subscriber . send ( value : . taskResult ( SpeechRecognitionResult ( result ) ) )
44
- case let ( _ , . some ( error ) ) :
45
- subscriber . send ( error : . taskError )
46
- case ( . none , . none ) :
47
- fatalError ( " It should not be possible to have both a nil result and nil error. " )
39
+ audioEngine = AVAudioEngine ( )
40
+ let audioSession = AVAudioSession . sharedInstance ( )
41
+ do {
42
+ try audioSession . setCategory ( . record , mode : . measurement , options : . duckOthers )
43
+ try audioSession . setActive ( true , options : . notifyOthersOnDeactivation )
44
+ } catch {
45
+ subscriber . send ( error : . couldntConfigureAudioSession )
46
+ return
48
47
}
49
- }
48
+ inputNode = audioEngine! . inputNode
50
49
51
- dependencies [ id] = SpeechDependencies (
52
- audioEngine: audioEngine,
53
- inputNode: inputNode,
54
- recognitionTask: recognitionTask,
55
- speechRecognizer: speechRecognizer,
56
- speechRecognizerDelegate: speechRecognizerDelegate,
57
- subscriber: subscriber
58
- )
50
+ recognitionTask = speechRecognizer. recognitionTask ( with: request) { result, error in
51
+ switch ( result, error) {
52
+ case let ( . some( result) , _) :
53
+ subscriber. send ( value: . taskResult( SpeechRecognitionResult ( result) ) )
54
+ case ( _, . some) :
55
+ subscriber. send ( error: . taskError)
56
+ case ( . none, . none) :
57
+ fatalError ( " It should not be possible to have both a nil result and nil error. " )
58
+ }
59
+ }
59
60
60
- inputNode. installTap (
61
- onBus: 0 ,
62
- bufferSize: 1024 ,
63
- format: inputNode. outputFormat ( forBus: 0 )
64
- ) { buffer, when in
65
- request. append ( buffer)
66
- }
61
+ inputNode!. installTap (
62
+ onBus: 0 ,
63
+ bufferSize: 1024 ,
64
+ format: inputNode!. outputFormat ( forBus: 0 )
65
+ ) { buffer, when in
66
+ request. append ( buffer)
67
+ }
68
+
69
+ audioEngine!. prepare ( )
70
+ do {
71
+ try audioEngine!. start ( )
72
+ } catch {
73
+ subscriber. send ( error: . couldntStartAudioEngine)
74
+ return
75
+ }
67
76
68
- audioEngine. prepare ( )
69
- do {
70
- try audioEngine. start ( )
71
- } catch {
72
- subscriber. send ( error: . couldntStartAudioEngine)
73
77
return
74
78
}
75
-
76
- return
77
- }
78
- . cancellable ( id: id)
79
- } ,
80
- requestAuthorization: {
81
- . future { callback in
82
- SFSpeechRecognizer . requestAuthorization { status in
83
- callback ( . success( status) )
79
+ } ,
80
+ requestAuthorization: {
81
+ . future { callback in
82
+ SFSpeechRecognizer . requestAuthorization { status in
83
+ callback ( . success( status) )
84
+ }
84
85
}
85
86
}
86
- }
87
- )
88
- }
89
-
90
- private struct SpeechDependencies {
91
- let audioEngine : AVAudioEngine
92
- let inputNode : AVAudioInputNode
93
- let recognitionTask : SFSpeechRecognitionTask
94
- let speechRecognizer : SFSpeechRecognizer
95
- let speechRecognizerDelegate : SpeechRecognizerDelegate
96
- let subscriber : Signal < SpeechClient . Action , SpeechClient . Error > . Observer
97
-
98
- func finish( ) {
99
- self . audioEngine. stop ( )
100
- self . inputNode. removeTap ( onBus: 0 )
101
- self . recognitionTask. finish ( )
102
- }
103
-
104
- func cancel( ) {
105
- self . audioEngine. stop ( )
106
- self . inputNode. removeTap ( onBus: 0 )
107
- self . recognitionTask. cancel ( )
87
+ )
108
88
}
109
89
}
110
90
111
- private var dependencies : [ AnyHashable : SpeechDependencies ] = [ : ]
112
-
113
91
private class SpeechRecognizerDelegate : NSObject , SFSpeechRecognizerDelegate {
114
92
var availabilityDidChange : ( Bool ) -> Void
115
93
0 commit comments