导航app引导中经常遇到破音,这里也将之前经历过的方案收集以下,方便以后选择:
1 对于开始和结尾破音: 可以用升降音来处理
   两种方式
  一种是 直接对开始和结束的时间段进行音量直接渐进改变。这里配的是200ms的渐变。
   VolumeShaper.Configuration cfg_out= null;
         if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
             cfg_out = new VolumeShaper.Configuration.Builder()
                     .setCurve(new float[]{0f,1f},new float[]{1f,0f})
                     .setInterpolatorType(VolumeShaper.Configuration.INTERPOLATOR_TYPE_LINEAR)
                     .setDuration(200)
                     .build();
             VolumeShaper vShaper = mAudioTrack.createVolumeShaper(cfg_out);
             vShaper.apply(VolumeShaper.Operation.PLAY);
         }
  一种是 开始的那帧数据进行音量从零渐进增加到当前音量,结束的那几帧数据进行音量从当前音量降到零
       /**
      * 对音频数据做 fade out
      * @param byteBuffer byteBuffer
      * @param channelCount channelCount
      */
     private ByteBuffer shortFadeOut(ByteBuffer byteBuffer, int channelCount) {
         int shortCount = byteBuffer.limit() / 2;
         if(1 == channelCount) {
             for(int i = 0; i < shortCount; i++) {
                 short data = (short) (byteBuffer.getShort(i * 2) * 1.0f * (shortCount - i) / (2*shortCount));
                 byteBuffer.putShort(i * 2, data);
             }
         } else {
             for(int i = 0; i < shortCount; i += 2) {
                 short data = (short) (byteBuffer.getShort(i * 2) * 1.0f * (shortCount - i) / (2*shortCount));
                 byteBuffer.putShort(i * 2, data);
                 data = (short)(byteBuffer.getShort((i + 1) * 2) * 1.0f * (shortCount - i) / (2*shortCount));
                 byteBuffer.putShort((i + 1) * 2, data);
             }
         }
         byteBuffer.rewind();
         return byteBuffer;
     }
2 适用于自己的tts引擎
   tts放入app进程会受当前app的业务影响,导致tts 不稳定,尤其是导航app,大量的cpu,内存占用是常有的事,可单独放到一个独立进程里,并且启动个前台服务提高优先级。
   怎么两个进程沟通呢,由于是低频的沟通,直接广播即可。
3 不固定位置的破音:直接控制tts解析出来的数据块
    原理:破音由于系统处理的数据不足,或数据塞入间隔时间过长过短,我们这里直接控制每次写入的数据大小及间隔数据:
    详细看下代码(系统不同,代码效果也不一样,要和系统tts端配合,而且要能拿到tts解析数据,我们是自己的tts引擎):
