Advertisement
VandroiyX

Cocos2dxSound.java APK Expansion

Sep 11th, 2013
1,340
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 11.64 KB | None | 0 0
  1. /****************************************************************************
  2. Copyright (c) 2010-2011 cocos2d-x.org
  3.  
  4. http://www.cocos2d-x.org
  5.  
  6. Permission is hereby granted, free of charge, to any person obtaining a copy
  7. of this software and associated documentation files (the "Software"), to deal
  8. in the Software without restriction, including without limitation the rights
  9. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. copies of the Software, and to permit persons to whom the Software is
  11. furnished to do so, subject to the following conditions:
  12.  
  13. The above copyright notice and this permission notice shall be included in
  14. all copies or substantial portions of the Software.
  15.  
  16. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22. THE SOFTWARE.
  23.  ****************************************************************************/
  24. package org.cocos2dx.lib;
  25.  
  26. import java.io.IOException;
  27. import java.util.ArrayList;
  28. import java.util.HashMap;
  29. import java.util.Iterator;
  30. import java.util.Map;
  31. import java.util.Map.Entry;
  32. import java.util.concurrent.Semaphore;
  33.  
  34. import com.android.vending.expansion.zipfile.APKExpansionSupport;
  35. import com.android.vending.expansion.zipfile.ZipResourceFile;
  36.  
  37. import android.content.Context;
  38. import android.content.res.AssetFileDescriptor;
  39. import android.media.AudioManager;
  40. import android.media.SoundPool;
  41. import android.util.Log;
  42.  
  43. public class Cocos2dxSound {
  44.     // ===========================================================
  45.     // Constants
  46.     // ===========================================================
  47.  
  48.     private static final String TAG = "Cocos2dxSound";
  49.  
  50.     // ===========================================================
  51.     // Fields
  52.     // ===========================================================
  53.  
  54.     private final Context mContext;
  55.     private SoundPool mSoundPool;
  56.     private float mLeftVolume;
  57.     private float mRightVolume;
  58.  
  59.     // sound path and stream ids map
  60.     // a file may be played many times at the same time
  61.     // so there is an array map to a file path
  62.     private final HashMap<String, ArrayList<Integer>> mPathStreamIDsMap = new HashMap<String, ArrayList<Integer>>();
  63.  
  64.     private final HashMap<String, Integer> mPathSoundIDMap = new HashMap<String, Integer>();
  65.    
  66.     private final ArrayList<SoundInfoForLoadedCompleted> mEffecToPlayWhenLoadedArray = new ArrayList<SoundInfoForLoadedCompleted>();
  67.    
  68.     private int mStreamIdSyn;
  69.     private Semaphore mSemaphore;
  70.  
  71.     private static final int MAX_SIMULTANEOUS_STREAMS_DEFAULT = 5;
  72.     private static final float SOUND_RATE = 1.0f;
  73.     private static final int SOUND_PRIORITY = 1;
  74.     private static final int SOUND_QUALITY = 5;
  75.  
  76.     private final static int INVALID_SOUND_ID = -1;
  77.     private final static int INVALID_STREAM_ID = -1;
  78.  
  79.     private static ZipResourceFile zip_resource_file = null;
  80.    
  81.     // ===========================================================
  82.     // Constructors
  83.     // ===========================================================
  84.  
  85.     public Cocos2dxSound(final Context pContext) {
  86.         this.mContext = pContext;
  87.         try {
  88.             //Change the second argument to match with your version code
  89.             zip_resource_file = APKExpansionSupport.getAPKExpansionZipFile(pContext, 2, 0);
  90.         } catch ( IOException e ) {
  91.             Log.e( "Cocos2dxMusic" ,  "Initializing ZipResourceFile: ", e );
  92.         }
  93.         this.initData();
  94.     }
  95.  
  96.     private void initData() {
  97.         this.mSoundPool = new SoundPool(Cocos2dxSound.MAX_SIMULTANEOUS_STREAMS_DEFAULT, AudioManager.STREAM_MUSIC, Cocos2dxSound.SOUND_QUALITY);
  98.         this.mSoundPool.setOnLoadCompleteListener(new OnLoadCompletedListener());
  99.        
  100.         this.mLeftVolume = 0.5f;
  101.         this.mRightVolume = 0.5f;
  102.        
  103.         this.mSemaphore = new Semaphore(0, true);
  104.     }
  105.  
  106.     // ===========================================================
  107.     // Getter & Setter
  108.     // ===========================================================
  109.  
  110.     // ===========================================================
  111.     // Methods for/from SuperClass/Interfaces
  112.     // ===========================================================
  113.  
  114.     // ===========================================================
  115.     // Methods
  116.     // ===========================================================
  117.  
  118.     public int preloadEffect(final String pPath) {
  119.         Integer soundID = this.mPathSoundIDMap.get(pPath);
  120.  
  121.         if (soundID == null) {
  122.             soundID = this.createSoundIDFromAsset(pPath);
  123.             // save value just in case if file is really loaded
  124.             if (soundID != Cocos2dxSound.INVALID_SOUND_ID) {
  125.                 this.mPathSoundIDMap.put(pPath, soundID);
  126.             }
  127.         }
  128.  
  129.         return soundID;
  130.     }
  131.  
  132.     public void unloadEffect(final String pPath) {
  133.         // stop effects
  134.         final ArrayList<Integer> streamIDs = this.mPathStreamIDsMap.get(pPath);
  135.         if (streamIDs != null) {
  136.             for (final Integer pStreamID : streamIDs) {
  137.                 this.mSoundPool.stop(pStreamID);
  138.             }
  139.         }
  140.         this.mPathStreamIDsMap.remove(pPath);
  141.  
  142.         // unload effect
  143.         final Integer soundID = this.mPathSoundIDMap.get(pPath);
  144.         if(soundID != null){
  145.             this.mSoundPool.unload(soundID);
  146.             this.mPathSoundIDMap.remove(pPath);
  147.         }
  148.     }
  149.  
  150.     public int playEffect(final String pPath, final boolean pLoop) {
  151.         Integer soundID = this.mPathSoundIDMap.get(pPath);
  152.         int streamID = Cocos2dxSound.INVALID_STREAM_ID;
  153.  
  154.         if (soundID != null) {
  155.             // play sound
  156.             streamID = this.doPlayEffect(pPath, soundID.intValue(), pLoop);
  157.         } else {
  158.             // the effect is not prepared
  159.             soundID = this.preloadEffect(pPath);
  160.             if (soundID == Cocos2dxSound.INVALID_SOUND_ID) {
  161.                 // can not preload effect
  162.                 return Cocos2dxSound.INVALID_SOUND_ID;
  163.             }
  164.            
  165.             // only allow one playEffect at a time, or the semaphore will not work correctly
  166.             synchronized(this.mSoundPool) {
  167.                 // add this effect into mEffecToPlayWhenLoadedArray, and it will be played when loaded completely
  168.                 mEffecToPlayWhenLoadedArray.add(new SoundInfoForLoadedCompleted(pPath, soundID.intValue(), pLoop));
  169.                
  170.                 try {
  171.                     // wait OnloadedCompleteListener to set streamID
  172.                     this.mSemaphore.acquire();
  173.                    
  174.                     streamID = this.mStreamIdSyn;
  175.                 } catch(Exception e) {
  176.                     return Cocos2dxSound.INVALID_SOUND_ID;
  177.                 }
  178.             }
  179.         }
  180.  
  181.         return streamID;
  182.     }
  183.  
  184.     public void stopEffect(final int pStreamID) {
  185.         this.mSoundPool.stop(pStreamID);
  186.  
  187.         // remove record
  188.         for (final String pPath : this.mPathStreamIDsMap.keySet()) {
  189.             if (this.mPathStreamIDsMap.get(pPath).contains(pStreamID)) {
  190.                 this.mPathStreamIDsMap.get(pPath).remove(this.mPathStreamIDsMap.get(pPath).indexOf(pStreamID));
  191.                 break;
  192.             }
  193.         }
  194.     }
  195.  
  196.     public void pauseEffect(final int pStreamID) {
  197.         this.mSoundPool.pause(pStreamID);
  198.     }
  199.  
  200.     public void resumeEffect(final int pStreamID) {
  201.         this.mSoundPool.resume(pStreamID);
  202.     }
  203.    
  204.     public boolean isSoundPlaying(final int pStreamID){
  205.         return false;
  206.     }
  207.  
  208.     public void pauseAllEffects() {
  209.         this.mSoundPool.autoPause();
  210.     }
  211.  
  212.     public void resumeAllEffects() {
  213.         // can not only invoke SoundPool.autoResume() here, because
  214.         // it only resumes all effects paused by pauseAllEffects()
  215.         if (!this.mPathStreamIDsMap.isEmpty()) {
  216.             final Iterator<Entry<String, ArrayList<Integer>>> iter = this.mPathStreamIDsMap.entrySet().iterator();
  217.             while (iter.hasNext()) {
  218.                 final Entry<String, ArrayList<Integer>> entry = iter.next();
  219.                 for (final int pStreamID : entry.getValue()) {
  220.                     this.mSoundPool.resume(pStreamID);
  221.                 }
  222.             }
  223.         }
  224.     }
  225.  
  226.     @SuppressWarnings("unchecked")
  227.     public void stopAllEffects() {
  228.         // stop effects
  229.         if (!this.mPathStreamIDsMap.isEmpty()) {
  230.             final Iterator<?> iter = this.mPathStreamIDsMap.entrySet().iterator();
  231.             while (iter.hasNext()) {
  232.                 final Map.Entry<String, ArrayList<Integer>> entry = (Map.Entry<String, ArrayList<Integer>>) iter.next();
  233.                 for (final int pStreamID : entry.getValue()) {
  234.                     this.mSoundPool.stop(pStreamID);
  235.                 }
  236.             }
  237.         }
  238.  
  239.         // remove records
  240.         this.mPathStreamIDsMap.clear();
  241.     }
  242.  
  243.     public float getEffectsVolume() {
  244.         return (this.mLeftVolume + this.mRightVolume) / 2;
  245.     }
  246.  
  247.     public void setEffectsVolume(float pVolume) {
  248.         // pVolume should be in [0, 1.0]
  249.         if (pVolume < 0) {
  250.             pVolume = 0;
  251.         }
  252.         if (pVolume > 1) {
  253.             pVolume = 1;
  254.         }
  255.        
  256.         this.mLeftVolume = this.mRightVolume = pVolume;
  257.  
  258.         // change the volume of playing sounds
  259.         if (!this.mPathStreamIDsMap.isEmpty()) {
  260.             final Iterator<Entry<String, ArrayList<Integer>>> iter = this.mPathStreamIDsMap.entrySet().iterator();
  261.             while (iter.hasNext()) {
  262.                 final Entry<String, ArrayList<Integer>> entry = iter.next();
  263.                 for (final int pStreamID : entry.getValue()) {
  264.                     this.mSoundPool.setVolume(pStreamID, this.mLeftVolume, this.mRightVolume);
  265.                 }
  266.             }
  267.         }
  268.     }
  269.  
  270.     public void end() {
  271.         this.mSoundPool.release();
  272.         this.mPathStreamIDsMap.clear();
  273.         this.mPathSoundIDMap.clear();
  274.         this.mEffecToPlayWhenLoadedArray.clear();
  275.  
  276.         this.mLeftVolume = 0.5f;
  277.         this.mRightVolume = 0.5f;
  278.        
  279.         this.initData();
  280.     }
  281.  
  282.     public int createSoundIDFromAsset(final String pPath) {
  283.         int soundID = Cocos2dxSound.INVALID_SOUND_ID;
  284.  
  285.         try {
  286.             if (pPath.startsWith("/")) {
  287.                 soundID = this.mSoundPool.load(pPath, 0);
  288.             } else {
  289.                 AssetFileDescriptor assetFileDescritor = zip_resource_file.getAssetFileDescriptor( "assets/" + pPath );
  290.                 soundID = mSoundPool.load( assetFileDescritor, 0 );
  291.             }
  292.         } catch (final Exception e) {
  293.             soundID = Cocos2dxSound.INVALID_SOUND_ID;
  294.             Log.e(Cocos2dxSound.TAG, "error: " + e.getMessage(), e);
  295.         }
  296.  
  297.         // mSoundPool.load returns 0 if something goes wrong, for example a file does not exist
  298.         if (soundID == 0) {
  299.             soundID = Cocos2dxSound.INVALID_SOUND_ID;
  300.         }
  301.  
  302.         return soundID;
  303.     }
  304.    
  305.     private int doPlayEffect(final String pPath, final int soundId, final boolean pLoop) {
  306.         // play sound
  307.         int streamID = this.mSoundPool.play(soundId, this.mLeftVolume, this.mRightVolume, Cocos2dxSound.SOUND_PRIORITY, pLoop ? -1 : 0, Cocos2dxSound.SOUND_RATE);
  308.  
  309.         // record stream id
  310.         ArrayList<Integer> streamIDs = this.mPathStreamIDsMap.get(pPath);
  311.         if (streamIDs == null) {
  312.             streamIDs = new ArrayList<Integer>();
  313.             this.mPathStreamIDsMap.put(pPath, streamIDs);
  314.         }
  315.         streamIDs.add(streamID);
  316.        
  317.         return streamID;
  318.     }
  319.  
  320.     // ===========================================================
  321.     // Inner and Anonymous Classes
  322.     // ===========================================================
  323.    
  324.     public class SoundInfoForLoadedCompleted {
  325.         public int soundID;
  326.         public boolean isLoop;
  327.         public String path;
  328.        
  329.         public SoundInfoForLoadedCompleted(String path, int soundId, boolean isLoop) {
  330.             this.path = path;
  331.             this.soundID = soundId;
  332.             this.isLoop = isLoop;
  333.         }
  334.     }
  335.    
  336.     public class OnLoadCompletedListener implements SoundPool.OnLoadCompleteListener {
  337.  
  338.         @Override
  339.         public void onLoadComplete(SoundPool soundPool, int sampleId, int status) {
  340.             if (status == 0)
  341.             {
  342.                 // only play effect that are in mEffecToPlayWhenLoadedArray
  343.                 for ( SoundInfoForLoadedCompleted info : mEffecToPlayWhenLoadedArray) {
  344.                     if (sampleId == info.soundID) {
  345.                         // set the stream id which will be returned by playEffect()
  346.                         mStreamIdSyn = doPlayEffect(info.path, info.soundID, info.isLoop);
  347.                        
  348.                         // remove it from array, because we will break here
  349.                         // so it is safe to do
  350.                         mEffecToPlayWhenLoadedArray.remove(info);
  351.  
  352.                         break;
  353.                     }
  354.                 }
  355.             } else {
  356.                 mStreamIdSyn = Cocos2dxSound.INVALID_SOUND_ID;
  357.             }
  358.            
  359.             mSemaphore.release();
  360.         }
  361.     }
  362. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement