123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167 |
- import { ref, Ref, watch, onUnmounted, computed, onMounted } from 'vue';
- import tickWav from './audio/tick.wav';
- import tockWav from './audio/tock.wav';
- /* 播放相关 */
- export default function useMetronome(
- beatVal: Ref<string>,
- beatSymbol: Ref<string>
- ) {
- /* 音量 */
- const volumeNum = ref(100);
- watch(volumeNum, () => {
- changeVolume(volumeNum.value / 100);
- });
- /* 播放状态 */
- const playState = ref<'play' | 'pause'>('pause');
- /* 速度 */
- const speedNum = ref(90);
- /* 音频hooks */
- const { start, stop, changeVolume } = useHandleAudio([tickWav, tockWav]);
- onUnmounted(() => {
- pausePlay();
- });
- // 开始播放
- async function startPlay() {
- (await start(computeTimeArr.value, {
- volume: volumeNum.value / 100,
- playbackRate: speedNum.value / 60
- })) && (playState.value = 'play');
- }
- //暂停播放
- function pausePlay() {
- stop();
- playState.value = 'pause';
- }
- const computeTimeArr = computed(() => {
- if (beatSymbol.value === '1') {
- return beatVal.value.split('-');
- }
- return beatSymbol.value.split('-');
- });
- return {
- volumeNum,
- playState,
- speedNum,
- startPlay,
- pausePlay
- };
- }
- import Crunker from 'crunker';
- function useHandleAudio(files: [File | Blob | string, File | Blob | string]) {
- const crunker = new Crunker();
- async function handleBatetimeToAudio(
- files: [File | Blob | string, File | Blob | string],
- timeArr: string[],
- playbackRate: number
- ) {
- try {
- const buffersArr = await crunker.fetchAudio(...files);
- const tickAudioBuff = buffersArr[0];
- const tockAudioBuff = buffersArr[1];
- let mergeAudio: AudioBuffer | undefined;
- /* 处理音频合并 */
- timeArr.map((time, index) => {
- const timeNum = Number(time);
- let nowBuff =
- index === 0 && timeNum !== 0 ? tickAudioBuff : tockAudioBuff;
- /* 当速度过快时候 响的时候大于整个拍子时候 对响进行裁剪 当间隔小于响的时候也进行裁剪 */
- if (
- 1 / playbackRate - nowBuff.duration * timeArr.length <= 0 ||
- (timeNum || 1) / playbackRate - nowBuff.duration <= 0
- ) {
- nowBuff = crunker.sliceAudio(
- nowBuff,
- 0,
- nowBuff.duration / playbackRate,
- 0,
- 0.12
- );
- }
- mergeAudio
- ? (mergeAudio = crunker.concatAudio([mergeAudio, nowBuff]))
- : (mergeAudio = nowBuff);
- mergeAudio = crunker.padAudio(
- mergeAudio,
- mergeAudio.duration - 0.01, // 预留0.01的安全距离 他这里有bug
- (timeNum || 1) / playbackRate - nowBuff.duration
- );
- });
- return mergeAudio;
- } catch (err) {
- console.log(err);
- return undefined;
- }
- }
- const audioCtx = crunker.context;
- let audioSourceNode: AudioBufferSourceNode | null;
- let audioGainNode: GainNode | null;
- async function start(
- timeArr: string[],
- opt: { volume: number; playbackRate: number }
- ) {
- const buffer = await handleBatetimeToAudio(
- files,
- timeArr,
- opt.playbackRate
- );
- if (buffer) {
- audioSourceNode = audioCtx.createBufferSource();
- audioSourceNode.buffer = buffer;
- audioGainNode = audioCtx.createGain();
- audioSourceNode.connect(audioGainNode);
- audioGainNode.connect(audioCtx.destination);
- audioGainNode.gain.value = opt.volume;
- audioSourceNode.loop = true;
- audioSourceNode.start();
- return true;
- } else {
- return false;
- }
- }
- function stop() {
- audioSourceNode?.stop();
- audioSourceNode = null;
- audioGainNode = null;
- }
- function changeVolume(volume: number) {
- audioGainNode && (audioGainNode.gain.value = volume);
- }
- return {
- start,
- stop,
- changeVolume
- };
- }
- // 缓存
- const localStorageName = 'metronomePos';
- export function getCachePos(
- useId: string
- ): null | undefined | Record<string, any> {
- const localCachePos = localStorage.getItem(localStorageName);
- if (localCachePos) {
- try {
- return JSON.parse(localCachePos)[useId + localStorageName];
- } catch {
- return null;
- }
- }
- return null;
- }
- export function setCachePos(useId: string, pos: Record<string, any>) {
- const localCachePos = localStorage.getItem(localStorageName);
- let cachePosObj: Record<string, any> = {};
- if (localCachePos) {
- try {
- cachePosObj = JSON.parse(localCachePos);
- } catch {
- //
- }
- }
- cachePosObj[useId + localStorageName] = pos;
- localStorage.setItem(localStorageName, JSON.stringify(cachePosObj));
- }
|