package net.osmand.plus.voice; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Iterator; import java.util.List; import net.osmand.LogUtil; import net.osmand.data.index.IndexConstants; import net.osmand.plus.OsmandSettings; import net.osmand.plus.R; import net.osmand.plus.ResourceManager; import org.apache.commons.logging.Log; import alice.tuprolog.InvalidLibraryException; import alice.tuprolog.InvalidTheoryException; import alice.tuprolog.NoSolutionException; import alice.tuprolog.Number; import alice.tuprolog.Prolog; import alice.tuprolog.SolveInfo; import alice.tuprolog.Struct; import alice.tuprolog.Term; import alice.tuprolog.Theory; import alice.tuprolog.Var; import android.content.Context; import android.media.MediaPlayer; /** * That class represents command player. * It gets commands from input, analyze what files should be played and play * them using media player */ public class CommandPlayer { private static final Log log = LogUtil.getLog(CommandPlayer.class); protected Context ctx; // or zip file private File voiceDir; // private ZipFile voiceZipFile; // resolving commands to play private Prolog prologSystem; // playing media private MediaPlayer mediaPlayer; // indicates that player is ready to play first file private boolean playNext = true; private List<String> filesToPlay = Collections.synchronizedList(new ArrayList<String>()); public CommandPlayer(Context ctx){ long time = System.currentTimeMillis(); try { this.ctx = ctx; prologSystem = new Prolog(new String[]{"alice.tuprolog.lib.BasicLibrary"}); //$NON-NLS-1$ } catch (InvalidLibraryException e) { log.error("Initializing error", e); //$NON-NLS-1$ throw new RuntimeException(e); } mediaPlayer = new MediaPlayer(); if (log.isInfoEnabled()) { log.info("Initializing prolog system : " + (System.currentTimeMillis() - time)); //$NON-NLS-1$ } } public String getCurrentVoice(){ if(voiceDir == null){ return null; } return voiceDir.getName(); } public String init(String voiceProvider){ prologSystem.clearTheory(); voiceDir = null; if(voiceProvider != null){ File parent = OsmandSettings.extendOsmandPath(ctx, ResourceManager.VOICE_PATH); voiceDir = new File(parent, voiceProvider); if(!voiceDir.exists()){ voiceDir = null; return ctx.getString(R.string.voice_data_unavailable); } } // see comments below why it is impossible to read from zip (don't know how to play file from zip) // voiceZipFile = null; if(voiceDir != null) { long time = System.currentTimeMillis(); boolean wrong = false; try { InputStream config; // if (voiceDir.getName().endsWith(".zip")) { //$NON-NLS-1$ // voiceZipFile = new ZipFile(voiceDir); // config = voiceZipFile.getInputStream(voiceZipFile.getEntry("_config.p")); //$NON-NLS-1$ // } else { config = new FileInputStream(new File(voiceDir, "_config.p")); //$NON-NLS-1$ // } if (!wrong) { prologSystem.setTheory(new Theory(config)); } } catch (InvalidTheoryException e) { log.error("Loading voice config exception " + voiceProvider, e); //$NON-NLS-1$ wrong = true; } catch (IOException e) { log.error("Loading voice config exception " + voiceProvider, e); //$NON-NLS-1$ wrong = true; } if(wrong){ return ctx.getString(R.string.voice_data_corrupted); } else { boolean versionSupported = false; Var v = new Var("VERSION"); //$NON-NLS-1$ SolveInfo s = prologSystem.solve(new Struct(P_VERSION, v)); if(s.isSuccess()){ prologSystem.solveEnd(); try { Term val = s.getVarValue(v.getName()); if(val instanceof Number){ versionSupported = ((Number) val).intValue() == IndexConstants.VOICE_VERSION; } } catch (NoSolutionException e) { } } if(!versionSupported){ return ctx.getString(R.string.voice_data_not_supported); } } if (log.isInfoEnabled()) { log.info("Initializing voice subsystem " + voiceProvider + " : " + (System.currentTimeMillis() - time)); //$NON-NLS-1$ //$NON-NLS-2$ } } return null; } public CommandBuilder newCommandBuilder(){ return new CommandBuilder(); } protected List<String> execute(List<Struct> listCmd){ Struct list = new Struct(listCmd.toArray(new Term[listCmd.size()])); Var result = new Var("RESULT"); //$NON-NLS-1$ List<String> files = new ArrayList<String>(); SolveInfo res = prologSystem.solve(new Struct(P_RESOLVE, list, result)); if (res.isSuccess()) { try { prologSystem.solveEnd(); Term solution = res.getVarValue(result.getName()); Iterator<?> listIterator = ((Struct) solution).listIterator(); while(listIterator.hasNext()){ Object term = listIterator.next(); if(term instanceof Struct){ files.add(((Struct) term).getName()); } } } catch (NoSolutionException e) { } } return files; } public void playCommands(CommandBuilder builder){ filesToPlay.addAll(builder.execute()); playQueue(); } private synchronized void playQueue() { while (!filesToPlay.isEmpty() && playNext) { String f = filesToPlay.remove(0); if (f != null && voiceDir != null) { boolean exists = false; // if(voiceZipFile != null){ // ZipEntry entry = voiceZipFile.getEntry(f); // exists = entry != null; // voiceZipFile.getInputStream(entry); // // } else { File file = new File(voiceDir, f); exists = file.exists(); // } if (exists) { log.debug("Playing file : " + f); //$NON-NLS-1$ playNext = false; try { // Can't play sound file from zip it seams to be impossible only unpack and play!!! mediaPlayer.setDataSource(file.getAbsolutePath()); mediaPlayer.prepare(); mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { public void onCompletion(MediaPlayer mp) { mp.release(); mediaPlayer = new MediaPlayer(); int sleep = 60; boolean delay = true; while (!filesToPlay.isEmpty() && delay) { delay = filesToPlay.get(0).startsWith(DELAY_CONST); if (delay) { String s = filesToPlay.remove(0).substring(DELAY_CONST.length()); try { sleep += Integer.parseInt(s); } catch (NumberFormatException e) { } } } try { Thread.sleep(sleep); } catch (InterruptedException e) { } playNext = true; playQueue(); } }); mediaPlayer.start(); } catch (Exception e) { log.error("Error while playing voice command", e); //$NON-NLS-1$ playNext = true; } } else { log.info("Play file not found : " + f); //$NON-NLS-1$ } } } } protected static final String P_VERSION = "version"; //$NON-NLS-1$ protected static final String P_RESOLVE = "resolve"; //$NON-NLS-1$ public static final String A_LEFT = "left"; //$NON-NLS-1$ public static final String A_LEFT_SH = "left_sh"; //$NON-NLS-1$ public static final String A_LEFT_SL = "left_sl"; //$NON-NLS-1$ public static final String A_RIGHT = "right"; //$NON-NLS-1$ public static final String A_RIGHT_SH = "right_sh"; //$NON-NLS-1$ public static final String A_RIGHT_SL = "right_sl"; //$NON-NLS-1$ protected static final String C_PREPARE_TURN = "prepare_turn"; //$NON-NLS-1$ protected static final String C_PREPARE_ROUNDABOUT = "prepare_roundabout"; //$NON-NLS-1$ protected static final String C_PREPARE_MAKE_UT = "prepare_make_ut"; //$NON-NLS-1$ protected static final String C_ROUNDABOUT = "roundabout"; //$NON-NLS-1$ protected static final String C_GO_AHEAD = "go_ahead"; //$NON-NLS-1$ protected static final String C_TURN = "turn"; //$NON-NLS-1$ protected static final String C_MAKE_UT = "make_ut"; //$NON-NLS-1$ protected static final String C_PREAMBLE = "preamble"; //$NON-NLS-1$ protected static final String C_AND_ARRIVE_DESTINATION = "and_arrive_destination"; //$NON-NLS-1$ protected static final String C_THEN = "then"; //$NON-NLS-1$ protected static final String C_REACHED_DESTINATION = "reached_destination"; //$NON-NLS-1$ protected static final String C_BEAR_LEFT = "bear_left"; //$NON-NLS-1$ protected static final String C_BEAR_RIGHT = "bear_right"; //$NON-NLS-1$ protected static final String C_ROUTE_RECALC = "route_recalc"; //$NON-NLS-1$ protected static final String C_ROUTE_NEW_CALC = "route_new_calc"; //$NON-NLS-1$ protected static final String DELAY_CONST = "delay_"; //$NON-NLS-1$ public class CommandBuilder { private boolean alreadyExecuted = false; private List<Struct> listStruct = new ArrayList<Struct>(); public CommandBuilder(){ this(true); } public CommandBuilder(boolean preamble) { if (preamble) { addCommand(C_PREAMBLE); } } private void checkState(){ if(alreadyExecuted){ throw new IllegalArgumentException(); } } private CommandBuilder addCommand(String name, Object... args){ checkState(); Term[] list = new Term[args.length]; for (int i = 0; i < args.length; i++) { Object o = args[i]; if(o instanceof java.lang.Number){ if(o instanceof java.lang.Double){ list[i] = new alice.tuprolog.Double((Double) o); } else if(o instanceof java.lang.Float){ list[i] = new alice.tuprolog.Float((Float) o); } else if(o instanceof java.lang.Long){ list[i] = new alice.tuprolog.Long((Long) o); } else { list[i] = new alice.tuprolog.Int(((java.lang.Number)o).intValue()); } } else if(o instanceof String){ list[i] = new Struct((String) o); } if(list[i]== null){ throw new NullPointerException(name +" " + o); //$NON-NLS-1$ } } Struct struct = new Struct(name, list); if(log.isDebugEnabled()){ log.debug("Adding command : " + name + " " + Arrays.toString(args)); //$NON-NLS-1$ //$NON-NLS-2$ } listStruct.add(struct); return this; } public CommandBuilder goAhead(){ return addCommand(C_GO_AHEAD); } public CommandBuilder goAhead(double dist){ return addCommand(C_GO_AHEAD, dist); } public CommandBuilder makeUT(){ return addCommand(C_MAKE_UT); } public CommandBuilder makeUT(double dist){ return addCommand(C_MAKE_UT, dist); } public CommandBuilder prepareMakeUT(double dist){ return addCommand(C_PREPARE_MAKE_UT, dist); } public CommandBuilder turn(String param){ return addCommand(C_TURN, param); } public CommandBuilder turn(String param, double dist){ return addCommand(C_TURN, param, dist); } /** * * @param param A_LEFT, A_RIGHT, ... * @param dist * @return */ public CommandBuilder prepareTurn(String param, double dist){ return addCommand(C_PREPARE_TURN, param, dist); } public CommandBuilder prepareRoundAbout(double dist){ return addCommand(C_PREPARE_ROUNDABOUT, dist); } public CommandBuilder roundAbout(double dist, double angle, int exit){ return addCommand(C_ROUNDABOUT, dist, angle, exit); } public CommandBuilder roundAbout(double angle, int exit){ return addCommand(C_ROUNDABOUT, angle, exit); } public CommandBuilder andArriveAtDestination(){ return addCommand(C_AND_ARRIVE_DESTINATION); } public CommandBuilder arrivedAtDestination(){ return addCommand(C_REACHED_DESTINATION); } public CommandBuilder bearLeft(){ return addCommand(C_BEAR_LEFT); } public CommandBuilder bearRight(){ return addCommand(C_BEAR_RIGHT); } public CommandBuilder then(){ return addCommand(C_THEN); } public CommandBuilder newRouteCalculated(double dist){ return addCommand(C_ROUTE_NEW_CALC, dist); } public CommandBuilder routeRecalculated(double dist){ return addCommand(C_ROUTE_RECALC, dist); } public void play(){ CommandPlayer.this.playCommands(this); } protected List<String> execute(){ alreadyExecuted = true; return CommandPlayer.this.execute(listStruct); } } }