235 lines
6.3 KiB
Plaintext
235 lines
6.3 KiB
Plaintext
//
|
|
// RTVoiceIOSBridge.mm
|
|
// Version 2020.4.0
|
|
//
|
|
// © 2016-2020 crosstales LLC (https://www.crosstales.com)
|
|
//
|
|
#import "RTVoiceIOSBridge.h"
|
|
#import <AVFoundation/AVFoundation.h>
|
|
#import <Foundation/Foundation.h>
|
|
//#define DEBUG
|
|
|
|
|
|
@implementation RTVoiceIOSBridge
|
|
|
|
static AVSpeechSynthesizer *_synthesizer;
|
|
static NSArray *_voices;
|
|
|
|
+ (AVSpeechSynthesizer *)synthesizer {
|
|
if (_synthesizer == nil) {
|
|
_synthesizer = [[AVSpeechSynthesizer alloc] init];
|
|
_synthesizer.delegate = self;
|
|
}
|
|
return _synthesizer;
|
|
}
|
|
|
|
+ (NSArray *)voices {
|
|
if (_voices == nil) {
|
|
_voices = [AVSpeechSynthesisVoice speechVoices];
|
|
}
|
|
return _voices;
|
|
}
|
|
|
|
/**
|
|
* Speaks the string with a given rate, pitch, volume and culture.
|
|
* @param id ID of the voice to speak
|
|
* @param text Text to speak
|
|
* @param rate Speech rate of the speaker in percent
|
|
* @param pitch Pitch of the speech in percent
|
|
* @param volume Volume of the speaker in percent
|
|
*/
|
|
+ (void)speak: (NSString *)id text:(NSString *)text rate:(float)rate pitch:(float)pitch volume:(float)volume
|
|
{
|
|
#ifdef DEBUG
|
|
NSLog(@"speak: %@ - Text: %@, Rate: %.3f, Pitch: %.3f, Volume: %.3f", id, text, rate, pitch, volume);
|
|
#endif
|
|
|
|
if (text)
|
|
{
|
|
//[RTVoiceIOSBridge stop];
|
|
|
|
if (RTVoiceIOSBridge.voices) {
|
|
AVSpeechSynthesisVoice *voice = RTVoiceIOSBridge.voices[0]; // one voice must be available
|
|
|
|
for (AVSpeechSynthesisVoice *v in RTVoiceIOSBridge.voices) {
|
|
if ([v.identifier isEqualToString:id])
|
|
{
|
|
voice = v;
|
|
break;
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
NSLog(@"speak - selected voice: %@", voice.name);
|
|
#endif
|
|
AVSpeechUtterance *utterance = [[AVSpeechUtterance alloc] initWithString:text];
|
|
utterance.voice = voice;
|
|
|
|
float adjustedRate = AVSpeechUtteranceDefaultSpeechRate * rate;
|
|
|
|
if (adjustedRate > AVSpeechUtteranceMaximumSpeechRate)
|
|
{
|
|
adjustedRate = AVSpeechUtteranceMaximumSpeechRate;
|
|
}
|
|
|
|
if (adjustedRate < AVSpeechUtteranceMinimumSpeechRate)
|
|
{
|
|
adjustedRate = AVSpeechUtteranceMinimumSpeechRate;
|
|
}
|
|
|
|
utterance.rate = adjustedRate;
|
|
utterance.volume = volume;
|
|
|
|
utterance.pitchMultiplier = pitch;
|
|
|
|
[RTVoiceIOSBridge.synthesizer speakUtterance:utterance];
|
|
} else {
|
|
NSLog(@"ERROR: no voices found - could not speak!");
|
|
}
|
|
} else {
|
|
NSLog(@"WARNING: text was null!");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Stops speaking
|
|
*/
|
|
+ (void)stop
|
|
{
|
|
#ifdef DEBUG
|
|
NSLog(@"stop");
|
|
#endif
|
|
|
|
[RTVoiceIOSBridge.synthesizer stopSpeakingAtBoundary:AVSpeechBoundaryImmediate];
|
|
}
|
|
|
|
/**
|
|
* Collects and sends all voices to RT-Voice.
|
|
*/
|
|
+ (void)setVoices
|
|
{
|
|
NSString *appendstring = @"";
|
|
|
|
if (RTVoiceIOSBridge.voices) {
|
|
for (AVSpeechSynthesisVoice *voice in RTVoiceIOSBridge.voices) {
|
|
appendstring = [appendstring stringByAppendingString:voice.identifier];
|
|
appendstring = [appendstring stringByAppendingString:@","];
|
|
appendstring = [appendstring stringByAppendingString:voice.name];
|
|
appendstring = [appendstring stringByAppendingString:@","];
|
|
appendstring = [appendstring stringByAppendingString:voice.language];
|
|
appendstring = [appendstring stringByAppendingString:@","];
|
|
|
|
#ifdef DEBUG
|
|
NSLog(@"Voice-ID: %@ - Name: %@, Language: %@, Quality: %ld", voice.identifier, voice.name, voice.language, (long)voice.quality);
|
|
#endif
|
|
}
|
|
} else {
|
|
NSLog(@"ERROR: no voices found!");
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
NSLog(@"setVoices: %@", appendstring);
|
|
#endif
|
|
|
|
UnitySendMessage("RTVoice", "SetVoices", [appendstring UTF8String]);
|
|
}
|
|
|
|
/**
|
|
* Called when the speak is finished and informs RT-Voice.
|
|
*/
|
|
+ (void)speechSynthesizer:(AVSpeechSynthesizer *)synthesizer didFinishSpeechUtterance:(AVSpeechUtterance *)utterance
|
|
{
|
|
#ifdef DEBUG
|
|
NSLog(@"didFinishSpeechUtterance");
|
|
#endif
|
|
|
|
UnitySendMessage("RTVoice", "SetState", "Finish");
|
|
}
|
|
|
|
/**
|
|
* Called when the synthesizer have began to speak a word and informs RT-Voice.
|
|
*/
|
|
+ (void)speechSynthesizer:(AVSpeechSynthesizer *)synthesizer willSpeakRangeOfSpeechString:(NSRange)characterRange utterance:(AVSpeechUtterance *)utterance
|
|
{
|
|
#ifdef DEBUG
|
|
NSLog(@"willSpeakRangeOfSpeechString");
|
|
#endif
|
|
|
|
UnitySendMessage("RTVoice", "WordSpoken", "w");//[substringcutout UTF8String]);
|
|
}
|
|
|
|
/**
|
|
* Called when the speak is canceled and informs RTVoice.
|
|
*/
|
|
+ (void)speechSynthesizer:(AVSpeechSynthesizer *)synthesizer didCancelSpeechUtterance:(AVSpeechUtterance *)utterance
|
|
{
|
|
#ifdef DEBUG
|
|
NSLog(@"didCancelSpeechUtterance");
|
|
#endif
|
|
|
|
UnitySendMessage("RTVoice", "SetState", "Cancel");
|
|
}
|
|
|
|
/**
|
|
* Called when the speak is started and informs RT-Voice.
|
|
*/
|
|
+ (void)speechSynthesizer:(AVSpeechSynthesizer *)synthesizer didStartSpeechUtterance:(AVSpeechUtterance *)utterance
|
|
{
|
|
#ifdef DEBUG
|
|
NSLog(@"didStartSpeechUtterance");
|
|
#endif
|
|
|
|
UnitySendMessage("RTVoice", "SetState", "Start");
|
|
}
|
|
|
|
@end
|
|
|
|
extern void sendMessage(const char *, const char *, const char *);
|
|
|
|
extern "C" {
|
|
|
|
/**
|
|
* Bridge to speak the string that it receives with a given rate, pitch, volume and identifier.
|
|
* @param id ID of the voice to speak
|
|
* @param text Text to speak
|
|
* @param rate Speech rate of the speaker in percent
|
|
* @param pitch Pitch of the speech in percent
|
|
* @param volume Volume of the speaker in percent
|
|
*/
|
|
void RTVSpeak(char *id, char *text, float rate, float pitch, float volume)
|
|
{
|
|
NSString *voiceId = [NSString stringWithUTF8String:id];
|
|
NSString *messageFromRTVoice = [NSString stringWithUTF8String:text];
|
|
|
|
#ifdef DEBUG
|
|
NSLog(@"Speak: %@ - Text: %@, Rate: %.3f, Pitch: %.3f, Volume: %.3f", voiceId, messageFromRTVoice, rate, pitch, volume);
|
|
#endif
|
|
|
|
[RTVoiceIOSBridge speak:voiceId text:messageFromRTVoice rate:rate pitch:pitch volume:volume];
|
|
}
|
|
|
|
/**
|
|
* Bridge to stop speaking.
|
|
*/
|
|
void RTVStop()
|
|
{
|
|
#ifdef DEBUG
|
|
NSLog(@"Stop");
|
|
#endif
|
|
|
|
[RTVoiceIOSBridge stop];
|
|
}
|
|
|
|
/**
|
|
* Bridge to get all voices.
|
|
*/
|
|
void RTVGetVoices()
|
|
{
|
|
#ifdef DEBUG
|
|
NSLog(@"GetVoices");
|
|
#endif
|
|
|
|
[RTVoiceIOSBridge setVoices];
|
|
}
|
|
}
|