Android13 AudioManager playSoundEffect流程分析

xml文件地址:frameworks/base/core/res/res/xml/audio_assets.xml

音效文件地址:frameworks/base/data/sounds/effects/ogg

AudioManager的playSoundEffect用于播放Beep音,代码如下:

//frameworks/base/media/java/android/media/AudioManager.java
public class AudioManager {
    public void playSoundEffect(@SystemSoundEffect int effectType) {
        playSoundEffect(effectType, UserHandle.USER_CURRENT);
    }
}

调用playSoundEffect的重载方法:

//frameworks/base/media/java/android/media/AudioManager.java
public class AudioManager {
    public void  playSoundEffect(@SystemSoundEffect int effectType, int userId) {
        if (effectType < 0 || effectType >= NUM_SOUND_EFFECTS) {
            return;
        }


        final IAudioService service = getService();
        try {
            service.playSoundEffect(effectType, userId);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
}

调用IAudioService的playSoundEffect方法,IAudioService是一个接口,由AudioService实现:

//frameworks/base/services/core/java/com/android/server/audio/AudioService.java
public class AudioService extends IAudioService.Stub
        implements AccessibilityManager.TouchExplorationStateChangeListener,
            AccessibilityManager.AccessibilityServicesStateChangeListener,
            AudioSystemAdapter.OnRoutingUpdatedListener,
            AudioSystemAdapter.OnVolRangeInitRequestListener {
    public void playSoundEffect(int effectType, int userId) {
        if (querySoundEffectsEnabled(userId)) {
            playSoundEffectVolume(effectType, -1.0f);
        }
    }
}

调用playSoundEffectVolume方法:

//frameworks/base/services/core/java/com/android/server/audio/AudioService.java
public class AudioService extends IAudioService.Stub
        implements AccessibilityManager.TouchExplorationStateChangeListener,
            AccessibilityManager.AccessibilityServicesStateChangeListener,
            AudioSystemAdapter.OnRoutingUpdatedListener,
            AudioSystemAdapter.OnVolRangeInitRequestListener {
    private AudioHandler mAudioHandler;
    public void playSoundEffectVolume(int effectType, float volume) {
        // do not try to play the sound effect if the system stream is muted
        if (isStreamMute(STREAM_SYSTEM)) {
            return;
        }


        if (effectType >= AudioManager.NUM_SOUND_EFFECTS || effectType < 0) {
            Log.w(TAG, "AudioService effectType value " + effectType + " out of range");
            return;
        }


        sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SENDMSG_QUEUE,
                effectType, (int) (volume * 1000), null, 0);
    }
}

发送MSG_PLAY_SOUND_EFFECT消息,发送的消息在AudioHandler的handleMessage中处理:

public class AudioService extends IAudioService.Stub
        implements AccessibilityManager.TouchExplorationStateChangeListener,
            AccessibilityManager.AccessibilityServicesStateChangeListener,
            AudioSystemAdapter.OnRoutingUpdatedListener,
            AudioSystemAdapter.OnVolRangeInitRequestListener {
    private SoundEffectsHelper mSfxHelper;
    /** Handles internal volume messages in separate volume thread. */
    private class AudioHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_PLAY_SOUND_EFFECT:
                    mSfxHelper.playSoundEffect(msg.arg1, msg.arg2);
                    break;
        }
    }
}

调用SoundEffectsHelper的playSoundEffect方法

//frameworks/base/service/java/com/android/server/audio/SoundEffectsHelper.java
class SoundEffectsHelper {
    /*package*/ void playSoundEffect(int effect, int volume) {
        sendMsg(MSG_PLAY_EFFECT, effect, volume, null, 0);
    }
}

发送MSG_PLAY_EFFECT消息:

//frameworks/base/service/java/com/android/server/audio/SoundEffectsHelper.java
class SoundEffectsHelper {
    private class SfxHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_PLAY_EFFECT:
                    final int effect = msg.arg1, volume = msg.arg2;
                    onLoadSoundEffects(new OnEffectsLoadCompleteHandler() {
                        @Override
                        public void run(boolean success) {
                            if (success) {
                                onPlaySoundEffect(effect, volume);
                            }
                        }
                    });
                    break;
            }
        }
    }
}

SoundEffectsHelper onLoadSoundEffects

调用onLoadSoundEffects方法:

