OSDN Git Service

iOS: Audio & Sound feature complete. Tested on superjumper & Tropical Stormfront...
authorChristoph Aschwanden <contact@noblemaster.com>
Wed, 26 Sep 2012 15:02:50 +0000 (00:02 +0900)
committerChristoph Aschwanden <contact@noblemaster.com>
Wed, 26 Sep 2012 15:02:50 +0000 (00:02 +0900)
backends/gdx-backend-iosmonotouch/src/com/badlogic/gdx/backends/ios/IOSAudio.java
backends/gdx-backend-iosmonotouch/src/com/badlogic/gdx/backends/ios/IOSMusic.java
backends/gdx-backend-iosmonotouch/src/com/badlogic/gdx/backends/ios/IOSSound.java

index 6b661e3..4665f24 100644 (file)
@@ -40,115 +40,48 @@ public class IOSAudio implements Audio {
        public AudioRecorder newAudioRecorder(int samplingRate, boolean isMono) {
                return null;
        }
-
-       @Override
-       public Sound newSound(FileHandle fileHandle) {\r
-               // verify file format\r
+\r
+       /**\r
+        * Let's verify the file format. \r
+        * \r
+        * @param fileHandle  The file to load.\r
+        * @throws GdxRuntimeException  If we are using a OGG file (not supported under iOS).\r
+        */\r
+       private void verify(FileHandle fileHandle) {\r
                if (fileHandle.extension().equalsIgnoreCase("ogg")) {  \r
                        // Ogg is not supported on iOS (return a sound object that does nothing)\r
-                       Gdx.app.error("IOSAudio", "Audio format .ogg is not supported on iOS. Cannot load: " + fileHandle.path());\r
-                       return new Sound() {\r
-                               @Override\r
-                               public long play () {\r
-                                       return 0;\r
-                               }\r
-                               @Override\r
-                               public long play (float volume) {\r
-                                       return 0;\r
-                               }\r
-                               @Override\r
-                               public long play (float volume, float pitch, float pan) {\r
-                                       return 0;\r
-                               }\r
-                               @Override\r
-                               public long loop () {\r
-                                       return 0;\r
-                               }\r
-                               @Override\r
-                               public long loop (float volume) {\r
-                                       return 0;\r
-                               }\r
-                               @Override\r
-                               public long loop (float volume, float pitch, float pan) {\r
-                                       return 0;\r
-                               }\r
-                               @Override\r
-                               public void stop () {\r
-                               }\r
-                               @Override\r
-                               public void dispose () {\r
-                               }\r
-                               @Override\r
-                               public void stop (long soundId) {\r
-                               }\r
-                               @Override\r
-                               public void setLooping (long soundId, boolean looping) {\r
-                               }\r
-                               @Override\r
-                               public void setPitch (long soundId, float pitch) {\r
-                               }\r
-                               @Override\r
-                               public void setVolume (long soundId, float volume) {\r
-                               }\r
-                               @Override\r
-                               public void setPan (long soundId, float pan, float volume) {\r
-                               }                               \r
-                       };\r
+                       throw new GdxRuntimeException("Audio format .ogg is not supported on iOS. Cannot load: " + fileHandle.path());\r
                }\r
+       }\r
+       \r
+       /**\r
+        * Returns a new sound object. We are playing from memory, a.k.a. suited for short \r
+        * sound FXs.\r
+        * \r
+        * @return  The new sound object.\r
+        * @throws GdxRuntimeException  If we are unable to load the file for some reason.\r
+        */
+       @Override
+       public Sound newSound(FileHandle fileHandle) {\r
+               // verify file format (make sure we don't have an OGG file)\r
+               verify(fileHandle);\r
                                
                // create audio player - from byte array\r
-               NSError[] error = new NSError[1];\r
                NSData data = NSData.FromArray(fileHandle.readBytes());\r
-               AVAudioPlayer player = AVAudioPlayer.FromData(data, error);\r
-               if (error[0] == null) {\r
-                       // no error: return the music object\r
-                       return new IOSSound(player);\r
-               }\r
-               else {\r
-                       // throw an exception\r
-                       throw new GdxRuntimeException("Error opening file at " + fileHandle.path() + ": " + error[0].ToString());\r
-               }\r
+          return new IOSSound(data);\r
        }
 
+       /**\r
+        * Returns a new music object. We are playing directly from file, a.k.a. suited for\r
+        * background music.\r
+        * \r
+        * @return  The new music object.\r
+        * @throws GdxRuntimeException  If we are unable to load the file for some reason.\r
+        */\r
        @Override
        public Music newMusic(FileHandle fileHandle) {\r
-               // verify file format\r
-               if (fileHandle.extension().equalsIgnoreCase("ogg")) {  \r
-                       // Ogg is not supported on iOS (return a music object that does nothing)\r
-                       Gdx.app.error("IOSAudio", "Audio format .ogg is not supported on iOS. Cannot load: " + fileHandle.path());\r
-                       return new Music() {\r
-                               @Override\r
-                               public void play () {\r
-                               }\r
-                               @Override\r
-                               public void pause () {\r
-                               }\r
-                               @Override\r
-                               public void stop () {\r
-                               }\r
-                               @Override\r
-                               public boolean isPlaying () {\r
-                                       return false;\r
-                               }\r
-                               @Override\r
-                               public void setLooping (boolean isLooping) {\r
-                               }\r
-                               @Override\r
-                               public boolean isLooping () {\r
-                                       return false;\r
-                               }\r
-                               @Override\r
-                               public void setVolume (float volume) {\r
-                               }\r
-                               @Override\r
-                               public float getPosition () {\r
-                                       return 0;\r
-                               }\r
-                               @Override\r
-                               public void dispose () {\r
-                               }                               \r
-                       };\r
-               }\r
+               // verify file format (make sure we don't have an OGG file)\r
+               verify(fileHandle);\r
                \r
                // create audio player - from file path\r
                NSError[] error = new NSError[1];\r
@@ -159,7 +92,7 @@ public class IOSAudio implements Audio {
                }\r
                else {\r
                        // throw an exception\r
-                       throw new GdxRuntimeException("Error opening file at " + fileHandle.path() + ": " + error[0].ToString());\r
+                       throw new GdxRuntimeException("Error opening music file at " + fileHandle.path() + ": " + error[0].ToString());\r
                }\r
        }
 }
\ No newline at end of file
index 91d3048..55cf62d 100644 (file)
@@ -18,7 +18,15 @@ package com.badlogic.gdx.backends.ios;
 import cli.MonoTouch.AVFoundation.AVAudioPlayer;\r
 \r
 import com.badlogic.gdx.audio.Music;
-
+\r
+/**\r
+ * A music player, suitable for background music. Supports MP3 and WAV\r
+ * files which are played via hardware on iOS.\r
+ * <p>\r
+ * Limitations: does not play OGG.\r
+ * \r
+ * @author noblemaster\r
+ */
 public class IOSMusic implements Music {
 \r
        private AVAudioPlayer player;\r
@@ -49,14 +57,13 @@ public class IOSMusic implements Music {
        }
 
        @Override
-       public void setLooping(boolean isLooping) {
-               // TODO Auto-generated method stub              
+       public void setLooping(boolean isLooping) {             \r
+               player.set_NumberOfLoops(isLooping ? -1 : 0);  // Note: -1 for looping!
        }
 
        @Override
-       public boolean isLooping() {
-               // TODO Auto-generated method stub
-               return false;
+       public boolean isLooping() {    
+               return player.get_NumberOfLoops() == -1;  // Note: -1 for looping!
        }
 
        @Override
@@ -66,8 +73,7 @@ public class IOSMusic implements Music {
 
        @Override
        public float getPosition() {
-               // TODO Auto-generated method stub
-               return 0;
+               return (float)(player.get_CurrentTime() * 1000);  // Note: player returns seconds => x1000 to convert to millis!
        }
 
        @Override
index 8848a50..28fb1f2 100644 (file)
  ******************************************************************************/
 package com.badlogic.gdx.backends.ios;
 
+import java.util.ArrayList;\r
+import java.util.List;\r
+\r
 import cli.MonoTouch.AVFoundation.AVAudioPlayer;\r
+import cli.MonoTouch.Foundation.NSData;\r
+import cli.MonoTouch.Foundation.NSError;\r
 \r
+import com.badlogic.gdx.Gdx;\r
 import com.badlogic.gdx.audio.Sound;
+import com.badlogic.gdx.utils.GdxRuntimeException;\r
 
+/**\r
+ * A music player, suitable for short sound effects. Effects are played from\r
+ * memory. Supports MP3 and WAV files which are played via hardware on iOS.\r
+ * <p>\r
+ * Limitations: does not play OGG. Does not support pitched audio (i.e. variable \r
+ * rate playback).\r
+ * \r
+ * @author noblemaster\r
+ */\r
 public class IOSSound implements Sound {
 \r
-       private AVAudioPlayer player;\r
+       /** The maximum number of players to instantiate to support simultaneous playback. Bigger? */\r
+       private static final int MAX_PLAYERS = 10;\r
+       \r
+       /** Our sound players. More than one to support simultaneous playback. */\r
+       private AVAudioPlayer[] players;\r
 \r
        \r
-       public IOSSound(AVAudioPlayer player) {\r
-               this.player = player;\r
+       /**\r
+        * Creates a new sound object. We are creating several AVAudioPlayer objects to\r
+        * support synchronous playback. A single AVAudioPlayer can only play one sound \r
+        * at a time (non-simultaneous).\r
+        * \r
+        * @param data  The audio data stored in memory.\r
+        * @throws GdxRuntimeException  If we are unable to create the player for some reason.\r
+        */\r
+       public IOSSound(NSData data) {\r
+               // create players for playback (more than 1 to support simultaneous playback)\r
+               NSError[] error = new NSError[1];\r
+               players = new AVAudioPlayer[MAX_PLAYERS];\r
+               for (int i = 0; i < players.length; i++) {\r
+             players[i] = AVAudioPlayer.FromData(data, error);\r
+             \r
+             // check for errors\r
+                       if (error[0] != null) {\r
+                               // error creating the player (maybe file missing? unsupported format?)\r
+                               throw new GdxRuntimeException("Error creating audio player (index: " + i + "): " + error[0].ToString());\r
+                       }\r
+               }\r
        }\r
-
+\r
+       /**\r
+        * Let's find a player that isn't currently playing.\r
+        * \r
+        * @return  The index of the player or -1 if none is available.\r
+        */\r
+       private int findAvailablePlayer() {\r
+               for (int i = 0; i < players.length; i++) {\r
+                       if (!players[i].get_Playing()) {\r
+                               return i;\r
+                       }\r
+               }\r
+               \r
+               // all are busy playing... :/\r
+               return -1;\r
+       }\r
+       
        @Override
        public long play() {\r
                return play(1.0f);
@@ -35,74 +90,101 @@ public class IOSSound implements Sound {
 
        @Override
        public long play(float volume) {\r
-               return play(volume, 1.0f, 0.5f);
+               return play(volume, 1.0f, 0.0f);
        }
 
        @Override
        public long play(float volume, float pitch, float pan) {\r
-               player.set_Volume(volume);\r
-               player.set_Pan(pan);\r
-               player.Play();\r
-               
-               // TODO Auto-generated method stub
-               return 0;
+               return play(false, volume, pitch, pan);\r
+       }\r
+       \r
+       /**\r
+        * Our actual player. Will be call both for looping and non-looping \r
+        * playback.\r
+    *\r
+        * @return  The sound index or -1 if none was available for playback.\r
+        */\r
+   private int play(boolean looping, float volume, float pitch, float pan) {\r
+               int soundId = findAvailablePlayer();\r
+               if (soundId >= 0) {\r
+                       AVAudioPlayer player = players[soundId];\r
+                       player.set_NumberOfLoops(looping ? -1 : 0);  // Note: -1 for looping!\r
+                       player.set_Volume(volume);\r
+                       player.set_Pan(pan);\r
+                       player.Play();\r
+               }\r
+               \r
+               // and return the index/id of the player\r
+               return soundId;
        }
 
        @Override
        public long loop() {
-               // TODO Auto-generated method stub
-               return 0;
+               return loop(1.0f);
        }
 
        @Override
        public long loop(float volume) {
-               // TODO Auto-generated method stub
-               return 0;
+               return loop(volume, 1.0f, 0.0f);
        }
 
        @Override
        public long loop(float volume, float pitch, float pan) {
-               // TODO Auto-generated method stub
-               return 0;
+               return play(true, volume, pitch, pan);
        }
 
        @Override
        public void stop() {
-               player.Stop();
+               // stop all players\r
+               for (int i = 0; i < players.length; i++) {\r
+                       players[i].Stop();\r
+               }\r
        }
 
        @Override
-       public void dispose() {
-               player.Dispose();
+       public void dispose() {\r
+               // dispose all players\r
+               for (int i = 0; i < players.length; i++) {
+                       players[i].Dispose();\r
+                       players[i] = null;\r
+               }
        }
 
        @Override
-       public void stop(long soundId) {
-               // TODO Auto-generated method stub
-               
+       public void stop(long soundId) {\r
+               if (soundId >= 0) {
+                       players[(int)soundId].Stop();\r
+               }
        }
 
        @Override
        public void setLooping(long soundId, boolean looping) {
-               // TODO Auto-generated method stub
-               
+               if (soundId >= 0) {\r
+                       players[(int)soundId].set_NumberOfLoops(looping ? -1 : 0);  // Note: -1 for looping!\r
+               }\r
        }
 
        @Override
-       public void setPitch(long soundId, float pitch) {
-               // TODO Auto-generated method stub
-               
+       public void setPitch(long soundId, float pitch) {\r
+               if (soundId >= 0) {\r
+                       // NOTE: It's odd, AVAudioPlayer supports variable rate playing, but is not\r
+                       // available via MonoTouch!? Let's put out a warning...\r
+                       Gdx.app.debug("IOSSound", "Warning: setting a pitch not supported on iOS.");\r
+               }\r
        }
 
        @Override
        public void setVolume(long soundId, float volume) {
-               // TODO Auto-generated method stub
-               
+               if (soundId >= 0) {\r
+                       players[(int)soundId].set_Volume(volume);\r
+               }\r
        }
 
        @Override
        public void setPan(long soundId, float pan, float volume) {
-               // TODO Auto-generated method stub
-               
+               if (soundId >= 0) {\r
+                       players[(int)soundId].set_Pan(pan);\r
+                       players[(int)soundId].set_Volume(volume);\r
+               }
        }
 }
\ No newline at end of file