-
-
Save thinkbeforecoding/37ae152c6fe24a649d86 to your computer and use it in GitHub Desktop.
Speaking computers for more fun !
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(**# Speaking computers for more fun ! | |
*I didn't try it on mono, but it should also work with some tweaking, see details [here](http://go-mono.com/status/status.aspx?reference=4.0&profile=4.5&assembly=System.Speech)* | |
Xmas is a good time to surprise kids, and what's more fun than a talking computer ?! | |
## Hello world ! | |
Nothing's easier, and this kind of Hello World will appeal them to programming in a flash : | |
*) | |
#r "System.Speech" | |
open System.Speech.Synthesis | |
let synt = new SpeechSynthesizer() | |
let say s = synt.Speak(s: string) | |
say "Hello world !" | |
(** Of course, if you're french like me, it'll say this with an awful french accent - something | |
like **hélo ouorld** ! | |
But you can select a different voice if available by providing hints: | |
*) | |
open System.Globalization | |
let english = CultureInfo.GetCultureInfo("en-US") | |
synt.SelectVoiceByHints(VoiceGender.NotSet, VoiceAge.NotSet, 1, english) | |
say "Hello world !" | |
(** Far better ! | |
## Can you beat it ? | |
Now, a talking fizz buzz, up to 100 ! Can you beat it ? | |
*) | |
[1 .. 100] | |
|> List.map (fun n -> | |
match n%3, n%5 with | |
| 0, 0 -> "FizzBuzz" | |
| 0, _ -> "Fizz" | |
| _, 0 -> "Buzz" | |
| _ -> string n ) | |
|> List.iter say | |
(** | |
## Even harder ! | |
Now with a recognizer, we can wait for voice user input. | |
The problem with the Grammar API is that it's totally mutable and not really DSL oriented. | |
Let's correct that : | |
*) | |
open System.Speech.Recognition | |
type Grammar = | |
| Phrase of text:string * result: string | |
| Lst of Grammar list | |
| Alt of Grammar list | |
| Repeat of min: int * max: int * Grammar | |
let rec build = function | |
| Phrase (text, result) -> | |
// Just build the a single phrase | |
GrammarBuilder(SemanticResultValue(text,result)) | |
| Lst grammars -> | |
// Append parts of grammars one after the other | |
let builder = GrammarBuilder() | |
grammars | |
|> List.map build | |
|> List.iter builder.Append | |
builder | |
| Alt alternatives -> | |
// Create alternatives | |
let choices = | |
alternatives | |
|> List.map build | |
|> List.toArray | |
GrammarBuilder(Choices()) | |
| Repeat(min, max, grammar) -> | |
// Repeat a part of the grammar | |
GrammarBuilder(build grammar, min, max) | |
(** | |
This is not a full DSL for speach recognition, you can look at all the GrammarBuilder methods | |
to add more possibilities.. Even here, I'll use only Phrase and Alt. | |
Now, we need a recognizer and wire the grammar with functions that will be called when a part of | |
the grammar is recognized or rejected. | |
It is mandatory to set grammar's culture to the recognizer's culture. | |
There's usually a single recognizer installed by default on your system and it uses installed system's | |
culture. In my case, it'll be french. | |
*) | |
let recog = new SpeechRecognizer() | |
let recognize grammar recognized rejected = | |
let builder = build grammar | |
builder.Culture <- recog.RecognizerInfo.Culture | |
printfn "%A" recog.RecognizerInfo.Culture | |
recog.LoadGrammar(Grammar builder) | |
recog.SpeechRecognized |> Event.add (fun e -> recognized e.Result.Text (string e.Result.Semantics.Value)) | |
recog.SpeechRecognitionRejected |> Event.add (fun e -> rejected ()) | |
recog.Enabled | |
(** | |
We can then use this to create a little Christmass quizz thanks to the FSharp.Data FreeBase Type Provider ! | |
We'll use free base to find a list of Actors who plaid Santa in movies. | |
For this, install the FSharp.Data NuGet: | |
` | |
nuget install FSharp.Data -o packages -x | |
` | |
The dll should be in .\packages\FSharp.Data\lib\net40\FSharp.Data.dll | |
*) | |
#r @"packages\FSharp.Data\lib\net40\FSharp.Data.dll" | |
open FSharp.Data | |
let fb =FreebaseData.GetDataContext() | |
(** | |
Let's build the grammar | |
*) | |
let santaActorsFilms = | |
fb.``Arts and Entertainment`` | |
.Film | |
.``Film characters`` | |
.IndividualsAZ.S | |
.``Santa Claus`` | |
.``Portrayed in films`` | |
|> Seq.map (fun c -> c.Actor.Name, c.Film.Name) | |
|> Seq.toList | |
let santaActorsGrammar = | |
santaActorsFilms | |
|> List.map (fun (actor,film) -> Phrase(actor, film)) | |
|> Alt | |
(** | |
Here is the function to call when an actor is recognized. | |
I tried to pass a discriminated union as a value, but even if the API uses an object, the documentation | |
states that it has to be a bool, an int or a string. I used only strings here. | |
*) | |
let recognized text value = | |
say (sprintf "True ! %s was Santa in %s" text value) | |
(** | |
Here is the function when the speech could not be matched with the grammar. | |
It is also possible to get the audio of the text in this case. I decided to ignore it due to time constraints. | |
*) | |
let rejected () = say "No, Not a Santa !" | |
(** Now, let's run it !! *) | |
recognize santaActorsGrammar recognized rejected | |
(** | |
At this point the speech recognition configuration should appear if it's the | |
first time you use it. | |
Once done you should be able to try the quizz ! | |
If your OS culture is not english, don't hesitate to use a local accent for | |
actor's name ! | |
*) | |
(** | |
## Conlusion | |
I hope you had fun with this API, and that you'll want to tweak it for your own demo ! | |
**Happy Christmass !** | |
*) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment