{"id":2252,"date":"2014-08-13T20:47:56","date_gmt":"2014-08-13T08:47:56","guid":{"rendered":"https:\/\/www.deltics.co.nz\/blog\/?p=2252"},"modified":"2014-08-13T20:58:09","modified_gmt":"2014-08-13T08:58:09","slug":"making-a-noise-about-on-a-thread","status":"publish","type":"post","link":"https:\/\/www.deltics.co.nz\/blog\/posts\/2252\/","title":{"rendered":"Making a Noise <DEL>About<\/DEL> on a Thread"},"content":{"rendered":"<span class=\"rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">[Estimated Reading Time: <\/span> <span class=\"rt-time\">5<\/span> <span class=\"rt-label rt-postfix\">minutes]<\/span><\/span><p>I&#8217;m working on an Android app at the moment, and for a bit of fun I decided to add a startup sound to brighten the day of every user that launches it.  Which gives me another opportunity to present some of the advanced language features in Oxygene that make threading such a breeze.<\/p>\n<p><!--more--><\/p>\n<p>First of all, playing sounds on Android is simplicity itself, especially when the audio is provided as resources (files) within the application package.<\/p>\n<p>The Android SDK prescribes the layout of certain folders within an application package.  Most of these folders reside under a <strong>res<\/strong> folder (for &#8220;resource&#8221;).  In the case of audio resources the resource folder used is &#8220;raw&#8221;.<\/p>\n<p>Despite this name, the sound resources (files) placed in this folder must be not raw audio data, but one of the <a href=\"http:\/\/developer.android.com\/guide\/appendix\/media-formats.html\">Android supported file formats<\/a> (of which there are many).  In my case I was using a <strong>Vorbis .ogg<\/strong> format file.  This is quickly and easily added in the Oxygene solution in Visual Studio:<\/p>\n<p><a href=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Audio-Resource.png?ssl=1\"><img decoding=\"async\" loading=\"lazy\" src=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Audio-Resource.png?resize=262%2C252&#038;ssl=1\" alt=\"Audio Resource\" width=\"262\" height=\"252\" class=\"aligncenter size-full wp-image-2253\" data-recalc-dims=\"1\" \/><\/a><\/p>\n<p>Playing this audio is then simply a matter of initialising a <strong>MediaPlayer<\/strong> instance, preparing it with the required audio resource file, and playing it.  As I said, I want this to be played when my application is launched, so I add this code to my <strong>MainActivity.onCreate()<\/strong> method:<\/p>\n<pre class=\"brush: oxygene; title: ; notranslate\" title=\"\">\r\n  var media := MediaPlayer.create(ApplicationContext, R.raw.ping);\r\n  media.start;\r\n<\/pre>\n<p>The audio resource is referenced via a bit of Android framework magic which exposes the contents of the res folders &#8211; and their contents &#8211; to the code as referencable identifiers.  Oxygene picks these up and presents them in code completion lists, as you would expect:<\/p>\n<p><a href=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2014-08-13-at-19.54.22-.png?ssl=1\"><img decoding=\"async\" loading=\"lazy\" src=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2014-08-13-at-19.54.22-.png?resize=172%2C188&#038;ssl=1\" alt=\"Raw Suggestion\" width=\"172\" height=\"188\" class=\"aligncenter size-full wp-image-2254\" data-recalc-dims=\"1\" \/><\/a><\/p>\n<p>And within the <strong>raw<\/strong> folder, the <strong>ping<\/strong> file (actually, in the context of the code, it&#8217;s an <strong>integer<\/strong> identifier that corresponds to &#8211; and identifies &#8211; the file resource itself):<\/p>\n<p><a href=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2014-08-13-at-19.54.37-.png?ssl=1\"><img decoding=\"async\" loading=\"lazy\" src=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2014-08-13-at-19.54.37-.png?resize=137%2C61&#038;ssl=1\" alt=\"Ping Suggestion\" width=\"137\" height=\"61\" class=\"aligncenter size-full wp-image-2255\" data-recalc-dims=\"1\" \/><\/a><\/p>\n<p>The first line of my code declares an inline variable (type: <strong>MediaPlayer<\/strong>, being inferred).  To this is assigned the result of the <strong>create()<\/strong> factory method on the <strong>MediaPlayer<\/strong> class.<\/p>\n<pre class=\"brush: oxygene; title: ; notranslate\" title=\"\">\r\n  var media := MediaPlayer.create(ApplicationContext, R.raw.ping);\r\n<\/pre>\n<p>The factory method overload employed accepts a context and a resource file ID.  This factory method both instantiates the MediaPlayer and prepares it with the specified file.  All that remains is to actually initiate the playback of the file, with the call to <strong>start()<\/strong>.<\/p>\n<pre class=\"brush: oxygene; title: ; notranslate\" title=\"\">\r\n  media.start();\r\n<\/pre>\n<p>Job done.  But this implementation is dangerously simplistic.<\/p>\n<p>First of all, it is strongly emphasised in the <a href=\"http:\/\/developer.android.com\/reference\/android\/media\/MediaPlayer.html\">MediaPlayer documentation<\/a> that any MediaPlayer instance should be <strong>release()<\/strong>&#8216;d once no longer required as it may be holding on to scarce &#8211; and expensive &#8211; resources that other applications may need.<\/p>\n<p>Also, this implementation performs the loading of the audio resource in my UI thread.  For a small audio file such as this, any blocking of the thread that might arise is negligible, but I should take care of this in preparation for the day that I upgrade the audio resource to a 7.1 channel lossless HD format&#8230; <ahem>. \ud83d\ude42<\/p>\n<p>The playback of the audio is performed in a thread by the MediaPlayer, so my UI startup isn&#8217;t blocked by that.  But this in itself will cause a problem as we will see in a moment.<\/p>\n<p>But first things first.  Let&#8217;s move all of this in a background thread.  For this, I will employ an anonymous class, creating a sub-class of <strong>Thread<\/strong> and overriding the <strong>run()<\/strong> method:<\/p>\n<pre class=\"brush: oxygene; title: ; notranslate\" title=\"\">\r\n    var audioThread := new class Thread(\r\n                          run := method\r\n                          begin\r\n                            var media := MediaPlayer.create(ApplicationContext, R.raw.ping);\r\n                            media.start;\r\n                          end);\r\n    audioThread.start;\r\n<\/pre>\n<p>So, my media player code is moved to the body of the <strong>run()<\/strong> method of my anonymous thread class.  Similar inline variable declaration and inferred typing provides me a reference to the new thread, which I <strong>start()<\/strong>.<\/p>\n<p>Great, now my audio initialisation and playback is all occurring in a background thread and my UI thread can continue to initialise and run my activity while my startup sound plays in the background.  Superb!<\/p>\n<p>But still there is the issue of <strong>release()<\/strong>&#8216;ing the media player.<\/p>\n<p>We cannot simply call <strong>release()<\/strong> immediately after calling <strong>start()<\/strong> on the media player, like this:<\/p>\n<pre class=\"brush: oxygene; title: ; notranslate\" title=\"\">\r\n  media.start;\r\n  media.release;  \/\/ NOT a good idea\r\n<\/pre>\n<p>Remember, the media player already uses a background thread for playback, so <strong>start()<\/strong> returns almost instantly and if we call <strong>release()<\/strong> at that point the media player will simply stop playing the audio.  What we need is some mechanism that will notify us when playback is complete.<\/p>\n<p>And what do you know ?  The MediaPlayer provides exactly such a mechanism, in the form of an <strong>OnCompletion<\/strong> callback.  This takes the form of a reference to an <strong>OnCompletionListener<\/strong> interface, assigned to the media player.  At the risk of stating the obvious, the media player will call the <strong>onCompletion()<\/strong> method when playback is complete.<\/p>\n<p>Fortunately, as well as anonymous classes, Oxygene allows us to declare inline, anonymous interface implementations, which we can exploit to install a listener for the completion callback.  The syntax for an anonymous interface implementation is similar to that for an anonymous class (after all, an interface has to be implemented by a class).  When implementing an interface in this way we declare an anonymous class but rather than the name of a super-class, we instead identify the interface we are implementing.<\/p>\n<p>In this case that listener interface type is declared as a member of the MediaPlayer class itself, so our anonymous inline class implementing this interface takes the initial form:<\/p>\n<pre class=\"brush: oxygene; title: ; notranslate\" title=\"\">\r\n    new class MediaPlayer.OnCompletionListener( ... );\r\n<\/pre>\n<p>The methods we then assign provide the implementation of the interface methods (as opposed to overriding inherited methods in the case of an anonymous sub-class).  In this case there is just the one method to implement, <strong>onCompletion()<\/strong>, which is passed a reference to the media player that has completed playback.  We will use this to <strong>release()<\/strong> that player:<\/p>\n<pre class=\"brush: oxygene; title: ; notranslate\" title=\"\">\r\n   var audioThread := new class Thread(\r\n                        run := method\r\n                               begin\r\n                                 var media := MediaPlayer.create(ApplicationContext, R.raw.ping);\r\n\r\n                                 media.OnCompletionListener := new class MediaPlayer.OnCompletionListener(\r\n                                                                 onCompletion := method(aPlayer: MediaPlayer)\r\n                                                                                 begin\r\n                                                                                   aPlayer.release();\r\n                                                                                 end);\r\n                                 media.start;\r\n                               end);\r\n   audioThread.start;\r\n<\/pre>\n<p>And that&#8217;s it.<\/p>\n<p>As you can see, indentation can rapidly get out of hand when using this technique, but fortunately you can also see how this entire implementation could be wrapped up inside a neat convenience static method on some utility class for subsequent re-use:<\/p>\n<pre class=\"brush: oxygene; title: ; notranslate\" title=\"\">\r\n\r\n   class method AsyncAudio.play(aFileID: Integer);\r\n   begin\r\n     var audioThread := new class Thread(\r\n                          run := method\r\n                                 begin\r\n                                   var media := MediaPlayer.create(ApplicationContext, aFileID);\r\n\r\n                                   media.OnCompletionListener := new class MediaPlayer.OnCompletionListener(\r\n                                                                   onCompletion := method(aPlayer: MediaPlayer)\r\n                                                                                   begin\r\n                                                                                     aPlayer.release();\r\n                                                                                   end);\r\n                                   media.start;\r\n                                 end);\r\n     audioThread.start;\r\n   end;\r\n\r\n\r\n   \/\/ As an example of use, you could then do:\r\n\r\n   AsyncAudio.play(R.raw.ping);\r\n  \r\n<\/pre>\n<p>And that&#8217;s it.  Not bad for an hour&#8217;s tinkering.  \ud83d\ude42<\/p>\n<p>Such a helper method could also hide the implementation specifics of Android and provide an equivalent mechanism on other platforms, thus providing a consistent cross-platform API for playing audio in the background on any of the platforms supported by the Elements compiler(s), Android, iOS or .NET<\/p>\n<pre class=\"brush: oxygene; title: ; notranslate\" title=\"\">\r\n   class method AsyncAudio.play(aFileID: Integer);\r\n   begin\r\n   {$if COOPER} \/\/ Android specifics\r\n     ..\r\n   {$elseif NOUGAT} \/\/ iOS (actually, Cocoa generally so further conditions may \r\n                    \/\/  be needed to discriminate between iOS (Cocoa Touch) and OS X (Cocoa, um, not Touch)\r\n     ..\r\n   {$elseif ECHOES} \/\/ .NET \/ WinPhone \/ WinRT etc\r\n     ..\r\n   {$endif}\r\n   end;\r\n<\/pre>\n<p>Worth noting is that a class such as this, implemented in Oxygene (ObjectPascal), is directly usable in Hydrogene (RemObjects C#) and vice-versa (you can mix and match Oxygene and Hydrogene within a project, if you feel so inclined).<\/p>\n<p>And if a Java developer starts casting covetous eyes at your convenience method, you could build them a jar and share it with them as well (though they only get to play with the Java version, obviously) !<\/p>\n<p>But, such things are for another time.  \ud83d\ude42<\/p>\n","protected":false},"excerpt":{"rendered":"<p><span class=\"rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">[Estimated Reading Time: <\/span> <span class=\"rt-time\">5<\/span> <span class=\"rt-label rt-postfix\">minutes]<\/span><\/span> I&#8217;m working on an Android app at the moment, and for a bit of fun I decided to add a startup sound to brighten the day of every user that launches it. Which gives me another opportunity to present some of the advanced language features in Oxygene that make threading such a breeze.<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"jetpack_publicize_message":"","jetpack_is_tweetstorm":false,"jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":[]},"categories":[212,205,4,180],"tags":[153,272,271,181,16],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p1TKYv-Ak","jetpack_sharing_enabled":true,"jetpack-related-posts":[{"id":1658,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/1658\/","url_meta":{"origin":2252,"position":0},"title":"Crash Bang Wallop, What a Picture!","date":"19 Sep 2013","format":false,"excerpt":"The fourth and final part in the not-as-short-as-I-thought-it-would be series on building a camera app for Android using Oxygene. In this penultimate instalment we will add the capability to actually take a picture. But that won't take very long, so then we will spend a bit of time tidying up\u2026","rel":"","context":"In &quot;Android&quot;","img":{"alt_text":"Very Ugly Duckling","src":"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screenshot_2013-09-19-19-38-56-1024x640.jpg?resize=350%2C200&ssl=1","width":350,"height":200},"classes":[]},{"id":1774,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/1774\/","url_meta":{"origin":2252,"position":1},"title":"Developing and Debugging an AppWidget &#8211; Part 1","date":"29 Sep 2013","format":false,"excerpt":"With my external HDD trials and tribulations behind me, I have finally been able to complete a project I started last weekend - implementing an Android AppWidget. Along the way I have learned some more about both Android and Oxygene and what can be involved with working with the two\u2026","rel":"","context":"In &quot;Android&quot;","img":{"alt_text":"Adding the xml folder","src":"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2013-09-29-at-15.08.09-.png?resize=350%2C200&ssl=1","width":350,"height":200},"classes":[]},{"id":1605,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/1605\/","url_meta":{"origin":2252,"position":2},"title":"Anatomy of a Camera App","date":"15 Sep 2013","format":false,"excerpt":"Part 1 in an as yet unknown number of articles using a (very) simple camera application to demonstrate building first class Android applications using \"Pascal for Java\" - i.e. Oxygene Cooper. In this first instalment we will look at the basics - the components we are going to need and\u2026","rel":"","context":"In &quot;Android&quot;","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2013-09-15-at-10.12.32-.png?resize=350%2C200&ssl=1","width":350,"height":200},"classes":[]},{"id":1503,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/1503\/","url_meta":{"origin":2252,"position":3},"title":"Sharing Code Across Platforms in Oxygene","date":"22 Aug 2013","format":false,"excerpt":"There seems to be a perception among some people that Delphi is in the unique position of allowing developers to share and re-use code across the various platforms that it's compiler can now (and will soon) target. But this is not the case. Oxygene has had this capability right from\u2026","rel":"","context":"In &quot;Cooper&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1791,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/1791\/","url_meta":{"origin":2252,"position":4},"title":"Developing and Debugging an AppWidget &#8211; Part 2","date":"30 Sep 2013","format":false,"excerpt":"In the first instalment of this series, I implemented the basic framework of a new appwidget and established a means by which I could debug the widget code. Now it's time to add some code worth debugging. You may recall that the aim of my widget is to display the\u2026","rel":"","context":"In &quot;Android&quot;","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screenshot_2013-09-30-20-03-27.jpg?resize=350%2C200&ssl=1","width":350,"height":200},"classes":[]},{"id":1845,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/1845\/","url_meta":{"origin":2252,"position":5},"title":"Another Great Oxygene Release","date":"03 Oct 2013","format":false,"excerpt":"RemObjects have officially released the September update to Oxygene with the usual round of bug fixes and some dramatic improvements in the tool chain. Cocoa - Principally iOS 7 My focus with Oxygene most recently has been on the Android side. My previous experience with using the Cocoa (iOS) support\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"_links":{"self":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/2252"}],"collection":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/comments?post=2252"}],"version-history":[{"count":5,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/2252\/revisions"}],"predecessor-version":[{"id":2260,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/2252\/revisions\/2260"}],"wp:attachment":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/media?parent=2252"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/categories?post=2252"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/tags?post=2252"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}