音频-剪辑

object AudioEditUtil {

    private const val TAG = "AudioEditUtil"

    /**
     * 裁剪音频
     * @param audio 音频信息
     * @param cutStartTime 裁剪开始时间
     * @param cutEndTime 裁剪结束时间
     */
    fun cutAudio(audio: Audio?, cutStartTime: Float, cutEndTime: Float): Boolean {
        MLog.d(
            TAG,
            "cutAudio: audio = $audio, cutStartTime = $cutStartTime, cutEndTime = $cutEndTime"
        )
        if (audio == null) {
            return false
        }
        if (cutStartTime == 0f && cutEndTime == audio.duration / 1000f) {
            return false
        }
        if (cutStartTime >= cutEndTime) {
            return false
        }
        // WAV 文件路径、采样率、声道数、采样数
        val srcWavePath = audio.path
        val sampleRate = audio.sampleRate
        val channels = audio.channel
        val bitNum = audio.bitNum
        // 输入流
        var srcFis: RandomAccessFile? = null
        var newFos: RandomAccessFile? = null
        // 临时存储路径
        val tempOutPath = "${srcWavePath.substring(0, srcWavePath.lastIndexOf("."))}${AudioConstant.SUFFIX_TEMP}"
        MLog.d(
            TAG,
            "cutAudio: srcWavePath = $srcWavePath, sampleRate = $sampleRate, channels = $channels, bitNum = $bitNum, tempOutPath = $tempOutPath"
        )

        try {
            // 创建输入流
            srcFis = RandomAccessFile(srcWavePath, "rw")
            newFos = RandomAccessFile(tempOutPath, "rw")
            // 源文件开始读取位置,结束读取文件,读取数据的大小
            val cutStartPos = getPositionFromWave(cutStartTime, sampleRate, channels, bitNum)
            val cutEndPos = getPositionFromWave(cutEndTime, sampleRate, channels, bitNum)
            val contentSize = cutEndPos - cutStartPos
            MLog.d(
                TAG,
                "cutAudio: cutStartPos = $cutStartPos, cutEndPos = $cutEndPos, contentSize = $contentSize"
            )
            // 复制 wav head 字节数据
            val headerData =
                AudioEncodeUtil.getWaveHeader(contentSize.toLong(), sampleRate, channels, bitNum)
            copyHeadData(headerData, newFos)
            // 移动到文件开始读取处
            srcFis.seek((AudioConstant.WAVE_HEAD_SIZE + cutStartPos).toLong())
            // 复制裁剪的音频数据
            copyData(srcFis, newFos, contentSize)
        } catch (e: Exception) {
            MLog.d(TAG, "cutAudio: error =$e")
            e.printStackTrace()
            return false
        } finally {
            // 关闭输入流
            MLog.d(TAG, "cutAudio: 关闭流")
            if (srcFis != null) {
                try {
                    srcFis.close()
                } catch (e: IOException) {
                    e.printStackTrace()
                }
            }
            if (newFos != null) {
                try {
                    newFos.close()
                } catch (e: IOException) {
                    e.printStackTrace()
                }
            }
        }
        return true
    }

    /**
     * 获取 WAVE 文件某个时间对应的数据位置
     * @param time 时间
     * @param sampleRate 采样率
     * @param channels 声道数
     * @param bitNum 采样位数
     * @return
     */
    private fun getPositionFromWave(time: Float, sampleRate: Int, channels: Int, bitNum: Int): Int {
        MLog.d(
            TAG,
            "getPositionFromWave: time = $time, sampleRate = $sampleRate, channels = $channels, bitNum = $bitNum"
        )
        val byteNum = bitNum / 8
        var position = (time * sampleRate * channels * byteNum).toInt()
        position = position / (byteNum * channels) * (byteNum * channels)
        return position
    }

    /**
     * 复制wav header 数据
     *
     * @param headerData wav header 数据
     * @param fos 目标输出流
     */
    private fun copyHeadData(headerData: ByteArray, fos: RandomAccessFile) {
        try {
            fos.seek(0)
            fos.write(headerData)
        } catch (ex: Exception) {
            ex.printStackTrace()
        }
    }

    /**
     * 复制数据
     *
     * @param fis 源输入流
     * @param fos 目标输出流
     * @param copySize 复制大小
     */
    private fun copyData(fis: RandomAccessFile, fos: RandomAccessFile, copySize: Int) {
        MLog.d(TAG, "copyData: copySize = $copySize")
        var buffer = ByteArray(2048)
        var length: Int
        var totalReadLength = 0
        try {
            while (fis.read(buffer).also { length = it } != -1) {
                MLog.d(TAG, "copyData: 写入数据 length = $length")
                fos.write(buffer, 0, length)
                totalReadLength += length
                val remainSize = copySize - totalReadLength
                if (remainSize <= 0) {
                    // 读取指定位置完成
                    MLog.d(TAG, "copyData: 读取指定位置完成")
                    break
                } else if (remainSize < buffer.size) {
                    // 离指定位置的大小小于 buffer 的大小,换 remainSize 的 buffer
                    MLog.d(TAG, "copyData: 离指定位置的大小小于 buffer 的大小,换 remainSize 的 buffer")
                    buffer = ByteArray(remainSize)
                }
            }
        } catch (e: Exception) {
            MLog.d(TAG, "copyData: error = $e")
            e.printStackTrace()
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值