public class AudioTrackManager {
     public static final String TAG = "AudioTrackManager";
     private AudioTrack audioTrack;
     private static AudioTrackManager mInstance;
     private int bufferSize;
     private byte[] simpleBytes = null;
     private int writeRate = 180;
     private int pushRate = 90;
     //系统一次处理的数据块的最小值,小于的话,就会破音
     private static int RateSize = 1900;
    private SyncStack syncStack = new SyncStack();
     private long oldTime = 0;
     private ExecutorService pool = Executors.newSingleThreadExecutor();
    //类似生产者,消费者的一个读写类(每写一次,都给一次取的机会,目的是不耽误取出播报的节奏)
     class SyncStack {
        LinkedBlockingQueue<byte[]> datas = new LinkedBlockingQueue<byte[]>();
         long oldTime = 0;
        public void clearData(){
             datas.clear();
         }
        public synchronized void push(byte[] data) {
             try {
                 datas.put(data);
                 long time  = System.currentTimeMillis()-oldTime;
                 //空出机会给写入线程机会
                 if (time > pushRate) {
                     time = 5;
                 } else {
                     time = pushRate - time;
                 }
                if(time>0) {
                     wait(time);
                 }
                 oldTime = System.currentTimeMillis();
             } catch (InterruptedException e) {
                 e.printStackTrace();
             }
 //            this.notify();
         }
        public synchronized byte[] pop() throws InterruptedException {
             if (datas == null || datas.size() == 0) {
                 //50ms后不再等待数据,自动结束流程
                 if (datas == null || datas.size() == 0) {
                     wait(50);
                 }
                 if(datas==null||datas.size()==0) {
                     return null;
                 }
             }
             return datas.take();
         }
     }
    public AudioTrackManager() {
         bufferSize = AudioTrack.getMinBufferSize(8000, AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_PCM_16BIT);
         audioTrack = new AudioTrack(AudioPolicyManager.STREAM_NAVI, 8000, AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_PCM_16BIT, bufferSize, AudioTrack.MODE_STREAM);
     }
    private void initTrack() {
         if (audioTrack == null) {
             bufferSize = AudioTrack.getMinBufferSize(8000, AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_PCM_16BIT);
             audioTrack = new AudioTrack(AudioPolicyManager.STREAM_NAVI, 8000, AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_PCM_16BIT, bufferSize, AudioTrack.MODE_STREAM);
         }
     }
    public static AudioTrackManager getInstance() {
         if (mInstance == null) {
             synchronized (AudioTrackManager.class) {
                 if (mInstance == null) {
                     mInstance = new AudioTrackManager();
                 }
             }
         }
         return mInstance;
     }
    public void startReady() {
         initTrack();
         if(syncStack!=null) {
             syncStack.clearData();
         }else{
             syncStack = new SyncStack();
         }
     }
    //System.arraycopy()方法
     public static byte[] byteMerger(byte[] bt1, byte[] bt2) {
         byte[] bt3 = new byte[bt1.length + bt2.length];
         System.arraycopy(bt1, 0, bt3, 0, bt1.length);
         System.arraycopy(bt2, 0, bt3, bt1.length, bt2.length);
         return bt3;
     }
     /**
      * 停止播放
      */
     public void stopPlay() {
         try {
             //destroyThread();
             Log.v(TAG, "yangtest--stopTTS");
             if(syncStack!=null){
                 syncStack.clearData();
             }
             if (audioTrack != null) {
                 if (audioTrack.getState() == AudioRecord.STATE_INITIALIZED) {
                     audioTrack.stop();
                 }
                 if (audioTrack != null) {
                     audioTrack.release();
                 }
                 audioTrack = null;
             }
         } catch (Exception e) {
             e.printStackTrace();
         }
     }
    //tts 服务会不停的传过来解析出来的据
     public void startPush(byte[] data) {
         syncStack.push(data);
     }
     //启动播报线程
     public void startPop() {
         Log.e("yangtest","startpop-bufferSize-"+bufferSize);
         pool.execute(
                new Runnable(){
public void run() {
                       android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_AUDIO);
                         try {
                             //等待先写入数据一定的数据,防止进来就破音
                             Thread.sleep(getStartTime());
                         } catch (InterruptedException e) {
                             e.printStackTrace();
                         }
                        audioTrack.play();
                         try {
                             while ((simpleBytes = syncStack.pop()) != null) {
                                while (simpleBytes.length < RateSize) {
                                     try {
                                         //一次取的不够,先等待最小间隔时间再操作
                                         Thread.sleep(writeRate);
                                     } catch (InterruptedException e) {
                                         e.printStackTrace();
                                     }
                                     byte[] temp = syncStack.pop();
                                     if (temp != null) {
                                         simpleBytes = byteMerger(simpleBytes, temp);
                                     } else {
                                         Log.e("yangtest", "no-data");
                                         break;
                                     }
                                 }
                                 startWrite();
                             }
                         } catch (InterruptedException e) {
                             e.printStackTrace();
                         }
                        if (endPlay != null) {
                             endPlay.onEnd();
                         }
                     }
                });
     }
     /**
      * 启动播放线程
      */
     private void startWrite() {
         //需先等待最小的间隔时间,保持播报节奏
         long timelen = System.currentTimeMillis() - oldTime;
         if (timelen < writeRate) {
             try {
                 Thread.sleep(writeRate - timelen);
             } catch (InterruptedException e) {
                 e.printStackTrace();
             }
         }
         oldTime = System.currentTimeMillis();
         audioTrack.write(simpleBytes, 0, simpleBytes.length);
         simpleBytes = null;
     }
    public long getStartTime(){
         int txtLen = BdTTSPlayer.speechs.length();
         int len = 60 + txtLen * 10;
         return len;
     }
    public void setEndPlay(EndPlay endPlay) {
         this.endPlay = endPlay;
     }
EndPlay endPlay;
    interface EndPlay {
         public void onEnd();
     }
 }
 该方案需要自己调时间间隔值,没有一个固定的答案。