Initial commit
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
# Cordova Text-to-Speech Plugin
|
||||
|
||||
## Platforms
|
||||
|
||||
iOS 7+
|
||||
Windows Phone 8
|
||||
Android 4.0.3+ (API Level 15+)
|
||||
|
||||
## Installation
|
||||
|
||||
```sh
|
||||
cordova plugin add cordova-plugin-tts
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```javascript
|
||||
// basic usage
|
||||
TTS
|
||||
.speak('hello, world!', function () {
|
||||
alert('success');
|
||||
}, function (reason) {
|
||||
alert(reason);
|
||||
});
|
||||
|
||||
// or with more options
|
||||
TTS
|
||||
.speak({
|
||||
text: 'hello, world!',
|
||||
locale: 'en-GB',
|
||||
rate: 0.75
|
||||
}, function () {
|
||||
alert('success');
|
||||
}, function (reason) {
|
||||
alert(reason);
|
||||
});
|
||||
```
|
||||
|
||||
**Tips:** `speak` an empty string to interrupt.
|
||||
|
||||
## API Definitions
|
||||
|
||||
The `onfulfilled` callback will be called when the speech finishes,
|
||||
and the `onrejected` callback (Windows Phone only) will be called when an error occurs.
|
||||
|
||||
If the API is invoked when it's still speaking, the previous speaking will be canceled immediately,
|
||||
but the `onfulfilled` callback of the previous speaking will be called when it stops.
|
||||
|
||||
```typescript
|
||||
declare module TTS {
|
||||
interface IOptions {
|
||||
/** text to speak */
|
||||
text: string;
|
||||
/** a string like 'en-US', 'zh-CN', etc */
|
||||
locale?: string;
|
||||
/** speed rate, 0 ~ 1 */
|
||||
rate?: number;
|
||||
}
|
||||
|
||||
function speak(options: IOptions, onfulfilled: () => void, onrejected: (reason) => void): void;
|
||||
function speak(text: string, onfulfilled: () => void, onrejected: (reason) => void): void;
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"name": "cordova-plugin-tts",
|
||||
"version": "0.2.3",
|
||||
"description": "Cordova Text-to-Speech Plugin",
|
||||
"cordova": {
|
||||
"id": "cordova-plugin-tts",
|
||||
"platforms": [
|
||||
"ios",
|
||||
"wp8",
|
||||
"android"
|
||||
]
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/vilic/cordova-plugin-tts.git"
|
||||
},
|
||||
"keywords": [
|
||||
"cordova",
|
||||
"tts",
|
||||
"text-to-speech",
|
||||
"ecosystem:cordova",
|
||||
"cordova-ios",
|
||||
"cordova-wp8",
|
||||
"cordova-android"
|
||||
],
|
||||
"author": "VILIC VANE",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/vilic/cordova-plugin-tts/issues"
|
||||
},
|
||||
"homepage": "https://github.com/vilic/cordova-plugin-tts#readme"
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
|
||||
<plugin
|
||||
xmlns="http://apache.org/cordova/ns/plugins/1.0"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
id="cordova-plugin-tts"
|
||||
version="0.2.3">
|
||||
<name>TTS</name>
|
||||
<description>Cordova Text-to-Speech Plugin</description>
|
||||
<author>VILIC VANE</author>
|
||||
<license>MIT</license>
|
||||
<keywords>cordova,tts,text-to-speech</keywords>
|
||||
<repo>https://github.com/vilic/cordova-plugin-tts.git</repo>
|
||||
<issue>https://github.com/vilic/cordova-plugin-tts/issues</issue>
|
||||
|
||||
<js-module src="www/tts.js" name="tts">
|
||||
<clobbers target="TTS" />
|
||||
</js-module>
|
||||
|
||||
<!-- ios -->
|
||||
<platform name="ios">
|
||||
<config-file target="config.xml" parent="/*">
|
||||
<feature name="TTS">
|
||||
<param name="ios-package" value="CDVTTS"/>
|
||||
</feature>
|
||||
</config-file>
|
||||
|
||||
<header-file src="src/ios/CDVTTS.h" />
|
||||
<source-file src="src/ios/CDVTTS.m" />
|
||||
</platform>
|
||||
|
||||
<!-- windows phone 8 -->
|
||||
<platform name="wp8">
|
||||
<config-file target="config.xml" parent="/*">
|
||||
<feature name="TTS">
|
||||
<param name="wp-package" value="TTS"/>
|
||||
</feature>
|
||||
</config-file>
|
||||
|
||||
<config-file target="Properties/WMAppManifest.xml" parent="/*/App/Capabilities">
|
||||
<Capability Name="ID_CAP_SPEECH_RECOGNITION" />
|
||||
</config-file>
|
||||
|
||||
<source-file src="src/wp/TTS.cs" />
|
||||
</platform>
|
||||
|
||||
<!-- android -->
|
||||
<platform name="android">
|
||||
<config-file target="config.xml" parent="/*">
|
||||
<feature name="TTS">
|
||||
<param name="android-package" value="com.wordsbaking.cordova.tts.TTS" />
|
||||
<param name="onload" value="true" />
|
||||
</feature>
|
||||
</config-file>
|
||||
|
||||
<source-file src="src/android/TTS.java" target-dir="src/com/wordsbaking/cordova/tts" />
|
||||
</platform>
|
||||
</plugin>
|
||||
@@ -0,0 +1,144 @@
|
||||
package com.wordsbaking.cordova.tts;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
import org.apache.cordova.CordovaPlugin;
|
||||
|
||||
import org.apache.cordova.CordovaWebView;
|
||||
import org.apache.cordova.CordovaInterface;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.speech.tts.TextToSpeech;
|
||||
import android.speech.tts.TextToSpeech.OnInitListener;
|
||||
import android.speech.tts.UtteranceProgressListener;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
|
||||
/*
|
||||
Cordova Text-to-Speech Plugin
|
||||
https://github.com/vilic/cordova-plugin-tts
|
||||
|
||||
by VILIC VANE
|
||||
https://github.com/vilic
|
||||
|
||||
MIT License
|
||||
*/
|
||||
|
||||
public class TTS extends CordovaPlugin implements OnInitListener {
|
||||
|
||||
public static final String ERR_INVALID_OPTIONS = "ERR_INVALID_OPTIONS";
|
||||
public static final String ERR_NOT_INITIALIZED = "ERR_NOT_INITIALIZED";
|
||||
public static final String ERR_ERROR_INITIALIZING = "ERR_ERROR_INITIALIZING";
|
||||
public static final String ERR_UNKNOWN = "ERR_UNKNOWN";
|
||||
|
||||
boolean ttsInitialized = false;
|
||||
TextToSpeech tts = null;
|
||||
|
||||
@Override
|
||||
public void initialize(CordovaInterface cordova, final CordovaWebView webView) {
|
||||
tts = new TextToSpeech(cordova.getActivity().getApplicationContext(), this);
|
||||
tts.setOnUtteranceProgressListener(new UtteranceProgressListener() {
|
||||
@Override
|
||||
public void onStart(String s) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDone(String callbackId) {
|
||||
if (!callbackId.equals("")) {
|
||||
CallbackContext context = new CallbackContext(callbackId, webView);
|
||||
context.success();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(String callbackId) {
|
||||
if (!callbackId.equals("")) {
|
||||
CallbackContext context = new CallbackContext(callbackId, webView);
|
||||
context.error(ERR_UNKNOWN);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(String action, JSONArray args, CallbackContext callbackContext)
|
||||
throws JSONException {
|
||||
if (action.equals("speak")) {
|
||||
speak(args, callbackContext);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInit(int status) {
|
||||
if (status != TextToSpeech.SUCCESS) {
|
||||
tts = null;
|
||||
} else {
|
||||
// warm up the tts engine with an empty string
|
||||
HashMap<String, String> ttsParams = new HashMap<String, String>();
|
||||
ttsParams.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "");
|
||||
tts.setLanguage(new Locale("en", "US"));
|
||||
tts.speak("", TextToSpeech.QUEUE_FLUSH, ttsParams);
|
||||
|
||||
ttsInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void speak(JSONArray args, CallbackContext callbackContext)
|
||||
throws JSONException, NullPointerException {
|
||||
JSONObject params = args.getJSONObject(0);
|
||||
|
||||
if (params == null) {
|
||||
callbackContext.error(ERR_INVALID_OPTIONS);
|
||||
return;
|
||||
}
|
||||
|
||||
String text;
|
||||
String locale;
|
||||
double rate;
|
||||
|
||||
if (params.isNull("text")) {
|
||||
callbackContext.error(ERR_INVALID_OPTIONS);
|
||||
return;
|
||||
} else {
|
||||
text = params.getString("text");
|
||||
}
|
||||
|
||||
if (params.isNull("locale")) {
|
||||
locale = "en-US";
|
||||
} else {
|
||||
locale = params.getString("locale");
|
||||
}
|
||||
|
||||
if (params.isNull("rate")) {
|
||||
rate = 1.0;
|
||||
} else {
|
||||
rate = params.getDouble("rate");
|
||||
}
|
||||
|
||||
if (tts == null) {
|
||||
callbackContext.error(ERR_ERROR_INITIALIZING);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ttsInitialized) {
|
||||
callbackContext.error(ERR_NOT_INITIALIZED);
|
||||
return;
|
||||
}
|
||||
|
||||
HashMap<String, String> ttsParams = new HashMap<String, String>();
|
||||
ttsParams.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, callbackContext.getCallbackId());
|
||||
|
||||
String[] localeArgs = locale.split("-");
|
||||
tts.setLanguage(new Locale(localeArgs[0], localeArgs[1]));
|
||||
tts.setSpeechRate((float) rate);
|
||||
|
||||
tts.speak(text, TextToSpeech.QUEUE_FLUSH, ttsParams);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
Cordova Text-to-Speech Plugin
|
||||
https://github.com/vilic/cordova-plugin-tts
|
||||
|
||||
by VILIC VANE
|
||||
https://github.com/vilic
|
||||
|
||||
MIT License
|
||||
*/
|
||||
|
||||
#import <Cordova/CDV.h>
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
|
||||
@interface CDVTTS : CDVPlugin <AVSpeechSynthesizerDelegate> {
|
||||
AVSpeechSynthesizer* synthesizer;
|
||||
NSString* lastCallbackId;
|
||||
NSString* callbackId;
|
||||
}
|
||||
|
||||
- (void)speak:(CDVInvokedUrlCommand*)command;
|
||||
@end
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
Cordova Text-to-Speech Plugin
|
||||
https://github.com/vilic/cordova-plugin-tts
|
||||
|
||||
by VILIC VANE
|
||||
https://github.com/vilic
|
||||
|
||||
MIT License
|
||||
*/
|
||||
|
||||
#import <Cordova/CDV.h>
|
||||
#import "CDVTTS.h"
|
||||
|
||||
@implementation CDVTTS
|
||||
|
||||
- (void)pluginInitialize {
|
||||
synthesizer = [AVSpeechSynthesizer new];
|
||||
synthesizer.delegate = self;
|
||||
}
|
||||
|
||||
- (void)speechSynthesizer:(AVSpeechSynthesizer*)synthesizer didFinishSpeechUtterance:(AVSpeechUtterance*)utterance {
|
||||
CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
|
||||
if (lastCallbackId) {
|
||||
[self.commandDelegate sendPluginResult:result callbackId:lastCallbackId];
|
||||
lastCallbackId = nil;
|
||||
} else {
|
||||
[self.commandDelegate sendPluginResult:result callbackId:callbackId];
|
||||
callbackId = nil;
|
||||
}
|
||||
|
||||
[[AVAudioSession sharedInstance] setActive:NO withOptions:0 error:nil];
|
||||
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAmbient
|
||||
withOptions: 0 error: nil];
|
||||
[[AVAudioSession sharedInstance] setActive:YES withOptions: 0 error:nil];
|
||||
}
|
||||
|
||||
- (void)speak:(CDVInvokedUrlCommand*)command {
|
||||
[[AVAudioSession sharedInstance] setActive:NO withOptions:0 error:nil];
|
||||
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback
|
||||
withOptions:AVAudioSessionCategoryOptionDuckOthers error:nil];
|
||||
|
||||
if (callbackId) {
|
||||
lastCallbackId = callbackId;
|
||||
}
|
||||
|
||||
callbackId = command.callbackId;
|
||||
|
||||
[synthesizer stopSpeakingAtBoundary:AVSpeechBoundaryImmediate];
|
||||
|
||||
NSDictionary* options = [command.arguments objectAtIndex:0];
|
||||
|
||||
NSString* text = [options objectForKey:@"text"];
|
||||
NSString* locale = [options objectForKey:@"locale"];
|
||||
double rate = [[options objectForKey:@"rate"] doubleValue];
|
||||
|
||||
if (!locale || (id)locale == [NSNull null]) {
|
||||
locale = @"en-US";
|
||||
}
|
||||
|
||||
if (!rate) {
|
||||
rate = 1.0;
|
||||
}
|
||||
|
||||
AVSpeechUtterance* utterance = [[AVSpeechUtterance new] initWithString:text];
|
||||
utterance.voice = [AVSpeechSynthesisVoice voiceWithLanguage:locale];
|
||||
// Rate expression adjusted manually for a closer match to other platform.
|
||||
utterance.rate = (AVSpeechUtteranceMinimumSpeechRate * 1.5 + AVSpeechUtteranceDefaultSpeechRate) / 2.5 * rate * rate;
|
||||
utterance.pitchMultiplier = 1.2;
|
||||
[synthesizer speakUtterance:utterance];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
Cordova Text-to-Speech Plugin
|
||||
https://github.com/vilic/cordova-plugin-tts
|
||||
|
||||
by VILIC VANE
|
||||
https://github.com/vilic
|
||||
|
||||
MIT License
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Phone.Speech.Synthesis;
|
||||
using WPCordovaClassLib.Cordova;
|
||||
using WPCordovaClassLib.Cordova.Commands;
|
||||
using WPCordovaClassLib.Cordova.JSON;
|
||||
|
||||
namespace Cordova.Extension.Commands {
|
||||
[DataContract]
|
||||
class Options {
|
||||
[DataMember]
|
||||
public string text;
|
||||
[DataMember]
|
||||
public string locale;
|
||||
[DataMember]
|
||||
public double? rate;
|
||||
}
|
||||
|
||||
class TTS : BaseCommand {
|
||||
SpeechSynthesizer synth = new SpeechSynthesizer();
|
||||
|
||||
string lastCallbackId;
|
||||
|
||||
public async void speak(string argsJSON) {
|
||||
if (lastCallbackId != null) {
|
||||
DispatchCommandResult(new PluginResult(PluginResult.Status.OK), lastCallbackId);
|
||||
lastCallbackId = null;
|
||||
synth.CancelAll();
|
||||
}
|
||||
|
||||
var args = JsonHelper.Deserialize<string[]>(argsJSON);
|
||||
var options = JsonHelper.Deserialize<Options>(args[0]);
|
||||
lastCallbackId = args[1];
|
||||
|
||||
var locale = options.locale != null ? options.locale : "en-US";
|
||||
var rate = options.rate != null ? options.rate : 1.0;
|
||||
|
||||
var ssml =
|
||||
@"<?xml version=""1.0""?>
|
||||
<speak version=""1.0""
|
||||
xmlns=""http://www.w3.org/2001/10/synthesis""
|
||||
xml:lang=""" + locale + @""">
|
||||
<prosody pitch=""x-high"" rate=""" + rate + @""">" + xmlEncode(options.text) + @"</prosody>
|
||||
</speak>";
|
||||
|
||||
try {
|
||||
await synth.SpeakSsmlAsync(ssml);
|
||||
lastCallbackId = null;
|
||||
DispatchCommandResult(new PluginResult(PluginResult.Status.OK));
|
||||
} catch (OperationCanceledException) {
|
||||
// do nothing
|
||||
} catch (Exception e) {
|
||||
Debug.WriteLine(e.Message);
|
||||
lastCallbackId = null;
|
||||
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, e.Message));
|
||||
}
|
||||
}
|
||||
|
||||
string xmlEncode(string text) {
|
||||
return text
|
||||
.Replace("&", "&")
|
||||
.Replace("<", "<")
|
||||
.Replace(">", ">");
|
||||
}
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
|
||||
Cordova Text-to-Speech Plugin
|
||||
https://github.com/vilic/cordova-plugin-tts
|
||||
|
||||
by VILIC VANE
|
||||
https://github.com/vilic
|
||||
|
||||
MIT License
|
||||
|
||||
*/
|
||||
|
||||
exports.speak = function (text, onfulfilled, onrejected) {
|
||||
var options = {};
|
||||
|
||||
if (typeof text == 'string') {
|
||||
options.text = text;
|
||||
} else {
|
||||
options = text;
|
||||
}
|
||||
|
||||
cordova
|
||||
.exec(function () {
|
||||
onfulfilled();
|
||||
}, function (reason) {
|
||||
onrejected(reason);
|
||||
}, 'TTS', 'speak', [options]);
|
||||
};
|
||||
Reference in New Issue
Block a user