Android實(shí)現(xiàn)伴奏錄音合成MP3
本文實(shí)例為大家分享了Android實(shí)現(xiàn)伴奏錄音合成MP3的具體代碼,供大家參考,具體內(nèi)容如下
基本實(shí)現(xiàn)思路如下:
1.利用android自帶的錄音類(AudioRecord)實(shí)現(xiàn)錄音.
/** * 播放伴奏 */ private MediaPlayer player; /** * 返回按鈕 */ private ImageView btnBack; /** * 切換歌曲 */ private Button btnSwitchSong; /** * 伴唱時(shí)長 */ private TextView tv_recod_time; /** * 歌詞VIEW */ private LyricView lv_lyric; /** * 開始錄制 */ private Button btnPlay; /** * 標(biāo)題 */ private TextView ivTitle; private boolean canPlay = true; private boolean isPause = false; /*** * 背景音樂模式 */ private BackgroudMusicMode mode = BackgroudMusicMode.Accompany; /** * 歌曲id */ private String songId; /** * 歌曲名稱 */ private String songName; /** * 歌手名字 */ private String singerName; /** * 伴奏文件 */ private File file; /** * 是否正在錄制 */ private boolean isStart = false; /** * 錄音狀態(tài) */ private boolean starting = false; /** * 伴奏時(shí)間 */ private int bztimetmp = 0; /** * 伴奏時(shí)間 */ private String bztime = ""; /** * 錄制時(shí)間 */ private int recordTimeLength=0; /** * 更新伴奏時(shí)間 */ private RecordTask rt = null; /** * 錄制頻率,單位hz.這里的值注意了,寫的不好,可能實(shí)例化AudioRecord對(duì)象的時(shí)候,會(huì)出錯(cuò)。我開始寫成11025就不行。這取決于硬件設(shè)備 * 設(shè)置音頻采樣率,44100是目前的標(biāo)準(zhǔn),但是某些設(shè)備仍然支持22050,16000,11025 */ private int sampleRateInHz = 44100; /** * 設(shè)置音頻的錄制的聲道CHANNEL_IN_STEREO為雙聲道,CHANNEL_CONFIGURATION_MONO為單聲道 */ private int channelConfig = AudioFormat.CHANNEL_CONFIGURATION_MONO; /** * 音頻數(shù)據(jù)格式:PCM 16位每個(gè)樣本。保證設(shè)備支持。PCM 8位每個(gè)樣本。不一定能得到設(shè)備支持。 */ private int audioFormat = AudioFormat.ENCODING_PCM_16BIT; /** * 調(diào)整播放音量 */ private AudioManager audioManager; /** * 最大音量 */ private int maxVolume = 0; /** * 當(dāng)前音量 */ private int currentVolume = 0; /** * AudioRecord 寫入緩沖區(qū)大小 */ protected int m_in_buf_size; /** * 錄制音頻對(duì)象 */ private AudioRecord mRecorder; /** * 錄入的字節(jié)數(shù)組 */ private byte[] m_in_bytes; /** * 存放錄入字節(jié)數(shù)組的大小 */ private LinkedList<byte[]> m_in_q; /** * AudioTrack 播放緩沖大小 */ private int m_out_buf_size; /** * 播放音頻對(duì)象 */ private AudioTrack mAudioTrack; /** * 播放的字節(jié)數(shù)組 */ private byte[] m_out_bytes; /** * 錄制音頻線程 */ private Thread record; /** * 播放音頻線程 */ private Thread play; /** * 讓線程停止的標(biāo)志 */ private boolean flag = true; /** * 是否啟動(dòng)回聲 */ private boolean room_flag = true; /***上面有個(gè)播放歌詞的組件 /*** * 初始化 */ private void init() { audioManager = (AudioManager)getSystemService(Context.AUDIO_SERVICE); maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_VOICE_CALL); currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL); registerHeadsetPlugReceiver(); ycApplication = (YueChangApplication) getApplication(); coverDao = new CoverDao(getApplicationContext()); Bundle bundle = getIntent().getExtras(); songId = bundle.getString("songId"); songName = bundle.getString("songName"); singerName = bundle.getString("singerName"); if (songId != null) { // AudioRecord 得到錄制最小緩沖區(qū)的大小 m_in_buf_size = AudioRecord.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat); // 實(shí)例化播放音頻對(duì)象 mRecorder = new AudioRecord(MediaRecorder.AudioSource.MIC, sampleRateInHz, channelConfig, audioFormat, m_in_buf_size); // 實(shí)例化一個(gè)字節(jié)數(shù)組,長度為最小緩沖區(qū)的長度 m_in_bytes = new byte[m_in_buf_size]; // 實(shí)例化一個(gè)鏈表,用來存放字節(jié)組數(shù) m_in_q = new LinkedList<byte[]>(); // AudioTrack 得到播放最小緩沖區(qū)的大小 m_out_buf_size = AudioTrack.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat); // 實(shí)例化播放音頻對(duì)象 mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRateInHz, channelConfig, audioFormat, m_out_buf_size, AudioTrack.MODE_STREAM); // 實(shí)例化一個(gè)長度為播放最小緩沖大小的字節(jié)數(shù)組 m_out_bytes = new byte[m_out_buf_size]; record = new Thread(new recordSound()); // if(ycApplication.isHeadsetplug()){ // }else{ // m_out_trk = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRateInHz, channelConfig, audioFormat, // m_out_buf_size, AudioTrack.MODE_STREAM); // } } } /** * * 類描述:錄音線程 * * @version 1.0 */ class recordSound implements Runnable { @Override public void run() { // 初始化輸出流 DataOutputStream dos = null; try { File audioFile = new File(SongUtil.getRecordSingPCMPath(songId)); // 初始化輸出流 dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(audioFile))); byte[] bytes_pkg; if (mRecorder.getState() == AudioRecord.STATE_UNINITIALIZED) { // 實(shí)例化播放音頻對(duì)象 mRecorder = new AudioRecord(MediaRecorder.AudioSource.MIC, sampleRateInHz, channelConfig, audioFormat, m_in_buf_size); } // 開始錄音 mRecorder.startRecording(); while (flag) { int size = mRecorder.read(m_in_bytes, 0, m_in_buf_size); bytes_pkg = m_in_bytes.clone(); if (m_in_q.size() >= 2) { m_in_q.removeFirst(); } m_in_q.add(bytes_pkg); if ((ycApplication.isHeadsetplug() && ycApplication.isOpenInEarphone()) || (!ycApplication.isHeadsetplug() && ycApplication.isOpenInSpeaker())) { //Log.d(SingSingleActivity.this.getClass().getName(), "啟動(dòng)錄音播放1"); if (play == null||!room_flag) { //Log.d(SingSingleActivity.this.getClass().getName(), "啟動(dòng)錄音播放2"); room_flag = true; play = new Thread(new playRecord()); // 啟動(dòng)播放線程 play.start(); } } else { if(room_flag||play != null){ //Log.d(SingSingleActivity.this.getClass().getName(), "關(guān)閉錄音播放1"); room_flag = false; if (play != null) { play.interrupt(); } play = null; } } // 寫入PCM文件 dos.write(bytes_pkg, 0, size); dos.flush(); } } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } finally { try { // 關(guān)閉錄音 if (mRecorder != null) { try { if (mRecorder.getState() == AudioRecord.STATE_INITIALIZED) { // 關(guān)閉錄音 mRecorder.stop(); mRecorder.release(); } } catch (Exception e2) { // TODO: handle exception } } if (dos != null) { dos.close(); } } catch (Exception e2) { // TODO: handle exception e2.printStackTrace(); } } } }
2.錄音完成后,調(diào)用開源工具(Mad)實(shí)現(xiàn)PCM合成輸出到MP3文件.
主要調(diào)用的合成方法:
/*** * 方法描述:本地方法調(diào)用JNI合并mp3PCM與sourcePCM * @param sourcePCM * @param mp3PCM * @param mixPCM * @return */ public static native int mix2PCMToPCM(String sourcePCM, String mp3PCM, String mixPCM); String recordPCMPath = SongUtil.getRecordSingPCMPath(songId); //錄音生成的PCM文件 String accompanyPCMPath = SongUtil.getAccompanySongPCMPath(songId); //伴奏解碼生成的PCM文件 String mixPCMPath = SongUtil.getMixSingPCMPath(songId); //合成后的PCM文件 String mixMP3Path = SongUtil.getMixSingMp3Path(songId); //合成后的MP3文件 // 混音 int code = SongEncodeUtil.mix2PCMToPCM(recordPCMPath, accompanyPCMPath, mixPCMPath); if (code == 0) { // 轉(zhuǎn)換混合后音頻格式 TO mp3 int i = SimpleLame.convert(mixPCMPath, mixMP3Path, m_in_buf_size); Log.i(SingSingleActivity.this.getClass().getName(), "轉(zhuǎn)換" + i + "混音完成"); saveMp3File(mixMP3Path); }
以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
android?studio項(xiàng)目:綁定服務(wù)和線程實(shí)現(xiàn)計(jì)時(shí)器
這篇文章主要介紹了android?studio項(xiàng)目:綁定服務(wù)和線程實(shí)現(xiàn)計(jì)時(shí)器,完成一個(gè)秒表,具備啟停功能,通過綁定服務(wù)實(shí)現(xiàn)功能,通過Thread+handler更新界面,需要的朋友可以參考一下2021-12-12詳解Android Service與Activity之間通信的幾種方式
這篇文章主要介紹了詳解Android Service與Activity之間通信的幾種方式,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-04-04Android 提交或者上傳數(shù)據(jù)時(shí)的dialog彈框動(dòng)畫效果
我們?cè)谑褂弥Ц秾氈Ц兜臅r(shí)候會(huì)看到類似這種彈框動(dòng)畫效果,下面通過實(shí)例代碼給大家分享android 提交或者上傳數(shù)據(jù)時(shí)的彈框動(dòng)畫效果,感興趣的的朋友參考下2017-07-07自定義ListView實(shí)現(xiàn)拖拽ListItem項(xiàng)交換位置(附源碼)
本文要實(shí)現(xiàn)的是拖拽ListView的Item項(xiàng),在布局方面還是用基于布局泵LayoutInflater來從不同的Layout模板拿到不同的布局然后將view返回,感興趣的朋友可以了解下哈2013-06-06Android Studio中導(dǎo)入JNI生成的.so庫的實(shí)現(xiàn)方法
這篇文章主要介紹了Android Studio中導(dǎo)入JNI生成的.so庫的實(shí)現(xiàn)方法的相關(guān)資料,這里不僅提供實(shí)現(xiàn)方案并提供了實(shí)現(xiàn)的方法,需要的朋友可以參考下2017-07-07Android實(shí)現(xiàn)PDF預(yù)覽打印功能
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)PDF預(yù)覽打印功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-12-12Android實(shí)例代碼理解設(shè)計(jì)模式SOLID六大原則
程序設(shè)計(jì)領(lǐng)域, SOLID (單一功能、開閉原則、里氏替換、接口隔離以及依賴反轉(zhuǎn))是由羅伯特·C·馬丁在21世紀(jì)早期 引入的記憶術(shù)首字母縮略字,指代了面向?qū)ο缶幊毯兔嫦驅(qū)ο笤O(shè)計(jì)的基本原則2021-10-10