From 7955e017b6a35f4463cdde98eefa59d3c1f98859 Mon Sep 17 00:00:00 2001 From: Matthew Chung Date: Fri, 26 May 2017 17:27:00 -0700 Subject: [PATCH 1/3] Added `speakWithFinish` method added `speakWithFinish` with the callback responding when the utterance is finished --- SpeechSynthesizer.ios.js | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/SpeechSynthesizer.ios.js b/SpeechSynthesizer.ios.js index e148dda..0edf4c5 100644 --- a/SpeechSynthesizer.ios.js +++ b/SpeechSynthesizer.ios.js @@ -13,9 +13,9 @@ var NativeSpeechSynthesizer = NativeModules.SpeechSynthesizer; */ var SpeechSynthesizer = { - speak(options) { - return new Promise(function(resolve, reject) { - NativeSpeechSynthesizer.speakUtterance(options, function(error, success) { + speak(options: { [string]: string | number }) { + return new Promise(function (resolve, reject) { + NativeSpeechSynthesizer.speakUtterance(options, function (error, success) { if (error) { return reject(error); } @@ -25,6 +25,17 @@ var SpeechSynthesizer = { }); }, + speakWithFinish(options: { [string]: string | number }) { + return new Promise(function (resolve, reject) { + NativeSpeechSynthesizer.speakUtteranceWithFinish(options, function (error, success) { + if (error) { + return reject(error); + } + resolve(true); + }); + }); + }, + stop: NativeSpeechSynthesizer.stopSpeakingAtBoundary, pause: NativeSpeechSynthesizer.pauseSpeakingAtBoundary, @@ -32,8 +43,8 @@ var SpeechSynthesizer = { resume: NativeSpeechSynthesizer.continueSpeakingAtBoundary, isPaused() { - return new Promise(function(resolve, reject) { - NativeSpeechSynthesizer.paused(function(error, paused) { + return new Promise(function (resolve, reject) { + NativeSpeechSynthesizer.paused(function (error, paused) { if (error) { return reject(error); } @@ -48,8 +59,8 @@ var SpeechSynthesizer = { }, isSpeaking() { - return new Promise(function(resolve, reject) { - NativeSpeechSynthesizer.speaking(function(error, speaking) { + return new Promise(function (resolve, reject) { + NativeSpeechSynthesizer.speaking(function (error, speaking) { if (error) { return reject(error); } @@ -64,8 +75,8 @@ var SpeechSynthesizer = { }, supportedVoices() { - return new Promise(function(resolve, reject) { - NativeSpeechSynthesizer.speechVoices(function(error, locales) { + return new Promise(function (resolve, reject) { + NativeSpeechSynthesizer.speechVoices(function (error, locales) { if (error) { return reject(error); } From 1f1b5c8c220881706d5051f986d2363e5d6668d7 Mon Sep 17 00:00:00 2001 From: Matthew Chung Date: Fri, 26 May 2017 17:27:57 -0700 Subject: [PATCH 2/3] Added callback as instance var Added callback as instance var so it can be called from the delegate cb --- SpeechSynthesizer.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/SpeechSynthesizer.h b/SpeechSynthesizer.h index f61d036..82b728a 100644 --- a/SpeechSynthesizer.h +++ b/SpeechSynthesizer.h @@ -4,4 +4,8 @@ @interface SpeechSynthesizer : NSObject @property (nonatomic, strong) AVSpeechSynthesizer *synthesizer; +@property (nonatomic) RCTResponseSenderBlock cb; + + + @end From a27906ac77fea50add3bac0e6bda912fad4bc18e Mon Sep 17 00:00:00 2001 From: Matthew Chung Date: Fri, 26 May 2017 17:29:59 -0700 Subject: [PATCH 3/3] Added `speakUtteranceWithFinish` as api call for handling completion Added to `didFinishSpeechUtterance` to call completion cb Added `speakUtteranceWithFinish` as api call for handling completion of utterances --- SpeechSynthesizer.m | 56 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/SpeechSynthesizer.m b/SpeechSynthesizer.m index f7de53c..12ea603 100644 --- a/SpeechSynthesizer.m +++ b/SpeechSynthesizer.m @@ -56,6 +56,56 @@ @implementation SpeechSynthesizer callback(@[[NSNull null], @true]); } +// SpeakWithFinish +RCT_EXPORT_METHOD(speakUtteranceWithFinish:(NSDictionary *)args callback:(RCTResponseSenderBlock)callback) +{ + // Error if self.synthesizer was already initialized + if (self.synthesizer || self.cb) { + return callback(@[RCTMakeError(@"There is a speech in progress. Use the `paused` method to know if it's paused.", nil, nil)]); + } + + self.cb = callback; + + // Set args to variables + NSString *text = args[@"text"]; + NSString *voice = args[@"voice"]; + NSNumber *rate = args[@"rate"]; + + // Error if no text is passed + if (!text) { + RCTLogError(@"[Speech] You must specify a text to speak."); + return; + } + + // Set default voice + NSString *voiceLanguage; + + // Set voice if provided + if (voice) { + voiceLanguage = voice; + + // Fallback to en-US + } else { + voiceLanguage = @"en-US"; + } + + // Setup utterance and voice + AVSpeechUtterance *utterance = [[AVSpeechUtterance alloc] initWithString:text]; + + utterance.voice = [AVSpeechSynthesisVoice voiceWithLanguage:voiceLanguage]; + + if (rate) { + utterance.rate = [rate doubleValue]; + } + + self.synthesizer = [[AVSpeechSynthesizer alloc] init]; + self.synthesizer.delegate = self; + + // Speak + [self.synthesizer speakUtterance:utterance]; +} + + // Stops synthesizer RCT_EXPORT_METHOD(stopSpeakingAtBoundary) { @@ -116,6 +166,12 @@ -(void)speechSynthesizer:(AVSpeechSynthesizer *)synthesizer didFinishSpeechUtter { NSLog(@"Speech finished"); self.synthesizer = nil; + + if (self.cb) { + // Return that the speach has started + self.cb(@[[NSNull null], @true]); + self.cb = nil; + } } // Started Handler