Freigeben über


How to make a Hogwarts Sorting Hat using Windows Phone

Harry Potter wearing the Sorting HatIn the world of Harry Potter, when a new student entered the Hogwarts School of Witchcraft and Wizardry, they are “sorted” by a Sorting Hat into one of four houses.  The student puts the Sorting Hat on his/her head, and the Sorting Hat yells out the house to which the magical students will belong during their time at school. 

When I was asked to keynote the Lansing Makers Week, I wanted to make something to show.  After a few cups of coffee, a Sorting Hat seemed like a good idea.  My design requirements were (in rough order of importance to me):

  • I needed text-to-speech support for the Hat to call out the chosen house. 
  • I wanted the algorithm to choose the house to be deterministic, not random.  Since I was demoing it at a keynote, I wanted to be able to control what house it would pick for me.  (I mean, how embarrassing would it be to get sorted into Hufflepuff live on stage?) 
  • Optimally, I would prefer that the Hat use a male British accent. 
  • I was thinking about using the accelerometer to determine the house, but that wasn’t a hard-and-fast requirement. 

I recently received a Galileo board and I’ve been looking for a good project to build with it, but the more I thought about it, it made sense to me to build a Windows Phone app.  Here’s why:

  • The Galileo board would need extra components and likely cloud integration.  With Window Phone, I could do it all on-device in a single app, and Windows Phone already has support for text-to-speech and an accelerometer.  
  • The Galileo board seems more breakable than my phone.  I wanted to let people try on the Hat, and they might drop it accidentally.  I’ve dropped my phone a couple of times and it’s been okay.  The Galileo board has many more exposed parts that could be damaged.  

So, I built a Hogwarts Sorting Hat.  A Windows Phone 8.1 app in a pocket in the hat uses an accelerometer to pick the house and text-to-speech to yell out the house name.  Currently, the trigger is touching a button, but I may change that.  I built in a 3-second pause for dramatic effect, and then the Hat announces your house. 

To try this yourself:

  1. Download the Sorting Hat application from the Windows Phone Store at https://www.windowsphone.com/en-us/store/app/sorting-hat/ab9d9e52-5df7-40fd-aac8-a85119b0d053.  
  2. Make a hat.  I bought a witch’s hat for around $5 USD on https://amazon.com.  I turned the hat inside out to make it look more straggly-looking (the Hogwarts Sorting Hat is supposed to be in rough shape) and used duct tape to make a pocket on the hat to hold the phone.  I colored the grey duct tape black with a Sharpie permanent marker so it would blend in more nicely.  I thought I would need to counterbalance the weight of the phone with a deck of cards or something on the back, but it was pretty stable without it.  Here is what it looks like:Hogwarts Sorting Hat, up close
  3. Finally, put your Windows Phone in the Sorting Hat.  For best results, turn your phone’s volume up all the way.  Touch the button to activate it, wait 3 seconds, and the Sorting Hat will announce your house.  There is a quick video demonstration at https://www.youtube.com/watch?v=kY0Igi-vcmc

Now, some code samples!  This is a pretty simple app, but I’ll highlight some of the interesting bits. 

First, here is the voice initialization code.  It contains logic to choose the voice (with a preference for a male British voice), and it sets up the dropdown ComboBox with all of the installed voices so you can select a different voice if you prefer. 

 private void ComboBoxVoiceChooser_Initialize()
 {
     // Get all of the installed voices.
     var voices = Windows.Media.SpeechSynthesis.SpeechSynthesizer.AllVoices;
  
     // Get the currently selected voice. 
     VoiceInformation currentVoice = this.synth.Voice;
     VoiceInformation updatedVoice = null;
  
     // First, try to use a British male voice. 
     updatedVoice = voices.FirstOrDefault(voice => voice.Language.Equals("en-GB", StringComparison.OrdinalIgnoreCase) && voice.Gender == VoiceGender.Male);
     // If not available, try another English-speaking male voice. 
     if (updatedVoice == null)
     {
         updatedVoice = voices.FirstOrDefault(voice => voice.Gender == VoiceGender.Male && voice.Language.StartsWith("en"));
     }
     // Finally, fall back to any male voice. 
     if (updatedVoice == null && currentVoice.Gender == VoiceGender.Female)
     {
         updatedVoice = voices.FirstOrDefault(voice => voice.Gender == VoiceGender.Male);
     }
     // If we have updated the voice in the above logic, set the speech synthesizer to use it. 
     if (updatedVoice != null)
     {
         this.synth.Voice = updatedVoice;
     }
  
     // Add each voice to the ComboBox display
     foreach (VoiceInformation voice in voices)
     {
         System.Diagnostics.Debug.WriteLine(voice.DisplayName + ": " + voice.Gender + "" + voice.Language);
  
         ComboBoxItem item = new ComboBoxItem();
         item.Name = voice.DisplayName;
         item.Tag = voice;
         item.Content = voice.DisplayName;
         this.comboBoxVoiceChooser.Items.Add(item);
  
         // Check to see if this is the current voice, so that we can set it to be selected
         if (this.synth.Voice.Id == voice.Id)
         {
             item.IsSelected = true;
             this.comboBoxVoiceChooser.SelectedItem = item;
         }
     }
 }
  

Currently, when you press the big button in the UI, it starts a timer.  After this dramatic 3-second pause, the below code is invoked to read the accelerometer values and choose a house based on that. 

 private void DisplayCurrentReading(object sender, object args)
 {
     AccelerometerReading reading = acc.GetCurrentReading();
     if (reading != null)
     {
         timer.Stop();       // so it only grabs one reading
  
         double x = reading.AccelerationX;
         double y = reading.AccelerationY;
         double z = reading.AccelerationZ;
  
         ChooseHouse(x, y, z);
  
         btnReady.Content = "Ready";
     }
 }
 private async void ChooseHouse(double x, double y, double z)
 {
     // Logic: 
     // Strongest in -Z direction: Ravenclaw
     // Strongest in Z direction: Gryffindor
     // Strongest in X direction: Hufflepuff
     // Strongest in -X direction: Slytherin
  
     if (Math.Abs(z) >= Math.Abs(x))     // check if accleration is stronger in the X or Z direction
     {
         if (z < 0) house = "Ravenclaw";
         else house = "Gryffindor";
     }
     else
     {
         if (x < 0) house = "Slytherin";
         else house = "Hufflepuff";
     }
  
     await Speak(house);
 }

Finally, here is the text-to-speech. 

 private async Task Speak(string textToSay)
 {
     SpeechSynthesisStream stream = await synth.SynthesizeTextToStreamAsync(textToSay);
     MediaElement mediaElement = new MediaElement();
     mediaElement.SetSource(stream, stream.ContentType);
     mediaElement.Play();
 }

At Lansing Maker Week, I was ridiculously excited to get sorted into Ravenclaw.  :)  I hope you enjoy too!

Jennifer wearing Sorting Hat at Lansing Maker Week