//frameworks/base/service/java/com/android/server/audio/SoundEffectsHelper.java
class SoundEffectsHelper {
    private SoundPool mSoundPool;
    private void onLoadSoundEffects(OnEffectsLoadCompleteHandler onComplete) {
        if (mSoundPoolLoader != null) {
            // Loading is ongoing.
            mSoundPoolLoader.addHandler(onComplete);
            return;
        }
        if (mSoundPool != null) {
            if (onComplete != null) {
                onComplete.run(true /*success*/);
            }
            return;
        }


        logEvent("effects loading started");
        mSoundPool = new SoundPool.Builder()
                .setMaxStreams(NUM_SOUNDPOOL_CHANNELS)
                .setAudioAttributes(new AudioAttributes.Builder()
                        .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
                        .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
                        .build())
                .build(); //创建SoundPool对象
        loadSoundAssets(); //加载声音资料


        mSoundPoolLoader = new SoundPoolLoader(); //创建SoundPoolLoader对象
        mSoundPoolLoader.addHandler(new OnEffectsLoadCompleteHandler() { //增加OnEffectsLoadComplete Handler
            @Override
            public void run(boolean success) {
                mSoundPoolLoader = null;
                if (!success) {
                    Log.w(TAG, "onLoadSoundEffects(), Error while loading samples");
                    onUnloadSoundEffects();
                }
            }
        });
        mSoundPoolLoader.addHandler(onComplete); //增加onComplete Handler


        int resourcesToLoad = 0;
        for (Resource res : mResources) {
            String filePath = getResourceFilePath(res);
            int sampleId = mSoundPool.load(filePath, 0);
            if (sampleId > 0) {
                res.mSampleId = sampleId;
                res.mLoaded = false;
                resourcesToLoad++;
            } else {
                logEvent("effect " + filePath + " rejected by SoundPool");
                Log.w(TAG, "SoundPool could not load file: " + filePath);
            }
        }


        if (resourcesToLoad > 0) {
            sendMsg(MSG_LOAD_EFFECTS_TIMEOUT, 0, 0, null, SOUND_EFFECTS_LOAD_TIMEOUT_MS);
        } else {
            logEvent("effects loading completed, no effects to load");
            mSoundPoolLoader.onComplete(true /*success*/);
        }
    }
}

上面方法主要处理如下:

1、创建SoundPool对象

2、调用loadSoundAssets方法,建立音效文件的索引

3、创建SoundPoolLoader对象

4、调用SoundPoolLoader的addHandler方法

5、调用SoundPool的load方法

6、调用SoundPoolLoader的onComplete方法

下面分别进行分析:

new SoundPool

待更新

loadSoundAssets

调用loadSoundAssets方法,加载声音资料:

//frameworks/base/service/java/com/android/server/audio/SoundEffectsHelper.java
class SoundEffectsHelper {
    private final int[] mEffects = new int[AudioManager.NUM_SOUND_EFFECTS]; // indexes in mResources
    private void loadSoundAssets() {
        XmlResourceParser parser = null;


        // only load assets once.
        if (!mResources.isEmpty()) {
            return;
        }


        loadSoundAssetDefaults(); //加载默认音效文件


        try {
            parser = mContext.getResources().getXml(com.android.internal.R.xml.audio_assets); //获取Xml,xml文件地址:frameworks/base/core/res/res/xml/audio_assets.xml


            XmlUtils.beginDocument(parser, TAG_AUDIO_ASSETS);
            String version = parser.getAttributeValue(null, ATTR_VERSION);
            Map<Integer, Integer> parserCounter = new HashMap<>();
            if (ASSET_FILE_VERSION.equals(version)) {
                while (true) {
                    XmlUtils.nextElement(parser);
                    String element = parser.getName();
                    if (element == null) {
                        break;
                    }
                    if (element.equals(TAG_GROUP)) {
                        String name = parser.getAttributeValue(null, ATTR_GROUP_NAME);
                        if (!GROUP_TOUCH_SOUNDS.equals(name)) {
                            Log.w(TAG, "Unsupported group name: " + name);
                        }
                    } else if (element.equals(TAG_ASSET)) {
                        String id = parser.getAttributeValue(null, ATTR_ASSET_ID);
                        String file = parser.getAttributeValue(null, ATTR_ASSET_FILE);
                        int fx;


                        try {
                            Field field = AudioManager.class.getField(id);
                            fx = field.getInt(null);
                        } catch (Exception e) {
                            Log.w(TAG, "Invalid sound ID: " + id);
                            continue;
                        }
                        int currentParserCount = parserCounter.getOrDefault(fx, 0) + 1;
                        parserCounter.put(fx, currentParserCount);
                        if (currentParserCount > 1) {
                            Log.w(TAG, "Duplicate definition for sound ID: " + id);
                        }
                        mEffects[fx] = findOrAddResourceByFileName(file);  //建立音效文件的索引
                    } else {
                        break;
                    }
                }


                boolean navigationRepeatFxParsed = allNavigationRepeatSoundsParsed(parserCounter);
                boolean homeSoundParsed = parserCounter.getOrDefault(AudioManager.FX_HOME, 0) > 0;
                if (navigationRepeatFxParsed || homeSoundParsed) {
                    AudioManager audioManager = mContext.getSystemService(AudioManager.class); //获取AudioManager对象
                    if (audioManager != null && navigationRepeatFxParsed) {
                        audioManager.setNavigationRepeatSoundEffectsEnabled(true); //调用audioManager的setNavigationRepeatSoundEffectsEnabled方法
                    }
                    if (audioManager != null && homeSoundParsed) {
                        audioManager.setHomeSoundEffectEnabled(true); //调用audioManager的setHomeSoundEffectEnabled方法
                    }
                }
            }
        } catch (Resources.NotFoundException e) {
            Log.w(TAG, "audio assets file not found", e);
        } catch (XmlPullParserException e) {
            Log.w(TAG, "XML parser exception reading sound assets", e);
        } catch (IOException e) {
            Log.w(TAG, "I/O exception reading sound assets", e);
        } finally {
            if (parser != null) {
                parser.close();
            }
        }
    }
}

