Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using Voice class instead of Map + Including gender when available #269

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
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
6 changes: 3 additions & 3 deletions android/gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#Fri Sep 27 12:18:40 PDT 2019
#Mon Aug 23 14:56:56 CST 2021
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-bin.zip
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
zipStoreBase=GRADLE_USER_HOME
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,10 @@ void getVoices(Result result) {
HashMap<String, String> voiceMap = new HashMap<>();
voiceMap.put("name", voice.getName());
voiceMap.put("locale", voice.getLocale().toLanguageTag());

String gender = voice.getName().contains("female") ? "female"
: voice.getName().contains("male") ? "male" : "unspecified";
voiceMap.put("gender", gender);
voices.add(voiceMap);
}
result.success(voices);
Expand Down
13 changes: 12 additions & 1 deletion example/android/.settings/org.eclipse.buildship.core.prefs
Original file line number Diff line number Diff line change
@@ -1,2 +1,13 @@
#Sat Apr 14 11:55:40 PDT 2018
arguments=
auto.sync=false
build.scans.enabled=false
connection.gradle.distribution=GRADLE_DISTRIBUTION(VERSION(7.0-rc-1))
connection.project.dir=
eclipse.preferences.version=1
gradle.user.home=
java.home=/Library/Java/JavaVirtualMachines/adoptopenjdk-11.jdk/Contents/Home
jvm.arguments=
offline.mode=false
override.workspace.settings=true
show.console.view=true
show.executions.view=true
4 changes: 2 additions & 2 deletions example/android/app/.classpath
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8/"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11/"/>
<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/>
<classpathentry kind="output" path="bin"/>
<classpathentry kind="output" path="bin/default"/>
</classpath>
9 changes: 9 additions & 0 deletions ios/Classes/SwiftFlutterTtsPlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,15 @@ public class SwiftFlutterTtsPlugin: NSObject, FlutterPlugin, AVSpeechSynthesizer
for voice in AVSpeechSynthesisVoice.speechVoices() {
voiceDict["name"] = voice.name
voiceDict["locale"] = voice.language

if #available(iOS 13.0, *) {
let gender = voice.gender == AVSpeechSynthesisVoiceGender.female ? "female"
: voice.gender == AVSpeechSynthesisVoiceGender.male ? "male" : "unspecified"
voiceDict["gender"] = gender
} else {
voiceDict["gender"] = "unspecified"
}

voices.add(voiceDict)
}
result(voices)
Expand Down
19 changes: 14 additions & 5 deletions lib/flutter_tts.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import 'dart:io' show Platform;

import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:flutter_tts/models/models.dart';

export 'package:flutter_tts/models/models.dart';

typedef void ErrorHandler(dynamic message);
typedef ProgressHandler = void Function(
Expand Down Expand Up @@ -210,8 +213,8 @@ class FlutterTts {

/// [Future] which invokes the platform specific method for setVoice
/// ***Android, iOS, and macOS supported only***
Future<dynamic> setVoice(Map<String, String> voice) async =>
_channel.invokeMethod('setVoice', voice);
Future<void> setVoice(TTSVoice voice) async =>
_channel.invokeMethod('setVoice', voice.asVoiceMap);

/// [Future] which invokes the platform specific method for stop
Future<dynamic> stop() async => _channel.invokeMethod('stop');
Expand Down Expand Up @@ -241,10 +244,16 @@ class FlutterTts {
}

/// [Future] which invokes the platform specific method for getVoices
/// Returns a `List` of `Maps` containing a voice name and locale
/// Returns a `List` of [TTSVoice] containing a voice object
/// ***Android, iOS, and macOS supported only***
Future<dynamic> get getVoices async {
final voices = await _channel.invokeMethod('getVoices');
Future<List<TTSVoice>> get getVoices async {
final voicesObjs = await _channel.invokeMethod('getVoices') as List;
// Parse voices to class.
final voices = voicesObjs.map<TTSVoice>((Object? voiceObj) {
final voiceMap = voiceObj as Map?;

return TTSVoice.fromMap(voiceMap!);
}).toList();
return voices;
}

Expand Down
1 change: 1 addition & 0 deletions lib/models/models.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export 'tts_voice.dart';
67 changes: 67 additions & 0 deletions lib/models/tts_voice.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/// {@template tts_voice}
/// Represents a wapper around Android's Voice class and
/// Swift's AVSpeechSynthesisVoice class.
///
/// It exposes basic information about the underlying classes like the name,
/// locale and gender of the synthetic voice.
///
/// {@endtemplate}
class TTSVoice {
/// {@macro tts_voice}
const TTSVoice({
required this.name,
required this.locale,
required this.gender,
});

TTSVoice.fromMap(Map map)
: this.name = map['name']!.toString(),
this.locale = map['locale']!.toString(),
this.gender = TTSVoiceGenderFromString.fromString(
map['gender']!.toString(),
);

/// The tts_voice's name.
final String name;

/// The tts_voice's locale.
final String locale;

/// The tts_voice's gender.
final TTSVoiceGender gender;

Map<String, String> get asVoiceMap => {
'name': name,
'locale': locale,
};
}

enum TTSVoiceGender {
male,
female,
unspecified,
}

extension TTSVoiceGenderFromString on TTSVoiceGender {
static TTSVoiceGender fromString(String value) {
switch (value) {
case "female":
return TTSVoiceGender.female;

case "male":
return TTSVoiceGender.male;

default:
return TTSVoiceGender.unspecified;
}
}

/// Helper to determine if value is male.
bool get isMale => this == TTSVoiceGender.male;

/// Helper to determine if value is female.
bool get isFemale => this == TTSVoiceGender.female;

/// Helper to determine if value is unspecified.
bool get isUnspecified => this == TTSVoiceGender.unspecified;
}
7 changes: 7 additions & 0 deletions macos/Classes/FlutterTtsPlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,13 @@ public class FlutterTtsPlugin: NSObject, FlutterPlugin, AVSpeechSynthesizerDeleg
for voice in AVSpeechSynthesisVoice.speechVoices() {
voiceDict["name"] = voice.name
voiceDict["locale"] = voice.language
if #available(iOS 13.0, *) {
let gender = voice.gender == AVSpeechSynthesisVoiceGender.female ? "female"
: voice.gender == AVSpeechSynthesisVoiceGender.male ? "male" : "unspecified"
voiceDict["gender"] = gender
} else {
voiceDict["gender"] = "unspecified"
}
voices.add(voiceDict)
}
result(voices)
Expand Down