开源一款超级好用的mp3剪切器app
本文已授權微信公眾號:鴻洋(hongyangAndroid)在微信公眾號平臺原創首發。
app&技術介紹
該app使用了MD規范,界面風格簡潔,功能上mp3剪切鈴聲制作,實用性比較強。 功能上雖然簡潔,但是技術上該項目“麻雀雖小,五臟俱全”。
下面從技術層面上做一些簡單介紹:
- 首頁使用了CoordinatorLayout+AppBarLayout+DrawerLayout+NavigationView的經典MD設計風格。
- 項目整體采用了MVP+databinding+rxjava2+rxandroid2+dagger2框架設計,數據緩存使用了greendao。
- 音頻頻譜的繪制主要是通過Visualizer中獲取到的波形數據來進行繪制。
- 剪切功能上,mp3剪切核心功能使用了jaudiotagger jar包獲取mp3元數據獲取字節位置并進行文件io操作生成目標文件。此功能作為重點,本文后續會做詳細的說明。
- 動畫方面,歡迎頁使用了lottie動畫,如感興趣可以看這篇博客做了詳盡的步驟介紹,制作lottie動畫并應用到android項目。 項目中文件選擇頁以及關于頁面使用了屬性動畫和屬性動畫組件AVLoadingIndicatorView。
- 自定義控件,范圍選取控件CustomRangeSeekBar,不是本文重點可以看之前的博文android 自定義范圍選取控件CustomRangeSeekBar。
使用說明+gif
Step1. 選擇mp3文件
Step2. 通過滑塊選擇剪切范圍然后點擊剪切按鈕
Tips:主界面上可以看到三個按鈕,從左到右的功能分別為:
- 播放\暫停
- 切換播放的滑塊(切換當前播放的位置,前滑塊or后滑塊)
- 音樂剪切
mp3剪切實現思想
實現思想主要有兩點
- 獲取mp3開始時間(要剪切的開始時間)所在的文件字節位置及結束時間所在文件的字節位置
- 根據開始時間的字節位置和結束時間的字節位置結合源文件生成我們的目標文件
mp3剪切實現技術點
那么如何來獲取mp3開始時間所在文件的字節位置呢? 這里用到了jaudiotagger.jar。 它的主頁是這樣描述它的
Jaudiotagger is a Java API for audio metatagging. Both a common API and format specific APIs are available, currently supports reading and writing metadata for:Mp3、Flac、OggVorbis、Mp4、Aiff、Wav、Wma、Dsf
它是一個音頻元標記的java庫,可以支持mp3等特定格式進行讀寫元數據操作。
mp3剪切實現細節:
一、我們要做的事通過Jaudiotagger獲取到mp3的元數據,通過元數據取到mp3的首幀字節位置以及比特率。然后根據首幀字節位置以及比特率和開始時間可以其對應文件的字節位置。最后得到開始字節位置和結束字節位置。
可能你會問,什么是比特率?
比特率是每秒傳輸的比特(bit)數
來看我們取mp3比特率的方法看注釋
long bitRate = header.getBitRateAsNumber(); 看該方法源碼注釋如下:
通過注釋得知,此方法返回的比特率單位為kbps(每秒千字節) ,而我們需要的比特率的單位是(每毫秒位),下一步進行單位轉換計算。
這里我們需要換算它為每毫秒位數,1字節是8位,1秒是1000毫秒,千字節是1024字節,那么轉換后算到的也就是getBitRateAsNumber() *1024L / 8L / 1000L。代碼如下:
這個值就是開始時間所在文件的字節位置嗎?當然不是,我們的mp3文件當中并不只包含音樂的數據,還包含有音樂的信息頭數據。同樣我們可以從頭信息中取到我們的mp3首幀字節位置。首幀字節位置+每毫秒位為單位比特率,就是我們要的mp3開始字節位置了。代碼如下:
同理, 利用上面計算出來的開始字節beginType+時間差(剪切結束時間-開始時間)的比特率(單位為每毫秒位)就可以計算出結束的字節位置了,代碼入下:
long endIndex(截取結束字節位置) = beginIndex(截取開始字節位置) + bitRate *1024L / 8L / 1000L(比特率每毫秒位) * (endTime - beginTime)(截取的時長毫秒單位);
二、 有了開始時間的字節位置和結束時間的字節位置,那我們就可以結合源文件生成我們的目標文件拉。讀寫文件我們可以使用RandomAccessFile實現隨機的讀寫操作,通過RandomAccessFile.seek()方法調到指定位置。
- 問題&解決方案
如果我們要操作的mp3文件很大,比如我們截取的字節大小為100MB,這時候我們的app就會因為OOM直接crash掉了。
這里我的解決方案是通過一個緩存數組來限制每次讀寫的數據大小,每次操作指定大小的數據,這樣無論文件多大,我們都不會出現OOM問題啦。
到這里就結束啦,能力有限,寫的不對好的地方,請多提意見。
項目計劃講一直進行維護升級,謝謝您的關注!!!
源碼&apk
-
代碼已上傳Github
-
github APK下載
-
蒲公英 APK下載
單元測試
如果沒有手機或其他原因不方便使用app。項目中提供了單元測試和mp3文件,可以通過單元測試來體驗mp3剪切功能。
- laozi.mp3是源mp3
- test.mp3是運行完單元測試,生成的mp3文件。
- startTime、endTime為剪切的開始時間及結束時間
后續
博文被鴻洋發布后,github受到了很多關注,有人提了issue, “部分MP3文件剪切失敗”。原因是之前的mp3剪切中只是對恒定比特率做了支持,在可變比特率那一塊邏輯沒有實現,直接拋了異常。
public void generateNewMp3ByTime(String targetFileStr, long beginTime, long endTime) throws Exception {MP3File mp3 = new MP3File(this.mSourceMp3File);MP3AudioHeader header = (MP3AudioHeader) mp3.getAudioHeader();if (header.isVariableBitRate()) {throw new Exception("This is nonsupport variableBitRate!!!");} else {...} } 復制代碼可以看到之前版本并沒有支持可變比特率。這里講述一下對實現可變比特率mp3剪切的實現思想。
- 重要的一點:每幀的時間是相等的
- 公式:每幀比特大小 = ( 每幀采樣次數 × 比特率(bit/s) ÷ 8 ÷采樣率) + Padding
- mp3總比特大小 = mp3幀數*每幀比特大小
- 開始時間占總時長比例 = 開始時間/mp3總時長 、結束時間占總時長比例 = 結束時間/mp3總時長
- 開始時間對應比特 = mp3總比特大小 *開始時間占總時長比例、結束時間對應比特 = mp3總比特大小*結束時間占總時長比例
上代碼:
/*** 根據時間和源文件生成MP3文件 (源文件mp3 比特率為vbr可變比特率)** @param header* @param targetFileStr* @param beginTime* @param endTime* @throws IOException*/private void generateMp3ByTimeAndVBR(MP3AudioHeader header, String targetFileStr, long beginTime, long endTime) throws IOException {long frameCount = header.getNumberOfFrames();int sampleRate = header.getSampleRateAsNumber();int sampleCount = 1152;//header.getNoOfSample();int paddingLength = header.isPadding() ? 1 : 0;//幀大小 = ( 每幀采樣次數 × 比特率(bit/s) ÷ 8 ÷采樣率) + Padding//getBitRateAsNumber 返回的為kbps 所以要*1000float frameSize = sampleCount * header.getBitRateAsNumber() / 8f / sampleRate * 1000 + paddingLength;//獲取音軌時長int trackLengthMs = header.getTrackLength() * 1000;//開始時間與總時間的比值float beginRatio = (float) beginTime / (float) trackLengthMs;//結束時間與總時間的比值float endRatio = (float) endTime / (float) trackLengthMs;long startFrameSize = (long) (beginRatio * frameCount * frameSize);long endFrameSize = (long) (endRatio * frameCount * frameSize);//返回音樂數據的第一個字節long firstFrameByte = header.getMp3StartByte();generateTargetMp3File(targetFileStr, startFrameSize, endFrameSize, firstFrameByte);} 復制代碼感謝
- jaudiotagger
- RXJava
- RxAndroid
- greendao
- StatusBarUtil
- Dagger2
- PermissionsDispatcher
- logger
- AVLoadingIndicatorView
- baseAdapter
- CustomRangeSeekBar
License
Mp3Cutter is under CC BY-NC-SA license.
總結
以上是生活随笔為你收集整理的开源一款超级好用的mp3剪切器app的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Unity自定义美术字体(图片字体fon
- 下一篇: 路由器密码太弱,IP 被黑客利用发虐童图