柚子快報激活碼778899分享:js錄制音頻
柚子快報激活碼778899分享:js錄制音頻
整體思路
1、監(jiān)聽用戶onTouchStart事件,設置一個定時器記錄是否長按,然后調用JSBridge開始錄制事件 2、通過JSBridge監(jiān)聽錄音過程,拿到錄音的數據,前端用數組變量存放 3、監(jiān)聽用戶onTouchEnd松開事件,清除定時器,處理錄音數組轉換成一個文件上傳到oss
難點
難點一:將base64錄音片段轉為WAV文件 首先將一系列Base64編碼的音頻段合并成一個PCM數據流; 然后創(chuàng)建一個WAV文件的頭部信息; 最后合并WAV文件頭和PCM數據
難點二:TypedArray數據的合并 TypedArray: 存儲的是固定類型的數值數據,如整數或浮點數。 Array: 可以存儲任何類型的數據,包括數字、字符串、對象等
開始錄音
/**
* 開始錄音
*/
const handleTouchStart = (event) => {
event.preventDefault();
timerId = setTimeout(() => {
setLongPress(true);
console.log('handleTouchStart 長按了');
JSBridge(XX.startRecording', {
numberOfChannels: 1, // 聲道數
// sampleRate: 16000, // 采樣率
sampleRate: 44100, // 更改采樣率為 44100 Hz
bitsPerChannel: 16, // 位深
format: 'PCM',
}).then(() => {
setRecordStatus('dialog_listening');
});
}, 100); // 長按時長,這里設置為100ms
};
監(jiān)聽錄音過程
const onRecordChange = (event) => {
console.log(event);
const { error, param } = event || {};
const { pcm } = param || {};
const { errorCode, errorMsg } = error || {};
if (errorCode) {
Toast.show({
type: 'error',
content: `錄制失敗,${errorMsg}`,
});
baseArrayRef.current = [];
} else {
baseArrayRef.current.push(pcm);
}
};
useEffect(() => {
document.addEventListener('RecordingDataBufferTransfer', onRecordChange);
return () => {
// 清除長按定時器
if (timerId !== null) clearTimeout(timerId);
};
}, []);
結束錄制
/**
* 結束錄音
* @returns
*/
const handleTouchEnd = (event) => {
if (timerId !== null) {
clearTimeout(timerId)
timerId = null
}
if (!longPress) return;
setLongPress(false);
console.log('handleTouchEnd 松開了');
JSBridge('XX.stopRecording').then(() => {
// 移除事件監(jiān)聽器
document.removeEventListener(
'RecordingDataBufferTransfer',
onRecordChange,
);
setRecordStatus('dialog_sleep');
onMerge();
});
};
音頻波動動畫
VoiceAnimation/index.tsx
import cls from 'classnames';
import debounce from 'lodash/debounce';
import { useLayoutEffect, useMemo, useRef } from 'react';
import styles from './index.module.less';
interface IProps {
status: string;
}
export default function (props: IProps) {
const { status = 'dialog_sleep' } = props;
const list = useMemo(() => new Array(5).fill(true), []);
return (
{list.map((_, index) => (
))}
);
}
function getTransationByStatus(status: string, index?) {
return {
dialog_sleep: {
transition: 'all 0.3s',
height: '8px',
transform: 'translateY(0)',
},
dialog_idle: {
transition: 'all 0.3s',
height: '8px',
transform: 'translateY(0)',
},
dialog_listening: {
transition: 'all 0.3s',
height: '24px',
transform: index % 2 ? 'translateY(8px)' : 'translateY(-8px)',
onTransitionEnd: debounce(
(event) => {
if (
event.target.parentElement.className.indexOf('dialog_listening') ===
-1
)
return;
event.target.style.transitionDuration = '0.5s';
event.target.style.height = '24px';
event.target.style.transform =
event.target.style.transform === 'translateY(8px)'
? 'translateY(-8px)'
: 'translateY(8px)';
},
{
leading: true,
trailing: false,
},
),
},
dialog_thinking: {
transition: 'all 0.3s',
height: `${[52, 44, 36, 28, 24][index]}px`,
transform: 'translateY(0)',
onTransitionEnd: debounce(
(event) => {
if (
event.target.parentElement.className.indexOf('dialog_thinking') ===
-1
)
return;
event.target.style.transitionDuration = '0.5s';
event.target.style.height = {
'52px': '24px',
'44px': '28px',
'36px': '32px',
'32px': '36px',
'28px': '44px',
'24px': '52px',
}[event.target.style.height];
},
{
leading: true,
trailing: false,
},
),
},
dialog_responding: {
transition: 'all 0.2s',
height: `${Math.random() * (index + 1) * 10 + 24}px`,
transform: 'translateY(0)',
onTransitionEnd: debounce(
(event) => {
if (
event.target.parentElement.className.indexOf(
'dialog_responding',
) === -1
)
return;
event.target.style.transitionDuration = '0.15s';
event.target.style.height = `${Math.random() * (index + 1) * 10 + 24}px`;
},
{
leading: true,
trailing: false,
},
),
},
}[status];
}
function AnimationItem({ status, index }: { status: string; index?: number }) {
const div = useRef
useLayoutEffect(() => {
const container = div.current as HTMLDivElement;
function reset() {
container.ontransitionend = (e) => {};
container.style.transition = 'all .1s';
container.style.height = '24px';
container.style.transform = 'translateY(0)';
}
reset();
const { onTransitionEnd = () => {}, ...style } =
getTransationByStatus(status, index) || {};
container.ontransitionend = onTransitionEnd;
for (let prop in style) {
container.style[prop] = style[prop];
}
return () => {};
}, [status]);
return (
);
}
VoiceAnimation/index.module.less
.voice {
display: flex;
justify-content: center;
align-items: center;
height: 56px;
.item {
// width: 24px;
// height: 24px;
background-color: var(--TY-Text-Brand-1);
border-radius: 20px;
margin: 0 4px;
transform: translateY(0);
}
}
.loop(@n, @i: 0) when (@i <= @n) {
&:nth-child(@{i}) {
animation-delay: (@i * 0.2s);
}
.loop(@n, (@i + 1));
}
一個完整的音頻錄制——播放的例子
getUserMedia需要https,使用localhost或127.0.0.1時,可用http。
柚子快報激活碼778899分享:js錄制音頻
好文推薦
本文內容根據網絡資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點和立場。
轉載請注明,如有侵權,聯系刪除。