无论是使用openal,soundpool,在播放高频声音时都会出现没声音的情况,只能使用音轨自己混音的方式来实现。
下方代码能跑起来,效率不高,骁龙8gen2在一秒100次刷新的情况下,50个音频混音延迟最高10ms,有什么好一点的优化思路,大神可以来指教一下
import android.media.*;
import java.util.concurrent.locks.*;
public class SoundUtil
{
private int bufferSize=0;//单次刷新缓存大小
private int tick=0;//一秒刷新多少次缓存
private AudioTrack at;
private boolean isPlay=false;
private ReentrantReadWriteLock lock;//线程锁
private byte playBuffer[][];//可以同时拥有多少个列队
private int playBufferIndex[];
private float playBufferVolume[];
private int playBufferLoop[];
//采样率,声道数量(混音设计是单声道),缓存区可以放多少个pcm,一秒刷新多少次(大于100次会出现问题,不能跟采样率整除也有问题)
public SoundUtil(int sampleRate,int channels,int bufferNum,int refTick)
{
tick=refTick;
bufferSize=sampleRate*2/tick;
playBuffer=new byte[bufferNum][];
playBufferIndex=new int[bufferNum];
playBufferVolume=new float[bufferNum];
playBufferLoop=new int[bufferNum];
at=new AudioTrack(AudioManager.STREAM_MUSIC,sampleRate,channels==1?AudioFormat.CHANNEL_OUT_MONO:AudioFormat.CHANNEL_OUT_STEREO,AudioFormat.ENCODING_PCM_16BIT,bufferSize,AudioTrack.MODE_STREAM);
at.setBufferSizeInFrames(bufferSize*2);
lock=new ReentrantReadWriteLock();
}
public SoundUtil(int sampleRate,int channels)
{
this(sampleRate,channels,32,100);
}
public void prepare()
{
if(!isPlay)
{
at.play();
isPlay=true;
new Thread()
{
public void run()
{
//局部变量可以快速解锁,而且比全局变量更快
byte buffer[]=new byte[bufferSize];
byte pcm[][]=new byte[playBuffer.length][];
float volume[]=new float[playBuffer.length];
int start[]=new int[playBuffer.length];
int end[]=new int[playBuffer.length];
float mixNum=1;
while(isPlay)
{
//写入音轨,一秒100次延迟play到write最高20ms左右,不知道为什么会这样
at.write(buffer,0,bufferSize);
buffer=new byte[bufferSize];
lock.writeLock().lock();
int pcmNum=0;
for(int x2=0,y2=playBuffer.length;x2<y2;x2++)
{
if(playBuffer[x2]!=null)
{
pcm[pcmNum]=playBuffer[x2];
volume[pcmNum]=playBufferVolume[x2];
start[pcmNum]=playBufferIndex[x2];
end[pcmNum]=playBufferIndex[x2]+bufferSize;
if(end[pcmNum]<pcm[pcmNum].length)
{
playBufferIndex[x2]+=bufferSize;
}
else
{
end[pcmNum]=pcm[pcmNum].length;
if(playBufferLoop[x2]==0)
{
playBuffer[x2]=null;
}
else
{
playBufferIndex[x2]=0;
playBufferLoop[x2]--;
}
}
pcmNum++;
}
}
lock.writeLock().unlock();
if(pcmNum==0)
{
continue;
}
//混音算法
int index=0;
for(int x=0;x<bufferSize;x+=2)
{
int temp=0;
for(int x2=0;x2<pcmNum;x2++)//多个音频流
{
byte pcm2[]=pcm[x2];
if(pcm2!=null)
{
index=x+start[x2];
if(index==end[x2])
{
pcm[x2]=null;
continue;
}
short s=(short)((pcm2[index]&0xff)+((pcm2[index+1]<<8)&0xff00));
temp+=(int)((float)s*volume[x2]);
}
}
temp=(int)(temp*mixNum);
if(temp>32767)
{
mixNum=32767f/(float)temp;
temp=32767;
}
else if(temp<-32768)
{
mixNum=-32768f/(float)temp;
temp=-32768;
}
if(mixNum<1)
{
mixNum+=(1f-mixNum)/(float)bufferSize;
}
buffer[x]=(byte)(temp&0xff);
buffer[x+1]=(byte)((temp>>8)&0xff);
}
}
}
}.start();
}
}
public void release()
{
if(isPlay)
{
isPlay=false;
at.stop();
}
at.release();
}
public int play(byte pcm[])
{
return play(pcm,1,0);
}
public int play(byte pcm[],float volume)
{
return play(pcm,volume,0);
}
public int play(byte pcm[],float volume,int loop)//loop -1为一直播放,0为播放一次
{
if(volume>0)
{
lock.writeLock().lock();
for(int x=0,y=playBuffer.length;x<y;x++)
{
if(playBuffer[x]==null)
{
playBuffer[x]=pcm;
playBufferIndex[x]=0;
playBufferVolume[x]=volume;
playBufferLoop[x]=loop;
lock.writeLock().unlock();
return x;
}
}
lock.writeLock().unlock();
}
return -1;
}
//pcm用于校验,如果已播放完就不继续
public void stop(byte pcm[],int bufferIndex)
{
if(bufferIndex>-1)
{
lock.writeLock().lock();
if(pcm==null||playBuffer[bufferIndex]==pcm)
{
playBuffer[bufferIndex]=null;
}
lock.writeLock().unlock();
}
}
public void setVolume(byte pcm[],int bufferIndex,float volume)
{
if(bufferIndex>-1)
{
lock.writeLock().lock();
if(pcm==null||playBuffer[bufferIndex]==pcm)
{
playBufferVolume[bufferIndex]=volume;
}
lock.writeLock().unlock();
}
}
}