Skip to content

Automatic stop of recording after some seconds of silence #15

Description

@iamklim

Hello ✌️ Thank you for such a wonderful library!

In my app I wanted to implement something similar to dictation button in Safari search:

  1. User taps button
  2. User speaks
  3. When user is not speaking for about 2 seconds, dictation stops automatically
RPReplay_Final1698318345.MP4

This way user doesn't need to tap on a button again to stop dictation. There are built-in methods swiftSpeechRecordOnHold and swiftSpeechToggleRecordingOnTap, but both of them need additional interaction from user. Also there was a need for different button.

Here is how I solved this, maybe this will be helpful for somebody in the future. Will be happy to hear any comments on how this can be done better:


import SwiftUI
import SwiftSpeech

// Creating new extension with custom record button view
public extension SwiftSpeech {
    struct RecordButtonCustom: View {
        public var body: some View {
            RecordButtonView()
        }
    }
}

// Define new EnvironmentKey for custom state
struct DictationState: EnvironmentKey {
    static let defaultValue: SwiftSpeech.State = .pending
}

// Define new Environment Values for custom state
extension EnvironmentValues {
    var dictationState: SwiftSpeech.State {
        get {
            self[DictationState.self]
        }
        set {
            self[DictationState.self] = newValue
        }
    }
}

struct SwiftSpeechView: View {
    @State private var text = "Tap to Speak"
    @State private var timer: Timer?
    @State var dictationState: SwiftSpeech.State = .pending
    
    var body: some View {
        VStack() {

            Text(text)
            
            SwiftSpeech
                .RecordButtonCustom()
                .swiftSpeechToggleRecordingOnTap(locale: Locale(identifier: "en_US"))
                .onRecognizeLatest(
                    includePartialResults: true,
                    handleResult: { session, result in
                        text = result.bestTranscription.formattedString
                        
                        timer?.invalidate()
                        // initiate timer to stop recording after 2 seconds of silence
                        timer = Timer.scheduledTimer(withTimeInterval: 2.0, repeats: false) { timer in
                            session.stopRecording()
                            dictationState = .pending
                        }
                    },
                    handleError: { session, error in
                        text = "Error \((error as NSError).code)"
                    
                        session.stopRecording()
                        dictationState = .pending
                })
                .onStartRecording { session in
                    dictationState = .recording
                }
                .onStopRecording { session in
                    dictationState = .pending
                }
                .onCancelRecording{ session in
                    dictationState = .cancelling
                }

        }
        .onAppear {
            SwiftSpeech.requestSpeechRecognitionAuthorization()
        }
        .environment(\.dictationState, dictationState)
    }
}

#Preview {
    SwiftSpeechView()
}
import SwiftUI
import SwiftSpeech

struct RecordButtonView: View {
    
    @Environment(\.dictationState) var state: SwiftSpeech.State
    
    public init() { }
    
    var icon: String {
        switch state {
        case .pending:
            return "mic"
        case .recording:
            return "mic.fill"
        case .cancelling:
            return "xmark"
        }
    }
    
    public var body: some View {
        Button("Dictate", systemImage: icon, action: {
            print("Dictate")
        })
        .buttonStyle(.borderless)
        .labelStyle(.iconOnly)
        .help("Dictate")
    }
    
}

#Preview {
    RecordButtonView()
}

My stack:
Xcode 15.1 beta
visionOS 1.0

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions