swift-tts

2.1.2

A straightforward package containing version for Swift modern concurrency, Point-Free Swift Dependency compatible with TCA and a Combine wrapper to provide Text to Speech (TTS) over AVFoundation/AVSpeechSynthesizer
renaudjenny/swift-tts

What's New

2.1.2 Error if built with macOS

2023-03-29T20:31:58Z

SwiftTTS

This package contains some very straightforward wrappers around TTS part of AVFoundation/AVSpeechSynthesizer to allow you using Text to Speech with ease.

  • SwiftTTS Using Swift Concurrency with async await, a couple of AsyncStream
  • SwiftTTSDependency A wrapper around the library above facilitating the integration with Point-Free Dependencies library or a project made with The Composable Architecture (TCA).
  • SwiftTTSCombine the OG library still available in this package

Modern concurrency usage

  • speak(String) -> Void - call this method when you simply want to use the TTS with a simple String
    • isSpeaking() -> AsyncStream<Bool> - to know when the utterance starts to be heard, and when it's stopped
    • speakingProgress() -> AsyncStream<Double> - to know the progress, from 0 to 1
  • rateRatio() -> Float - set the rate to slow down or accelerate the TTS engine
  • setRateRatio(Float) -> Void - set the rate to slow down or accelerate the TTS engine
  • voice() -> AVSpeechSynthesisVoice? - the voice of the TTS engine, by default, it's the voice for en-GB
  • setVoice(AVSpeechSynthesisVoice) -> Void - set the voice of the TTS engine

Example

import SwiftTTS

let tts = SwiftTTS.live

tts.speak("Hello World!")

Task {
    for await isSpeaking in tts.isSpeaking() {
        print("TTS is currently \(isSpeaking ? "speaking" : "not speaking")")
    }
}

Task {
    for await progress in tts.speakingProgress() {
        print("Progress: \(Int(progress * 100))%")
    }
}

tts.setRateRatio(3/4)

tts.speak("Hello World! But slower")

Add @Dependency(\.tts) var tts in your Reducer, you will have access to all functions mentioned above.

Example

import ComposableArchitecture
import Foundation
import SwiftTTSDependency

public struct TTS: ReducerProtocol {
    public struct State: Equatable {
        public var text = ""
        public var isSpeaking = false
        public var speakingProgress = 1.0
        public var rateRatio: Float = 1.0

        public init(
            text: String = "",
            isSpeaking: Bool = false,
            speakingProgress: Double = 1.0,
            rateRatio: Float = 1.0
        ) {
            self.text = text
            self.isSpeaking = isSpeaking
            self.speakingProgress = speakingProgress
            self.rateRatio = rateRatio
        }
    }

    public enum Action: Equatable {
        case changeRateRatio(Float)
        case speak
        case startSpeaking
        case stopSpeaking
        case changeSpeakingProgress(Double)
    }

    @Dependency(\.tts) var tts

    public init() {}

    public var body: some ReducerProtocol<State, Action> {
        Reduce { state, action in
            switch action {
            case let .changeRateRatio(rateRatio):
                state.rateRatio = rateRatio
                tts.setRateRatio(rateRatio)
                return .none
            case .speak:
                tts.speak(state.text)
                return .run { send in
                    for await isSpeaking in tts.isSpeaking() {
                        if isSpeaking {
                            await send(.startSpeaking)
                        } else {
                            await send(.stopSpeaking)
                        }
                    }
                }
            case .startSpeaking:
                state.isSpeaking = true
                return .run { send in
                    for await progress in tts.speakingProgress() {
                        await send(.changeSpeakingProgress(progress))
                    }
                }
            case .stopSpeaking:
                state.isSpeaking = false
                return .none
            case let .changeSpeakingProgress(speakingProgress):
                state.speakingProgress = speakingProgress
                return .none
            }
        }
    }
}

Combine Usage

You can instantiate/inject TTSEngine object, it has this behavior

  • func speak(string: String): call this method when you simply want to use the TTS with a simple String
    • subscribe to isSpeakingPublisher to know when the utterance starts to be heard, and when it's stopped
    • subscribe to speakingProgressPublisher to know the progress, from 0 to 1
  • var rateRatio: Float: set the rate to slow down or accelerate the TTS engine
  • var voice: AVSpeechSynthesisVoice?: set the voice of the TTS engine, by default, it's the voice for en-GB

Example

import Combine
import SwiftTTSCombine

let engine: TTSEngine = SwiftTTSCombine.Engine()
var cancellables = Set<AnyCancellable>()

engine.speak(string: "Hello World!")

engine.isSpeakingPublisher
    .sink { isSpeaking in
        print("TTS is currently \(isSpeaking ? "speaking" : "not speaking")")
    }
    .store(in: &cancellables)

engine.speakingProgressPublisher
    .sink { progress in
        print("Progress: \(Int(progress * 100))%")
    }
    .store(in: &cancellables)

engine.rateRatio = 3/4

engine.speak(string: "Hello World! But slower")

Installation

Xcode

You can add SwiftTTS libs to an Xcode project by adding it as a package dependency.

  1. From the File menu, select Swift Packages › Add Package Dependency...
  2. Enter "https://github.com/renaudjenny/swift-tts" into the package repository URL test field
  3. Select one of the three package that your are interested in. See above

As package dependency

Edit your Package.swift to add one of the library you want among the three available.

let package = Package(
    ...
    dependencies: [
        .package(url: "https://github.com/renaudjenny/swift-tts", from: "2.0.0"),
        ...
    ],
    targets: [
        .target(
            name: "<Your project name>",
            dependencies: [
                .product(name: "SwiftTTS", package: "swift-tts"), // <-- Modern concurrency
                .product(name: "SwiftTTSDependency", package: "swift-tts"), // <-- Point-Free Dependencies library wrapper
                .product(name: "SwiftTTSCombine", package: "swift-tts"), // <-- Combine wrapper
            ]),
        ...
    ]
)

App using this library

Description

  • Swift Tools 5.7.0
View More Packages from this Author

Dependencies

Last updated: Tue Apr 16 2024 15:36:14 GMT-0900 (Hawaii-Aleutian Daylight Time)