TTS 不受 AudioManager 设置的音量影响

TTS not impacted by the volume set by the AudioManager

我在实施 TTS(文本转语音)应用程序时遇到问题。基本上,当我触发我的 Text To Speech 方法时,我试图用最大音量覆盖当前音量。我可以在 phone 上看到音量控制的变化,但它似乎不会影响 TTS 的音量,因为 TTS 音量保持不变。我无法找出确切的问题。

我依次调用的3个方法

setMaxVolume();
activateTTS(myString);
setDefaultVolume();

设置最大音量

private void setMaxVolume(){
    audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
    currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
    audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC), 0);
}

激活TTS

private void activateTTS(String myString) {

    if(androidAPILevel < 21){
        HashMap<String, String> params = new HashMap<>();
        params.put(TextToSpeech.Engine.KEY_PARAM_VOLUME, "1");
        textToSpeech.speak(myString, TextToSpeech.QUEUE_FLUSH, params);
    } else{
        Bundle params = new Bundle();
        params.putFloat(TextToSpeech.Engine.KEY_PARAM_VOLUME, 1f);
        textToSpeech.speak(myString, TextToSpeech.QUEUE_FLUSH, params, null);
    }

}

设置默认音量

private void setDefaultVolume(){
    audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, currentVolume, 0);
}

目的是将音量控制权交给用户,而不是让 phone 音量影响 TTS 服务。

辅助求解的附加函数:

private TextToSpeech textToSpeech;
private int androidAPILevel = Build.VERSION.SDK_INT;
AudioManager audioManager;
int currentVolume;

@Nullable
@Override
public IBinder onBind(Intent intent) {
    return null;
}

@Override
public void onCreate() {
    textToSpeech = new TextToSpeech(this,
            this
    );

    textToSpeech.setOnUtteranceProgressListener(new UtteranceProgressListener() {
        @Override
        public void onStart(String s) {

        }

        @Override
        public void onDone(String s) {
            if (!getUserPreference().getTest()) {
                Toast.makeText(TimeWhisperService.this, "TTS Finished", Toast.LENGTH_SHORT).show();
                setDefaultVolume();
            }
        }

        @Override
        public void onError(String s) {

        }
    });

    Log.v(TAG, "oncreate_service");
    super.onCreate();
}

@Override
public void onDestroy() {
    // TODO Auto-generated method stub
    if (textToSpeech != null) {
        textToSpeech.stop();
        textToSpeech.shutdown();
    }
    super.onDestroy();
}

@Override
public void onStart(Intent intent, int startId) {
    textToSpeech.setSpeechRate(Float.parseFloat(getUserPreference().getSpeed() + "f"));
    textToSpeech.setPitch(Float.parseFloat(getUserPreference().getPitch() + "f"));


    if(getUserPreference().getTest()) {
        activateTTS(getMyString());
    }
    else{
        Timer timer = new Timer();
        timer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                setMaxVolume();
                activateTTS(getMyString());
                //setDefaultVolume();
            }
        }, 0, getUserPreference().getTimer() * 10000);
        Log.v(TAG, "onstart_service");
    }
    super.onStart(intent, startId);
}

@Override
public void onInit(int status) {
    Log.v(TAG, "oninit");
    if (status == TextToSpeech.SUCCESS) {
        int result = textToSpeech.setLanguage(Locale.UK);
        if (result == TextToSpeech.LANG_MISSING_DATA ||
            result == TextToSpeech.LANG_NOT_SUPPORTED) {
            Log.v(TAG, "Language is not available.");
        } else {
            //textToSpeech.setOnUtteranceCompletedListener(this);
            activateTTS(getMyString());
        }
    } else {
        Log.v(TAG, "Could not initialize TextToSpeech.");
    }
}

问题是您使用的是 "calling in sequence" 三种方法,但实际发生的是第三种方法被调用,将系统音量重置为正常...甚至在 TTS 开始说话之前。

原因是,根据 documentation,speak() 方法是异步的。

所以...解决此问题的方法是创建一个 UtteranceProgressListener 并将 setDefaultVolume() 方法移动到它的 onDone() 方法中。

旁注:为了调用任何 UtteranceProgressListener 回调,utteranceID 不能为 null...因此也进行了更改:

public class MainActivity extends AppCompatActivity {

    int androidAPILevel = android.os.Build.VERSION.SDK_INT;
    TextToSpeech textToSpeech;
    AudioManager audioManager;
    int currentVolume;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        textToSpeech = new TextToSpeech(this, new TextToSpeech.OnInitListener() {
            @Override
            public void onInit(int i) {
                start();
            }
        });

        textToSpeech.setOnUtteranceProgressListener(new UtteranceProgressListener() {
            @Override
            public void onStart(String s) {
            }
            @Override
            public void onDone(String s) {
                Log.i("XXX", "onDone() called.");
                setDefaultVolume(); // sets volume back to normal *after* speech is done.
            }
            @Override
            public void onError(String s) {
                Log.i("XXX", "onError() called.");
                setDefaultVolume(); // sets volume back to normal *after* speech is done.
            }
        });
    }

    private void start() {
        setMaxVolume();
        activateTTS("hello! hello! hello! hello! hello! hello!");
    }

    private void activateTTS(String myString) {

        if(androidAPILevel < 21){
            HashMap<String, String> params = new HashMap<>();
            params.put(TextToSpeech.Engine.KEY_PARAM_VOLUME, "1");
            params.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "thisNeedsToBeSet");
            textToSpeech.speak(myString, TextToSpeech.QUEUE_FLUSH, params);
        } else{
            Bundle params = new Bundle();
            params.putFloat(TextToSpeech.Engine.KEY_PARAM_VOLUME, 1f);
            textToSpeech.speak(myString, TextToSpeech.QUEUE_FLUSH, params, "thisNeedsToBeSet");
        }

    }

    private void setMaxVolume(){
        audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
        currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
        audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC), 0);
    }

    private void setDefaultVolume(){
        audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, currentVolume, 0);
    }

}

编辑:最好在 onError() 和 onDone() 回调中实现您想要的行为。据我所知,其中一个或另一个总是被称为。这样,如果出现错误,你的音量就不会卡在高位了。