Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/.idea/
/android/build/
19 changes: 17 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,22 @@ var Speech = require('react-native-speech');
var YourComponent = React.createClass({
_startHandler() {
Speech.speak({
text: 'Aujourd\'hui, Maman est morte. Ou peut-être hier, je ne sais pas.',
text: 'Nous faisons le test 1',
voice: 'fr-FR'
})
.then(started => {
console.log('Speech started');
console.log('Fin du test 1');
return Speech.speak({
text: 'Et voilà le test 2',
voice: 'fr-FR'
})
})
.then(started => {
console.log('Fin du test 2');
return Speech.speak({
text: 'Et maintenant le test 3',
voice: 'fr-FR'
})
})
.catch(error => {
console.log('You\'ve already started a speech instance.');
Expand Down Expand Up @@ -120,6 +131,10 @@ Speech.speak({
});
```

__Android feature__
If you don't add forceStop = true argument to speak parameters your next speech will be queue.


### pause()
Pauses the speech instance.

Expand Down
20 changes: 20 additions & 0 deletions SpeechEventEmitter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//
// SpeechEventEmitter.h
// Ola Mundo PA
//
// Created by Alex Pavtoulov on 10/11/2016.
// Copyright © 2016 Ola Mundo Ltd. All rights reserved.
//

#ifndef SpeechEventEmitter_h
#define SpeechEventEmitter_h

#import "RCTEventEmitter.h"

@interface SpeechEventEmitter : RCTEventEmitter

+ (BOOL)application:(UIApplication *)application speechFinished:(NSString *)speechID;

@end

#endif /* SpeechEventEmitter_h */
51 changes: 51 additions & 0 deletions SpeechEventEmitter.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//
// SpeechEventEmitter.m
// Ola Mundo PA
//
// Created by Alex Pavtoulov on 10/11/2016.


#import <Foundation/Foundation.h>
#import "SpeechEventEmitter.h"
#import "RCTBridge.h"
#import "RCTEventDispatcher.h"

NSString *const speechFinishEvent = @"speech-finished";
NSString *const kSpeechFinishNotification = @"SpeechFinishNotification";


@implementation SpeechEventEmitter

RCT_EXPORT_MODULE();

- (NSDictionary<NSString *, NSString *> *)constantsToExport {
return @{@"SPEECH_FINISH_EVENT": speechFinishEvent};
}

- (NSArray<NSString *> *)supportedEvents {
return @[speechFinishEvent];
}

- (void)startObserving {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleSpeechFinishNotification:)
name:speechFinishEvent object:nil];
}

- (void)stopObserving {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}

+ (BOOL)application:(UIApplication *)application speechFinished:(NSString *)speechID {
NSDictionary<NSString *, id> *payload = @{@"payload": speechID};
[[NSNotificationCenter defaultCenter] postNotificationName:speechFinishEvent
object:self
userInfo:payload];
return YES;
}

- (void)handleSpeechFinishNotification:(NSNotification *)notification {
[self sendEventWithName:speechFinishEvent body:notification.userInfo];
}

@end
42 changes: 37 additions & 5 deletions SpeechSynthesizer.android.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,48 @@
/**
* Stub of SpeechSynthesizer for Android.
*
* @providesModule SpeechSynthesizer
* @flow
*/
'use strict';

var warning = require('warning');
var React = require('react-native');
var { NativeModules } = React;
var NativeSpeechSynthesizer = NativeModules.SpeechSynthesizer;

/**
* High-level docs for the SpeechSynthesizer Android API can be written here.
*/

var SpeechSynthesizer = {
test: function() {
warning("Not yet implemented for Android.");
test () {
return NativeSpeechSynthesizer.reactNativeSpeech();
},

supportedVoices() {
return NativeSpeechSynthesizer.supportedVoices();
},

isSpeaking() {
return NativeSpeechSynthesizer.isSpeaking();
},

isPaused() {
return NativeSpeechSynthesizer.isPaused();
},

resume() {
return NativeSpeechSynthesizer.resume();
},

pause() {
return NativeSpeechSynthesizer.pause();
},

stop() {
return NativeSpeechSynthesizer.stop();
},

speak(options) {
return NativeSpeechSynthesizer.speak(options);
}
};

Expand Down
33 changes: 32 additions & 1 deletion SpeechSynthesizer.ios.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,45 @@
'use strict';

var React = require('react-native');
var { NativeModules } = React;
var { NativeModules, NativeEventEmitter } = React;
var NativeSpeechSynthesizer = NativeModules.SpeechSynthesizer;

const { SpeechEventEmitter } = NativeModules;
/**
* High-level docs for the SpeechSynthesizer iOS API can be written here.
* To implement event emitter of didFinishSpeechUtterance event use in your code:
* componentWillMount() {
* if (Platform.OS === 'ios') {
* this.eventEmitter = new NativeEventEmitter(SpeechEventEmitter);
* this.unsubscribeSpeechEvents = this.eventEmitter.addListener(SpeechEventEmitter.SPEECH_FINISH_EVENT, (result) =>
* console.log('Speech Finished! Text spoken: ', result.payload)
* );
* }
* }
*
* componentWillUnmount() {
* // prevent leaking
* if (Platform.OS === 'ios') {this.unsubscribeSpeechEvents()}
*
* }
*
* The default pitch is 1.0. Allowed values are in the range from 0.5 (for lower pitch) to 2.0 (for higher pitch).
* Allowed values are in the range from 0.0 (silent) to 1.0 (loudest). The default volume is 1.0.
* beforeInterval and afterInterval are in seconds, e.g. afterInterval: 1, will wait after the speech before playing the next ßspeech.
* e.g.:
* options = {
* text: 'Hello World',
* voice: 'en-US',
* rate: 0.35,
* pitch: 1,
* beforeInterval: 0.5,
* afterInterval: 0.5,
* volume: 1
* }
*/

var SpeechSynthesizer = {

speak(options) {
return new Promise(function(resolve, reject) {
NativeSpeechSynthesizer.speakUtterance(options, function(error, success) {
Expand Down
47 changes: 30 additions & 17 deletions SpeechSynthesizer.m
Original file line number Diff line number Diff line change
@@ -1,27 +1,34 @@
#import "SpeechSynthesizer.h"
#import "RCTUtils.h"
#import "RCTLog.h"
#import "RCTBridge.h"
#import "SpeechEventEmitter.h"

@implementation SpeechSynthesizer

@synthesize bridge = _bridge;

RCT_EXPORT_MODULE()

// Speak
RCT_EXPORT_METHOD(speakUtterance:(NSDictionary *)args callback:(RCTResponseSenderBlock)callback)
{
RCT_EXPORT_METHOD(speakUtterance:(NSDictionary *)args callback:(RCTResponseSenderBlock)callback) {
// Error if self.synthesizer was already initialized
if (self.synthesizer) {
return callback(@[RCTMakeError(@"There is a speech in progress. Use the `paused` method to know if it's paused.", nil, nil)]);
RCTLogInfo(@"There is a speech in progress. This means your speech request is added to a speech queue.");
}

// Set args to variables
NSString *text = args[@"text"];
NSString *voice = args[@"voice"];
NSNumber *rate = args[@"rate"];
NSNumber *beforeInterval = args[@"beforeInterval"];
NSNumber *afterInterval = args[@"afterInterval"];
NSNumber *pitch = args[@"pitch"];
NSNumber *volume = args[@"volume"];

// Error if no text is passed
if (!text) {
RCTLogError(@"[Speech] You must specify a text to speak.");
RCTLogWarn(@"[Speech] You must specify a text to speak.");
return;
}

Expand All @@ -37,18 +44,22 @@ @implementation SpeechSynthesizer
voiceLanguage = @"en-US";
}

// Setup utterance and voice
// Setup utterance and voice if no instance exists
AVSpeechUtterance *utterance = [[AVSpeechUtterance alloc] initWithString:text];

utterance.voice = [AVSpeechSynthesisVoice voiceWithLanguage:voiceLanguage];

if (rate) {
utterance.rate = [rate doubleValue];

if (rate) { utterance.rate = [rate doubleValue]; }
if (beforeInterval) { utterance.preUtteranceDelay = [beforeInterval doubleValue]; }
if (afterInterval) { utterance.postUtteranceDelay = [afterInterval doubleValue]; }
if (pitch) { utterance.pitchMultiplier = [pitch floatValue]; }
if (volume) { utterance.volume = [volume floatValue]; }

if (!self.synthesizer) {
self.synthesizer = [[AVSpeechSynthesizer alloc] init];
self.synthesizer.delegate = self;
}

self.synthesizer = [[AVSpeechSynthesizer alloc] init];
self.synthesizer.delegate = self;

// Speak
[self.synthesizer speakUtterance:utterance];

Expand Down Expand Up @@ -84,19 +95,19 @@ @implementation SpeechSynthesizer
RCT_EXPORT_METHOD(paused:(RCTResponseSenderBlock)callback)
{
if (self.synthesizer.paused) {
callback(@[@true]);
callback(@[[NSNull null], @true]);
} else {
callback(@[@false]);
callback(@[[NSNull null], @false]);
}
}

// Returns true if synthesizer is speaking
RCT_EXPORT_METHOD(speaking:(RCTResponseSenderBlock)callback)
{
if (self.synthesizer.speaking) {
callback(@[@true]);
callback(@[[NSNull null], @true]);
} else {
callback(@[@false]);
callback(@[[NSNull null], @false]);
}
}

Expand All @@ -114,8 +125,10 @@ @implementation SpeechSynthesizer
// Finished Handler
-(void)speechSynthesizer:(AVSpeechSynthesizer *)synthesizer didFinishSpeechUtterance:(AVSpeechUtterance *)utterance
{
NSLog(@"Speech finished");
NSString *finishString = utterance.speechString;
NSLog(@"Speech finished with - %@", finishString);
self.synthesizer = nil;
[SpeechEventEmitter application:[UIApplication sharedApplication] speechFinished:finishString];
}

// Started Handler
Expand Down
14 changes: 14 additions & 0 deletions SpeechSynthesizer.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

/* Begin PBXBuildFile section */
13BE3DEE1AC21097009241FE /* SpeechSynthesizer.m in Sources */ = {isa = PBXBuildFile; fileRef = 13BE3DED1AC21097009241FE /* SpeechSynthesizer.m */; };
216E87F51DD725770055AA4D /* SpeechEventEmitter.m in Sources */ = {isa = PBXBuildFile; fileRef = 216E87F31DD725770055AA4D /* SpeechEventEmitter.m */; };
/* End PBXBuildFile section */

/* Begin PBXCopyFilesBuildPhase section */
Expand All @@ -26,6 +27,8 @@
134814201AA4EA6300B7C361 /* libSpeechSynthesizer.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libSpeechSynthesizer.a; sourceTree = BUILT_PRODUCTS_DIR; };
13BE3DEC1AC21097009241FE /* SpeechSynthesizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SpeechSynthesizer.h; sourceTree = "<group>"; };
13BE3DED1AC21097009241FE /* SpeechSynthesizer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SpeechSynthesizer.m; sourceTree = "<group>"; };
216E87F31DD725770055AA4D /* SpeechEventEmitter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SpeechEventEmitter.m; sourceTree = "<group>"; };
216E87F41DD725770055AA4D /* SpeechEventEmitter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SpeechEventEmitter.h; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand All @@ -47,9 +50,19 @@
name = Products;
sourceTree = "<group>";
};
216E87F21DD7255C0055AA4D /* EventEmitters */ = {
isa = PBXGroup;
children = (
216E87F31DD725770055AA4D /* SpeechEventEmitter.m */,
216E87F41DD725770055AA4D /* SpeechEventEmitter.h */,
);
name = EventEmitters;
sourceTree = "<group>";
};
58B511D21A9E6C8500147676 = {
isa = PBXGroup;
children = (
216E87F21DD7255C0055AA4D /* EventEmitters */,
13BE3DEC1AC21097009241FE /* SpeechSynthesizer.h */,
13BE3DED1AC21097009241FE /* SpeechSynthesizer.m */,
134814211AA4EA7D00B7C361 /* Products */,
Expand Down Expand Up @@ -113,6 +126,7 @@
buildActionMask = 2147483647;
files = (
13BE3DEE1AC21097009241FE /* SpeechSynthesizer.m in Sources */,
216E87F51DD725770055AA4D /* SpeechEventEmitter.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 23 additions & 0 deletions android/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
apply plugin: 'com.android.library'

android {
compileSdkVersion 23
buildToolsVersion "23.0.2"

defaultConfig {
minSdkVersion 16
targetSdkVersion 23
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
}
}
}

dependencies {
compile 'com.android.support:appcompat-v7:23.1.0'
compile 'com.facebook.react:react-native:+'
}
3 changes: 3 additions & 0 deletions android/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.omega.speech">
</manifest>
Loading