上一篇文章用 Streamlit 写了一个录音按钮的组件,实现了按下去时开始录音、放开结束录音的功能。但是只支持桌面端网页用鼠标点击,这次对齐进行扩展,使其能够实现在手机端按下录音的功能。
Touch 事件
在桌面端监听的是鼠标的 mousedown
/mouseup
事件,但是在移动端则是手指触屏屏幕的事件 touchstart
/touchend
,因此需要修改按钮的监听事件。另外之前采用样式中 :hover
来实现按下时按钮颜色的变化,现在使用一个变量来控制
<template>
<div class="button-container">
<button
@touchstart="startRecording"
@touchcancel="stopRecording"
@touchend="stopRecording"
@mousedown="startRecording"
@mouseup="stopRecording"
@mouseleave="stopRecording"
class="red-round-button" :style="{ backgroundColor: buttonColor }" ></button>
</div>
<audio ref="audioPlayer" controls class="audio-player"></audio>
</template>
<script setup>
...
const buttonColor = ref('red');
const dynamicStyles = computed(() => {
return {
width: props.args.width,
height: props.args.height,
backgroundColor: buttonColor
};
});
...
</script>
另外,移动端长按按钮的话会唤出 Text Selection,需要取消在监听时间时取消掉,详见 Prevent text selection on tap and hold on iOS 13 / Mobile Safari
function startRecording(event) {
event.preventDefault();
...
}
function stopRecording(event) {
event.preventDefault();
...
}
录音格式兼容
由于 iOS 的浏览器录制的音频格式与桌面端浏览器不一样(参考 MediaRecorder: isTypeSupported() static method),需要在代码中先判断平台,再根据平台决定录音格式,其中桌面 Chrome 格式为 webm/opus,iOS Safari 为 mp4/aac,然后要将对应格式传回组件返回值
const isIOS = ref(false);
function checkPlatform() {
if (navigator.userAgentData) {
console.log(navigator.userAgentData)
// Use userAgentData for modern browsers
navigator.userAgentData.getHighEntropyValues(["platform"])
.then(ua => {
if (/iPhone|iPad|iPod/.test(ua.platform)) {
isIOS.value = true;
}
});
} else {
// Fallback for browsers that do not support userAgentData
const userAgent = navigator.userAgent;
console.log(navigator.userAgent)
if (/iPhone|iPad|iPod/.test(userAgent)) {
isIOS.value = true;
}
}
}
// 在录音时根据平台选择格式
function stopRecording(event) {
event.preventDefault();
if (mediaRecorder.value && mediaRecorder.value.state !== 'inactive') {
mediaRecorder.value.stop();
mediaRecorder.value.onstop = async function() {
let format = 'webm';
if (isIOS.value) {
format = 'mp4'
}
const audioBlob = new Blob(audioChunks.value, { type: 'audio/' + format });
const reader = new FileReader();
reader.onload = function(event) {
if (event.target && event.target.result) {
const dataUrl = event.target.result;
const base64 = dataUrl.split(',')[1]; // Extract the base64 part
Streamlit.setComponentValue({data: base64, format: format}); // 将数据与格式回传
} else {
console.log('Failed to read Blob as base64');
}
};
reader.readAsDataURL(audioBlob);
};
}
}
onMounted(() => {
// 先判断平台
checkPlatform();
});
python 代码也要做对应修改
def record_button(size, key=None):
component_value = _component_func(size=size, key=key, default=None)
if component_value:
component_value['data'] = base64.b64decode(component_value['data'])
return component_value
audio = record_button(size='20px')
if audio:
audio_data, _format = audio['data'], audio['format']
print(f'len: {len(audio_data)}')
st.audio(audio_data)
buffer = io.BytesIO(audio_data)
try:
if _format == 'mp4':
audio = AudioSegment.from_file(buffer, codec="aac")
else:
audio = AudioSegment.from_file(buffer, codec="opus")
audio = audio.set_frame_rate(16000).export(format="mp3").read()
query = flash_recognition(audio)
st.markdown(f'you said: {query}')
reply = completion(query)
st.markdown(f'reply: {reply}')
except Exception as e:
st.error(e)
调试
手机的页面没有 F12 开发者工具不方便调试页面,好在 Apple 的 Mac 上的 Safari 可以打开手机 Safari 页面的调试工具
- 手机打开设置 -> Safari -> 高级 -> 网页检查器
- 打开 Mac Safari -> Settings -> Advanced -> Show features for web developers
具体步骤参考 Develop menu