Android實現(xiàn)伴奏錄音合成MP3
本文實例為大家分享了Android實現(xiàn)伴奏錄音合成MP3的具體代碼,供大家參考,具體內(nèi)容如下
基本實現(xiàn)思路如下:
1.利用android自帶的錄音類(AudioRecord)實現(xiàn)錄音.
/**
* 播放伴奏
*/
private MediaPlayer player;
/**
* 返回按鈕
*/
private ImageView btnBack;
/**
* 切換歌曲
*/
private Button btnSwitchSong;
/**
* 伴唱時長
*/
private TextView tv_recod_time;
/**
* 歌詞VIEW
*/
private LyricView lv_lyric;
/**
* 開始錄制
*/
private Button btnPlay;
/**
* 標題
*/
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;
/**
* 伴奏時間
*/
private int bztimetmp = 0;
/**
* 伴奏時間
*/
private String bztime = "";
/**
* 錄制時間
*/
private int recordTimeLength=0;
/**
* 更新伴奏時間
*/
private RecordTask rt = null;
/**
* 錄制頻率,單位hz.這里的值注意了,寫的不好,可能實例化AudioRecord對象的時候,會出錯。我開始寫成11025就不行。這取決于硬件設(shè)備
* 設(shè)置音頻采樣率,44100是目前的標準,但是某些設(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位每個樣本。保證設(shè)備支持。PCM 8位每個樣本。不一定能得到設(shè)備支持。
*/
private int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
/**
* 調(diào)整播放音量
*/
private AudioManager audioManager;
/**
* 最大音量
*/
private int maxVolume = 0;
/**
* 當前音量
*/
private int currentVolume = 0;
/**
* AudioRecord 寫入緩沖區(qū)大小
*/
protected int m_in_buf_size;
/**
* 錄制音頻對象
*/
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;
/**
* 播放音頻對象
*/
private AudioTrack mAudioTrack;
/**
* 播放的字節(jié)數(shù)組
*/
private byte[] m_out_bytes;
/**
* 錄制音頻線程
*/
private Thread record;
/**
* 播放音頻線程
*/
private Thread play;
/**
* 讓線程停止的標志
*/
private boolean flag = true;
/**
* 是否啟動回聲
*/
private boolean room_flag = true;
/***上面有個播放歌詞的組件
/***
* 初始化
*/
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);
// 實例化播放音頻對象
mRecorder = new AudioRecord(MediaRecorder.AudioSource.MIC, sampleRateInHz, channelConfig, audioFormat,
m_in_buf_size);
// 實例化一個字節(jié)數(shù)組,長度為最小緩沖區(qū)的長度
m_in_bytes = new byte[m_in_buf_size];
// 實例化一個鏈表,用來存放字節(jié)組數(shù)
m_in_q = new LinkedList<byte[]>();
// AudioTrack 得到播放最小緩沖區(qū)的大小
m_out_buf_size = AudioTrack.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat);
// 實例化播放音頻對象
mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRateInHz, channelConfig, audioFormat,
m_out_buf_size, AudioTrack.MODE_STREAM);
// 實例化一個長度為播放最小緩沖大小的字節(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) {
// 實例化播放音頻對象
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(), "啟動錄音播放1");
if (play == null||!room_flag) {
//Log.d(SingSingleActivity.this.getClass().getName(), "啟動錄音播放2");
room_flag = true;
play = new Thread(new playRecord());
// 啟動播放線程
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)實現(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)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
android?studio項目:綁定服務(wù)和線程實現(xiàn)計時器
這篇文章主要介紹了android?studio項目:綁定服務(wù)和線程實現(xiàn)計時器,完成一個秒表,具備啟停功能,通過綁定服務(wù)實現(xiàn)功能,通過Thread+handler更新界面,需要的朋友可以參考一下2021-12-12
詳解Android Service與Activity之間通信的幾種方式
這篇文章主要介紹了詳解Android Service與Activity之間通信的幾種方式,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-04-04
Android 提交或者上傳數(shù)據(jù)時的dialog彈框動畫效果
我們在使用支付寶支付的時候會看到類似這種彈框動畫效果,下面通過實例代碼給大家分享android 提交或者上傳數(shù)據(jù)時的彈框動畫效果,感興趣的的朋友參考下2017-07-07
自定義ListView實現(xiàn)拖拽ListItem項交換位置(附源碼)
本文要實現(xiàn)的是拖拽ListView的Item項,在布局方面還是用基于布局泵LayoutInflater來從不同的Layout模板拿到不同的布局然后將view返回,感興趣的朋友可以了解下哈2013-06-06
Android Studio中導(dǎo)入JNI生成的.so庫的實現(xiàn)方法
這篇文章主要介紹了Android Studio中導(dǎo)入JNI生成的.so庫的實現(xiàn)方法的相關(guān)資料,這里不僅提供實現(xiàn)方案并提供了實現(xiàn)的方法,需要的朋友可以參考下2017-07-07
Android實例代碼理解設(shè)計模式SOLID六大原則
程序設(shè)計領(lǐng)域, SOLID (單一功能、開閉原則、里氏替換、接口隔離以及依賴反轉(zhuǎn))是由羅伯特·C·馬丁在21世紀早期 引入的記憶術(shù)首字母縮略字,指代了面向?qū)ο缶幊毯兔嫦驅(qū)ο笤O(shè)計的基本原則2021-10-10

