Skip to content

Commit 63b48c8

Browse files
committed
feat: adding the max input length
1 parent 9c03ad8 commit 63b48c8

5 files changed

Lines changed: 71 additions & 7 deletions

File tree

android/src/main/java/com/speech/SpeechModule.kt

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ class SpeechModule(reactContext: ReactApplicationContext) :
2929
return NAME
3030
}
3131

32+
override fun getTypedExportedConstants(): MutableMap<String, Any> {
33+
return mutableMapOf(
34+
"maxInputLength" to maxInputLength
35+
)
36+
}
37+
3238
companion object {
3339
const val NAME = "Speech"
3440

@@ -41,7 +47,7 @@ class SpeechModule(reactContext: ReactApplicationContext) :
4147
)
4248
}
4349
private val queueLock = Any()
44-
50+
private val maxInputLength = TextToSpeech.getMaxSpeechInputLength()
4551
private val isSupportedPausing = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
4652

4753
private lateinit var synthesizer: TextToSpeech
@@ -480,6 +486,13 @@ class SpeechModule(reactContext: ReactApplicationContext) :
480486
promise.reject("speech_error", "Text cannot be null")
481487
return
482488
}
489+
if (text.length > maxInputLength) {
490+
promise.reject(
491+
"speech_error",
492+
"Text exceeds the maximum input length of $maxInputLength characters"
493+
)
494+
return
495+
}
483496
ensureInitialized(promise) {
484497
isDucking = globalOptions["ducking"] as? Boolean ?: false
485498
activateDuckingSession()
@@ -501,8 +514,15 @@ class SpeechModule(reactContext: ReactApplicationContext) :
501514
promise.reject("speech_error", "Text cannot be null")
502515
return
503516
}
517+
if (text.length > maxInputLength) {
518+
promise.reject(
519+
"speech_error",
520+
"Text exceeds the maximum input length of $maxInputLength characters"
521+
)
522+
return
523+
}
504524
ensureInitialized(promise) {
505-
val validatedOptions = getValidatedOptions(options)
525+
val validatedOptions = getValidatedOptions(options)
506526
isDucking = validatedOptions["ducking"] as? Boolean ?: false
507527
activateDuckingSession()
508528
val utteranceId = getUniqueID()

docs/USAGE.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
- [Bare React Native](#bare-react-native)
66
- [Expo](#expo)
77
- [API Overview](#api-overview)
8+
- [Constants](#constants)
89
- [Getting Available Voices](#getting-available-voices)
910
- [Engine Management (Android)](#engine-management-android)
1011
- [Get Available Engines](#get-available-engines)
@@ -79,6 +80,18 @@ import Speech from '@mhpdev/react-native-speech';
7980

8081
---
8182

83+
### Constants
84+
85+
The `Speech` class static constants.
86+
87+
**Values**
88+
89+
`maxInputLength`
90+
91+
The **maximum number of characters** allowed in a single call to the speak methods.
92+
93+
Android enforces this limit, which is determined by `TextToSpeech.getMaxSpeechInputLength`. If your text exceeds this limit, you must manually split it into smaller utterances on the JavaScript side. (_iOS has no synthesis system limit, and by default, the speech class returns `Number.MAX_VALUE`_)
94+
8295
### Getting Available Voices
8396

8497
Retrieve a list of all available voices on the device. Optionally, you can filter voices by providing a language code or tag ([IETF BCP 47 language tag](https://www.techonthenet.com/js/language_tags.php)).

ios/Speech.mm

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,16 @@ + (BOOL)requiresMainQueueSetup {
1414
return NO;
1515
}
1616

17+
- (NSDictionary<NSString *, id> *)constantsToExport
18+
{
19+
return @{};
20+
}
21+
22+
- (NSDictionary<NSString *, id> *)getConstants
23+
{
24+
return [self constantsToExport];
25+
}
26+
1727
- (instancetype)init {
1828
self = [super init];
1929

src/NativeSpeech.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export interface VoiceOptions {
3939
/**
4040
* Determines how speech audio interacts with the device's silent (ringer) switch.
4141
* This option is ignored if `ducking` is `true`.
42-
* @platform ios
42+
* @platform iOS
4343
*
4444
* - `obey`: (Default) Does not change the app's audio session. Speech follows the system default.
4545
* - `respect`: Speech will be silenced by the ringer switch. Use for non-critical audio.
@@ -80,7 +80,18 @@ export interface EngineProps {
8080
*/
8181
isDefault: boolean;
8282
}
83+
84+
interface ConstantsProps {
85+
/**
86+
* The maximum number of characters allowed in a single call to `speak()` or `speakWithOptions()`.
87+
* @platform Android
88+
*/
89+
maxInputLength?: number;
90+
}
91+
8392
export interface Spec extends TurboModule {
93+
getConstants: () => ConstantsProps;
94+
//Methods
8495
reset: () => void;
8596
stop: () => Promise<void>;
8697
pause: () => Promise<boolean>;
@@ -93,7 +104,7 @@ export interface Spec extends TurboModule {
93104
setEngine: (engineName: string) => Promise<void>;
94105
getAvailableVoices: (language: string) => Promise<VoiceProps[]>;
95106
speakWithOptions: (text: string, options: VoiceOptions) => Promise<void>;
96-
107+
//Listeners
97108
readonly onError: EventEmitter<EventProps>;
98109
readonly onStart: EventEmitter<EventProps>;
99110
readonly onFinish: EventEmitter<EventProps>;

src/Speech.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,16 @@ import TurboSpeech from './NativeSpeech';
22
import type {VoiceProps, VoiceOptions, EngineProps} from './NativeSpeech';
33

44
export default class Speech {
5+
/**
6+
* The *maximum number of characters allowed in a single call to the speak methods.
7+
*
8+
* On `Android`, this value is determined by `TextToSpeech.getMaxSpeechInputLength`.
9+
* Text exceeding this length must be manually split into smaller utterances on the JavaScript side.
10+
*
11+
* On `iOS`, there is no synthesis system limit, and by default, the speech class returns `Number.MAX_VALUE`.
12+
*/
13+
static readonly maxInputLength =
14+
TurboSpeech.getConstants().maxInputLength ?? Number.MAX_VALUE;
515
/**
616
* Gets a list of all available voices on the device
717
* @param language - Optional language code to filter voices (e.g., 'en', 'fr', 'en-US', 'fr-FR').
@@ -21,7 +31,7 @@ export default class Speech {
2131
/**
2232
* Gets a list of all available text-to-speech engines on the device
2333
* @returns Promise<EngineProps[]> Array of engine properties including name, label, and isDefault flag
24-
* @platform android
34+
* @platform Android
2535
* @example
2636
* const engines = await Speech.getEngines();
2737
* engines.forEach(engine => {
@@ -38,7 +48,7 @@ export default class Speech {
3848
* Sets the text-to-speech engine to use for speech synthesis
3949
* @param engineName - The name of the engine to use (obtained from getEngines())
4050
* @returns Promise<void> Resolves when engine is set
41-
* @platform android
51+
* @platform Android
4252
* @example
4353
* // First, get available engines
4454
* const engines = await Speech.getEngines();
@@ -54,7 +64,7 @@ export default class Speech {
5464
* Opens the system UI to install or update TTS voice data.
5565
* @returns Promise<void> Resolves when the installer activity has been launched.
5666
* @throws If the installer activity cannot be opened on the device.
57-
* @platform android
67+
* @platform Android
5868
*/
5969
public static openVoiceDataInstaller(): Promise<void> {
6070
return TurboSpeech.openVoiceDataInstaller();

0 commit comments

Comments
 (0)