{"id":240,"date":"2017-10-18T22:20:41","date_gmt":"2017-10-19T03:20:41","guid":{"rendered":"http:\/\/blog.cubicleninja.com\/?p=240"},"modified":"2017-10-18T22:20:41","modified_gmt":"2017-10-19T03:20:41","slug":"creating-a-cross-platform-soundboard-with-xamarin-forms-part-1","status":"publish","type":"post","link":"https:\/\/blog.cubicleninja.com\/?p=240","title":{"rendered":"Creating a Cross-Platform Soundboard with Xamarin Forms &#8211; Part 1"},"content":{"rendered":"<p>One of the things I enjoy doing when I&#8217;m going to jump into a new development area is to create a soundboard-type application. I like to do this for a few reasons:<\/p>\n<ol>\n<li>I like playing with soundboards<\/li>\n<li>It lets me work with media playback on the platform(s)<\/li>\n<li>It lets me work with file IO on the platform(s)<\/li>\n<li>It gives me a straight-forward UI structure to model towards<\/li>\n<\/ol>\n<p>I did one for iOS native with Xcode, Android native with Android Studio, and a web-based solution using Visual Studio with .NET, and now I&#8217;m going to create a cross-platform soundboard using Xamarin Forms (for iOS, Android, and UWP). I plan to go through the entire process in a way that anyone new to Xamarin development will be able to follow step-by-step and have a working application by the end of these posts.\u00a0 An important thing to bear in mind with is that the code probably won&#8217;t be perfect and there will most likely be multiple different ways to reach the same goal (and some of the other ways could be better than the one I&#8217;ve chosen).<\/p>\n<p>We&#8217;ll start out in Visual Studio and create a project of type <strong>Visual C#<\/strong> -&gt; <strong>Cross-Platform<\/strong> -&gt; <strong>Cross Platform App (Xamarin)<\/strong><\/p>\n<p><a style=\"margin-left: 16px;\" href=\"http:\/\/blog.cubicleninja.com\/wp-content\/uploads\/2017\/10\/Soundboard_1.png\" target=\"_blank\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-medium wp-image-243\" src=\"http:\/\/blog.cubicleninja.com\/wp-content\/uploads\/2017\/10\/Soundboard_1-300x134.png\" alt=\"Soundboard_1\" width=\"300\" height=\"134\" \/><\/a><\/p>\n<p>On the next project wizard screen, we&#8217;ll create it as a <b>Blank App<\/b> using <b>Xamarin.Forms<\/b> and with a <b>Portable Class Library (PCL)<\/b> (NOTE &#8211; I would normally recommend going with a Master Detail project and leveraging MVVM, but I want to keep this one very simple and basic).<\/p>\n<p><a style=\"margin-left: 16px;\" href=\"http:\/\/blog.cubicleninja.com\/wp-content\/uploads\/2017\/10\/Soundboard_2.png\" target=\"_blank\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-medium wp-image-244\" src=\"http:\/\/blog.cubicleninja.com\/wp-content\/uploads\/2017\/10\/Soundboard_2-300x233.png\" alt=\"Soundboard_2\" width=\"300\" height=\"233\" \/><\/a><\/p>\n<p>At this point, Visual Studio will churn for a bit as it creates all the necessary projects for your new Xamarin.Forms solution, relax and let it do its thing. Eventually, you will be prompted to select the version details for the Universal Windows Project (UWP), I&#8217;ll leave them at the defaults it pre-selects for me.<\/p>\n<p><a style=\"margin-left: 16px;\" href=\"http:\/\/blog.cubicleninja.com\/wp-content\/uploads\/2017\/10\/Soundboard_3.png\" target=\"_blank\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-medium wp-image-245\" src=\"http:\/\/blog.cubicleninja.com\/wp-content\/uploads\/2017\/10\/Soundboard_3-300x122.png\" alt=\"Soundboard_3\" width=\"300\" height=\"122\" \/><\/a><\/p>\n<p>Once you select your targets, Visual Studio will do some more processing to finishing setting everything up for you.\u00a0When it is finished, you&#8217;ll be presented with a solution that contains your PCL, Android, iOS, and UWP projects.<\/p>\n<p><a style=\"margin-left: 16px;\" href=\"http:\/\/blog.cubicleninja.com\/wp-content\/uploads\/2017\/10\/Soundboard_4.png\" target=\"_blank\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-medium wp-image-246\" src=\"http:\/\/blog.cubicleninja.com\/wp-content\/uploads\/2017\/10\/Soundboard_4-300x179.png\" alt=\"Soundboard_4\" width=\"300\" height=\"179\" \/><\/a><\/p>\n<p>At this point, I&#8217;m going to deviate for a moment to discuss a key bit of knowledge that you&#8217;ll want to have at least a passing understanding of if you plan to do much with Xamarin and cross-platform development: the <a href=\"https:\/\/developer.xamarin.com\/guides\/xamarin-forms\/application-fundamentals\/dependency-service\/introduction\/\" target=\"_blank\">DependencyService<\/a>.<\/p>\n<p>That link goes out to true Xamarin documentation to give a full breakdown of what the DependencyService is and how you should use it, I highly recommend reading through it so you have a good grasp of the concepts. We&#8217;ll be making use of it in this application, which will give you a good real-world example of it, but the key elements are:<\/p>\n<ul>\n<li>Create the Interface in the PCL<\/li>\n<li>Create a custom class to implement the interface for each platform<\/li>\n<li>Each class must use a metadata attribute to register itself for the DependencyService<\/li>\n<li>Each class must have a parameterless constructor<\/li>\n<li>Calls through the DependencyService in the PCL will route to the correct platform implementation<\/li>\n<\/ul>\n<p>We&#8217;ll start with the easiest interface first, because it&#8217;s the easiest interface&#8230; In the case of the soundboard, the simplest interface will be the one that handles playing a sound file. Right click on the <b>Portable<\/b> project at the top of your solution and then select <b>Add<\/b> -&gt; <b>Add New Item<\/b>. From the list of templates, select <b>Visual C#<\/b> -&gt; <b>Interface<\/b> and give it a name of <b>ISoundPlayer.cs<\/b>.<\/p>\n<p><a style=\"margin-left: 16px;\" href=\"http:\/\/blog.cubicleninja.com\/wp-content\/uploads\/2017\/10\/Soundboard_5.png\" target=\"_blank\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-medium wp-image-247\" src=\"http:\/\/blog.cubicleninja.com\/wp-content\/uploads\/2017\/10\/Soundboard_5-300x267.png\" alt=\"Soundboard_5\" width=\"300\" height=\"267\" \/><\/a><\/p>\n<p>Add an accessor for the interface to make it public, and give it a single void method named <strong>PlaySoundFile<\/strong>\u00a0that takes a single string parameter named <strong>fileName.<\/strong><\/p>\n<pre class=\"lang:c# decode:true\">namespace XamarinSoundboard\n{\n    public interface ISoundPlayer\n    {\n        void PlaySoundFile(string fileName);\n    }\n}<\/pre>\n<p>That&#8217;s it for the interface, told you it would be easy!\u00a0 Now we need to create a class for each platform that implements the interface.\u00a0 For our purposes here, we&#8217;re going to assume that we will be including the sound files in each of the platforms native ways:\u00a0 Android and UWP will have them as assets and iOS will have them as bundle resources.\u00a0 This means that we&#8217;ll end up having multiple copies of them in our solution, and there are options available to prevent having to do that, but that will be for another post.<\/p>\n<p>Going from top to bottom in our solution, we&#8217;ll create the Android implementation first (lucky for us it is also the simplest of the three).\u00a0 Right click on the Android project in the solution and select\u00a0<strong>Add<\/strong> -&gt;\u00a0<strong>Class\u00a0<\/strong>and give it the name\u00a0<strong>SoundPlayerImplementation.<\/strong><\/p>\n<p><a style=\"margin-left: 16px;\" href=\"http:\/\/blog.cubicleninja.com\/wp-content\/uploads\/2017\/10\/Soundboard_6.png\" target=\"_blank\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-medium wp-image-248\" src=\"http:\/\/blog.cubicleninja.com\/wp-content\/uploads\/2017\/10\/Soundboard_6-300x268.png\" alt=\"Soundboard_6\" width=\"300\" height=\"268\" \/><\/a><\/p>\n<p>We want to add the public accessor to the new class, wire it up to the interface we created in the PCL (<strong>ISoundPlayer<\/strong>), and then implement the audio playback functionality for Android.\u00a0 Rather than use words to explain the implementation, I&#8217;ll just provided the actual code with a bunch of comments included.<\/p>\n<pre class=\"lang:default decode:true \">using Android.Media;\nusing Xamarin.Forms;\nusing XamarinSoundboard.Droid;\n\n[assembly: Dependency(typeof(SoundPlayerImplementation))]\nnamespace XamarinSoundboard.Droid\n{\n    public class SoundPlayerImplementation : ISoundPlayer\n    {\n        public SoundPlayerImplementation()\n        {\n\n        }\n\n        public void PlaySoundFile(string fileName)\n        {\n            \/\/ Create a new Android MediaPlayer object\n            \/\/ In android, it will remain in scope while it is playing, so we can create and instantiate it inside the method\n            var player = new MediaPlayer();\n            \/\/ Retrieve an asset file descriptor to the file name passed in out of the application assets\n            var fd = global::Android.App.Application.Context.Assets.OpenFd(fileName);\n\n            \/\/ Add a delegate handler that fires once the file is fully prepared in the media player\n            \/\/ that will start the sound playback\n            player.Prepared += (s, e) =&gt;\n            {\n                player.Start();\n            };\n\n            \/\/ Wire up the datasource using the FileDescriptor, start offset of the file, and the length of the file\n            player.SetDataSource(fd.FileDescriptor, fd.StartOffset, fd.Length);\n            \/\/ Close out the file descriptor\n            fd.Close();\n            \/\/ instruct the MediaPlayer to prepare for playback\n            player.Prepare();\n        }\n    }\n}<\/pre>\n<p>Key items to note are the metadata instruction that registers our class to the DependencyService and the implementation of the interface we created in the PCL.<\/p>\n<p>Next, we&#8217;ll create the implementation file for iOS &#8211; right click on the iOS project then choose\u00a0<strong>Add<\/strong> -&gt;\u00a0<strong>Class\u00a0<\/strong>and give it the same name of\u00a0<strong>SoundPlayerImplementation<\/strong> (the namespaces will take care of keeping them unique for us across the solution).<\/p>\n<p><a style=\"margin-left: 16px;\" href=\"http:\/\/blog.cubicleninja.com\/wp-content\/uploads\/2017\/10\/Soundboard_7.png\" target=\"_blank\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-medium wp-image-249\" src=\"http:\/\/blog.cubicleninja.com\/wp-content\/uploads\/2017\/10\/Soundboard_7-300x270.png\" alt=\"Soundboard_7\" width=\"300\" height=\"270\" \/><\/a><\/p>\n<p>Just like with the Android implementation, we want to add the public accessor to the new class, wire it up to the interface we created in the PCL (<strong>ISoundPlayer<\/strong>), and then implement the audio playback functionality for iOS.\u00a0 There is a little bit more to the code in iOS, mainly because of how we will instantiate the audio player from the resource bundle.\u00a0 The code and comments will explain it better than I could type it back out.<\/p>\n<pre class=\"lang:default decode:true\">using Xamarin.Forms;\nusing XamarinSoundboard.iOS;\nusing Foundation;\nusing System.IO;\nusing AVFoundation;\n\n[assembly: Dependency(typeof(SoundPlayerImplementation))]\nnamespace XamarinSoundboard.iOS\n{\n    public class SoundPlayerImplementation : ISoundPlayer\n    {\n        \/\/ Need to have the iOS audio player scoped outside of the method so the playback can persist\n        private AVAudioPlayer audioPlayer;\n\n        public SoundPlayerImplementation()\n        {\n\n        }\n\n        public void PlaySoundFile(string fileName)\n        {\n            \/\/ Retrieve the path to the file where it is housed within the bundle resources\n            string sFilePath = NSBundle.MainBundle.PathForResource(Path.GetFileNameWithoutExtension(fileName), Path.GetExtension(fileName));\n            \/\/ Create a URL for the file inside the bundle resources - we do this for simplicity in play back\n            var url = NSUrl.FromString(((NSString)sFilePath).CreateStringByAddingPercentEscapes(NSStringEncoding.UTF8));\n            \n            \/\/ Since the audio player is scoped outside of the method, we need to handle what to do if it is already playing\n            \/\/ a sound when they trigger another to play\n            if (audioPlayer != null)\n            {\n                audioPlayer.Stop();\n                audioPlayer.Dispose();\n            }\n            \n            \/\/ Instantiate the AudioPlayer from the URL we created above\n            audioPlayer = AVAudioPlayer.FromUrl(url);\n\n            \/\/ Sanity check to make sure it was able to properly create the player\n            if (audioPlayer != null)\n            {\n                \/\/ Set it for a single playback\n                audioPlayer.NumberOfLoops = 0;\n\n                \/\/ Add a delegate to perform some clean-up after it finishes playing\n                audioPlayer.FinishedPlaying += delegate\n                {\n                    audioPlayer = null;\n                };\n\n                \/\/ Start playing the sound file\n                audioPlayer.Play();\n            }\n        }\n    }\n}<\/pre>\n<p>Finally, we&#8217;ll create the implementation class for UWP\u00a0 &#8211; right click on the Universal Windows project then choose\u00a0<strong>Add<\/strong>\u00a0-&gt;\u00a0<strong>Class\u00a0<\/strong>and give it the same name of\u00a0<strong>SoundPlayerImplementation.<\/strong><\/p>\n<p><a style=\"margin-left: 16px;\" href=\"http:\/\/blog.cubicleninja.com\/wp-content\/uploads\/2017\/10\/Soundboard_8.png\" target=\"_blank\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-medium wp-image-251\" src=\"http:\/\/blog.cubicleninja.com\/wp-content\/uploads\/2017\/10\/Soundboard_8-300x267.png\" alt=\"Soundboard_8\" width=\"300\" height=\"267\" \/><\/a><\/p>\n<p>All the same words that I used about Android and iOS up above apply here, and then the code explains the rest.<\/p>\n<pre class=\"lang:c# decode:true\">using System;\nusing XamarinSoundboard.UWP;\nusing Windows.UI.Xaml.Controls;\nusing Xamarin.Forms;\n\n[assembly: Dependency(typeof(SoundPlayerImplementation))]\nnamespace XamarinSoundboard.UWP\n{\n    public class SoundPlayerImplementation : ISoundPlayer\n    {\n        \/\/ Need to have the UWP media element scoped outside of the method so the playback can persist\n        MediaElement el;\n\n        public SoundPlayerImplementation()\n        {\n\n        }\n\n        public async void PlaySoundFile(string fileName)\n        {\n            \/\/ Retrieve a storage folder for the Assets folder of the application\n            Windows.Storage.StorageFolder folder = await Windows.ApplicationModel.Package.Current.InstalledLocation.GetFolderAsync(\"Assets\");\n            \/\/ Retrieve the storage file from the assets folder\n            Windows.Storage.StorageFile file = await folder.GetFileAsync(fileName);\n            \/\/ Open the file stream from the storage file\n            Windows.Storage.Streams.IRandomAccessStream stream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read);\n\n            \/\/ Since the audio player is scoped outside of the method, we need to handle what to do if it is already playing\n            \/\/ a sound when they trigger another to play\n            if (el != null)\n            {\n                el.Stop();\n                el = null;\n            }\n\n            \/\/ Instantiate a new MediaElement\n            el = new MediaElement();\n            \/\/ Instruct the media element to load the storage file stream\n            el.SetSource(stream, file.ContentType);\n            \/\/ Make sure the media element isn't muted\n            el.IsMuted = false;\n            \/\/ Set the media element volume\n            el.Volume = 1;\n            \/\/ Add a delegate to perform some clean-up after it finishes playing\n            el.MediaEnded += (sender, e) =&gt;\n            {\n                ((MediaElement)sender).Stop();\n                sender = null;\n            };\n\n            \/\/ Ensure we start at the beginning of the sound file\n            el.Position = new TimeSpan(0, 0, 0);\n            \/\/ Start playing the sound file\n            el.Play();            \n        }\n    }\n}\n<\/pre>\n<p>Before we go any further with the soundboard, let&#8217;s allow ourselves a little satisfaction by testing out the SoundPlayer implementations and hearing some sweet, melodious sounds on each of the platforms.\u00a0 This will require a little throw-away work, but what we do here will inform the code we write for the real soundboard logic later.\u00a0 The first thing to do will be to add an MP3 file to each of the platform-specific projects, we&#8217;ll do this by clicking on\u00a0<strong>Add\u00a0<\/strong>-&gt;\u00a0<strong>Existing Item<\/strong> and then selecting an MP3 file from your computer in each of the following locations:<\/p>\n<ul>\n<li>For Android, right click on the Assets folder<\/li>\n<li>For iOS, right click on the Resource folder<\/li>\n<li>For UWP, right click on the Assets folder<\/li>\n<\/ul>\n<p>Adding the MP3 file at these locations will <em>automagically<\/em> set the Build Action to the correct type so that the files go out correctly when we deploy to each platform.\u00a0 To trigger the DependencyService and play the MP3 file, we&#8217;ll need to go back up to the PCL and edit the\u00a0<strong>MainPage.xaml\u00a0<\/strong>file to include a button.\u00a0 For this test, just remove the generic label that was included and stick in a button that has a\u00a0<strong>Clicked<\/strong> event<\/p>\n<pre class=\"lang:yaml decode:true\">&lt;?xml version=\"1.0\" encoding=\"utf-8\" ?&gt;\n&lt;ContentPage xmlns=\"http:\/\/xamarin.com\/schemas\/2014\/forms\"\n             xmlns:x=\"http:\/\/schemas.microsoft.com\/winfx\/2009\/xaml\"\n             xmlns:local=\"clr-namespace:XamarinSoundboard\"\n             x:Class=\"XamarinSoundboard.MainPage\"&gt;\n\n    &lt;Button x:Name=\"SoundButton\" Text=\"Sound\" Clicked=\"SoundButton_Clicked\"&gt;&lt;\/Button&gt;\n\n&lt;\/ContentPage&gt;<\/pre>\n<p>Now, head to the code-behind file for the XAML (hotkey F7, or right-click in the page and select View Code, or open MainPage.xaml.cs from Solution Explorer) and add in the SoundButton_Clicked event:<\/p>\n<pre class=\"lang:c# decode:true \">        private void SoundButton_Clicked(object sender, EventArgs e)\n        {\n            \/\/ Make use of the dependency service to call the platform specific implementation of our SoundPlayer\n            \/\/ Make sure to put in the actual file name of your MP3 file -- It is case sensitive!\n            DependencyService.Get&lt;ISoundPlayer&gt;().PlaySoundFile(\"Awesome.mp3\");\n        }<\/pre>\n<p>And that&#8217;s it, now you can run it on each of your platforms and you&#8217;ll be presented with a full-screen button with the text\u00a0<strong>Sound<\/strong> on it.\u00a0 Clicking that button on any of the platforms will cause it to play the MP3 file that you added to your project.<\/p>\n<p>That&#8217;s it for part 1, you should now have the base project created and be able to run a program that will display a gigantic button that plays an embedded MP3 file upon being clicked on each of the target platforms!<\/p>\n<p>-CubicleNinja<\/p>\n","protected":false},"excerpt":{"rendered":"<p>One of the things I enjoy doing when I&#8217;m going to jump into a new development area is to create a soundboard-type application. I like to do this for a few reasons: I like playing with soundboards It lets me work with media playback on the platform(s) It lets me work with file IO on &hellip; <a href=\"https:\/\/blog.cubicleninja.com\/?p=240\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Creating a Cross-Platform Soundboard with Xamarin Forms &#8211; Part 1&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2,3,6,9,10,12],"tags":[14,30,40,42],"class_list":["post-240","post","type-post","status-publish","format-standard","hentry","category-net","category-android","category-ios","category-mobile","category-uwp","category-xamarin","tag-android","tag-ios","tag-uwp","tag-xamarin"],"_links":{"self":[{"href":"https:\/\/blog.cubicleninja.com\/index.php?rest_route=\/wp\/v2\/posts\/240","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.cubicleninja.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.cubicleninja.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.cubicleninja.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.cubicleninja.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=240"}],"version-history":[{"count":0,"href":"https:\/\/blog.cubicleninja.com\/index.php?rest_route=\/wp\/v2\/posts\/240\/revisions"}],"wp:attachment":[{"href":"https:\/\/blog.cubicleninja.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=240"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.cubicleninja.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=240"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.cubicleninja.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=240"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}