To get your own customized tutorial showing you how to use any feature of OpenEars or its plugins in your app, just flip the switches for the features you want. Anywhere that you see the icon
you can click it to have the code which follows it copied to your clipboard so you can paste it into your app. Remember, never test recognition on the Simulator since it doesn't use the real OpenEars audio driver.
Show me a 5-minute tutorial for an OpenEars-enabled app with the following features:
Preparing to use OpenEars
1
• Inside your downloaded OpenEars distribution there is a folder called "Frameworks". Drag that folder into your app project in Xcode. Make absolutely sure that in the add dialog "Create groups for any added folders" is selected and NOT "Create folder references for any added folders" because the wrong setting here will prevent your app from working.
• Optional: to save space in your app binary, go to the build settings for your app target and search for the setting "Deployment Postprocessing" and set it to "Yes".
• Add the iOS frameworks AudioToolbox and AVFoundation to your app.

Preparing to use Plugins
1
Then open up the Build Settings tab of the target app project and find the entry "Other Linker Flags" and add the linker flag "-ObjC":
And then drag your downloaded demo framework into your app project.
Using LanguageModelGenerator
1
#import <OpenEars/LanguageModelGenerator.h>Wherever you need to instantiate the language model generator, do it as follows:
LanguageModelGenerator *lmGenerator = [[LanguageModelGenerator alloc] init];
2
In the method where you want to create your language model (for instance your viewDidLoad method), add the following method call (replacing the placeholders like "WORD" and "A PHRASE" with actual words and phrases you want to be able to recognize):
NSArray *words = [NSArray arrayWithObjects:@"WORD", @"STATEMENT", @"OTHER WORD", @"A PHRASE", nil];
NSString *name = @"NameIWantForMyLanguageModelFiles";
NSError *err = [lmGenerator generateLanguageModelFromArray:words withFilesNamed:name];
NSDictionary *languageGeneratorResults = nil;
NSString *lmPath = nil;
NSString *dicPath = nil;
if([err code] == noErr) {
languageGeneratorResults = [err userInfo];
lmPath = [languageGeneratorResults objectForKey:@"LMPath"];
dicPath = [languageGeneratorResults objectForKey:@"DictionaryPath"];
} else {
NSLog(@"Error: %@",[err localizedDescription]);
}
If you are using the default English-language model generation, it is a requirement to enter your words and phrases in all capital letters, since the model is generated against a dictionary in which the entries are capitalized (meaning that if the words in the array aren't capitalized, they will not match the dictionary and you will not have the widest variety of pronunciations understood for the word you are using).
If you need to create a fixed language model ahead of time instead of creating it dynamically in your app, just use this method (or generateLanguageModelFromTextFile:withFilesNamed:) to submit your full language model using the Simulator and then use the Simulator documents folder script to get the language model and dictionary file out of the documents folder and add it to your app bundle, referencing it from there.Using PocketsphinxController
1
To use PocketsphinxController, you need a language model and a phonetic dictionary for it. These files define which words PocketsphinxController is capable of recognizing. They are created above by using LanguageModelGenerator.
2
#import <OpenEars/PocketsphinxController.h>In the middle part where instance variables go:
PocketsphinxController *pocketsphinxController;In the bottom part where class properties go:
@property (strong, nonatomic) PocketsphinxController *pocketsphinxController;
3
@synthesize pocketsphinxController;Among the other methods of the class, add this lazy accessor method for confident memory management of the object:
- (PocketsphinxController *)pocketsphinxController {
if (pocketsphinxController == nil) {
pocketsphinxController = [[PocketsphinxController alloc] init];
}
return pocketsphinxController;
}
4
[self.pocketsphinxController startListeningWithLanguageModelAtPath:lmPath dictionaryAtPath:dicPath languageModelIsJSGF:NO];
Using FliteController
1
To use FliteController, you need to have at least one Flite voice added to your project. When you added the "framework" folder of OpenEars to your app, you already imported a voice called Slt, so these instructions will use the Slt voice. You can get eight more free voices in OpenEarsExtras, available at https://bitbucket.org/Politepix/openearsextras
2
#import <Slt/Slt.h> #import <OpenEars/FliteController.h>In the middle part where instance variables go:
FliteController *fliteController; Slt *slt;In the bottom part where class properties go:
@property (strong, nonatomic) FliteController *fliteController; @property (strong, nonatomic) Slt *slt;
3
@synthesize fliteController; @synthesize slt;Among the other methods of the class, add these lazy accessor methods for confident memory management of the object:
- (FliteController *)fliteController {
if (fliteController == nil) {
fliteController = [[FliteController alloc] init];
}
return fliteController;
}
- (Slt *)slt {
if (slt == nil) {
slt = [[Slt alloc] init];
}
return slt;
}
4
[self.fliteController say:@"A short statement" withVoice:self.slt];
Using OpenEarsEventsObserver
1
#import <OpenEars/OpenEarsEventsObserver.h>at the @interface declaration, add the OpenEarsEventsObserverDelegate inheritance. An example of this for a view controller called ViewController would look like this:
@interface ViewController : UIViewController <OpenEarsEventsObserverDelegate> {
In the middle part where instance variables go:
OpenEarsEventsObserver *openEarsEventsObserver;In the bottom part where class properties go:
@property (strong, nonatomic) OpenEarsEventsObserver *openEarsEventsObserver;
2
@synthesize openEarsEventsObserver;Among the other methods of the class, add this lazy accessor method for confident memory management of the object:
- (OpenEarsEventsObserver *)openEarsEventsObserver {
if (openEarsEventsObserver == nil) {
openEarsEventsObserver = [[OpenEarsEventsObserver alloc] init];
}
return openEarsEventsObserver;
}
and then right before you start your first OpenEars functionality (for instance, right before your first self.fliteController say:withVoice: message or right before your first self.pocketsphinxController startListeningWithLanguageModelAtPath:dictionaryAtPath:languageModelIsJSGF: message) send this message:
[self.openEarsEventsObserver setDelegate:self];
3
- (void) pocketsphinxDidReceiveHypothesis:(NSString *)hypothesis recognitionScore:(NSString *)recognitionScore utteranceID:(NSString *)utteranceID {
NSLog(@"The received hypothesis is %@ with a score of %@ and an ID of %@", hypothesis, recognitionScore, utteranceID);
}
- (void) pocketsphinxDidStartCalibration {
NSLog(@"Pocketsphinx calibration has started.");
}
- (void) pocketsphinxDidCompleteCalibration {
NSLog(@"Pocketsphinx calibration is complete.");
}
- (void) pocketsphinxDidStartListening {
NSLog(@"Pocketsphinx is now listening.");
}
- (void) pocketsphinxDidDetectSpeech {
NSLog(@"Pocketsphinx has detected speech.");
}
- (void) pocketsphinxDidDetectFinishedSpeech {
NSLog(@"Pocketsphinx has detected a period of silence, concluding an utterance.");
}
- (void) pocketsphinxDidStopListening {
NSLog(@"Pocketsphinx has stopped listening.");
}
- (void) pocketsphinxDidSuspendRecognition {
NSLog(@"Pocketsphinx has suspended recognition.");
}
- (void) pocketsphinxDidResumeRecognition {
NSLog(@"Pocketsphinx has resumed recognition.");
}
- (void) pocketsphinxDidChangeLanguageModelToFile:(NSString *)newLanguageModelPathAsString andDictionary:(NSString *)newDictionaryPathAsString {
NSLog(@"Pocketsphinx is now using the following language model: \n%@ and the following dictionary: %@",newLanguageModelPathAsString,newDictionaryPathAsString);
}
- (void) pocketSphinxContinuousSetupDidFail { // This can let you know that something went wrong with the recognition loop startup. Turn on OPENEARSLOGGING to learn why.
NSLog(@"Setting up the continuous recognition loop has failed for some reason, please turn on OpenEarsLogging to learn more.");
}
Using LanguageModelGenerator+Rejecto
1
#import <OpenEars/LanguageModelGenerator.h>in your app and add the following line right underneath it:
#import <RejectoDemo/LanguageModelGenerator+Rejecto.h>Next, change this line where you create a language model:
NSError *err = [lmGenerator generateLanguageModelFromArray:words withFilesNamed:name];to use this method instead:
NSError *err = [lmGenerator generateRejectingLanguageModelFromArray:words withFilesNamed:name withOptionalExclusions:nil usingVowelsOnly:FALSE withWeight:nil];You will use the same array for languageModelArray (again, for the default English language model generator the words and phrases in your array must be in all-capital letters) and the same files name for fileName as you did with the old generateLanguageModelFromArray method, and to get started you can use the value "nil" for optionalExclusions, vowelsOnly, and weight, since they are there to help you refine your results and might not be needed. You can learn more about fine-tuning your results with those optional parameters in the Rejecto documentation.
Using PocketsphinxController+RapidEars
1
Like PocketsphinxController which it extends, we need a language model created with LanguageModelGenerator before using PocketsphinxController+RapidEars. We have already completed that step above.
2
#import <RapidEarsDemo/PocketsphinxController+RapidEars.h>Next, comment out all calls in your app to the method
startListeningWithLanguageModelAtPath:dictionaryAtPath:languageModelIsJSGF:and in the same part of your app where you were formerly using this method, place the following:
[self.pocketsphinxController setRapidEarsToVerbose:FALSE]; // This defaults to FALSE but will give a lot of debug readout if set TRUE [self.pocketsphinxController setRapidEarsAccuracy:1]; // This defaults to 20, maximum accuracy, but can be set as low as 1 to save CPU [self.pocketsphinxController setFinalizeHypothesis:TRUE]; // This defaults to TRUE and will return a final hypothesis, but can be turned off to save a little CPU and will then return no final hypothesis; only partial "live" hypotheses. [self.pocketsphinxController setFasterPartials:TRUE]; // This will give faster rapid recognition with less accuracy. This is what you want in most cases since more accuracy for partial hypotheses will have a delay. [self.pocketsphinxController setFasterFinals:FALSE]; // This will give an accurate final recognition. You can have earlier final recognitions with less accuracy as well by setting this to TRUE. [self.pocketsphinxController startRealtimeListeningWithLanguageModelAtPath:lmPath andDictionaryAtPath:dicPath]; // Starts the rapid recognition loop.If you find that sometimes you are getting live recognition and other times not, make sure that you have definitely replaced all instances of startListeningWithLanguageModelAtPath: with startRealtimeListeningWithLanguageModelAtPath:.
Using OpenEarsEventsObserver+RapidEars
1
#import <OpenEars/OpenEarsEventsObserver.h>Add the line
#import <RapidEarsDemo/OpenEarsEventsObserver+RapidEars.h>And after this OpenEarsEventsObserver delegate method you added when setting up your OpenEars app:
- (void) pocketSphinxContinuousSetupDidFail {
}
Just add the following extended delegate methods:
- (void) rapidEarsDidDetectLiveSpeechAsWordArray:(NSArray *)words andScoreArray:(NSArray *)scores {
NSLog(@"detected words: %@",[words componentsJoinedByString:@" "]);
// NSLog(@"detected scores: %@",scores);
}
- (void) rapidEarsDidDetectFinishedSpeechAsWordArray:(NSArray *)words andScoreArray:(NSArray *)scores {
NSString *hypothesis = [words componentsJoinedByString:@" "];
NSLog(@"detected complete statement: %@",hypothesis);
// NSLog(@"detected scores: %@",scores);
}
- (void) rapidEarsDidDetectBeginningOfSpeech {
NSLog(@"rapidEarsDidDetectBeginningOfSpeech");
}
- (void) rapidEarsDidDetectEndOfSpeech {
NSLog(@"rapidEarsDidDetectEndOfSpeech");
}
Using FliteController+NeatSpeech
1
FliteController+NeatSpeech preconditions
In order to use NeatSpeech, as well as importing the framework into your OpenEars-enabled project, it is also necessary to import the voices and voice data files by dragging the "Voice" folder in the disk image into your app project. Make sure that in Xcode's "Add" dialog, "Create groups for any added folders" is selected. Make sure that "Create folder references for any added folders" is not selected or your app will not work.2
FliteController+NeatSpeech implementation
FliteController+Neatspeech just replaces FliteController's voice type with the advanced NeatSpeech voice type and it replaces FliteController's say:withVoice: method with NeatSpeech's sayWithNeatSpeech:withVoice: method. So, in order to use it in a project that has FliteController enabled, you will only have to do the following: In your header replace this:#import <Slt/Slt.h> #import <OpenEars/FliteController.h>with this:
#import <Emma/Emma.h> #import <OpenEars/FliteController.h> #import <NeatSpeechDemo/FliteController+NeatSpeech.h>and replace this:
Slt *slt;with this:
Emma *emma;and replace this:
@property (strong, nonatomic) Slt *slt;with this:
@property (strong, nonatomic) Emma *emma;in your implementation, replace this:
@synthesize slt;with this:
@synthesize emma;and replace this:
- (Slt *)slt {
if (slt == nil) {
slt = [[Slt alloc] init];
}
return slt;
}
with this:
- (Emma *)emma {
if (emma == nil) {
emma = [[Emma alloc]initWithPitch:0.0 speed:0.0 transform:0.0];
}
return emma;
}
and replace this:
[self.fliteController say:@"A short statement" withVoice:self.slt];with this:
[self.fliteController sayWithNeatSpeech:@"I have always wished for my computer to be as easy to use as my telephone; my wish has come true because I can no longer figure out how to use my telephone." withVoice:self.emma];And replace any other calls to say:withVoice with sayWithNeatSpeech:withVoice: Once this is definitely working you can remove the Slt or other Flite voice frameworks from your app to reduce app size. You can replace references to the Emma framework and object with any of the other voices to try them out. Emma is a US-accent female voice, William is a US-accent male voice, Beatrice is a UK-accent female voice and Elliott is a UK-accent male voice. You can also change the speaking speed, pitch of the voice, and inflection of the voice using the voice's initializer arguments speed, pitch and transform respectively. As an example, to initialize the Emma voice with a higher pitch you could use the following initialization: Emma *emma = [[Emma alloc]initWithPitch:0.2 speed:0.0 transform:0.0]; You can pass the sayWithNeatSpeech:withVoice: method as much data as you want at a time. It will process the speech in phases in the background and return it for playback once it is ready. This means that you should rarely experience long pauses while waiting for synthesis, even for very long paragraphs. Very long statements need to include pause indicators such as periods, exclamation points, question marks, commas, colons, semicolons, etc. To interrupt ongoing speech while it is in progress, send the message [self.fliteController stopSpeaking];. This will not interrupt speech instantaneously but halt it at the next available opportunity.
Using OpenEarsEventsObserver+SaveThatWave
1
#import <OpenEars/OpenEarsEventsObserver.h>Add the line
#import <SaveThatWaveDemo/OpenEarsEventsObserver+SaveThatWave.h>And after this OpenEarsEventsObserver delegate method you added when setting up your OpenEars app:
- (void) pocketSphinxContinuousSetupDidFail {
}
Just add the following extended delegate method:
- (void) wavWasSavedAtLocation:(NSString *)location {
NSLog(@"WAV was saved at the path %@", location);
}
Using SaveThatWaveController
1
#import <SaveThatWaveDemo/SaveThatWaveController.h>In the middle part where instance variables go:
SaveThatWaveController *saveThatWaveController;In the bottom part where class properties go:
@property (strong, nonatomic) SaveThatWaveController *saveThatWaveController;
2
@synthesize saveThatWaveController;Among the other methods of the class, add this lazy accessor method for confident memory management of the object:
- (SaveThatWaveController *)saveThatWaveController {
if (saveThatWaveController == nil) {
saveThatWaveController = [[SaveThatWaveController alloc] init];
}
return saveThatWaveController;
}
and change this block of code:
- (PocketsphinxController *)pocketsphinxController {
if (pocketsphinxController == nil) {
pocketsphinxController = [[PocketsphinxController alloc] init];
}
return pocketsphinxController;
}
to this:
- (PocketsphinxController *)pocketsphinxController {
if (pocketsphinxController == nil) {
pocketsphinxController = [[PocketsphinxController alloc] init];
pocketsphinxController.outputAudio = TRUE;
}
return pocketsphinxController;
}
Alternately, if you only want to record speech and you don't want to perform local speech recognition on it, use the following settings instead:
- (PocketsphinxController *)pocketsphinxController {
if (pocketsphinxController == nil) {
pocketsphinxController = [[PocketsphinxController alloc] init];
pocketsphinxController.outputAudio = TRUE;
pocketsphinxController.processSpeechLocally = FALSE; // This shuts off offline speech recognition.
}
return pocketsphinxController;
}
Then, after this line:
[self.pocketsphinxController startListeningWithLanguageModelAtPath:lmPath dictionaryAtPath:dicPath languageModelIsJSGF:NO];You can add the line:
[self.saveThatWaveController start];