package org.onebillion.onecourse.utils; import java.io.InputStream; import java.lang.reflect.Constructor; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import android.content.res.AssetFileDescriptor; import org.onebillion.onecourse.mainui.MainActivity; import org.onebillion.onecourse.mainui.OBSectionController; /** * OBAudioManager * Controls the functionality of playing audio files and waiting for those files to finish. * Can also preload audio into channels and retrive the duration of the audio file */ public class OBAudioManager { public static final String AM_MAIN_CHANNEL = "0", AM_BACKGROUND_CHANNEL = "1", AM_SFX_CHANNEL = "2"; final static int MAX_AUDIOPATH_CACHE_COUNT = 40; public static OBAudioManager audioManager; public Map<String, OBGeneralAudioPlayer> players; Map<String, AssetFileDescriptor> pathCacheDict = new HashMap<>(); List<String> pathCacheList = new ArrayList<>(); public OBAudioManager () { players = new HashMap<String, OBGeneralAudioPlayer>(); audioManager = this; } public static Map<String, Object> loadAudioXML (InputStream xmlStream) throws Exception { Map<String, Object> audioDict = new HashMap<String, Object>(); ; if (xmlStream != null) { OBXMLManager xmlManager = new OBXMLManager(); Object xmlobj = xmlManager.parseFile(xmlStream); if (xmlobj != null) { Map<String, Float> sfxvols = new HashMap<>(); List<String> evts = new ArrayList<>(); audioDict.put("__sfxvols", sfxvols); List<OBXMLNode> children; if (xmlobj instanceof OBXMLNode) children = ((OBXMLNode) xmlobj).childrenOfType("event"); else { children = ((List<OBXMLNode>) xmlobj).get(0).childrenOfType(("event")); } for (OBXMLNode xmlevent : children) { String ekey = xmlevent.attributeStringValue("id"); evts.add(ekey); Map<String, List<Object>> phrasegroups = new HashMap<String, List<Object>>(); List<String> groupList = new ArrayList<>(); for (OBXMLNode xmlphrasegroup : xmlevent.childrenOfType("phrasegroup")) { String pgkey = xmlphrasegroup.attributeStringValue("id"); groupList.add(pgkey); List<Object> phrases = new ArrayList<Object>(); for (OBXMLNode xmlphrase : xmlphrasegroup.childrenOfType("phrase")) { String phrase = xmlphrase.contents.trim(); try { int n = Integer.parseInt(phrase); phrases.add(n); } catch (Exception e) { phrases.add(phrase); } } phrasegroups.put(pgkey, phrases); String volk = xmlphrasegroup.attributeStringValue("vol"); if (volk != null && phrases.size() > 0) sfxvols.put((String)phrases.get(phrases.size()-1), Float.parseFloat(volk)); } phrasegroups.put("__keys", (List<Object>) (Object) groupList); audioDict.put(ekey, phrasegroups); } audioDict.put("__events",evts); } } return audioDict; } public void stopPlayingOnChannel (String ch) { OBGeneralAudioPlayer player = players.get(ch); player.stopPlaying(true); } public void stopPlaying () { stopPlayingOnChannel(AM_MAIN_CHANNEL); } public void stopPlayingSFX () { stopPlayingOnChannel(AM_SFX_CHANNEL); } public void stopPlayingBackground () { stopPlayingOnChannel(AM_BACKGROUND_CHANNEL); } public void stopAllAudio () { for (String k : players.keySet()) stopPlayingOnChannel(k); } public String getAudioPath(String fileName) { for (String suffix : OBConfigManager.sharedManager.getAudioExtensions()) { for (String path : OBConfigManager.sharedManager.getAudioSearchPaths()) { String fullPath = path + "/" + fileName + "." + suffix; AssetFileDescriptor fd = OBUtils.getAssetFileDescriptorForPath(fullPath); if (fd != null) { return fullPath; } } } return null; } public AssetFileDescriptor getAudioPathFD (String fileName) { AssetFileDescriptor fd = pathCacheDict.get(fileName); if (fd == null) { for (String suffix : OBConfigManager.sharedManager.getAudioExtensions()) { for (String path : OBConfigManager.sharedManager.getAudioSearchPaths()) { String fullPath = path + "/" + fileName + "." + suffix; fd = OBUtils.getAssetFileDescriptorForPath(fullPath); if (fd != null) { break; } else { fd = null; } } } if (fd == null) { return null; } } pathCacheList.remove(fileName); pathCacheList.add(fileName); pathCacheDict.put(fileName, fd); if (pathCacheList.size() >= MAX_AUDIOPATH_CACHE_COUNT) { String firstobj = pathCacheList.get(0); pathCacheList.remove(0); pathCacheDict.remove(firstobj); } return fd; } public void playOnChannel(String ch) { playerForChannel(ch).play(); } public void startPlaying (String fileName, String channel) { OBGeneralAudioPlayer player = playerForChannel(channel); if (fileName == null) player.stopPlaying(); else { AssetFileDescriptor fd = getAudioPathFD(fileName); if (fd != null) { player.startPlaying(fd); } else { MainActivity.log("Error caught in OBAudioManager.startPlaying [" + fileName + "] returned a null file descriptor"); } } } public void startPlaying (String fileName, String channel, double atTime) { startPlaying(fileName, channel, atTime, 1.0f); } public void startPlaying (String fileName, String channel, double atTime, float atVolume) { OBGeneralAudioPlayer player = playerForChannel(channel); if (fileName == null) player.stopPlaying(); else { AssetFileDescriptor fd = getAudioPathFD(fileName); player.startPlayingAtTimeVolume(fd, (int) (atTime * 1000), atVolume); } } public void startPlayingFromTo(String fileName, String channel, double fromSecs, double toSecs) { OBAudioBufferPlayer player = (OBAudioBufferPlayer)playerForChannel(channel,OBAudioBufferPlayer.class); if (fileName == null) player.stopPlaying(); else { AssetFileDescriptor fd = getAudioPathFD(fileName); player.startPlaying(fd, fromSecs,toSecs); } } public void startPlayingFromTo(String fileName,double fromSecs, double toSecs) { startPlayingFromTo(fileName,AM_MAIN_CHANNEL,fromSecs,toSecs); } public void startPlaying (String fileName, double atTime) { startPlaying(fileName, AM_MAIN_CHANNEL, atTime); } public void startPlaying (String fileName) { startPlaying(fileName, AM_MAIN_CHANNEL); } public void startPlayingSFX (String fileName, float vol) { startPlaying(fileName, AM_SFX_CHANNEL, 0, vol); } public void waitPrepared () { waitPrepared(AM_MAIN_CHANNEL); } public void waitUntilPlaying () { waitUntilPlaying(AM_MAIN_CHANNEL); } public void waitPrepared (String ch) { OBGeneralAudioPlayer player = players.get(ch); if (player != null) player.waitPrepared(); } public void waitUntilPlaying (String ch) { OBGeneralAudioPlayer player = players.get(ch); if (player != null) player.waitUntilPlaying(); } public void waitAudioChannel (String ch) { OBGeneralAudioPlayer player = players.get(ch); if (player != null) player.waitAudio(); } public void waitAudio () { waitAudioChannel(AM_MAIN_CHANNEL); } public void waitSFX () { waitAudioChannel(AM_SFX_CHANNEL); } public Boolean isPlayingChannel (String ch) { OBGeneralAudioPlayer player = players.get(ch); if (player != null) return player.isPlaying(); return false; } public Boolean isPlaying () { return isPlayingChannel(AM_MAIN_CHANNEL); } Boolean isPreparingChannel (String ch) { OBGeneralAudioPlayer player = players.get(ch); if (player != null) return player.isPreparing(); return false; } public Boolean isPreparing () { return isPreparingChannel(AM_MAIN_CHANNEL); } public double durationForChannel (String ch) { OBGeneralAudioPlayer player = players.get(ch); if (player != null) return player.duration(); return 0.0; } public double duration () { return durationForChannel(AM_MAIN_CHANNEL); } public double durationSFX () { return durationForChannel(AM_SFX_CHANNEL); } public OBGeneralAudioPlayer playerForChannel (String channel) { OBGeneralAudioPlayer player = players.get(channel); if (player == null) { player = new OBAudioPlayer(); synchronized (this) { players.put(channel, player); } } return player; } public OBGeneralAudioPlayer playerForChannel (String channel,Class cls) { OBGeneralAudioPlayer player = players.get(channel); if (player == null || (player.getClass() != cls)) { Constructor<?> cons; try { cons = cls.getConstructor(); player = (OBGeneralAudioPlayer) cons.newInstance(); } catch (Exception e) { e.printStackTrace(); return null; } synchronized (this) { players.put(channel, player); } } return player; } public void prepareForChannel (final String fileName, final String channel) { OBGeneralAudioPlayer player = playerForChannel(channel); AssetFileDescriptor fd = getAudioPathFD(fileName); player.prepare(fd); } public void prepare(final String fileName) { prepareForChannel(fileName,AM_MAIN_CHANNEL); } public void clearCaches () { pathCacheDict.clear(); pathCacheList.clear(); synchronized (players) { Set<String> tempPlayers = new HashSet(players.keySet()); for (String s : tempPlayers) { if (!s.equals(AM_MAIN_CHANNEL)) { players.remove(s); } } } } }