本文主要是介绍ExoPlayer 漫谈之Sonic调整音量,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
提一个问题:如何在播放视频的时候调整声音的大小?
我们使用Android手机播放视频的时候,发现声音大了,我们手动调低音量;发现声音小了,我们手动调高音量。
这个过程中,都要依赖手动,如果你在不断地刷短视频的时候,如果需要用户不断地手动调整音量键,那这个体验是不能忍受的。
这对我们提了一个要求:我们能在解码音频流的时候通过矩阵运算调整音频原始数据的大小,达到调整音量的目的?
这个思路是可行的,接下来我们分析一下声音的特征,进而给出如何做的方式。
声音的三个特征:
- 音调:声音频率的高低叫做音调(Pitch),是声音的三个主要的主观属性,即音量(响度)、音调、音色(也称音品) 之一。表示人的听觉分辨一个声音的调子高低的程度。音调主要由声音的频率决定,同时也与声音强度有关
- 响度:人主观上感觉声音的大小(俗称音量),由“振幅”(amplitude)和人离声源的距离决定,振幅越大响度越大,人和声源的距离越小,响度越大。(单位:分贝dB)
- 音色:又称音品,波形决定了声音的音色。声音因不同物体材料的特性而具有不同特性,音色本身是一种抽象的东西,但波形是把这个抽象直观的表现。音色不同,波形则不同。典型的音色波形有方波,锯齿波,正弦波,脉冲波等。不同的音色,通过波形,完全可以分辨的。
声波的振幅表示声音的音量大小:
波长长短是衡量声音音调的因素:
音色主要和声波的波纹有关:
这儿我们要调整的是响度,就是调整声音的振幅,学过矩阵运算的同学都知道,声音振幅的调整通过简单的矩阵运算就能实现。 这儿先推荐一下ijkplayer调整声音的做法: ijkplayer 开源项目分析(十二)filter改变声音音量 这是ffmpeg提供的做法,通过在filter中传入af : volume=3dB 来动态调整音量的目的。
借助ffmpeg的强大后盾,实现这个功能自然不难,但是一些使用MediaCodec的播放器,能实现这个功能吗? 我们要在ExoPlayer中实现动态调整声音振幅的功能。
DefaultAudioSink.java是ExoPlayer中播放音频的控制类。 其中提供了多种AudioProcessor来处理音频数据。 AudioProcessor中主要函数:
AudioFormat configure(AudioFormat inputAudioFormat) throws UnhandledAudioFormatException;boolean isActive();void queueInput(ByteBuffer buffer);void queueEndOfStream();ByteBuffer getOutput();boolean isEnded();void flush();void reset();
复制代码
- configure : 配置当前的AudioFormat
- isActive : 当前的AudioProcessor是否可用
- queueInput : 输入input buffer数据,这个ByteBuffer是原始数据
- queueEndOfStream:当前队列中已经没有数据了
- getOutput:处理完的ByteBuffer数据,送给DefaultAudioSink,开始AudioTrack播放
- ChannelMappingAudioProcessor
- FloatResamplingAudioProcessor:将24-bit和32-bit 的整型audio转化为32-bit的浮点型audio,整型和浮点型占用的位宽不一样,浮点型表现出现的声音更加细腻
- ResamplingAudioProcessor:将其他位数的audio数据重采样位16-bit的audio数据
- SilenceSkippingAudioProcessor:跳过静音部分
- TeeAudioProcessor
- TrimmingAudioProcessor
SonicAudioProcessor很重要了,倍速和调整声音振幅都依赖他。SonicAudioProcessor是根据Sonic算法来调整pitch、speed、volume变化时的声音处理。
我们这里只谈如何调整声音振幅: https://github.com/JeffMony/PlayerSDK/commit/ed9c12f169bb2bdd62978e7fecbfcb0972ece234
private void processStreamInput() {// Resample as many pitch periods as we have buffered on the input.int originalOutputFrameCount = outputFrameCount;float s = speed / pitch;float r = rate * pitch;if (s > 1.00001 || s < 0.99999) {changeSpeed(s);} else {copyToOutput(inputBuffer, 0, inputFrameCount);inputFrameCount = 0;}if (r != 1.0f) {adjustRate(r, originalOutputFrameCount);}if(volume != 1.0f) {// Adjust output volume.scaleSamples(outputBuffer, originalOutputFrameCount, outputFrameCount - originalOutputFrameCount,volume);}}private void scaleSamples(short samples[],int position,int numSamples,float volume) {int fixedPointVolume = (int)(volume * 4096.0f);int start = position * channelCount;int stop = start + numSamples * channelCount;for(int xSample = start; xSample < stop; xSample++) {int value = (samples[xSample] * fixedPointVolume) >> 12;if(value > 32767) {value = 32767;} else if(value < -32767) {value = -32767;}samples[xSample] = (short)value;}}
复制代码
这里的volume是更新振幅的倍数。例如volume=1.2f,将振幅从原来的值调整为1.2倍。
声音的分贝如何计算? result = 20 * log(Cur/Max) Cur表示当前振幅 Max表示最大振幅 所以声音的分贝总是负的(Android平台下是的)。 volume(dB) = 20 * log(Cur / Max)
- volume 表示计算出来的分贝值
- Max表示最大振幅
- Cur表示当前振幅
输入的参数有两个: MeanVolume , BaseVolume
- MeanVolume: 平均分贝
- BaseVolume: 基准分贝
BaseVolume - MeanVolume = result
20*log(CurBase/Max) - 20*log(Cur/Max) = result
20*log(CurBase/Cur)=result
CurBase/Cur = 10^(result/20)
CurBase = Cur * 10^(result/20)
复制代码
我们最终关注的是 (10^(result/20))
如果要设置到ExoPlayer中的数也是 (10^(result/20))
本文所讲源码均来自项目:https://github.com/JeffMony/PlayerSDK
作者:码上就说
链接:https://juejin.cn/post/6917227403207000078
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
这篇关于ExoPlayer 漫谈之Sonic调整音量的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!