我想制作像Shazam这样的音频识别移动应用程序
我想通过Socket.IO每秒或每个样本将记录数据发送到基于机器学习的识别服务器(也许每秒发送数据采样率次数太多),然后移动应用程序接收并显示预测结果。
如何在录制时从
recordingInstance
获取数据?我读了世博音频文档,但我不知道该怎么做。
我运行了两个示例:
现在我想混合两个例子。感谢您的阅读。如果我可以
console.log
记录数据,那就会有很大帮助了。
https://forums.expo.io/t/measure-loudness-of-the-audio-in-realtime/18259
这可能是不可能的(播放动画?实时获取数据?)
https://forums.expo.io/t/how-to-get-the-volume-while-recording-an-audio/44100
没有答案
https://forums.expo.io/t/stream-microphone-recording/4314
根据这个问题,
https://www.npmjs.com/package/react-native-recording
似乎是一个解决方案,但它需要弹出。
我想我找到了解决这个问题的好方法。
await recordingInstance.prepareToRecordAsync(recordingOptions);
recordingInstance.setOnRecordingStatusUpdate(checkStatus);
recordingInstance.setProgressUpdateInterval(10000);
await recordingInstance.startAsync();
setRecording(recordingInstance);
在上面创建并准备录制之后,我添加了一个每 10 秒运行一次的回调函数。
const duration = status.durationMillis / 1000;
const info = await FileSystem.getInfoAsync(recording.getURI());
const uri = info.uri;
console.log(`Recording Status: ${status.isRecording}, Duration: ${duration}, Meterring: ${status.metering}, Uri: ${uri}`)
if(duration >10 && duration - prevDuration > 0){
sendBlob(uri);
}
setPrevDuration(duration);
回调函数检查持续时间是否大于10秒且与上次持续时间的差值大于0,然后通过WebSocket发送数据。
目前唯一的问题,第一次不运行回调,但第二次运行。
App.js:
import React, {useEffect, useState} from 'react';
import { StyleSheet, Text, View, Button } from 'react-native';
import { Audio } from 'expo-av';
import * as FileSystem from 'expo-file-system';
import {socket, uploadChunksToServer} from './socket';
export default function App() {
const [recording, setRecording] = React.useState();
const [recordings, setRecordings] = React.useState([]);
const [sending, setSending] = useState(false);
const [recordingBackLog, setRecordingBackLog] = useState([]);
const [isRecording, setIsRecording] = React.useState(false);
const [prevLen, setPrevLen] = useState(0);
const [isConnected, setIsConnected] = useState(false);
const [transport, setTransport] = useState('N/A');
let sum = 0;
let p_len = 0;
useEffect(() => {
if (socket.connected) {
onConnect();
}
function onConnect() {
setIsConnected(true);
setTransport(socket.io.engine.transport.name);
socket.io.engine.on('upgrade', (transport) => {
setTransport(transport.name);
});
}
function onDisconnect() {
setIsConnected(false);
setTransport('N/A');
}
socket.on('connect', onConnect);
socket.on('disconnect', onDisconnect);
return () => {
socket.off('connect', onConnect);
socket.off('disconnect', onDisconnect);
};
}, []);
const convertMP4ToBase64 = async (uri, delay = 0) => {
try {
// Read the file
const fileContent = await FileSystem.readAsStringAsync(uri, {
encoding: FileSystem.EncodingType.Base64,
position: 0,
length: 100
});
// Introduce optional delay (if delay is a positive number)
if (delay > 0) {
await new Promise((resolve) => setTimeout(resolve, delay));
}
return fileContent;
} catch (error) {
console.error('Error converting MP4 to base64:', error);
return null;
}
};
async function startRecording() {
try {
const perm = await Audio.requestPermissionsAsync();
if (perm.status === "granted") {
await Audio.setAudioModeAsync({
allowsRecordingIOS: true,
playsInSilentModeIOS: true
});
const recordingInstance = new Audio.Recording();
await recordingInstance.prepareToRecordAsync({
android:{
extension: '.wav',
linearPCMIsBigEndian:false,
},
ios:{
extension: '.wav',
linearPCMIsBigEndian:false,
},
});
await recordingInstance.startAsync();
setRecording(recordingInstance);
setRecordingBackLog(prevBackLog => [...prevBackLog, recordingInstance]);
await uploadChunksToServer(recordingInstance, 96000, 950);
recordingInstance.setOnRecordingStatusUpdate(async (status) => {
});
}
} catch (err) {
console.error('Failed to start recording', err);
}
}
async function stopRecording() {
setPrevLen(0);
try {
if (!recording) return;
await recording.stopAndUnloadAsync();
let allRecordings = [...recordings];
const { sound, status } = await recording.createNewLoadedSoundAsync();
allRecordings.push({
sound: sound,
duration: getDurationFormatted(status.durationMillis),
file: recording.getURI()
});
setRecordings(allRecordings);
} catch (err) {
console.error('Failed to stop recording', err);
} finally {
setRecording(undefined);
}
}
function getDurationFormatted(milliseconds) {
const minutes = milliseconds / 1000 / 60;
const seconds = Math.round((minutes - Math.floor(minutes)) * 60);
return seconds < 10 ? `${Math.floor(minutes)}:0${seconds}` : `${Math.floor(minutes)}:${seconds}`;
}
function toggleRecording() {
setIsRecording(prevState => !prevState);
}
function getRecordingLines() {
return recordings.map((recordingLine, index) => {
return (
<View key={index} style={styles.row}>
<Text style={styles.fill}>
Recording #{index + 1} | {recordingLine.duration}
</Text>
<Button onPress={() => recordingLine.sound.replayAsync()} title="Play"></Button>
<Button title="Send Recording to Backend"/>
</View>
);
});
}
function clearRecordings() {
setRecordings([]);
}
return (
<View style={styles.container}>
<Button title={recording ? 'Stop Recording' : 'Start Recording\n\n\n'} onPress={recording ? stopRecording : startRecording} />
{getRecordingLines()}
<Button title={recordings.length > 0 ? 'Clear Recordings' : ''} onPress={clearRecordings} />
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
row: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
marginLeft: 10,
marginRight: 40
},
fill: {
flex: 1,
margin: 15
}
});
socket.js
import { io } from 'socket.io-client';
import * as FileSystem from "expo-file-system";
export const socket = io('URL'); // use the IP address of your machine
const sleep = (ms) => {
return new Promise((resolve) => setTimeout(resolve, ms));
};
export async function uploadChunksToServer(recordingInstance, chunkSize, delayBetweenChunks) {
console.log('calling sending');
await sleep(4000);
let info = await FileSystem.getInfoAsync(recordingInstance.getURI());
let uri = info.uri;
let currentPosition = 0;
let current_file_size = info.size;
let prev_pos = 0;
do{
try{
let info = await FileSystem.getInfoAsync(recordingInstance.getURI());
current_file_size = info.size;
if (currentPosition + chunkSize >= current_file_size && currentPosition === prev_pos && prev_pos !== 0){
console.log('blocked')
continue;
}
else{
console.log(currentPosition, current_file_size);
const fileChunk = await FileSystem.readAsStringAsync(uri, {
encoding: FileSystem.EncodingType.Base64,
position: currentPosition,
length: chunkSize
})
currentPosition += chunkSize;
socket.emit('audioData', fileChunk);
}
prev_pos = currentPosition;
}
catch (e) {
console.log(e);
}
if (recordingInstance._isDoneRecording && current_file_size - currentPosition < chunkSize){
const fileChunk = await FileSystem.readAsStringAsync(uri, {
encoding: FileSystem.EncodingType.Base64,
position: currentPosition,
length: current_file_size - currentPosition
})
currentPosition += current_file_size - currentPosition;
socket.emit('audioData', fileChunk);
break
}
await sleep(delayBetweenChunks);
}
while(currentPosition < current_file_size)
console.log("final report >> ", currentPosition, current_file_size)
console.log('exiting')
const fileChunk = await FileSystem.readAsStringAsync(uri, {
encoding: FileSystem.EncodingType.Base64,
})
socket.emit('data', fileChunk);
}