Reputation: 111
I trying to develop an app which have both speech to text and text to speech one at a time that means , when I talking text to speech will be in off , when I playing a voice for text to speech then speech recognition will be in off state.
Steps: 1-Open a app-app should start recognize the voice 2-Start speaking the words like :hello 3-Then play -so if we say play app should play the text that present on the screen. 4-Again start speaking-After voice played again I want to start a speech recognition process. The above step is the regular process that i would expecting. The speech recognition should keep on running .For speech to text I used Apples Speech framework.For Text To speech I used AVFoundation and MediaPlayer library.
Here i am facing an issue ,i.e., 1-I am saying hello it is printing Hello in screen 2-And I am saying play as a command app is playing a voice 3-After that i am getting an error [Utility] +[AFAggregator logDictationFailedWithError:] Error Domain=kAFAssistantErrorDomain Code=209 "(null)". i don't know why it is occurring.I have searched in google ,but i didn't get any fair solution for this , please help me to resolve this.
Here is my code
@interface ViewController ()
@end
@implementation ViewController
{
SFSpeechAudioBufferRecognitionRequest *recognitionRequest;
SFSpeechRecognitionTask *recognitionTask;
AVAudioEngine *audioEngine;
NSMutableArray *speechStringsArray;
BOOL SpeechToText;
NSString* resultString;
NSString *str ;
NSString *searchString;
AVAudioInputNode *inputNode;
BOOL didStartSpeaking;
NSString *textToSpeak;
NSMutableArray *speechCommandArray;
}
- (void)viewDidLoad {
[super viewDidLoad];
//Speech To Text ****
speechStringsArray = [[NSMutableArray alloc]init];
[self.textView resignFirstResponder];
[self.textView setDelegate:self];
//// *****
// Initialize background audio session
NSError *error = NULL;
AVAudioSession *session = [AVAudioSession sharedInstance];
[session setCategory:AVAudioSessionCategoryPlayback error:&error];
if(error) {
NSLog(@"@error: %@", error);
}
[session setActive:YES error:&error];
if (error) {
NSLog(@"@error: %@", error);
}
// Enabled remote controls
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
// Voice setup
self.voicePicker.delegate = self;
self.voice = [AVSpeechSynthesisVoice voiceWithLanguage:@"en-us"];
self.voices = [NSMutableArray arrayWithObjects:
@{@"voice" : @"en-us", @"label" : @"American English (Female)"},
@{@"voice" : @"en-au", @"label" : @"Australian English (Female)"},
@{@"voice" : @"en-gb", @"label" : @"British English (Male)"},
@{@"voice" : @"en-ie", @"label" : @"Irish English (Female)"},
@{@"voice" : @"en-za", @"label" : @"South African English (Female)"},
nil];
// Synthesizer setup
self.synthesizer = [[AVSpeechSynthesizer alloc] init];
self.synthesizer.delegate = self;
// This notifcation is generated from the AppDelegate applicationDidBecomeActive method to make sure that if the play or pause button is updated in the background then the button will be updated in the toolbar
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateToolbar) name:@"updateToolbar" object:nil];
if (self.textView.text.length >0) {
[self.playPauseBarButtonItem setEnabled:YES];
}
else
{
[self.playPauseBarButtonItem setEnabled:NO];
}}
-(void)viewDidAppear:(BOOL)animated
{
self.speechRecognizer = [[SFSpeechRecognizer alloc]initWithLocale:[NSLocale localeWithLocaleIdentifier:@"en-US en-UK en-IN"]];
self.speechRecognizer.delegate = self;
audioEngine = [[AVAudioEngine alloc]init];
self.textView.text = @"";
[SFSpeechRecognizer requestAuthorization:^(SFSpeechRecognizerAuthorizationStatus authStatus) {
switch (authStatus) {
case SFSpeechRecognizerAuthorizationStatusAuthorized:
//User gave access to speech recognition
NSLog(@"Authorized");
[self start_record];
break;
case SFSpeechRecognizerAuthorizationStatusDenied:
//User denied access to speech recognition
NSLog(@"AuthorizationStatusDenied");
break;
case SFSpeechRecognizerAuthorizationStatusRestricted:
//Speech recognition restricted on this device
NSLog(@"AuthorizationStatusRestricted");
break;
case SFSpeechRecognizerAuthorizationStatusNotDetermined:
//Speech recognition not yet authorized
break;
default:
NSLog(@"Default");
break;
}
}];
//MARK : Interface Builder Actions
}
//methods for increase the speed
- (IBAction)handleSpeedStepper:(UIStepper *)sender
{
double speedValue = self.speedStepper.value;
[self.speedValueLabel setText:[NSString stringWithFormat:@"%.1f", speedValue]];
}
//methods for increase the pitch
- (IBAction)handlePitchStepper:(UIStepper *)sender
{
double pitchValue = self.pitchStepper.value;
[self.pitchValueLabel setText:[NSString stringWithFormat:@"%.1f", pitchValue]];
}
//methods for play and pause the voice
- (IBAction)handlePlayPauseButton:(UIBarButtonItem *)sender
{
if (self.synthesizer.speaking && !self.synthesizer.paused) {
if (self.pauseSettingSegmentedControl.selectedSegmentIndex == 0) {
// Stop immediately
[self.synthesizer pauseSpeakingAtBoundary:AVSpeechBoundaryImmediate];
}
else
{
// Stop at end of current word
[self.synthesizer pauseSpeakingAtBoundary:AVSpeechBoundaryWord];
}
[self updateToolbarWithButton:@"play"];
}
else if (self.synthesizer.paused) {
[self.synthesizer continueSpeaking];
[self updateToolbarWithButton:@"pause"];
}
else {
if(self.textView.text.length>0)
{
[self speakUtterance];
[self updateToolbarWithButton:@"pause"];
}
else
{
[self updateToolbarWithButton:@"play"];
}
}
}
//methods for recognize the voice spoken by user -speech recognize
-(void)start_record{
NSError * outError;
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
[audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:&outError];
[audioSession setMode:AVAudioSessionModeMeasurement error:&outError];
[audioSession setActive:YES withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:&outError];
recognitionRequest = [[SFSpeechAudioBufferRecognitionRequest alloc]init];
inputNode = audioEngine.inputNode;
if (recognitionRequest == nil) {
NSLog(@"Unable to created a SFSpeechAudioBufferRecognitionRequest object");
}
if (inputNode == nil) {
NSLog(@"Audio engine has no input node ");
}
//configure request so that results are returned before audio recording is finished
[recognitionRequest setShouldReportPartialResults:YES];
// A recognition task represents a speech recognition session.
//We keep a reference to the task so that it can be cancelled .
recognitionTask = [self.speechRecognizer recognitionTaskWithRequest:recognitionRequest resultHandler:^(SFSpeechRecognitionResult * result, NSError * error1) {
BOOL isFinal = false;
if ((result = result)) {
NSString *speech = result.bestTranscription.formattedString;
NSLog(@"the speech:%@",speech);
//storing the results in array.
for (int i = 0 ;i <speechStringsArray.count;i++)
{
str = [speechStringsArray objectAtIndex:i];
NSRange range = [speech rangeOfString:str options:NSCaseInsensitiveSearch];
NSLog(@"found: %@", (range.location != NSNotFound) ? @"Yes" : @"No");
if (range.location != NSNotFound) {
NSLog(@"inside if");
resultString = [speech stringByReplacingCharactersInRange:range withString:@""];
speech = resultString;
NSLog(@" the result is : %@",resultString);
}
else
{
NSLog(@"ifcondition fails");
//speech = resultString;
resultString = speech;
}
}
//commands are used like play to play a voice
if (resultString.length>0) {
if ([resultString isEqualToString:@" play"]) {
[self play];
}
else if ([resultString isEqualToString:@" exit"])
{
UIApplication *app = [UIApplication sharedApplication];
[self applicationWillTerminate:app];
}
else if ([speech isEqualToString:@" mute"])
{
[[MPMusicPlayerController applicationMusicPlayer] setVolume:0];
}
else if ([speech isEqualToString:@" up"])
{
float vol = [[AVAudioSession sharedInstance] outputVolume];
vol ++;
[[MPMusicPlayerController applicationMusicPlayer] setVolume:vol];
}
else if ([speech isEqualToString:@" down"])
{
float vol = [[AVAudioSession sharedInstance] outputVolume];
vol --;
[[MPMusicPlayerController applicationMusicPlayer] setVolume:vol];
}
else if ([speech isEqualToString:@" speed"])
{
double speedValue = self.speedStepper.value;
NSString *speedup = self.speedValueLabel.text;
double speed = [speedup doubleValue];
speed += 0.1;
[self.speedValueLabel setText:[NSString stringWithFormat:@"%.1f", speed]];
}
else
{
NSLog(@"coming");
self.textView.text = [NSString stringWithFormat:@"%@%@",self.textView.text,resultString];
if (![resultString isEqualToString:@" play"]) {
[speechStringsArray addObject:resultString];
}
}
}
else
{
if ([speech isEqualToString:@"Play"]) {
[self play];
}
if ([speech isEqualToString:@"Speed"])
{
double speedValue = self.speedStepper.value;
NSString *speedup = self.speedValueLabel.text;
double speed = [speedup doubleValue];
speed += 0.1;
[self.speedValueLabel setText:[NSString stringWithFormat:@"%.1f", speed]];
}
else if ([speech isEqualToString:@"Exit"])
{
//[[NSThread mainThread] exit];
UIApplication *app = [UIApplication sharedApplication];
[self applicationWillTerminate:app];
}
else if ([speech isEqualToString:@"Mute"])
{
[[MPMusicPlayerController applicationMusicPlayer] setVolume:0];
}
else if ([speech isEqualToString:@"Up"])
{
float vol = [[AVAudioSession sharedInstance] outputVolume];
// vol +=1.0;
vol ++;
[[MPMusicPlayerController applicationMusicPlayer] setVolume:vol];
}
else if ([speech isEqualToString:@"Down"])
{
float vol = [[AVAudioSession sharedInstance] outputVolume];
//vol -= 1.0;
vol --;
[[MPMusicPlayerController applicationMusicPlayer] setVolume:vol];
}
else
{
if (![speech isEqualToString:@"Play"]) {
[speechStringsArray addObject:speech];
if (self.textView.text.length > 0) {
self.textView.text = [NSString stringWithFormat:@"%@%@",self.textView.text,speech];
}
else
{
self.textView.text = speech;
}
}
}
}
NSLog(@" array %@",speechStringsArray);
isFinal = result.isFinal;
}
if (error1 != nil || isFinal) {
recognitionRequest = nil;
recognitionTask = nil;
[audioEngine stop];
[inputNode removeTapOnBus:0];
[recognitionRequest endAudio];
[self start_record];
}
}];
AVAudioFormat *recordingFormat = [inputNode outputFormatForBus:0];
[inputNode installTapOnBus:0 bufferSize:1024 format:recordingFormat block:^(AVAudioPCMBuffer * _Nonnull buffer, AVAudioTime * _Nonnull when){
[recognitionRequest appendAudioPCMBuffer:buffer];
}
];
NSError *error1;
[audioEngine prepare];
[audioEngine startAndReturnError:&error1];
}
-(void)play
{
if (self.synthesizer.speaking && !self.synthesizer.paused) {
if (self.pauseSettingSegmentedControl.selectedSegmentIndex == 0) {
// Stop immediately
[self.synthesizer pauseSpeakingAtBoundary:AVSpeechBoundaryImmediate];
}
else
{
// Stop at end of current word
[self.synthesizer pauseSpeakingAtBoundary:AVSpeechBoundaryWord];
}
[self updateToolbarWithButton:@"play"];
}
else if (self.synthesizer.paused) {
[self.synthesizer continueSpeaking];
[self updateToolbarWithButton:@"pause"];
}
else {
if(self.textView.text.length>0)
{
[self speakUtterance];
[self updateToolbarWithButton:@"pause"];
}
else
{
[self updateToolbarWithButton:@"play"];
}
}
}
- (void)applicationWillTerminate:(UIApplication *)application
{
exit(0);
}
//text to speech method
- (void)speakUtterance
{
if (audioEngine.isRunning) {
NSLog(@"Running");
recognitionRequest = nil;
recognitionTask = nil;
[speechStringsArray removeAllObjects];
resultString = @"";
[audioEngine stop];
[inputNode removeTapOnBus:0];
[recognitionRequest endAudio];
}
if (self.textView.text.length > 0) {
NSLog(@"speakUtterance");
didStartSpeaking = NO;
textToSpeak = [NSString stringWithFormat:@"%@", self.textView.text];
AVSpeechUtterance *utterance = [[AVSpeechUtterance alloc] initWithString:textToSpeak];
utterance.rate = self.speedStepper.value;
utterance.pitchMultiplier = self.pitchStepper.value;
utterance.voice = self.voice;
[self.synthesizer speakUtterance:utterance];
[self displayBackgroundMediaFields];
//y NSLog(@" after speaking:%@",self.textView.text);
}
else
{
[self updateToolbarWithButton:@"play"];
}
}
- (void)displayBackgroundMediaFields
{
MPMediaItemArtwork *artwork = [[MPMediaItemArtwork alloc] initWithImage:[UIImage imageNamed:@"Play"]];
NSDictionary *info = @{ MPMediaItemPropertyTitle: self.textView.text,
MPMediaItemPropertyAlbumTitle: @"TextToSpeech App",
MPMediaItemPropertyArtwork: artwork};
[MPNowPlayingInfoCenter defaultCenter].nowPlayingInfo = info;
}
- (void)updateToolbar
{
if (self.synthesizer.speaking && !self.synthesizer.paused) {
[self updateToolbarWithButton:@"pause"];
}
else {
[self updateToolbarWithButton:@"play"];
}
}
- (void)updateToolbarWithButton:(NSString *)buttonType
{
NSLog(@"updateToolbarWithButton: %@", buttonType);
UIBarButtonItem *audioControl;
if ([buttonType isEqualToString:@"play"]) {
// Play
audioControl = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemPlay target:self action:@selector(handlePlayPauseButton:)];
}
else {
// Pause
audioControl = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemPause target:self action:@selector(handlePlayPauseButton:)];
}
UIBarButtonItem *flexibleItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
[self.toolbar setItems:@[flexibleItem, audioControl, flexibleItem]];
}
- (void)remoteControlReceivedWithEvent:(UIEvent *)receivedEvent
{
NSLog(@"receivedEvent: %@", receivedEvent);
if (receivedEvent.type == UIEventTypeRemoteControl) {
switch (receivedEvent.subtype) {
case UIEventSubtypeRemoteControlPlay:
NSLog(@"UIEventSubtypeRemoteControlPlay");
if (self.synthesizer.speaking) {
[self.synthesizer continueSpeaking];
}
else {
[self speakUtterance];
}
break;
case UIEventSubtypeRemoteControlPause:
NSLog(@"pause - UIEventSubtypeRemoteControlPause");
if (self.pauseSettingSegmentedControl.selectedSegmentIndex == 0) {
// Pause immediately
[self.synthesizer pauseSpeakingAtBoundary:AVSpeechBoundaryImmediate];
}
else {
// Pause at end of current word
[self.synthesizer pauseSpeakingAtBoundary:AVSpeechBoundaryWord];
}
break;
case UIEventSubtypeRemoteControlTogglePlayPause:
if (self.synthesizer.paused) {
NSLog(@"UIEventSubtypeRemoteControlTogglePlayPause");
[self.synthesizer continueSpeaking];
}
else {
NSLog(@"UIEventSubtypeRemoteControlTogglePlayPause");
if (self.pauseSettingSegmentedControl.selectedSegmentIndex == 0) {
// Pause immediately
[self.synthesizer pauseSpeakingAtBoundary:AVSpeechBoundaryImmediate];
}
else {
// Pause at end of current word
[self.synthesizer pauseSpeakingAtBoundary:AVSpeechBoundaryWord];
}
}
break;
case UIEventSubtypeRemoteControlNextTrack:
NSLog(@"UIEventSubtypeRemoteControlNextTrack - appropriate for playlists");
break;
case UIEventSubtypeRemoteControlPreviousTrack:
NSLog(@"UIEventSubtypeRemoteControlPreviousTrack - appropriatefor playlists");
break;
default:
break;
}
}
}
#pragma mark UIPickerViewDelegate Methods
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
{
return 1;
}
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
return self.voices.count;
}
- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view
{
UILabel *rowLabel = [[UILabel alloc] init];
NSDictionary *voice = [self.voices objectAtIndex:row];
rowLabel.text = [voice objectForKey:@"label"];
return rowLabel;
}
- (void)pickerView:(UIPickerView *)pickerView didSelectRow: (NSInteger)row inComponent:(NSInteger)component
{
NSDictionary *voice = [self.voices objectAtIndex:row];
NSLog(@"new picker voice selected with label: %@", [voice objectForKey:@"label"]);
self.voice = [AVSpeechSynthesisVoice voiceWithLanguage:[voice objectForKey:@"voice"]];
}
#pragma mark SpeechSynthesizerDelegate methods
//in this method i called the method for speech recognition after voice is played.
- (void)speechSynthesizer:(AVSpeechSynthesizer *)synthesizer didFinishSpeechUtterance:(AVSpeechUtterance *)utterance
{
// This is a workaround of a bug. When we change the voice the first time the speech utterence is set fails silently. We check that the method willSpeakRangeOfSpeechString is called and set didStartSpeaking to YES there. If this method is not called (silent fail) then we simply request to speak again.
if (!didStartSpeaking) {
[self speakUtterance];
}
else {
[self updateToolbarWithButton:@"play"];
//here i am checking weather speech recognition is running or not , if not i am starting the speech recognition after playing a voice i.e,text to speech.
if (!audioEngine.isRunning) {
double delayInSeconds = 1.5;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
/// NSLog(@"Not Running");
[self start_record];
});
}
}
}
- (void)speechSynthesizer:(AVSpeechSynthesizer *)synthesizer willSpeakRangeOfSpeechString:(NSRange)characterRange utterance:(AVSpeechUtterance *)utterance
{
didStartSpeaking = YES;
}
#pragma mark UITextViewDelegate Methods
#pragma mark Cleanup Methods
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"updateToolbar" object:nil];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
In above code, in didFinishSpeechUtterance methods ,after ending the voice i.e, text to speech i am calling the start record which will start the speech recognition again ,So playing a voice the control is going to start record method,in that
recognitionTask = [self.speechRecognizer recognitionTaskWithRequest:recognitionRequest resultHandler:^(SFSpeechRecognitionResult * result, NSError * error1) {
in these line it throwing an error like [Utility] +[AFAggregator logDictationFailedWithError:] Error Domain=kAFAssistantErrorDomain Code=209 "(null)" I don't know why....
Please help me to resolve this issue.. Thanks in advance!!!
Upvotes: 3
Views: 2230
Reputation: 11
I was getting this 209 error and occasionally 203. I experimented a lot and finally was able to resolve it. Here's what I did:
Instead of doing [recogTask finish]
, I did [recogTask cancel]
.
Then I waited in a while loop for the recogTask
to cancel, before proceeding to set it to nil or any other step. e.g. I did:
while (recogTask && recogTask.cancelled == NO) { [NSThread sleepForTimeInterval:0.1]; }
So I didn't get any 209 or 203 code errors, also I stopped recog
for 2 seconds every 20 seconds. Yet to see if 2 seconds gap could be lowered.
Thanks.
Upvotes: 1