Skip to content

Instantly share code, notes, and snippets.

@thinkbeforecoding
Created December 23, 2014 10:43
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thinkbeforecoding/37ae152c6fe24a649d86 to your computer and use it in GitHub Desktop.
Save thinkbeforecoding/37ae152c6fe24a649d86 to your computer and use it in GitHub Desktop.
Speaking computers for more fun !
(**# 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