上面方法主要处理如下:

1、调用loadSoundAssetDefaults()方法,加载默认音效文件

2、查询xml,建立音效文件的索引mEffects

下面分别进行分析:

loadSoundAssetDefaults

调用loadSoundAssetDefaults()方法,加载默认音效文件:

//frameworks/base/service/java/com/android/server/audio/SoundEffectsHelper.java
class SoundEffectsHelper {
    private final int[] mEffects = new int[AudioManager.NUM_SOUND_EFFECTS]; // indexes in mResources
    private void loadSoundAssetDefaults() {
        int defaultResourceIdx = mResources.size();
        mResources.add(new Resource("Effect_Tick.ogg"));
        Arrays.fill(mEffects, defaultResourceIdx);
    }
}

new SoundPoolLoader

创建SoundPoolLoader对象,SoundPoolLoader为SoundEffectsHelper的内部类,构造方法如下:

//frameworks/base/service/java/com/android/server/audio/SoundEffectsHelper.java
class SoundEffectsHelper {
    private class SoundPoolLoader implements
            android.media.SoundPool.OnLoadCompleteListener {


        private List<OnEffectsLoadCompleteHandler> mLoadCompleteHandlers =
                new ArrayList<OnEffectsLoadCompleteHandler>();


        SoundPoolLoader() {
            // SoundPool use the current Looper when creating its message handler.
            // Since SoundPoolLoader is created on the SfxWorker thread, SoundPool's
            // message handler ends up running on it (it's OK to have multiple
            // handlers on the same Looper). Thus, onLoadComplete gets executed
            // on the worker thread.
            mSoundPool.setOnLoadCompleteListener(this); //设置OnLoadCompleteListener
        }
    }
}

SoundPoolLoader addHandler

调用SoundPoolLoader的addHandler方法,增加OnEffectsLoadCompleteHandler和onComplete。

//frameworks/base/service/java/com/android/server/audio/SoundEffectsHelper.java
class SoundEffectsHelper {
    private class SoundPoolLoader implements
            android.media.SoundPool.OnLoadCompleteListener {
        void addHandler(OnEffectsLoadCompleteHandler handler) {
            if (handler != null) {
                mLoadCompleteHandlers.add(handler);
            }
        }
    }
}

SoundPool load

调用SoundPool的load方法:

待更新

SoundPoolLoader onComplete

调用SoundPoolLoader的onComplete方法:

//frameworks/base/service/java/com/android/server/audio/SoundEffectsHelper.java
class SoundEffectsHelper {
    private SoundPool mSoundPool;
    private class SoundPoolLoader implements
            android.media.SoundPool.OnLoadCompleteListener {
        void onComplete(boolean success) {
            if (mSoundPool != null) {
                mSoundPool.setOnLoadCompleteListener(null);
            }
            for (OnEffectsLoadCompleteHandler handler : mLoadCompleteHandlers) {
                handler.run(success);
            }
            logEvent("effects loading " + (success ? "completed" : "failed"));
        }
    }
}

上面方法主要处理如下:

1、调用SoundPool的setOnLoadCompleteListener方法

2、调用OnEffectsLoadCompleteHandler的run方法,也就是在onLoadSoundEffects方法中调用SoundPoolLoader的addHandler注册的方法

下面分别进行分析:

SoundPool setOnLoadCompleteListener

调用SoundPool的setOnLoadCompleteListener方法:

