交河网站建设,wordpress 文章字体颜色,注册公司流程视频,广州番禺房价2022年最新房价导航app引导中经常遇到破音#xff0c;这里也将之前经历过的方案收集以下#xff0c;方便以后选择#xff1a;
1 对于开始和结尾破音#xff1a; 可以用升降音来处理 两种方式 一种是 直接对开始和结束的时间段进行音量直接渐进改变。这里配的是200ms的渐变。 VolumeSha…导航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 { LinkedBlockingQueuebyte[] datas new LinkedBlockingQueuebyte[](); 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(time0) { 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(datasnull||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(); } } 该方案需要自己调时间间隔值没有一个固定的答案。