From 8a532752eb158da5f9cb2f27058b79b1ab9996cb Mon Sep 17 00:00:00 2001 From: chgenly Date: Sat, 8 Sep 2012 22:28:39 -0700 Subject: [PATCH 1/6] Release media player when all audio files are played, rather than after each audio file is done. This prevents podcasts or music from interrupting speech. --- OsmAnd/assets/bundled_assets.xml | 0 OsmAnd/jni/Android.mk | 0 OsmAnd/jni/Application.mk | 0 OsmAnd/scripts/generate.sh | 0 .../plus/voice/MediaCommandPlayerImpl.java | 31 ++++++++++++------- 5 files changed, 19 insertions(+), 12 deletions(-) mode change 100755 => 100644 OsmAnd/assets/bundled_assets.xml mode change 100755 => 100644 OsmAnd/jni/Android.mk mode change 100755 => 100644 OsmAnd/jni/Application.mk mode change 100755 => 100644 OsmAnd/scripts/generate.sh diff --git a/OsmAnd/assets/bundled_assets.xml b/OsmAnd/assets/bundled_assets.xml old mode 100755 new mode 100644 diff --git a/OsmAnd/jni/Android.mk b/OsmAnd/jni/Android.mk old mode 100755 new mode 100644 diff --git a/OsmAnd/jni/Application.mk b/OsmAnd/jni/Application.mk old mode 100755 new mode 100644 diff --git a/OsmAnd/scripts/generate.sh b/OsmAnd/scripts/generate.sh old mode 100755 new mode 100644 diff --git a/OsmAnd/src/net/osmand/plus/voice/MediaCommandPlayerImpl.java b/OsmAnd/src/net/osmand/plus/voice/MediaCommandPlayerImpl.java index 654a006911..a46bf4248d 100644 --- a/OsmAnd/src/net/osmand/plus/voice/MediaCommandPlayerImpl.java +++ b/OsmAnd/src/net/osmand/plus/voice/MediaCommandPlayerImpl.java @@ -38,9 +38,7 @@ public class MediaCommandPlayerImpl extends AbstractPrologCommandPlayer { throws CommandPlayerException { super(ctx, settings, voiceProvider, CONFIG_FILE, MEDIA_VOICE_VERSION); - mediaPlayer = new MediaPlayer(); this.streamType = settings.AUDIO_STREAM_GUIDANCE.get(); - mediaPlayer.setAudioStreamType(streamType); } @Override @@ -61,32 +59,37 @@ public class MediaCommandPlayerImpl extends AbstractPrologCommandPlayer { } private synchronized void playQueue() { - while (!filesToPlay.isEmpty() && playNext) { + if (!playNext) + return; + while (!filesToPlay.isEmpty()) { 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) { + File file = new File(voiceDir, f); + if (file.exists()) { log.debug("Playing file : " + f); //$NON-NLS-1$ playNext = false; try { + if (mediaPlayer == null) { + mediaPlayer = new MediaPlayer(); + } + // Can't play sound file from zip it seams to be impossible only unpack and play!!! + mediaPlayer.setAudioStreamType(streamType); mediaPlayer.setDataSource(file.getAbsolutePath()); mediaPlayer.prepare(); mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { @Override public void onCompletion(MediaPlayer mp) { - mp.release(); - mediaPlayer = new MediaPlayer(); - mediaPlayer.setAudioStreamType(streamType); + // Reset prepares to speak again. Don't release because if we have more to + // say, we don't want our speech interrupted by other audio, such as music + // or a podcast. We will release when we are done speaking. + mp.reset(); int sleep = 60; boolean delay = true; while (!filesToPlay.isEmpty() && delay) { @@ -112,13 +115,17 @@ public class MediaCommandPlayerImpl extends AbstractPrologCommandPlayer { } 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$ } } } + // Release the media player only when we are done speaking. + if (mediaPlayer != null) { + mediaPlayer.release(); + mediaPlayer = null; + } } public static boolean isMyData(File voiceDir) { From a79e7e5da7a18d83c0283c14a8fb6c2df1747fc5 Mon Sep 17 00:00:00 2001 From: chgenly Date: Sun, 9 Sep 2012 00:13:14 -0700 Subject: [PATCH 2/6] Flow control for playing voice commands changed to clarify flow If API LEVEL > 8: AudioManager is used to gain temporary audio focus. --- .../plus/voice/MediaCommandPlayerImpl.java | 177 +++++++++++------- 1 file changed, 112 insertions(+), 65 deletions(-) diff --git a/OsmAnd/src/net/osmand/plus/voice/MediaCommandPlayerImpl.java b/OsmAnd/src/net/osmand/plus/voice/MediaCommandPlayerImpl.java index a46bf4248d..23aec32ea1 100644 --- a/OsmAnd/src/net/osmand/plus/voice/MediaCommandPlayerImpl.java +++ b/OsmAnd/src/net/osmand/plus/voice/MediaCommandPlayerImpl.java @@ -11,7 +11,9 @@ import net.osmand.plus.OsmandSettings; import org.apache.commons.logging.Log; +import android.annotation.SuppressLint; import android.content.Context; +import android.media.AudioManager; import android.media.MediaPlayer; /** @@ -19,7 +21,7 @@ import android.media.MediaPlayer; * It gets commands from input, analyze what files should be played and play * them using media player */ -public class MediaCommandPlayerImpl extends AbstractPrologCommandPlayer { +public class MediaCommandPlayerImpl extends AbstractPrologCommandPlayer implements MediaPlayer.OnCompletionListener { private static final String CONFIG_FILE = "_config.p"; private static final int[] MEDIA_VOICE_VERSION = new int[] { 0 }; // MUST BE SORTED, list of supported versions @@ -29,15 +31,17 @@ public class MediaCommandPlayerImpl extends AbstractPrologCommandPlayer { // playing media private MediaPlayer mediaPlayer; // indicates that player is ready to play first file - private volatile boolean playNext = true; private List filesToPlay = Collections.synchronizedList(new ArrayList()); private int streamType; + private final Context mCtx; + private AudioFocusHelper mAudioFocusHelper; public MediaCommandPlayerImpl(Context ctx, OsmandSettings settings, String voiceProvider) throws CommandPlayerException { super(ctx, settings, voiceProvider, CONFIG_FILE, MEDIA_VOICE_VERSION); + mCtx = ctx; this.streamType = settings.AUDIO_STREAM_GUIDANCE.get(); } @@ -53,82 +57,125 @@ public class MediaCommandPlayerImpl extends AbstractPrologCommandPlayer { } @Override - public void playCommands(CommandBuilder builder){ + public synchronized void playCommands(CommandBuilder builder) { filesToPlay.addAll(builder.execute()); - playQueue(); + + // If we have not already started to play audio, start. + if (mediaPlayer == null) { + if (android.os.Build.VERSION.SDK_INT >= 8) { + mAudioFocusHelper = new AudioFocusHelper(mCtx); + } else { + mAudioFocusHelper = null; + } + if (mAudioFocusHelper != null) + mAudioFocusHelper.requestFocus(); + playQueue(); + } } private synchronized void playQueue() { - if (!playNext) - return; + if (mediaPlayer == null) { + mediaPlayer = new MediaPlayer(); + } + + performDelays(); + File file = getNextFileToPlay(); + if (file != null) { + playFile(file); + // Will continue with onCompletion() + } else { + // Release the media player only when we are done speaking. + if (mediaPlayer != null) { + mediaPlayer.release(); + mediaPlayer = null; + + if (mAudioFocusHelper != null) + mAudioFocusHelper.abandonFocus(); + } + } + } + + @Override + public void onCompletion(MediaPlayer mp) { + playQueue(); + } + + + private void performDelays() { + int sleep = 0; + while (!filesToPlay.isEmpty() && filesToPlay.get(0).startsWith(DELAY_CONST)) { + String s = filesToPlay.remove(0).substring(DELAY_CONST.length()); + try { + sleep += Integer.parseInt(s); + } catch (NumberFormatException e) { + } + } + try { + if (sleep != 0) + Thread.sleep(sleep); + } catch (InterruptedException e) { + } + } + + private File getNextFileToPlay() { while (!filesToPlay.isEmpty()) { String f = filesToPlay.remove(0); if (f != null && voiceDir != null) { -// if(voiceZipFile != null){ -// ZipEntry entry = voiceZipFile.getEntry(f); -// exists = entry != null; -// voiceZipFile.getInputStream(entry); -// -// } else { File file = new File(voiceDir, f); - if (file.exists()) { - log.debug("Playing file : " + f); //$NON-NLS-1$ - playNext = false; - try { - if (mediaPlayer == null) { - mediaPlayer = new MediaPlayer(); - } - - // Can't play sound file from zip it seams to be impossible only unpack and play!!! - mediaPlayer.setAudioStreamType(streamType); - mediaPlayer.setDataSource(file.getAbsolutePath()); - mediaPlayer.prepare(); - mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { - @Override - public void onCompletion(MediaPlayer mp) { - // Reset prepares to speak again. Don't release because if we have more to - // say, we don't want our speech interrupted by other audio, such as music - // or a podcast. We will release when we are done speaking. - mp.reset(); - 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$ - } - } + if (file.exists()) + return file; + else + log.error("Unable to play, does not exist: "+file); + } } - // Release the media player only when we are done speaking. - if (mediaPlayer != null) { - mediaPlayer.release(); - mediaPlayer = null; + return null; + } + + private void playFile(File file) { + log.debug("Playing file : " + file); //$NON-NLS-1$ + try { + mediaPlayer.reset(); + mediaPlayer.setAudioStreamType(streamType); + mediaPlayer.setDataSource(file.getAbsolutePath()); + mediaPlayer.prepare(); + mediaPlayer.setOnCompletionListener(this); + mediaPlayer.start(); + } catch (Exception e) { + log.error("Error while playing voice command", e); //$NON-NLS-1$ + playQueue(); } } public static boolean isMyData(File voiceDir) { return new File(voiceDir, CONFIG_FILE).exists(); } + + // We Use API level 8 calls here, suppress warnings. + @SuppressLint("NewApi") + public class AudioFocusHelper implements AudioManager.OnAudioFocusChangeListener { + private Context mContext; + private AudioManager mAudioManager; + + public AudioFocusHelper(Context context) { + mContext = context; + mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); + } + + public boolean requestFocus() { + return AudioManager.AUDIOFOCUS_REQUEST_GRANTED == + mAudioManager.requestAudioFocus(this, streamType, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT); + } + + public boolean abandonFocus() { + return AudioManager.AUDIOFOCUS_REQUEST_GRANTED == + mAudioManager.abandonAudioFocus(this); + } + @Override + public void onAudioFocusChange(int focusChange) { + if (focusChange == AudioManager.AUDIOFOCUS_GAIN) + playQueue(); + else + log.error("Unable to gain audio focus"); + } + } } From bc17e703ca30e0a38966e182e4c24fe0ea5454b4 Mon Sep 17 00:00:00 2001 From: chgenly Date: Sun, 9 Sep 2012 11:40:16 -0700 Subject: [PATCH 3/6] A little work on focus listener. --- .../osmand/plus/voice/MediaCommandPlayerImpl.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/OsmAnd/src/net/osmand/plus/voice/MediaCommandPlayerImpl.java b/OsmAnd/src/net/osmand/plus/voice/MediaCommandPlayerImpl.java index 23aec32ea1..325b67e3ee 100644 --- a/OsmAnd/src/net/osmand/plus/voice/MediaCommandPlayerImpl.java +++ b/OsmAnd/src/net/osmand/plus/voice/MediaCommandPlayerImpl.java @@ -150,6 +150,12 @@ public class MediaCommandPlayerImpl extends AbstractPrologCommandPlayer implemen return new File(voiceDir, CONFIG_FILE).exists(); } + /** + * This helper class allows API level 8 calls to be isolated from the rest of the app. + * This class is only be instantiated on OS versions which support it. + * @author genly + * + */ // We Use API level 8 calls here, suppress warnings. @SuppressLint("NewApi") public class AudioFocusHelper implements AudioManager.OnAudioFocusChangeListener { @@ -167,15 +173,11 @@ public class MediaCommandPlayerImpl extends AbstractPrologCommandPlayer implemen } public boolean abandonFocus() { - return AudioManager.AUDIOFOCUS_REQUEST_GRANTED == - mAudioManager.abandonAudioFocus(this); + return AudioManager.AUDIOFOCUS_REQUEST_GRANTED == mAudioManager.abandonAudioFocus(this); } @Override public void onAudioFocusChange(int focusChange) { - if (focusChange == AudioManager.AUDIOFOCUS_GAIN) - playQueue(); - else - log.error("Unable to gain audio focus"); + log.error("MediaCommandPlayerImpl.onAudioFocusChange(): Unexpected audio focus change: "+focusChange); } } } From efff2c2d493883d82445f3a3620281631b41e653 Mon Sep 17 00:00:00 2001 From: chgenly Date: Sun, 9 Sep 2012 11:44:31 -0700 Subject: [PATCH 4/6] Random mode changes. --- OsmAnd/assets/bundled_assets.xml | 0 OsmAnd/jni/Android.mk | 0 OsmAnd/jni/Application.mk | 0 OsmAnd/scripts/generate.sh | 0 4 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 OsmAnd/assets/bundled_assets.xml mode change 100644 => 100755 OsmAnd/jni/Android.mk mode change 100644 => 100755 OsmAnd/jni/Application.mk mode change 100644 => 100755 OsmAnd/scripts/generate.sh diff --git a/OsmAnd/assets/bundled_assets.xml b/OsmAnd/assets/bundled_assets.xml old mode 100644 new mode 100755 diff --git a/OsmAnd/jni/Android.mk b/OsmAnd/jni/Android.mk old mode 100644 new mode 100755 diff --git a/OsmAnd/jni/Application.mk b/OsmAnd/jni/Application.mk old mode 100644 new mode 100755 diff --git a/OsmAnd/scripts/generate.sh b/OsmAnd/scripts/generate.sh old mode 100644 new mode 100755 From 59bea7f7414d28c3b515f59b25cab9d19842247c Mon Sep 17 00:00:00 2001 From: chgenly Date: Mon, 10 Sep 2012 22:30:22 -0700 Subject: [PATCH 5/6] Changes to use audio focus for TTS engine. --- .../voice/AbstractPrologCommandPlayer.java | 65 ++++++++++++++++++- .../plus/voice/MediaCommandPlayerImpl.java | 62 ++++-------------- .../plus/voice/TTSCommandPlayerImpl.java | 27 ++++++++ 3 files changed, 102 insertions(+), 52 deletions(-) diff --git a/OsmAnd/src/net/osmand/plus/voice/AbstractPrologCommandPlayer.java b/OsmAnd/src/net/osmand/plus/voice/AbstractPrologCommandPlayer.java index 8f581ec747..9daec0f271 100644 --- a/OsmAnd/src/net/osmand/plus/voice/AbstractPrologCommandPlayer.java +++ b/OsmAnd/src/net/osmand/plus/voice/AbstractPrologCommandPlayer.java @@ -28,12 +28,13 @@ import alice.tuprolog.Struct; import alice.tuprolog.Term; import alice.tuprolog.Theory; import alice.tuprolog.Var; +import android.annotation.SuppressLint; import android.content.Context; +import android.media.AudioManager; public abstract class AbstractPrologCommandPlayer implements CommandPlayer { - private static final Log log = LogUtil - .getLog(AbstractPrologCommandPlayer.class); + private static final Log log = LogUtil.getLog(AbstractPrologCommandPlayer.class); protected Context ctx; protected File voiceDir; @@ -51,10 +52,14 @@ public abstract class AbstractPrologCommandPlayer implements CommandPlayer { protected static final String DELAY_CONST = "delay_"; /** Must be sorted array! */ private final int[] sortedVoiceVersions; + private AudioFocusHelper mAudioFocusHelper; + + private int streamType; protected AbstractPrologCommandPlayer(Context ctx, OsmandSettings settings, String voiceProvider, String configFile, int[] sortedVoiceVersions) throws CommandPlayerException { + this.ctx = ctx; this.sortedVoiceVersions = sortedVoiceVersions; long time = System.currentTimeMillis(); try { @@ -67,6 +72,7 @@ public abstract class AbstractPrologCommandPlayer implements CommandPlayer { if (log.isInfoEnabled()) { log.info("Initializing prolog system : " + (System.currentTimeMillis() - time)); //$NON-NLS-1$ } + this.streamType = settings.AUDIO_STREAM_GUIDANCE.get(); init(voiceProvider, settings, configFile); } @@ -189,4 +195,59 @@ public abstract class AbstractPrologCommandPlayer implements CommandPlayer { prologSystem = null; } + @Override + public void updateAudioStream(int streamType) { + this.streamType = streamType; + } + + protected void requestAudioFocus() { + log.debug("requestAudioFocus"); + if (android.os.Build.VERSION.SDK_INT >= 8) { + mAudioFocusHelper = new AudioFocusHelper(ctx); + } + if (mAudioFocusHelper != null) + mAudioFocusHelper.requestFocus(); + } + + protected void abandonAudioFocus() { + log.debug("abandonAudioFocus"); + if (mAudioFocusHelper != null) { + mAudioFocusHelper.abandonFocus(); + mAudioFocusHelper = null; + } + } + + /** + * This helper class allows API level 8 calls to be isolated from the rest of the app. + * This class is only be instantiated on OS versions which support it. + * @author genly + * + */ + // We Use API level 8 calls here, suppress warnings. + @SuppressLint("NewApi") + public class AudioFocusHelper implements AudioManager.OnAudioFocusChangeListener { + private Context mContext; + private AudioManager mAudioManager; + + public AudioFocusHelper(Context context) { + mContext = context; + mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); + } + + public boolean requestFocus() { + return AudioManager.AUDIOFOCUS_REQUEST_GRANTED == + mAudioManager.requestAudioFocus(this, streamType, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT); + } + + public boolean abandonFocus() { + return AudioManager.AUDIOFOCUS_REQUEST_GRANTED == mAudioManager.abandonAudioFocus(this); + } + @Override + public void onAudioFocusChange(int focusChange) { + // Basically we ignore audio focus changes. There's not much we can do when we have interrupted audio + // for our speech, and we in turn get interrupted. Ignore it until a scenario comes up which gives us + // reason to change this strategy. + log.error("MediaCommandPlayerImpl.onAudioFocusChange(): Unexpected audio focus change: "+focusChange); + } + } } diff --git a/OsmAnd/src/net/osmand/plus/voice/MediaCommandPlayerImpl.java b/OsmAnd/src/net/osmand/plus/voice/MediaCommandPlayerImpl.java index 325b67e3ee..2645849cc0 100644 --- a/OsmAnd/src/net/osmand/plus/voice/MediaCommandPlayerImpl.java +++ b/OsmAnd/src/net/osmand/plus/voice/MediaCommandPlayerImpl.java @@ -11,11 +11,10 @@ import net.osmand.plus.OsmandSettings; import org.apache.commons.logging.Log; -import android.annotation.SuppressLint; import android.content.Context; -import android.media.AudioManager; import android.media.MediaPlayer; + /** * That class represents command player. * It gets commands from input, analyze what files should be played and play @@ -33,21 +32,12 @@ public class MediaCommandPlayerImpl extends AbstractPrologCommandPlayer implemen // indicates that player is ready to play first file private List filesToPlay = Collections.synchronizedList(new ArrayList()); private int streamType; - private final Context mCtx; - private AudioFocusHelper mAudioFocusHelper; public MediaCommandPlayerImpl(Context ctx, OsmandSettings settings, String voiceProvider) throws CommandPlayerException { super(ctx, settings, voiceProvider, CONFIG_FILE, MEDIA_VOICE_VERSION); - mCtx = ctx; - this.streamType = settings.AUDIO_STREAM_GUIDANCE.get(); - } - - @Override - public void updateAudioStream(int streamType) { - this.streamType = streamType; } @Override @@ -62,13 +52,7 @@ public class MediaCommandPlayerImpl extends AbstractPrologCommandPlayer implemen // If we have not already started to play audio, start. if (mediaPlayer == null) { - if (android.os.Build.VERSION.SDK_INT >= 8) { - mAudioFocusHelper = new AudioFocusHelper(mCtx); - } else { - mAudioFocusHelper = null; - } - if (mAudioFocusHelper != null) - mAudioFocusHelper.requestFocus(); + requestAudioFocus(); playQueue(); } } @@ -89,14 +73,17 @@ public class MediaCommandPlayerImpl extends AbstractPrologCommandPlayer implemen mediaPlayer.release(); mediaPlayer = null; - if (mAudioFocusHelper != null) - mAudioFocusHelper.abandonFocus(); + abandonAudioFocus(); } } } + /** + * Called when the MediaPlayer is done. + */ @Override public void onCompletion(MediaPlayer mp) { + // Work on the next file to play. playQueue(); } @@ -131,6 +118,11 @@ public class MediaCommandPlayerImpl extends AbstractPrologCommandPlayer implemen return null; } + /** + * Starts the MediaPlayer playing a file. This method will return immediately. + * OnCompletionListener() will be called when the MediaPlayer is done. + * @param file + */ private void playFile(File file) { log.debug("Playing file : " + file); //$NON-NLS-1$ try { @@ -150,34 +142,4 @@ public class MediaCommandPlayerImpl extends AbstractPrologCommandPlayer implemen return new File(voiceDir, CONFIG_FILE).exists(); } - /** - * This helper class allows API level 8 calls to be isolated from the rest of the app. - * This class is only be instantiated on OS versions which support it. - * @author genly - * - */ - // We Use API level 8 calls here, suppress warnings. - @SuppressLint("NewApi") - public class AudioFocusHelper implements AudioManager.OnAudioFocusChangeListener { - private Context mContext; - private AudioManager mAudioManager; - - public AudioFocusHelper(Context context) { - mContext = context; - mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); - } - - public boolean requestFocus() { - return AudioManager.AUDIOFOCUS_REQUEST_GRANTED == - mAudioManager.requestAudioFocus(this, streamType, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT); - } - - public boolean abandonFocus() { - return AudioManager.AUDIOFOCUS_REQUEST_GRANTED == mAudioManager.abandonAudioFocus(this); - } - @Override - public void onAudioFocusChange(int focusChange) { - log.error("MediaCommandPlayerImpl.onAudioFocusChange(): Unexpected audio focus change: "+focusChange); - } - } } diff --git a/OsmAnd/src/net/osmand/plus/voice/TTSCommandPlayerImpl.java b/OsmAnd/src/net/osmand/plus/voice/TTSCommandPlayerImpl.java index ec7f15d27f..3ad601c670 100644 --- a/OsmAnd/src/net/osmand/plus/voice/TTSCommandPlayerImpl.java +++ b/OsmAnd/src/net/osmand/plus/voice/TTSCommandPlayerImpl.java @@ -5,7 +5,10 @@ import java.util.HashMap; import java.util.List; import java.util.Locale; +import org.apache.commons.logging.Log; + import net.osmand.Algoritms; +import net.osmand.LogUtil; import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandSettings; import net.osmand.plus.R; @@ -21,6 +24,7 @@ import android.content.Intent; import android.net.Uri; import android.speech.tts.TextToSpeech; import android.speech.tts.TextToSpeech.OnInitListener; +import android.speech.tts.TextToSpeech.OnUtteranceCompletedListener; public class TTSCommandPlayerImpl extends AbstractPrologCommandPlayer { @@ -53,6 +57,7 @@ public class TTSCommandPlayerImpl extends AbstractPrologCommandPlayer { private static final String CONFIG_FILE = "_ttsconfig.p"; private static final int[] TTS_VOICE_VERSION = new int[] { 100, 101 }; // !! MUST BE SORTED + private static final Log log = LogUtil.getLog(TTSCommandPlayerImpl.class); private TextToSpeech mTts; private Context mTtsContext; private String language; @@ -76,6 +81,15 @@ public class TTSCommandPlayerImpl extends AbstractPrologCommandPlayer { + /** + * Since TTS requests are asynchronous, playCommands() can be called before + * the TTS engine is done. We use this field to keep track of concurrent tts + * activity. Where tts activity is defined as the time between tts.speak() + * and the call back to onUtteranceCompletedListener(). This allows us to + * optimize use of requesting and abandoning audio focus. + */ + private int ttsRequests; + @Override public void playCommands(CommandBuilder builder) { if (mTts != null) { @@ -84,7 +98,11 @@ public class TTSCommandPlayerImpl extends AbstractPrologCommandPlayer { for (String s : execute) { bld.append(s).append(' '); } + if (ttsRequests++ == 0) + requestAudioFocus(); + log.debug("ttsRequests="+ttsRequests); mTts.speak(bld.toString(), TextToSpeech.QUEUE_ADD, params); + // Audio focus will be released when onUtteranceCompleted() completed is called by the TTS engine. } } @@ -141,6 +159,14 @@ public class TTSCommandPlayerImpl extends AbstractPrologCommandPlayer { return ctx instanceof SettingsActivity; } }); + mTts.setOnUtteranceCompletedListener(new OnUtteranceCompletedListener() { + @Override + public void onUtteranceCompleted(String utteranceId) { + if (--ttsRequests == 0) + abandonAudioFocus(); + log.debug("ttsRequests="+ttsRequests); + } + }); } } @@ -175,6 +201,7 @@ public class TTSCommandPlayerImpl extends AbstractPrologCommandPlayer { @Override public void updateAudioStream(int streamType) { + super.updateAudioStream(streamType); params.put(TextToSpeech.Engine.KEY_PARAM_STREAM, streamType+""); } From d23e7c18fcc83d7f933fda5597481a0f1fa0d57a Mon Sep 17 00:00:00 2001 From: chgenly Date: Tue, 11 Sep 2012 10:52:40 -0700 Subject: [PATCH 6/6] Fixed problem with handling delay caused by getNextTilfeToPlay() Fixed problem where tts onUtteranceCompleted() was not called because utterance id was not set. Fixed synchronization problems. --- .../plus/voice/MediaCommandPlayerImpl.java | 20 +++++++++++-------- .../plus/voice/TTSCommandPlayerImpl.java | 7 +++++-- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/OsmAnd/src/net/osmand/plus/voice/MediaCommandPlayerImpl.java b/OsmAnd/src/net/osmand/plus/voice/MediaCommandPlayerImpl.java index 2645849cc0..6bc3414c2a 100644 --- a/OsmAnd/src/net/osmand/plus/voice/MediaCommandPlayerImpl.java +++ b/OsmAnd/src/net/osmand/plus/voice/MediaCommandPlayerImpl.java @@ -46,6 +46,7 @@ public class MediaCommandPlayerImpl extends AbstractPrologCommandPlayer implemen mediaPlayer = null; } + // Called from the calculating route thread. @Override public synchronized void playCommands(CommandBuilder builder) { filesToPlay.addAll(builder.execute()); @@ -79,7 +80,7 @@ public class MediaCommandPlayerImpl extends AbstractPrologCommandPlayer implemen } /** - * Called when the MediaPlayer is done. + * Called when the MediaPlayer is done. The call back is on the main thread. */ @Override public void onCompletion(MediaPlayer mp) { @@ -87,7 +88,6 @@ public class MediaCommandPlayerImpl extends AbstractPrologCommandPlayer implemen playQueue(); } - private void performDelays() { int sleep = 0; while (!filesToPlay.isEmpty() && filesToPlay.get(0).startsWith(DELAY_CONST)) { @@ -98,8 +98,10 @@ public class MediaCommandPlayerImpl extends AbstractPrologCommandPlayer implemen } } try { - if (sleep != 0) + if (sleep != 0) { + log.debug("Delaying "+sleep); Thread.sleep(sleep); + } } catch (InterruptedException e) { } } @@ -109,10 +111,7 @@ public class MediaCommandPlayerImpl extends AbstractPrologCommandPlayer implemen String f = filesToPlay.remove(0); if (f != null && voiceDir != null) { File file = new File(voiceDir, f); - if (file.exists()) - return file; - else - log.error("Unable to play, does not exist: "+file); + return file; } } return null; @@ -124,8 +123,13 @@ public class MediaCommandPlayerImpl extends AbstractPrologCommandPlayer implemen * @param file */ private void playFile(File file) { - log.debug("Playing file : " + file); //$NON-NLS-1$ + if (!file.exists()) { + log.error("Unable to play, does not exist: "+file); + playQueue(); + return; + } try { + log.debug("Playing file : " + file); //$NON-NLS-1$ mediaPlayer.reset(); mediaPlayer.setAudioStreamType(streamType); mediaPlayer.setDataSource(file.getAbsolutePath()); diff --git a/OsmAnd/src/net/osmand/plus/voice/TTSCommandPlayerImpl.java b/OsmAnd/src/net/osmand/plus/voice/TTSCommandPlayerImpl.java index 3ad601c670..6e5ee1dd5e 100644 --- a/OsmAnd/src/net/osmand/plus/voice/TTSCommandPlayerImpl.java +++ b/OsmAnd/src/net/osmand/plus/voice/TTSCommandPlayerImpl.java @@ -90,8 +90,9 @@ public class TTSCommandPlayerImpl extends AbstractPrologCommandPlayer { */ private int ttsRequests; + // Called from the calculating route thread. @Override - public void playCommands(CommandBuilder builder) { + public synchronized void playCommands(CommandBuilder builder) { if (mTts != null) { final List execute = builder.execute(); //list of strings, the speech text, play it StringBuilder bld = new StringBuilder(); @@ -101,6 +102,7 @@ public class TTSCommandPlayerImpl extends AbstractPrologCommandPlayer { if (ttsRequests++ == 0) requestAudioFocus(); log.debug("ttsRequests="+ttsRequests); + params.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID,""+System.currentTimeMillis()); mTts.speak(bld.toString(), TextToSpeech.QUEUE_ADD, params); // Audio focus will be released when onUtteranceCompleted() completed is called by the TTS engine. } @@ -160,8 +162,9 @@ public class TTSCommandPlayerImpl extends AbstractPrologCommandPlayer { } }); mTts.setOnUtteranceCompletedListener(new OnUtteranceCompletedListener() { + // The call back is on a binder thread. @Override - public void onUtteranceCompleted(String utteranceId) { + public synchronized void onUtteranceCompleted(String utteranceId) { if (--ttsRequests == 0) abandonAudioFocus(); log.debug("ttsRequests="+ttsRequests);