//frameworks/base/media/java/android/media/SoundPool.java
public class SoundPool extends PlayerBase {
    private final AtomicReference<EventHandler> mEventHandler = new AtomicReference<>(null);
    public void setOnLoadCompleteListener(OnLoadCompleteListener listener) {
        if (listener == null) {
            mEventHandler.set(null);
            return;
        }


        Looper looper;
        if ((looper = Looper.myLooper()) != null) {
            mEventHandler.set(new EventHandler(looper, listener));
        } else if ((looper = Looper.getMainLooper()) != null) {
            mEventHandler.set(new EventHandler(looper, listener));
        } else {
            mEventHandler.set(null);
        }
    }
}

OnEffectsLoadCompleteHandler run

调用OnEffectsLoadCompleteHandler的run方法,也就是在onLoadSoundEffects方法中调用SoundPoolLoader的addHandler注册的方法,第一个注册的方法:

        mSoundPoolLoader.addHandler(new OnEffectsLoadCompleteHandler() {
            @Override
            public void run(boolean success) {
                mSoundPoolLoader = null;
                if (!success) {
                    Log.w(TAG, "onLoadSoundEffects(), Error while loading samples");
                    onUnloadSoundEffects();
                }
            }
        });
SoundEffectsHelper onUnloadSoundEffects

调用onUnloadSoundEffects方法:

//frameworks/base/service/java/com/android/server/audio/SoundEffectsHelper.java
class SoundEffectsHelper {
    void onUnloadSoundEffects() {
        if (mSoundPool == null) {
            return;
        }
        if (mSoundPoolLoader != null) {
            mSoundPoolLoader.addHandler(new OnEffectsLoadCompleteHandler() {
                @Override
                public void run(boolean success) {
                    onUnloadSoundEffects();
                }
            });
        }


        logEvent("effects unloading started");
        for (Resource res : mResources) {
            if (res.mSampleId != EFFECT_NOT_IN_SOUND_POOL) {
                mSoundPool.unload(res.mSampleId); //调用SoundPool的unload方法
                res.unload();
            }
        }
        mSoundPool.release(); //调用SoundPool的release方法
        mSoundPool = null;
        logEvent("effects unloading completed");
    }
}

第二个注册的方法:

                    onLoadSoundEffects(new OnEffectsLoadCompleteHandler() {
                        @Override
                        public void run(boolean success) {
                            if (success) {
                                onPlaySoundEffect(effect, volume);
                            }
                        }
                    });
SoundEffectsHelper onPlaySoundEffect

调用SoundEffectsHelper的onPlaySoundEffect方法:

//frameworks/base/service/java/com/android/server/audio/SoundEffectsHelper.java
class SoundEffectsHelper {
    private SoundPool mSoundPool;
    void onPlaySoundEffect(int effect, int volume) {
        float volFloat;
        // use default if volume is not specified by caller
        if (volume < 0) {
            volFloat = (float) Math.pow(10, (float) mSfxAttenuationDb / 20);
        } else {
            volFloat = volume / 1000.0f;
        }


        Resource res = mResources.get(mEffects[effect]); //通过索获取音效资源
        if (mSoundPool != null && res.mSampleId != EFFECT_NOT_IN_SOUND_POOL && res.mLoaded) { //如果资源已经加载,则调用SoundPool的play进行播放
            mSoundPool.play(res.mSampleId, volFloat, volFloat, 0, 0, 1.0f); 
        } else { //否则使用MediaPlayer 进行播放
            MediaPlayer mediaPlayer = new MediaPlayer();
            try {
                String filePath = getResourceFilePath(res);
                mediaPlayer.setDataSource(filePath);
                mediaPlayer.setAudioStreamType(AudioSystem.STREAM_SYSTEM);
                mediaPlayer.prepare();
                mediaPlayer.setVolume(volFloat);
                mediaPlayer.setOnCompletionListener(new OnCompletionListener() {
                    public void onCompletion(MediaPlayer mp) {
                        cleanupPlayer(mp);
                    }
                });
                mediaPlayer.setOnErrorListener(new OnErrorListener() {
                    public boolean onError(MediaPlayer mp, int what, int extra) {
                        cleanupPlayer(mp);
                        return true;
                    }
                });
                mediaPlayer.start();
            } catch (IOException ex) {
                Log.w(TAG, "MediaPlayer IOException: " + ex);
            } catch (IllegalArgumentException ex) {
                Log.w(TAG, "MediaPlayer IllegalArgumentException: " + ex);
            } catch (IllegalStateException ex) {
                Log.w(TAG, "MediaPlayer IllegalStateException: " + ex);
            }
        }
    }
}

上面方法主要处理如下:

1、调用SoundPool的play方法。

2、创建MediaPlay。

3、调用MediaPlayer的prepare方法。

4、调用MediaPlayer的start方法。

下面分别进行分析:

SoundPool play

待更新

new MediaPlay

待更新

MediaPlayer prepare

待更新

MediaPlayer start

待更新

  • 5
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值