版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
【移動應(yīng)用開發(fā)技術(shù)】android中如何實(shí)現(xiàn)音頻裁剪
/upload/information/20200623/125/124408.png音頻編輯項(xiàng)目的整體結(jié)構(gòu)public
class
CutFragment
extends
Fragment
{
...
/**
*
裁剪音頻
*/
private
void
cutAudio()
{
String
path2
=
tvAudioPath2.getText().toString();
if(TextUtils.isEmpty(path2)){
ToastUtil.showToast("音頻路徑為空");
return;
}
float
startTime
=
Float.valueOf(etStartTime.getText().toString());
float
endTime
=
Float.valueOf(etEndTime.getText().toString());
if(startTime
<=
0){
ToastUtil.showToast("時(shí)間不對");
return;
}
if(endTime
<=
0){
ToastUtil.showToast("時(shí)間不對");
return;
}
if(startTime
>=
endTime){
ToastUtil.showToast("時(shí)間不對");
return;
}
//調(diào)用AudioTaskCreator發(fā)起音頻裁剪任務(wù)
AudioTaskCreator.createCutAudioTask(getContext(),
path2,
startTime,
endTime);
}
/**
*
接收并更新裁剪消息
*/
@Subscribe(threadMode
=
ThreadMode.MAIN)
public
void
onReceiveAudioMsg(AudioMsg
msg)
{
if(msg
!=
null
&&
!TextUtils.isEmpty(msg.msg)){
tvMsgInfo.setText(msg.msg);
mCurPath
=
msg.path;
}
}
}public
class
AudioTaskCreator
{
...
/**
*
啟動音頻裁剪任務(wù)
*
@param
context
*
@param
path
*/
public
static
void
createCutAudioTask(Context
context,
String
path,
float
startTime,
float
endTime){
Intent
intent
=
new
Intent(context,
AudioTaskService.class);
intent.setAction(ACTION_AUDIO_CUT);
intent.putExtra(PATH_1,
path);
intent.putExtra(START_TIME,
startTime);
intent.putExtra(END_TIME,
endTime);
context.startService(intent);
}
}/**
*
執(zhí)行后臺任務(wù)的服務(wù)
*/
public
class
AudioTaskService
extends
IntentService
{
private
AudioTaskHandler
mTaskHandler;
public
AudioTaskService()
{
super("AudioTaskService");
}
@Override
public
void
onCreate()
{
super.onCreate();
mTaskHandler
=
new
AudioTaskHandler();
}
/**
*
實(shí)現(xiàn)異步任務(wù)的方法
*
*
@param
intent
Activity傳遞過來的Intent,數(shù)據(jù)封裝在intent中
*/
@Override
protected
void
onHandleIntent(Intent
intent)
{
if
(mTaskHandler
!=
null)
{
mTaskHandler.handleIntent(intent);
}
}
}/**
*
*/
public
class
AudioTaskHandler
{
public
void
handleIntent(Intent
intent){
if(intent
==
null){
return;
}
String
action
=
intent.getAction();
switch
(action){
case
AudioTaskCreator.ACTION_AUDIO_CUT:
{
//裁剪
String
path
=
intent.getStringExtra(AudioTaskCreator.PATH_1);
float
startTime
=
intent.getFloatExtra(AudioTaskCreator.START_TIME,
0);
float
endTime
=
intent.getFloatExtra(AudioTaskCreator.END_TIME,
0);
cutAudio(path,
startTime,
endTime);
}
break;
//其他編輯任務(wù)
...
default:
break;
}
}
/**
*
裁剪音頻
*
@param
srcPath
源音頻路徑
*
@param
startTime
裁剪開始時(shí)間
*
@param
endTime
裁剪結(jié)束時(shí)間
*/
private
void
cutAudio(String
srcPath,
float
startTime,
float
endTime){
//具體裁剪操作
}
}音頻裁剪方法的實(shí)現(xiàn)/**
*
*/
public
class
AudioTaskHandler
{
/**
*
裁剪音頻
*
@param
srcPath
源音頻路徑
*
@param
startTime
裁剪開始時(shí)間
*
@param
endTime
裁剪結(jié)束時(shí)間
*/
private
void
cutAudio(String
srcPath,
float
startTime,
float
endTime){
String
fileName
=
new
File(srcPath).getName();
String
nameNoSuffix
=
fileName.substring(0,
fileName.lastIndexOf('.'));
fileName
=
nameNoSuffix
+
Constant.SUFFIX_WAV;
String
outName
=
nameNoSuffix
+
"_cut.wav";
//裁剪后音頻的路徑
String
destPath
=
FileUtils.getAudioEditStorageDirectory()
+
File.separator
+
outName;
//解碼源音頻,得到解碼后的文件
decodeAudio(srcPath,
destPath);
if(!FileUtils.checkFileExist(destPath)){
ToastUtil.showToast("解碼失敗"
+
destPath);
return;
}
//獲取根據(jù)解碼后的文件得到audio數(shù)據(jù)
Audio
audio
=
getAudioFromPath(destPath);
//裁剪操作
if(audio
!=
null){
AudioEditUtil.cutAudio(audio,
startTime,
endTime);
}
//裁剪完成,通知消息
String
msg
=
"裁剪完成";
EventBus.getDefault().post(new
AudioMsg(AudioTaskCreator.ACTION_AUDIO_CUT,
destPath,
msg));
}
/**
*
獲取根據(jù)解碼后的文件得到audio數(shù)據(jù)
*
@param
path
*
@return
*/
private
Audio
getAudioFromPath(String
path){
if(!FileUtils.checkFileExist(path)){
return
null;
}
if
(android.os.Build.VERSION.SDK_INT
>=
android.os.Build.VERSION_CODES.JELLY_BEAN)
{
try
{
Audio
audio
=
Audio.createAudioFromFile(new
File(path));
return
audio;
}
catch
(Exception
e)
{
e.printStackTrace();
}
}
return
null;
}
}獲取音頻文件相關(guān)信息/**
*
音頻信息
*/
public
class
Audio
{
private
String
path;
private
String
name;
private
float
volume
=
1f;
private
int
channel
=
2;
private
int
sampleRate
=
44100;
private
int
bitNum
=
16;
private
int
timeMillis;
...
@RequiresApi(api
=
Build.VERSION_CODES.JELLY_BEAN)
public
static
Audio
createAudioFromFile(File
inputFile)
throws
Exception
{
MediaExtractor
extractor
=
new
MediaExtractor();
MediaFormat
format
=
null;
int
i;
try
{
extractor.setDataSource(inputFile.getPath());
}catch
(Exception
ex){
ex.printStackTrace();
extractor.setDataSource(new
FileInputStream(inputFile).getFD());
}
int
numTracks
=
extractor.getTrackCount();
for
(i
=
0;
i
<
numTracks;
i++)
{
format
=
extractor.getTrackFormat(i);
if
(format.getString(MediaFormat.KEY_MIME).startsWith("audio/"))
{
extractor.selectTrack(i);
break;
}
}
if
(i
==
numTracks)
{
throw
new
Exception("No
audio
track
found
in
"
+
inputFile);
}
Audio
audio
=
new
Audio();
=
inputFile.getName();
audio.path
=
inputFile.getAbsolutePath();
audio.sampleRate
=
format.containsKey(MediaFormat.KEY_SAMPLE_RATE)
?
format.getInteger(MediaFormat.KEY_SAMPLE_RATE)
:
44100;
audio.channel
=
format.containsKey(MediaFormat.KEY_CHANNEL_COUNT)
?
format.getInteger(MediaFormat.KEY_CHANNEL_COUNT)
:
1;
audio.timeMillis
=
(int)
((format.getLong(MediaFormat.KEY_DURATION)
/
1000.f));
//根據(jù)pcmEncoding編碼格式,得到采樣精度,MediaFormat.KEY_PCM_ENCODING這個(gè)值不一定有
int
pcmEncoding
=
format.containsKey(MediaFormat.KEY_PCM_ENCODING)
?
format.getInteger(MediaFormat.KEY_PCM_ENCODING)
:
AudioFormat.ENCODING_PCM_16BIT;
switch
(pcmEncoding){
case
AudioFormat.ENCODING_PCM_FLOAT:
audio.bitNum
=
32;
break;
case
AudioFormat.ENCODING_PCM_8BIT:
audio.bitNum
=
8;
break;
case
AudioFormat.ENCODING_PCM_16BIT:
default:
audio.bitNum
=
16;
break;
}
extractor.release();
return
audio;
}
}public
class
AudioEditUtil
{
/**
*
裁剪音頻
*
@param
audio
音頻信息
*
@param
cutStartTime
裁剪開始時(shí)間
*
@param
cutEndTime
裁剪結(jié)束時(shí)間
*/
public
static
void
cutAudio(Audio
audio,
float
cutStartTime,
float
cutEndTime){
if(cutStartTime
==
0
&&
cutEndTime
==
audio.getTimeMillis()
/
1000f){
return;
}
if(cutStartTime
>=
cutEndTime){
return;
}
String
srcWavePath
=
audio.getPath();
int
sampleRate
=
audio.getSampleRate();
int
channels
=
audio.getChannel();
int
bitNum
=
audio.getBitNum();
RandomAccessFile
srcFis
=
null;
RandomAccessFile
newFos
=
null;
String
tempOutPath
=
srcWavePath
+
".temp";
try
{
//創(chuàng)建輸入流
srcFis
=
new
RandomAccessFile(srcWavePath,
"rw");
newFos
=
new
RandomAccessFile(tempOutPath,
"rw");
//源文件開始讀取位置,結(jié)束讀取文件,讀取數(shù)據(jù)的大小
final
int
cutStartPos
=
getPositionFromWave(cutStartTime,
sampleRate,
channels,
bitNum);
final
int
cutEndPos
=
getPositionFromWave(cutEndTime,
sampleRate,
channels,
bitNum);
final
int
contentSize
=
cutEndPos
-
cutStartPos;
//復(fù)制wav
head
字節(jié)數(shù)據(jù)
byte[]
headerData
=
AudioEncodeUtil.getWaveHeader(contentSize,
sampleRate,
channels,
bitNum);
copyHeadData(headerData,
newFos);
//移動到文件開始讀取處
srcFis.seek(WAVE_HEAD_SIZE
+
cutStartPos);
//復(fù)制裁剪的音頻數(shù)據(jù)
copyData(srcFis,
newFos,
contentSize);
}
catch
(Exception
e)
{
e.printStackTrace();
return;
}finally
{
//關(guān)閉輸入流
if(srcFis
!=
null){
try
{
srcFis.close();
}
catch
(IOException
e)
{
e.printStackTrace();
}
}
if(newFos
!=
null){
try
{
newFos.close();
}
catch
(IOException
e)
{
e.printStackTrace();
}
}
}
//
刪除源文件,
new
File(srcWavePath).delete();
//重命名為源文件
FileUtils.renameFile(new
File(tempOutPath),
audio.getPath());
}
}計(jì)算裁剪時(shí)間點(diǎn)對應(yīng)文件中數(shù)據(jù)的位置
/**
*
獲取wave文件某個(gè)時(shí)間對應(yīng)的數(shù)據(jù)位置
*
@param
time
時(shí)間
*
@param
sampleRate
采樣率
*
@param
channels
聲道數(shù)
*
@param
bitNum
采樣位數(shù)
*
@return
*/
private
static
int
getPositionFromWave(float
time,
int
sampleRate,
int
channels,
int
bitNum)
{
int
byteNum
=
bitNum
/
8;
int
position
=
(int)
(time
*
sampleRate
*
channels
*
byteNum);
//這里要特別注意,要取整(byteNum
*
channels)的倍數(shù)
position
=
position
/
(byteNum
*
channels)
*
(byteNum
*
channels);
return
position;
}寫入wav文件頭信息/**
*
獲取Wav
header
字節(jié)數(shù)據(jù)
*
@param
totalAudioLen
整個(gè)音頻PCM數(shù)據(jù)大小
*
@param
sampleRate
采樣率
*
@param
channels
聲道數(shù)
*
@param
bitNum
采樣位數(shù)
*
@throws
IOException
*/
public
static
byte[]
getWaveHeader(long
totalAudioLen,
int
sampleRate,
int
channels,
int
bitNum)
throws
IOException
{
//總大小,由于不包括RIFF和WAV,所以是44
-
8
=
36,在加上PCM文件大小
long
totalDataLen
=
totalAudioLen
+
36;
//采樣字節(jié)byte率
long
byteRate
=
sampleRate
*
channels
*
bitNum
/
8;
byte[]
header
=
new
byte[44];
header[0]
=
'R';
//
RIFF
header[1]
=
'I';
header[2]
=
'F';
header[3]
=
'F';
header[4]
=
(byte)
(totalDataLen
&
0xff);//數(shù)據(jù)大小
header[5]
=
(byte)
((totalDataLen
>>
8)
&
0xff);
header[6]
=
(byte)
((totalDataLen
>>
16)
&
0xff);
header[7]
=
(byte)
((totalDataLen
>>
24)
&
0xff);
header[8]
=
'W';//WAVE
header[9]
=
'A';
header[10]
=
'V';
header[11]
=
'E';
//FMT
Chunk
header[12]
=
'f';
//
'fmt
'
header[13]
=
'm';
header[14]
=
't';
header[15]
=
'
';//過渡字節(jié)
//數(shù)據(jù)大小
header[16]
=
16;
//
4
bytes:
size
of
'fmt
'
chunk
header[17]
=
0;
header[18]
=
0;
header[19]
=
0;
//編碼方式
10H為PCM編碼格式
header[20]
=
1;
//
format
=
1
header[21]
=
0;
//通道數(shù)
header[22]
=
(byte)
channels;
header[23]
=
0;
//采樣率,每個(gè)通道的播放速度
header[24]
=
(byte)
(sampleRate
&
0xff);
header[25]
=
(byte)
((sampleRate
>>
8)
&
0xff);
header[26]
=
(byte)
((sampleRate
>>
16)
&
0xff);
header[27]
=
(byte)
((sampleRate
>>
24)
&
0xff);
//音頻數(shù)據(jù)傳送速率,采樣率*通道數(shù)*采樣深度/8
header[28]
=
(byte)
(byteRate
&
0xff);
header[29]
=
(byte)
((byteRate
>>
8)
&
0xff);
header[30]
=
(byte)
((byteRate
>>
16)
&
0xff);
header[31]
=
(byte)
((byteRate
>>
24)
&
0xff);
//
確定系統(tǒng)一次要處理多少個(gè)這樣字節(jié)的數(shù)據(jù),確定緩沖區(qū),通道數(shù)*采樣位數(shù)
header[32]
=
(byte)
(channels
*
16
/
8);
header[33]
=
0;
//每個(gè)樣本的數(shù)據(jù)位數(shù)
header[34]
=
16;
header[35]
=
0;
//Data
chunk
header[36]
=
'd';//data
header[37]
=
'a';
header[38]
=
't';
header[39]
=
'a';
header[40]
=
(byte)
(totalAudioLen
&
0xff);
header[41]
=
(byte)
((totalAudioLen
>>
8)
&
0xff);
head
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- Module 4 unit 1 He lives the farthest from school 說課稿2024-2025學(xué)年外研版八年級英語上冊
- 全國人教版初中信息技術(shù)七年級上冊第四單元第14課一、《檢查與測試網(wǎng)站》說課稿
- 7 多元文化 多樣魅力 第三課時(shí) 說課稿-2023-2024學(xué)年道德與法治六年級下冊統(tǒng)編版
- Starter Unit 2 Keep Tidy!Section A 1a-2e 說課稿- 2024-2025學(xué)年人教版英語七年級上冊
- 函數(shù)方程的思想-2024-2025學(xué)年初高中銜接數(shù)學(xué)說課稿
- 人教版歷史與社會八年級下冊第七單元第三課第三目《俄國改革》說課稿
- 活動三《班級成長手冊》(說課稿)-2023-2024學(xué)年三年級上冊綜合實(shí)踐活動滬科黔科版
- 等式和方程的含義 說課稿-2023-2024學(xué)年數(shù)學(xué)蘇教版五年級下冊
- 全國泰山版初中信息技術(shù)七年級下冊第四章第一節(jié)《編輯數(shù)據(jù)》說課稿
- 20 青山不老 說課稿 -2024-2025學(xué)年語文六年級上冊統(tǒng)編版
- AQ 1029-2019 煤礦安全監(jiān)控系統(tǒng)及檢測儀器使用管理規(guī)范
- 太陽能驅(qū)動的污水處理技術(shù)研究與應(yīng)用
- 未成年旅游免責(zé)協(xié)議書
- 預(yù)防保健科主任競聘課件
- 團(tuán)隊(duì)成員介紹
- 水泵行業(yè)銷售人員工作匯報(bào)
- 《流感科普宣教》課件
- 離職分析報(bào)告
- 春節(jié)家庭用電安全提示
- 醫(yī)療糾紛預(yù)防和處理?xiàng)l例通用課件
- 廚邦醬油推廣方案
評論
0/150
提交評論