| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139 |
- /**
- * 音频合成节拍器
- */
- import Crunker from './crunker'
- import tickMp3 from './tick.mp3'
- import tockMp3 from './tock.mp3'
- import { getUploadSign, onOnlyFileUpload } from '@/utils/oss-file-upload'
- import { ref } from 'vue'
- const crunker = new Crunker()
- type musicSheetType = {
- audioFileUrl: string
- audioBeatMixUrl: null | string
- solmizationFileUrl: null | string
- solmizationBeatUrl: null | string
- }
- type taskAudioType = {
- obj: musicSheetType
- type: 'audioFileUrl' | 'solmizationFileUrl'
- audioBuff?: AudioBuffer
- }[]
- // 节拍器数据
- export const beatState = {
- times: [] as number[][],
- totalIndex: ref(0), // 总共需要处理的音频个数
- currentIndex: ref(0) // 当前处理了多少条数据
- }
- // 节拍器音源
- let tickMp3Buff: null | AudioBuffer = null
- let tockMp3Buff: null | AudioBuffer = null
- export default async function audioMergeBeats({
- musicSheetAccompanimentList,
- musicSheetSoundList
- }: {
- musicSheetAccompanimentList: musicSheetType[]
- musicSheetSoundList: musicSheetType[]
- }) {
- if (!beatState.times.length) return
- try {
- if (musicSheetSoundList.length + musicSheetAccompanimentList.length > 0) {
- // 扁平化数据 生成任务队列
- const taskAudio: taskAudioType = []
- ;[...musicSheetSoundList, ...musicSheetAccompanimentList].map((item) => {
- taskAudio.push({
- obj: item,
- type: 'audioFileUrl'
- })
- item.solmizationFileUrl && // 有唱名加上唱名
- taskAudio.push({
- obj: item,
- type: 'solmizationFileUrl'
- })
- })
- beatState.totalIndex.value = taskAudio.length
- /* 加载节拍器 */
- if (!tickMp3Buff || !tockMp3Buff) {
- const [tickMp3Bf, tockMp3Bf] = await crunker.fetchAudio(tickMp3, tockMp3)
- tickMp3Buff = tickMp3Bf
- tockMp3Buff = tockMp3Bf
- }
- /* 加上所有的音频文件 */
- await Promise.all(
- taskAudio.map(async (item) => {
- const [audioBuff] = await crunker.fetchAudio(item.obj[item.type]!)
- item.audioBuff = audioBuff
- })
- )
- /* 异步上传 */
- await new Promise((res) => {
- /* 合成音源 */
- taskAudio.map(async (item) => {
- const audioBlob = mergeBeats(item.audioBuff!)
- const url = await uploadFile(audioBlob)
- item.obj[item.type == 'audioFileUrl' ? 'audioBeatMixUrl' : 'solmizationBeatUrl'] = url
- beatState.currentIndex.value++
- if (beatState.currentIndex.value >= beatState.totalIndex.value) {
- res(null)
- }
- })
- })
- }
- } catch (err) {
- console.log('处理音频合成上传失败', err)
- }
- // 清空数据
- beatState.currentIndex.value = 0
- beatState.totalIndex.value = 0
- beatState.times = []
- }
- // 根据buffer合成音源返回blob
- function mergeBeats(audioBuff: AudioBuffer) {
- // 计算音频空白时间
- const silenceDuration = crunker.calculateSilenceDuration(audioBuff)
- const beats: AudioBuffer[] = []
- const currentTimes: number[] = []
- beatState.times.map((items) => {
- items.map((time, index) => {
- beats.push(index === 0 ? tickMp3Buff! : tockMp3Buff!)
- currentTimes.push(time + silenceDuration)
- })
- })
- //合并
- const mergeAudioBuff = crunker.mergeAudioBuffers([audioBuff, ...beats], [0, ...currentTimes])
- //转为 blob
- return crunker.audioBuffToBlob(mergeAudioBuff)
- }
- /**
- * 上传文件
- */
- async function uploadFile(audioBlob: Blob) {
- const filename = `${new Date().getTime()}.mp3`
- const { data } = await getUploadSign({
- filename,
- bucketName: 'cloud-coach',
- postData: {
- filename,
- acl: 'public-read',
- key: filename,
- unknowValueField: []
- }
- })
- const url = await onOnlyFileUpload('', {
- KSSAccessKeyId: data.KSSAccessKeyId,
- acl: 'public-read',
- file: audioBlob,
- key: filename,
- name: filename,
- policy: data.policy,
- signature: data.signature
- })
- return url
- }
|