Part 21: Permanently Saving the Audio Wav File
Description
Source Code: https://aka.ms/absbeginnerdevwp8
PDF Version: https://aka.ms/absbeginnerdevwp8pdf
At this point, we are recording audio and saving it to a temp file on the app's IsolatedStorage. Next, we need to allow the user to permanently save the sound providing details like the display name for the new custom sound.
The game plan:
- Add an event handler method to the "save" application bar button
- We'll manage the state of the application bar ... it should only be visible if a temporary audio file has been created and is ready to be saved permanently
- We'll use the Coding4Fun Toolkit once again, this time to display an InputDialog to capture the name of the new custom sound audio file
- We'll serialize the data for the CustomSounds into a JSON file
- And we'll modify our data model to also load the CustomSounds JSON file to create new instances of the data model for those custom sounds
1. Add an event handler method to the "save" button and manage application bar state
Earlier we created the application bar for the RecordAudio.xaml page by enabling the BuildLocalizedApplicationBar() method. So all we need to do is make it active:
- In line 43 I add an event handler to the Click method using the technique we've utilized throughout this series ... while we're at it, use the other technique I've demonstrated to generate a method stub for the SaveRecordingClick method (hover-mouse-over-blue-dash to reveal an Intellisense menu option to generate the method stub)
- In line 46 I immediately hide the application bar ... we only want to show it when we have a sound to save (after the user records a custom sound)
Next, we'll enable the application bar after the user stops recording. In the RecordAudioUnchecked() method, we'll set the IsVisible property to true (see line 67, below):
2. Use the Coding4Fun Toolkit to display an InputDialog to capture the name of the new custom sound audio file
In the previous step we added a method stub for the SaveRecordingClick() method.
I'll replace the line of code that throws an exception as a reminder and write the following (line 51, below):
Since the InputPrompt is from a different namespace than the others we've utilized so far, we'll need to add a using statement (using the hover-over-the-blue-dash method to reveal the contextual menu).
Next we'll configure and show the InputPrompt:
- Here we set the Title and Message we want to appear in the InputPrompt
- We attach an event handler method (and generate the method stub using techniques I've demonstrated before) to the Completed event ... we'll tackle that in the next step
- Once configured, I show the dialog
When the user types in a name for the new custom sound and clicks the checkmark button, the FileNameCompleted() event handler method will fire.
We'll ensure that the InputPrompt was exited properly by the user by checking the result. We'll check the PopUpResult that was sent into this event handler method as an input parameter. If the result is "OK" then we can perform the logic necessary to save the temporary file as a new "permanent" sound. See the code I added below, as well as the code comments which give me an outline of the "next steps" I'll want to perform:
- If the user correctly typed in a new name and clicked the checkmark button to exit the InputDialog, then we'll perform the tasks required to save the custom sound and make it available in the Custom Sounds view of the Sound Board.
- Finally, we'll navigate back to the MainPage.xaml
Between callouts 1 and 2, above, are an outline of what needs to happen in order for this to work correctly. Before we attempt to implement those ideas, let's make sure the flow works as we would expect so far by running the application.
I record a custom sound by using the ToggleButton. When I stop recording, I in fact see the application bar appear:
When I click the disk icon to save the custom sound, it displays the InputDialog:
And when I type in a new sound name and click the checkmark icon, the dialog disappears and returns me to the MainPage.xaml. Great!
Now, for the hard part ... we'll perform those tasks I outlined in the code comments.
3. Save the sound file into a permanent IsolatedStorage area, serialize the data for the CustomSounds into a JSON file
At this point we have a custom sound recorded and stored as a temporary file and we've just collected a friendly display name for that sound. We want to accomplish two basic tasks:
- First, we want to add the custom sound to our data model. If information about our new custom sound is never added to the data model, then we'll never be able to render it to the Custom Sounds view on our MainPage.xaml. So, we'll create a new instance of the SoundData class and fill in the FilePath and Title properties appropriately.
- Next, we'll want to move that file from its temporary location to a permanent subfolder called /customAudio/ ... this is purely to keep all our custom sound files organized in one place.
So, I add the following code to the FileNameCompleted() method:
- I create a new instance of SoundData and fill in the Title and FilePath attributes. Notice that we'll be giving our custom sound a new name, but the contents of the file will remain the same.
- Just like when we originally recorded the custom sound, we get a reference to the IsolatedStorage area specifically for our app. We do this with a using statement to properly let go of unmanaged resources (like the Phone's storage). The very first time this code is executed, it may need to create the special folder where we're storing our custom audio files (line 76). Finally, we're moving the temporary file to the new permanent storage area and giving it a new name all in one fell swoop (line 78).
- Next, we're adding the new instance of the SoundData class to our CustomSounds.Items collection. At this moment, we should be able to return back to the MainPage.xaml and see the new custom sound appear in the list of Custom Sounds.
However, what will happen when we close the application and it is complete removed from the Phone's memory? When that happens, the CustomSounds.Items collection will be removed from memory and the next time the app is run, it will have no memory of our custom sounds. We need a way to store our custom sounds data so that we can load it into our data model the next time the user runs our app.
4. Serialize and deserialize the CustomSounds SoundGroup into / out of Json
To do this, we'll need to serialize our CustomSounds.Items collection into a data format. There are many data formats we could choose, but we'll pick a very popular, light-weight easy to use format called JSON. It is short for the JavaScript Object Notation. It will allow us to easily represent our collection as JavaScript objects. If we utilize a third-party open source library called Json.NET, we won't even have to think about the data's format ... much of that complexity will be hidden behind simple method calls.
To begin, we'll open up the NuGet Package Manager (using the technique I demonstrated earlier ... right-click the References folder and choose the Manage NuGet Packages ... option.
- Search for: Json ... one of the top options should be Json.NET.
- Click the Install button next to the Json.NET package. It will take a few moments to install that package into your project.
- Click the Close button to continue.
To verify that Json.NET was installed successfully, open up the References folder in the SoundBoard project and verify that Newtonsoft.Json appears there:
Back in the FileNameCompleted() method, the next step is to convert the CustomSounds.Items collection to Json, then store it to disk.
We'll use the Newtonsoft.Json.JsonConvert class to perform the conversion ... you'll need to add the appropriate using statements to acc