import { SpeakOptions } from './index' ; @ NativeClass class MySpeechDelegate extends NSObject { doneCallback ; public static ObjCProtocols = [ AVSpeechSynthesizerDelegate ] ; public speechSynthesizerDidStartSpeechUtterance ( synthesizer , utterance ) { } public speechSynthesizerDidFinishSpeechUtterance ( synthesizer , utterance ) { if ( this . doneCallback ) { this . doneCallback ( ) ; } } public speechSynthesizerDidPauseSpeechUtterance ( synthesizer , utterance ) { } public speechSynthesizerDidContinueSpeechUtterance ( synthesizer , utterance ) { } public speechSynthesizerDidCancelSpeechUtterance ( synthesizer , utterance ) { } } function isString ( elem ) { return typeof elem === 'string' ; } function isBoolean ( elem ) { return typeof elem === 'boolean' ; } function isNumber ( elem ) { return typeof elem === 'number' && isFinite ( elem ) ; } export class TNSTextToSpeech { private _speechSynthesizer : AVSpeechSynthesizer ; private _lastOptions : SpeakOptions = null ; delegate : MySpeechDelegate ; public async init ( ) { } public async speak ( options ) { if ( ! this . _speechSynthesizer ) { this . _speechSynthesizer = AVSpeechSynthesizer . alloc ( ) . init ( ) ; this . _speechSynthesizer . delegate = this . delegate = new MySpeechDelegate ( ) ; } if ( ! isString ( options . text ) ) { throw new Error ( 'Text is required to speak.' ) ; } this . _lastOptions = options ; this . delegate . doneCallback = options . finishedCallback ; if ( ! isNumber ( options . pitch ) ) { options . pitch = 1.0 ; } else if ( options . pitch < 0.5 ) { options . pitch = 0.5 ; } else if ( options . pitch > 2.0 ) { options . pitch = 2.0 ; } if ( ! isNumber ( options . speakRate ) ) { options . speakRate = AVSpeechUtteranceMaximumSpeechRate / 4.0 ; } else if ( options . speakRate < AVSpeechUtteranceMinimumSpeechRate ) { options . speakRate = AVSpeechUtteranceMinimumSpeechRate ; } else if ( options . speakRate > AVSpeechUtteranceMaximumSpeechRate ) { options . speakRate = AVSpeechUtteranceMaximumSpeechRate ; } if ( ! isNumber ( options . volume ) || options . volume > 1.0 ) { options . volume = 1.0 ; } else if ( options . volume < 0.0 ) { options . volume = 0.0 ; } const speechUtterance = AVSpeechUtterance . alloc ( ) . initWithString ( options . text ) ; if ( isString ( options . locale ) && this . isValidLocale ( options . locale ) ) { speechUtterance . voice = AVSpeechSynthesisVoice . voiceWithLanguage ( options . locale ) ; } else if ( isString ( options . language ) && this . isValidLocale ( options . language ) ) { speechUtterance . voice = AVSpeechSynthesisVoice . voiceWithLanguage ( options . language ) ; } speechUtterance . pitchMultiplier = options . pitch ; speechUtterance . volume = options . volume ; speechUtterance . rate = options . speakRate ; if ( ! isBoolean ( options . queue ) ) { options . queue = false ; } if ( ! options . queue && this . _speechSynthesizer . speaking ) { this . _speechSynthesizer . stopSpeakingAtBoundary ( AVSpeechBoundary . Immediate ) ; } this . _speechSynthesizer . speakUtterance ( speechUtterance ) ; } public pause ( now ) { this . _speechSynthesizer . pauseSpeakingAtBoundary ( now ? AVSpeechBoundary . Immediate : AVSpeechBoundary . Word ) ; } public resume ( ) { this . _speechSynthesizer . continueSpeaking ( ) ; } public destroy ( ) { this . _speechSynthesizer = null ; } /**
     * Private Methods
     */ private isValidLocale ( locale ) { const re = new RegExp ( '\\w\\w-\\w\\w' ) ; return re . test ( locale ) ; } }