【移動應(yīng)用開發(fā)技術(shù)】android中如何實(shí)現(xiàn)音頻裁剪_第1頁
【移動應(yīng)用開發(fā)技術(shù)】android中如何實(shí)現(xiàn)音頻裁剪_第2頁
【移動應(yīng)用開發(fā)技術(shù)】android中如何實(shí)現(xiàn)音頻裁剪_第3頁
【移動應(yīng)用開發(fā)技術(shù)】android中如何實(shí)現(xiàn)音頻裁剪_第4頁
【移動應(yīng)用開發(fā)技術(shù)】android中如何實(shí)現(xiàn)音頻裁剪_第5頁
已閱讀5頁,還剩13頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(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)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論