android tts播报破音解决方案汇总

导航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 datas = new LinkedBlockingQueue();
        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();
    }
}
该方案需要自己调时间间隔值,没有一个固定的答案。

你可能感兴趣的:(常用知识点分类汇总,android)