Skip to content

Commit

Permalink
feat: some stability & alignment improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
mfkrause committed Nov 21, 2024
1 parent 79fce53 commit 0e4c873
Show file tree
Hide file tree
Showing 6 changed files with 56 additions and 9 deletions.
4 changes: 4 additions & 0 deletions android/src/main/java/com/voicekit/VoiceError.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ sealed class VoiceError(
"ERR_PERMISSION_NOT_DETERMINED",
"Speech recognition permission was not yet determined"
)
object InvalidState : VoiceError(
"ERR_INVALID_STATE",
"Invalid state, cannot perform action"
)
class Unknown(message: String = "An unknown error occurred") : VoiceError(
"ERR_UNKNOWN",
message
Expand Down
16 changes: 14 additions & 2 deletions android/src/main/java/com/voicekit/VoiceKitService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,19 @@ class VoiceKitService(private val context: ReactApplicationContext) {
audioManager?.setStreamVolume(AudioManager.STREAM_NOTIFICATION, previousNotificationVolume, AudioManager.FLAG_ALLOW_RINGER_MODES)
}

fun startListening(options: ReadableMap, skipMuteBeep: Boolean = false): Boolean {
fun startListening(options: ReadableMap, skipMuteBeep: Boolean = false) {
val currentActivity = context.currentActivity
if (currentActivity == null) {
Log.e(TAG, "Activity is null")
throw VoiceError.Unknown("Activity is null")
}

if (isListening) {
Log.w(TAG, "Already listening, aborting startListening")
sendEvent("RNVoiceKit.error", VoiceError.InvalidState)
return
}

this.options = options

// TODO: We currently don't wait for the permission to be granted, but we should
Expand Down Expand Up @@ -114,10 +120,16 @@ class VoiceKitService(private val context: ReactApplicationContext) {

isListening = true

return true
return
}

fun stopListening() {
if (!isListening) {
Log.w(TAG, "Not listening, aborting stopListening")
sendEvent("RNVoiceKit.error", VoiceError.InvalidState)
return
}

lastResultTimer?.removeCallbacksAndMessages(null)
lastResultTimer = null
lastTranscription = null
Expand Down
3 changes: 2 additions & 1 deletion example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ export default function App() {
console.error('Error starting listening', error, error instanceof VoiceError ? error.details : null);
});
}}
disabled={!available || listening}
/>
<Button title="Stop Listening" onPress={() => stopListening()} />
<Button title="Stop Listening" onPress={() => stopListening()} disabled={!listening} />
<Button title="Reset Transcript" onPress={() => resetTranscript()} />
<Text>Transcript: {transcript}</Text>
</View>
Expand Down
31 changes: 25 additions & 6 deletions ios/Core/VoiceKitService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ class VoiceKitService: NSObject, SFSpeechRecognizerDelegate {
Logger.log(level: .info, message: "Starting recording")
Logger.log(level: .debug, message: "Options: \(options)")

if isListening {
Logger.log(level: .warning, message: "Already listening, aborting startRecording")
delegate?.onError(.invalidState)
return
}

// Cancel any ongoing tasks
recognitionTask?.cancel()
recognitionTask = nil
Expand All @@ -64,12 +70,22 @@ class VoiceKitService: NSObject, SFSpeechRecognizerDelegate {
recognitionTask = speechRecognizer?.recognitionTask(with: recognitionRequest!) { [weak self] result, error in
guard let self else { return }

if let result {
Logger.log(level: .debug, message: "SpeechRecognizerResult received: \(result)")
if error != nil {
handleError(error)
}

if let result {
// Store the latest transcription
lastTranscription = result.bestTranscription.formattedString

// if trimmed lastTranscription is empty, stop and dont do anything
if lastTranscription?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty ?? true {
Logger.log(level: .debug, message: "Last transcription is empty, stopping")
return
}

Logger.log(level: .debug, message: "SpeechRecognizerResult received: \(lastTranscription)")

// Emit partial results
delegate?.onPartialResult(lastTranscription ?? "")

Expand All @@ -88,10 +104,6 @@ class VoiceKitService: NSObject, SFSpeechRecognizerDelegate {
}
}
}

if error != nil {
handleError(error)
}
}

// Configure audio engine
Expand Down Expand Up @@ -128,6 +140,13 @@ class VoiceKitService: NSObject, SFSpeechRecognizerDelegate {

func stopRecording() {
Logger.log(level: .info, message: "Stopping recording")

if !isListening {
Logger.log(level: .warning, message: "Not listening, aborting stopRecording")
delegate?.onError(.invalidState)
return
}

audioEngine.stop()
audioEngine.inputNode.removeTap(onBus: 0)
recognitionRequest?.endAudio()
Expand Down
4 changes: 4 additions & 0 deletions ios/Utils/VoiceError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ enum VoiceError: Error {
case permissionDenied
case permissionRestricted
case permissionNotDetermined
case invalidState
case unknown(message: String = "An unknown error occurred")

var code: String {
Expand All @@ -25,6 +26,7 @@ enum VoiceError: Error {
case .permissionDenied: "ERR_PERMISSION_DENIED"
case .permissionRestricted: "ERR_PERMISSION_RESTRICTED"
case .permissionNotDetermined: "ERR_PERMISSION_NOT_DETERMINED"
case .invalidState: "ERR_INVALID_STATE"
case .unknown: "ERR_UNKNOWN"
}
}
Expand All @@ -43,6 +45,8 @@ enum VoiceError: Error {
"Speech recognition is restricted on this device"
case .permissionNotDetermined:
"Speech recognition permission was not yet determined"
case .invalidState:
"Invalid state, cannot perform action"
case let .unknown(message):
message
}
Expand Down
7 changes: 7 additions & 0 deletions src/types/native.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import type { VoiceStartListeningOptions } from '.';

export enum VoiceErrorCode {
SPEECH_RECOGNIZER_NOT_AVAILABLE = 'ERR_SPEECH_RECOGNIZER_NOT_AVAILABLE',
RECORDING_START_FAILED = 'ERR_RECORDING_START_FAILED',
RECOGNITION_FAILED = 'ERR_RECOGNITION_FAILED',
PERMISSION_DENIED = 'ERR_PERMISSION_DENIED',
PERMISSION_RESTRICTED = 'ERR_PERMISSION_RESTRICTED',
PERMISSION_NOT_DETERMINED = 'ERR_PERMISSION_NOT_DETERMINED',
INVALID_STATE = 'ERR_INVALID_STATE',
UNKNOWN = 'ERR_UNKNOWN',
}

Expand Down

0 comments on commit 0e4c873

Please sign in to comment.