使用AndroidTrack播放高频声音:归一化混音(Java)

无论是使用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();
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值