在K歌或錄音類應用中變調是個常見需求,比如需要播出蘿莉音/大叔音等。變速播放在影視播放類應用中普遍存在,在傳統播放器Mediaplayer中這兩個功能都比較難以實現,特別在低版本SDK中,而Exoplayer作為google官方推出的Mediaplayer替代者就可以輕松實現。在前兩篇文章中向大家介紹了Exoplayer拓展FFmpeg實現音頻軟解碼和切換原伴唱功能,我們繼續在此基礎上實現變調和變速播放。
最新版本Exoplayer擴展FFmpeg音頻軟解碼保姆級教程
最新版本Exoplayer(MediaX)實現K歌原伴唱包括單音軌和雙音軌?
一·變調
首先我們回憶一下初中物理學習的聲音三大特性:音調-音色-響度
1.?音調
-
定義:音調是指聲音的高低,它是由發聲體振動的頻率決定的。
-
原理:振動頻率越高,音調越高;振動頻率越低,音調越低。例如,女高音的音調通常比男低音高,因為女高音的聲帶振動頻率更高。
-
單位:頻率的單位是赫茲(Hz),表示每秒振動的次數。
2.?響度
-
定義:響度是指聲音的強弱或大小,它是由發聲體振動的振幅決定的。
-
原理:振幅越大,聲音越響亮;振幅越小,聲音越微弱。同時,響度還與距離發聲體的遠近有關,距離越遠,響度越小。
-
單位:響度的單位是分貝(dB)。例如,安靜的圖書館聲音約為30分貝,而搖滾音樂會的聲音可能達到100分貝以上。
3.?音色
-
定義:音色是指不同物體發聲時,聲音的特色和品質。即使音調和響度相同,不同發聲體發出的聲音仍然可以通過音色進行區分。
-
原理:音色由發聲體的材料、結構和發聲方式決定。例如,小提琴和鋼琴演奏同一首曲子,音調和響度可能相似,但音色不同,因為它們的發聲結構和材料不同。
-
應用:音色是人們辨別不同樂器和人聲的重要依據。例如,我們可以通過音色區分不同人的說話聲,或者區分小提琴、大提琴和吉他等樂器的聲音。
變調就是要調節音調,加減音量就是要調節響度。Exoplayer本身并不支持調節音調,而是集成了第三方開源音效庫Sonic來實現的,源碼地址為:https://github.com/waywardgeek/sonic,Exoplayer在此基礎上封裝了SonicAudioProcessor供開發調用,源碼分析參考Android 分場景集成不同音頻倍速算法的實現。而?ijkplayer使用的是soundtouch音效庫。下面看具體實現:
1.自定義DefaultRenderersFactory,得到AudioSink
@SuppressLint("UnsafeOptInUsageError")public class PluginRenderFactory extends DefaultRenderersFactory {/*** @param context A {@link Context}.*/public PluginRenderFactory(Context context) {super(context);}@Nullable@Overrideprotected AudioSink buildAudioSink(Context context, boolean enableFloatOutput, boolean enableAudioTrackPlaybackParams) {audioSink = new DefaultAudioSink.Builder().setAudioProcessors(new AudioProcessor[]{new SonicAudioProcessor()}).setEnableAudioTrackPlaybackParams(true).build();return audioSink;// return super.buildAudioSink(context, enableFloatOutput, enableAudioTrackPlaybackParams);}@Overrideprotected void buildAudioRenderers(Context context, int extensionRendererMode, MediaCodecSelector mediaCodecSelector, boolean enableDecoderFallback, AudioSink audioSink, Handler eventHandler, AudioRendererEventListener eventListener, ArrayList<Renderer> out) {// audioSink.setPlaybackParameters(new PlaybackParameters(1.0f,0.1f));Log.i(TAG, "=audio pitch=" + audioSink.getPlaybackParameters().pitch);ffmpegAudioRenderer = new FfmpegAudioRenderer(eventHandler, eventListener, audioSink);out.add(ffmpegAudioRenderer);super.buildAudioRenderers(context, extensionRendererMode, mediaCodecSelector, enableDecoderFallback, audioSink, eventHandler, eventListener, out);}}
?2.初始化Exoplayer并設置上述自定義的Renderer
renderersFactory = new PluginRenderFactory(context).setEnableAudioTrackPlaybackParams(true)// .setEnableDecoderFallback(true).setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_ON);mExoPlayer = new ExoPlayer.Builder(context, renderersFactory).setTrackSelector(trackSelector)// .setHandleAudioBecomingNoisy(true).build();
3. 通過audioSink.setPlaybackParameters(new PlaybackParameters(1.0f, pitch));實現變調不變速,pitch取值【0.1~1.0】越大,音調越高,第一個參數是speed,可設置音頻播放速度
public void setPitch(int tone) {if (!mExoPlayer.isPlaying() || lastTone == tone)return;try {lastTone = tone;// Log.i(TAG,"=setPitch="+tone);// ffmpegAudioRenderer.setPitchMode(tone + 10);float pitch = 1.0f;if (tone >= -5 && tone <= 5) {pitch = 1.0f + 0.1f * tone;}Log.i(TAG, "=setPitch=" + tone + "=pitch=" + pitch);audioSink.pause();audioSink.setPlaybackParameters(new PlaybackParameters(1.0f, pitch));audioSink.play();} catch (Exception e) {Log.i(TAG, "=setPitch error=" + e.toString());}}
二 設置音量
方式一通過AudioSink設置:
audioSink.setVolume(defaultVol);
方式二·通過Exoplayer實例直接設置
mExoPlayer.setVolume(defaultVol);
三 變速播放
public void setSpeed(float speed) {PlaybackParameters parameters = new PlaybackParameters(speed);mExoPlayer.setPlaybackParameters(parameters);}